22324 lines
848 KiB
C++
22324 lines
848 KiB
C++
// Generated by the DN single header generator 2025-11-16 21:35:27
|
|
|
|
#define DN_BASE_INC_CPP
|
|
|
|
// DN: Single header generator inlined this file => #include "Base/dn_base.cpp"
|
|
#define DN_BASE_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
DN_API bool DN_MemEq(void const *lhs, DN_USize lhs_size, void const *rhs, DN_USize rhs_size)
|
|
{
|
|
bool result = lhs_size == rhs_size && DN_Memcmp(lhs, rhs, rhs_size) == 0;
|
|
return result;
|
|
}
|
|
|
|
#if !defined(DN_PLATFORM_ARM64) && !defined(DN_PLATFORM_EMSCRIPTEN)
|
|
#define DN_SUPPORTS_CPU_ID
|
|
#endif
|
|
|
|
#if defined(DN_SUPPORTS_CPU_ID) && (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_U64 DN_AtomicSetValue64(DN_U64 volatile *target, DN_U64 value)
|
|
{
|
|
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
|
__int64 result;
|
|
do {
|
|
result = *target;
|
|
} while (DN_AtomicCompareExchange64(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_API DN_U32 DN_AtomicSetValue32(DN_U32 volatile *target, DN_U32 value)
|
|
{
|
|
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
|
long result;
|
|
do {
|
|
result = *target;
|
|
} while (DN_AtomicCompareExchange32(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
|
|
}
|
|
|
|
DN_API DN_CPUIDResult DN_CPUID(DN_CPUIDArgs args)
|
|
{
|
|
DN_CPUIDResult result = {};
|
|
#if defined(DN_SUPPORTS_CPU_ID)
|
|
__cpuidex(result.values, args.eax, args.ecx);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_CPUHasFeatureArray(DN_CPUReport const *report, DN_CPUFeatureQuery *features, DN_USize features_size)
|
|
{
|
|
DN_USize result = 0;
|
|
DN_USize const BITS = sizeof(report->features[0]) * 8;
|
|
for (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_CPUHasFeature(DN_CPUReport const *report, DN_CPUFeature feature)
|
|
{
|
|
DN_CPUFeatureQuery query = {};
|
|
query.feature = feature;
|
|
bool result = DN_CPUHasFeatureArray(report, &query, 1) == 1;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_CPUHasAllFeatures(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_CPUHasFeature(report, features[index]);
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_CPUSetFeature(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_CPUGetReport()
|
|
{
|
|
DN_CPUReport result = {};
|
|
#if defined(DN_SUPPORTS_CPU_ID)
|
|
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_CPUID(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_CPUID(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_CPUID(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_CPUID(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_CPUSetFeature(&result, DN_Cast(DN_CPUFeature) ext_index);
|
|
}
|
|
#endif // DN_SUPPORTS_CPU_ID
|
|
return result;
|
|
}
|
|
|
|
// NOTE: DN_TicketMutex ////////////////////////////////////////////////////////////////////////////
|
|
DN_API void DN_TicketMutex_Begin(DN_TicketMutex *mutex)
|
|
{
|
|
unsigned int ticket = DN_AtomicAddU32(&mutex->ticket, 1);
|
|
DN_TicketMutex_BeginTicket(mutex, ticket);
|
|
}
|
|
|
|
DN_API void DN_TicketMutex_End(DN_TicketMutex *mutex)
|
|
{
|
|
DN_AtomicAddU32(&mutex->serving, 1);
|
|
}
|
|
|
|
DN_API DN_UInt DN_TicketMutex_MakeTicket(DN_TicketMutex *mutex)
|
|
{
|
|
DN_UInt result = DN_AtomicAddU32(&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_BitUnsetInplace(DN_USize *flags, DN_USize bitfield)
|
|
{
|
|
*flags = (*flags & ~bitfield);
|
|
}
|
|
|
|
DN_API void DN_BitSetInplace(DN_USize *flags, DN_USize bitfield)
|
|
{
|
|
*flags = (*flags | bitfield);
|
|
}
|
|
|
|
DN_API bool DN_BitIsSet(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_BitIsNotSet(DN_USize bits, DN_USize bits_to_check)
|
|
{
|
|
auto result = !DN_BitIsSet(bits, bits_to_check);
|
|
return result;
|
|
}
|
|
|
|
// NOTE: DN_Safe ///////////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_I64 DN_SafeAddI64(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_SafeMulI64(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_SafeAddU64(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_SafeSubU64(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_SafeMulU64(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_SafeSubU32(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_ASanPoisonMemoryRegion(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_ASanUnpoisonMemoryRegion(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
|
|
}
|
|
|
|
DN_API DN_F32 DN_EpsilonClampF32(DN_F32 value, DN_F32 target, DN_F32 epsilon)
|
|
{
|
|
DN_F32 delta = DN_Abs(target - value);
|
|
DN_F32 result = (delta < epsilon) ? target : value;
|
|
return result;
|
|
}
|
|
|
|
static DN_ArenaBlock *DN_ArenaBlockFromMemFuncs_(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_LeakTrackAlloc(&g_dn_->leak, result, result->reserve, alloc_can_leak);
|
|
|
|
return result;
|
|
}
|
|
|
|
static DN_ArenaBlock *DN_ArenaBlockFlagsFromMemFuncs_(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_ArenaBlockFromMemFuncs_(reserve, commit, track_alloc, alloc_can_leak, mem_funcs);
|
|
if (result && ((flags & DN_ArenaFlags_NoPoison) == 0))
|
|
DN_ASanPoisonMemoryRegion(DN_Cast(char *) result + DN_ARENA_HEADER_SIZE, result->commit - DN_ARENA_HEADER_SIZE);
|
|
return result;
|
|
}
|
|
|
|
static void DN_ArenaUpdateStatsOnNewBlock_(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_ArenaFromBuffer(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_ASanPoisonMemoryRegion(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_ArenaUpdateStatsOnNewBlock_(&result, result.curr);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Arena DN_ArenaFromMemFuncs(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_ArenaBlockFlagsFromMemFuncs_(reserve, commit, flags, mem_funcs);
|
|
DN_ArenaUpdateStatsOnNewBlock_(&result, result.curr);
|
|
return result;
|
|
}
|
|
|
|
static void DN_ArenaBlockDeinit_(DN_Arena const *arena, DN_ArenaBlock *block)
|
|
{
|
|
DN_USize release_size = block->reserve;
|
|
if (DN_BitIsNotSet(arena->flags, DN_ArenaFlags_NoAllocTrack))
|
|
DN_LeakTrackDealloc(&g_dn_->leak, block);
|
|
DN_ASanUnpoisonMemoryRegion(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_ArenaDeinit(DN_Arena *arena)
|
|
{
|
|
for (DN_ArenaBlock *block = arena ? arena->curr : nullptr; block;) {
|
|
DN_ArenaBlock *block_to_free = block;
|
|
block = block->prev;
|
|
DN_ArenaBlockDeinit_(arena, block_to_free);
|
|
}
|
|
if (arena)
|
|
*arena = {};
|
|
}
|
|
|
|
DN_API bool DN_ArenaCommitTo(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_ASanPoisonMemoryRegion(commit_ptr, commit_size);
|
|
|
|
curr->commit = end_commit;
|
|
return true;
|
|
}
|
|
|
|
DN_API bool DN_ArenaCommit(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_ArenaCommitTo(arena, pos);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_ArenaGrow(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_ArenaBlockFlagsFromMemFuncs_(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_ArenaUpdateStatsOnNewBlock_(arena, arena->curr);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API void *DN_ArenaAlloc(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem)
|
|
{
|
|
if (!arena)
|
|
return nullptr;
|
|
|
|
if (!arena->curr) {
|
|
arena->curr = DN_ArenaBlockFlagsFromMemFuncs_(DN_ARENA_RESERVE_SIZE, DN_ARENA_COMMIT_SIZE, arena->flags, arena->mem_funcs);
|
|
DN_ArenaUpdateStatsOnNewBlock_(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_ArenaGrow(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_ASanPoisonMemoryRegion(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_ASanUnpoisonMemoryRegion(result, size);
|
|
|
|
if (z_mem == DN_ZMem_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_ArenaAllocContiguous(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem)
|
|
{
|
|
DN_ArenaFlags prev_flags = arena->flags;
|
|
arena->flags |= (DN_ArenaFlags_NoGrow | DN_ArenaFlags_NoPoison);
|
|
void *memory = DN_ArenaAlloc(arena, size, align, z_mem);
|
|
arena->flags = prev_flags;
|
|
return memory;
|
|
}
|
|
|
|
DN_API void *DN_ArenaCopy(DN_Arena *arena, void const *data, DN_U64 size, uint8_t align)
|
|
{
|
|
if (!arena || !data || size == 0)
|
|
return nullptr;
|
|
void *result = DN_ArenaAlloc(arena, size, align, DN_ZMem_No);
|
|
if (result)
|
|
DN_Memcpy(result, data, size);
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_ArenaPopTo(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_ArenaBlockDeinit_(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_ASanPoisonMemoryRegion(poison_ptr, poison_size);
|
|
arena->stats.info.used += curr->used;
|
|
}
|
|
|
|
DN_API void DN_ArenaPop(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_ArenaPopTo(arena, pop_to);
|
|
}
|
|
|
|
DN_API DN_U64 DN_ArenaPos(DN_Arena const *arena)
|
|
{
|
|
DN_U64 result = (arena && arena->curr) ? arena->curr->reserve_sum + arena->curr->used : 0;
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_ArenaClear(DN_Arena *arena)
|
|
{
|
|
DN_ArenaPopTo(arena, 0);
|
|
}
|
|
|
|
DN_API bool DN_ArenaOwnsPtr(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_Str8x64 DN_ArenaInfoStr8x64(DN_ArenaInfo info)
|
|
{
|
|
DN_Str8x64 result = {};
|
|
DN_Str8x32 used = DN_ByteCountStr8x32(info.used);
|
|
DN_Str8x32 commit = DN_ByteCountStr8x32(info.commit);
|
|
DN_Str8x32 reserve = DN_ByteCountStr8x32(info.reserve);
|
|
result = DN_Str8x64FromFmt("Blks/Used/Comm/Resv (%u/%.*s/%.*s/%.*s)", DN_Cast(DN_U32)info.blocks, DN_Str8PrintFmt(used), DN_Str8PrintFmt(commit), DN_Str8PrintFmt(reserve));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_ArenaStats DN_ArenaSumStatsArray(DN_ArenaStats const *array, DN_USize size)
|
|
{
|
|
DN_ArenaStats result = {};
|
|
for (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_ArenaSumStats(DN_ArenaStats lhs, DN_ArenaStats rhs)
|
|
{
|
|
DN_ArenaStats array[] = {lhs, rhs};
|
|
DN_ArenaStats result = DN_ArenaSumStatsArray(array, DN_ArrayCountU(array));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_ArenaStats DN_ArenaSumArenaArrayToStats(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_ArenaSumStats(result, arena->stats);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_ArenaTempMem DN_ArenaTempMemBegin(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_ArenaTempMemEnd(DN_ArenaTempMem mem)
|
|
{
|
|
DN_ArenaPopTo(mem.arena, mem.used_sum);
|
|
};
|
|
|
|
DN_ArenaTempMemScope::DN_ArenaTempMemScope(DN_Arena *arena)
|
|
{
|
|
mem = DN_ArenaTempMemBegin(arena);
|
|
}
|
|
|
|
DN_ArenaTempMemScope::~DN_ArenaTempMemScope()
|
|
{
|
|
DN_ArenaTempMemEnd(mem);
|
|
}
|
|
|
|
// NOTE: DN_Pool ///////////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_Pool DN_PoolFromArena(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_PoolIsValid(DN_Pool const *pool)
|
|
{
|
|
bool result = pool && pool->arena && pool->align;
|
|
return result;
|
|
}
|
|
|
|
DN_API void *DN_PoolAlloc(DN_Pool *pool, DN_USize size)
|
|
{
|
|
void *result = nullptr;
|
|
if (!DN_PoolIsValid(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_CountLeadingZerosUSize(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_AssertF(register_size >= (dist_to_next_msb - size_to_slot_offset), "lhs=%zu, rhs=%zu", 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_AssertF(required_size <= (slot_size_in_bytes << 0), "slot_index=%zu, lhs=%zu, rhs=%zu", slot_index, required_size, (slot_size_in_bytes << 0));
|
|
DN_AssertF(required_size >= (slot_size_in_bytes >> 1), "slot_index=%zu, lhs=%zu, rhs=%zu", slot_index, 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_ArenaAlloc(pool->arena, slot_size_in_bytes, alignof(DN_PoolSlot), DN_ZMem_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 void DN_PoolDealloc(DN_Pool *pool, void *ptr)
|
|
{
|
|
if (!DN_PoolIsValid(pool) || !ptr)
|
|
return;
|
|
|
|
DN_Assert(DN_ArenaOwnsPtr(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_PoolCopy(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_PoolAlloc(pool, size);
|
|
if (result)
|
|
DN_Memcpy(result, data, size);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_CharIsAlphabet(char ch)
|
|
{
|
|
bool result = (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_CharIsDigit(char ch)
|
|
{
|
|
bool result = (ch >= '0' && ch <= '9');
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_CharIsAlphaNum(char ch)
|
|
{
|
|
bool result = DN_CharIsAlphabet(ch) || DN_CharIsDigit(ch);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_CharIsWhitespace(char ch)
|
|
{
|
|
bool result = (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_CharIsHex(char ch)
|
|
{
|
|
bool result = ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') || (ch >= '0' && ch <= '9'));
|
|
return result;
|
|
}
|
|
|
|
DN_API char DN_CharToLower(char ch)
|
|
{
|
|
char result = ch;
|
|
if (result >= 'A' && result <= 'Z')
|
|
result += 'a' - 'A';
|
|
return result;
|
|
}
|
|
|
|
DN_API char DN_CharToUpper(char ch)
|
|
{
|
|
char result = ch;
|
|
if (result >= 'a' && result <= 'z')
|
|
result -= 'a' - 'A';
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64FromResult DN_U64FromStr8(DN_Str8 string, char separator)
|
|
{
|
|
// NOTE: Argument check
|
|
DN_U64FromResult result = {};
|
|
if (string.size == 0) {
|
|
result.success = true;
|
|
return result;
|
|
}
|
|
|
|
// NOTE: Sanitize input/output
|
|
DN_Str8 trim_string = DN_Str8TrimWhitespaceAround(string);
|
|
if (trim_string.size == 0) {
|
|
result.success = true;
|
|
return result;
|
|
}
|
|
|
|
// NOTE: Handle prefix '+'
|
|
DN_USize start_index = 0;
|
|
if (!DN_CharIsDigit(trim_string.data[0])) {
|
|
if (trim_string.data[0] != '+')
|
|
return result;
|
|
start_index++;
|
|
}
|
|
|
|
// NOTE: Convert the string number to the binary number
|
|
for (DN_USize index = start_index; index < trim_string.size; index++) {
|
|
char ch = trim_string.data[index];
|
|
if (index) {
|
|
if (separator != 0 && ch == separator)
|
|
continue;
|
|
}
|
|
|
|
if (!DN_CharIsDigit(ch))
|
|
return result;
|
|
|
|
result.value = DN_SafeMulU64(result.value, 10);
|
|
uint64_t digit = ch - '0';
|
|
result.value = DN_SafeAddU64(result.value, digit);
|
|
}
|
|
|
|
result.success = true;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64FromResult DN_U64FromPtr(void const *data, DN_USize size, char separator)
|
|
{
|
|
DN_Str8 str8 = DN_Str8FromPtr((char *)data, size);
|
|
DN_U64FromResult result = DN_U64FromStr8(str8, separator);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64 DN_U64FromPtrUnsafe(void const *data, DN_USize size, char separator)
|
|
{
|
|
DN_U64FromResult from = DN_U64FromPtr(data, size, separator);
|
|
DN_U64 result = from.value;
|
|
DN_Assert(from.success);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64FromResult DN_U64FromHexPtr(void const *hex, DN_USize hex_count)
|
|
{
|
|
char *hex_ptr = DN_Cast(char *) hex;
|
|
if (hex_count >= 2 && hex_ptr[0] == '0' && (hex_ptr[1] == 'x' || hex_ptr[1] == 'X')) {
|
|
hex_ptr += 2;
|
|
hex_count -= 2;
|
|
}
|
|
|
|
DN_U64FromResult result = {};
|
|
DN_USize max_hex_count = sizeof(DN_U64) * 2;
|
|
DN_USize count = DN_Min(max_hex_count, hex_count);
|
|
DN_Assert(hex_count <= max_hex_count);
|
|
for (DN_USize index = 0; index < count; index++) {
|
|
char ch = hex_ptr[index];
|
|
DN_U8 val = DN_U8FromHexNibble(ch);
|
|
if (val == 0xFF)
|
|
return result;
|
|
result.value = (result.value << 4) | val;
|
|
}
|
|
result.success = true;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64 DN_U64FromHexPtrUnsafe(void const *hex, DN_USize hex_count)
|
|
{
|
|
DN_U64FromResult from = DN_U64FromHexPtr(hex, hex_count);
|
|
DN_U64 result = from.value;
|
|
DN_Assert(from.success);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64FromResult DN_U64FromHexStr8(DN_Str8 hex)
|
|
{
|
|
DN_U64FromResult result = DN_U64FromHexPtr(hex.data, hex.size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64 DN_U64FromHexStr8Unsafe(DN_Str8 hex)
|
|
{
|
|
DN_U64 result = DN_U64FromHexPtrUnsafe(hex.data, hex.size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_I64FromResult DN_I64FromStr8(DN_Str8 string, char separator)
|
|
{
|
|
// NOTE: Argument check
|
|
DN_I64FromResult result = {};
|
|
if (string.size == 0) {
|
|
result.success = true;
|
|
return result;
|
|
}
|
|
|
|
// NOTE: Sanitize input/output
|
|
DN_Str8 trim_string = DN_Str8TrimWhitespaceAround(string);
|
|
if (trim_string.size == 0) {
|
|
result.success = true;
|
|
return result;
|
|
}
|
|
|
|
bool negative = false;
|
|
DN_USize start_index = 0;
|
|
if (!DN_CharIsDigit(trim_string.data[0])) {
|
|
negative = (trim_string.data[start_index] == '-');
|
|
if (!negative && trim_string.data[0] != '+')
|
|
return result;
|
|
start_index++;
|
|
}
|
|
|
|
// NOTE: Convert the string number to the binary number
|
|
for (DN_USize index = start_index; index < trim_string.size; index++) {
|
|
char ch = trim_string.data[index];
|
|
if (index) {
|
|
if (separator != 0 && ch == separator)
|
|
continue;
|
|
}
|
|
|
|
if (!DN_CharIsDigit(ch))
|
|
return result;
|
|
|
|
result.value = DN_SafeMulU64(result.value, 10);
|
|
uint64_t digit = ch - '0';
|
|
result.value = DN_SafeAddU64(result.value, digit);
|
|
}
|
|
|
|
if (negative)
|
|
result.value *= -1;
|
|
|
|
result.success = true;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_I64FromResult DN_I64FromPtr(void const *data, DN_USize size, char separator)
|
|
{
|
|
DN_Str8 str8 = DN_Str8FromPtr((char *)data, size);
|
|
DN_I64FromResult result = DN_I64FromStr8(str8, separator);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_I64 DN_I64FromPtrUnsafe(void const *data, DN_USize size, char separator)
|
|
{
|
|
DN_I64FromResult from = DN_I64FromPtr(data, size, separator);
|
|
DN_I64 result = from.value;
|
|
DN_Assert(from.success);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_FmtAppendResult DN_FmtVAppend(char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, va_list args)
|
|
{
|
|
DN_FmtAppendResult result = {};
|
|
DN_USize starting_size = *buf_size;
|
|
result.size_req = DN_VSNPrintF(buf + *buf_size, DN_Cast(int)(buf_max - *buf_size), fmt, args);
|
|
*buf_size += result.size_req;
|
|
if (*buf_size >= (buf_max - 1))
|
|
*buf_size = buf_max - 1;
|
|
DN_Assert(*buf_size <= (buf_max - 1));
|
|
result.str8 = DN_Str8FromPtr(buf, *buf_size);
|
|
result.truncated = result.str8.size != (starting_size + result.size_req);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_FmtAppendResult DN_FmtAppend(char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_FmtAppendResult result = DN_FmtVAppend(buf, buf_size, buf_max - (*buf_size), fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_FmtAppendResult DN_FmtAppendTruncate(char *buf, DN_USize *buf_size, DN_USize buf_max, DN_Str8 truncator, char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_FmtAppendResult result = DN_FmtVAppend(buf, buf_size, buf_max, fmt, args);
|
|
if (result.truncated)
|
|
DN_Memcpy(result.str8.data + result.str8.size - truncator.size, truncator.data, truncator.size);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_FmtSize(DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_USize result = DN_VSNPrintF(nullptr, 0, fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_FmtVSize(DN_FMT_ATTRIB char const *fmt, va_list args)
|
|
{
|
|
va_list args_copy;
|
|
va_copy(args_copy, args);
|
|
DN_USize result = DN_VSNPrintF(nullptr, 0, fmt, args_copy);
|
|
va_end(args_copy);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_CStr8Size(char const *src)
|
|
{
|
|
DN_USize result = 0;
|
|
for (; src && src[0] != 0; src++, result++)
|
|
;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_CStr16Size(wchar_t const *src)
|
|
{
|
|
DN_USize result = 0;
|
|
for (; src && src[0] != 0; src++, result++)
|
|
;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str16Eq(DN_Str16 lhs, DN_Str16 rhs)
|
|
{
|
|
if (lhs.size != rhs.size)
|
|
return false;
|
|
bool result = (DN_Memcmp(lhs.data, rhs.data, lhs.size) == 0);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromCStr8(char const *src)
|
|
{
|
|
DN_USize size = DN_CStr8Size(src);
|
|
DN_Str8 result = DN_Str8FromPtr(src, size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromArena(DN_Arena *arena, DN_USize size, DN_ZMem z_mem)
|
|
{
|
|
DN_Str8 result = {};
|
|
result.data = DN_ArenaNewArray(arena, char, size + 1, z_mem);
|
|
if (result.data)
|
|
result.size = size;
|
|
result.data[result.size] = 0;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromPool(DN_Pool *pool, DN_USize size)
|
|
{
|
|
DN_Str8 result = {};
|
|
result.data = DN_PoolNewArray(pool, char, size + 1);
|
|
if (result.data)
|
|
result.size = size;
|
|
result.data[result.size] = 0;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromPtrArena(DN_Arena *arena, void const *data, DN_USize size)
|
|
{
|
|
DN_Str8 result = DN_Str8FromArena(arena, size, DN_ZMem_No);
|
|
if (result.size)
|
|
DN_Memcpy(result.data, data, size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromPtrPool(DN_Pool *pool, void const *data, DN_USize size)
|
|
{
|
|
DN_Str8 result = DN_Str8FromPool(pool, size);
|
|
if (result.size)
|
|
DN_Memcpy(result.data, data, size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromStr8Arena(DN_Arena *arena, DN_Str8 string)
|
|
{
|
|
DN_Str8 result = {};
|
|
result.data = DN_Cast(char *) DN_ArenaAlloc(arena, string.size + 1, alignof(char), DN_ZMem_No);
|
|
if (result.data) {
|
|
DN_Memcpy(result.data, string.data, string.size);
|
|
result.data[string.size] = 0;
|
|
result.size = string.size;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromStr8Pool(DN_Pool *pool, DN_Str8 string)
|
|
{
|
|
DN_Str8 result = {};
|
|
result.data = DN_Cast(char *) DN_PoolAlloc(pool, string.size + 1);
|
|
if (result.data) {
|
|
DN_Memcpy(result.data, string.data, string.size);
|
|
result.data[string.size] = 0;
|
|
result.size = string.size;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromFmtArena(DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, fmt);
|
|
DN_Str8 result = DN_Str8FromFmtVArena(arena, fmt, va);
|
|
va_end(va);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromFmtVArena(DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args)
|
|
{
|
|
DN_USize size = DN_FmtVSize(fmt, args);
|
|
DN_Str8 result = DN_Str8FromArena(arena, size, DN_ZMem_No);
|
|
if (result.data) {
|
|
DN_USize written = 0;
|
|
DN_FmtVAppend(result.data, &written, result.size + 1, fmt, args);
|
|
DN_Assert(written == result.size);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromFmtPool(DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_USize size = DN_FmtVSize(fmt, args);
|
|
DN_Str8 result = DN_Str8FromPool(pool, size);
|
|
if (result.data) {
|
|
DN_USize written = 0;
|
|
DN_FmtVAppend(result.data, &written, result.size + 1, fmt, args);
|
|
DN_Assert(written == result.size);
|
|
}
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x32 DN_Str8x32FromFmt(DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Str8x32 result = {};
|
|
DN_FmtVAppend(result.data, &result.size, sizeof(result.data), fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x32 DN_Str8x32FromFmtV(DN_FMT_ATTRIB char const *fmt, va_list args)
|
|
{
|
|
DN_Str8x32 result = {};
|
|
DN_FmtVAppend(result.data, &result.size, sizeof(result.data), fmt, args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x64 DN_Str8x64FromFmt(DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Str8x64 result = {};
|
|
DN_FmtVAppend(result.data, &result.size, sizeof(result.data), fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x64 DN_Str8x64FromFmtV(DN_FMT_ATTRIB char const *fmt, va_list args)
|
|
{
|
|
DN_Str8x64 result = {};
|
|
DN_FmtVAppend(result.data, &result.size, sizeof(result.data), fmt, args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x128 DN_Str8x128FromFmt(DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Str8x128 result = {};
|
|
DN_FmtVAppend(result.data, &result.size, sizeof(result.data), fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x128 DN_Str8x128FromFmtV(DN_FMT_ATTRIB char const *fmt, va_list args)
|
|
{
|
|
DN_Str8x128 result = {};
|
|
DN_FmtVAppend(result.data, &result.size, sizeof(result.data), fmt, args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x256 DN_Str8x256FromFmt(DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Str8x256 result = {};
|
|
DN_FmtVAppend(result.data, &result.size, sizeof(result.data), fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x256 DN_Str8x256FromFmtV(DN_FMT_ATTRIB char const *fmt, va_list args)
|
|
{
|
|
DN_Str8x256 result = {};
|
|
DN_FmtVAppend(result.data, &result.size, sizeof(result.data), fmt, args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x32 DN_Str8x32FromU64(DN_U64 val, char separator)
|
|
{
|
|
DN_Str8x32 result = {};
|
|
DN_Str8x32 temp = DN_Str8x32FromFmt("%" PRIu64, val);
|
|
DN_USize temp_index = 0;
|
|
|
|
// NOTE: Write the digits the first, up to [0, 2] digits that do not need a thousandth separator
|
|
DN_USize range_without_separator = temp.size % 3;
|
|
for (; temp_index < range_without_separator; temp_index++)
|
|
result.data[result.size++] = temp.data[temp_index];
|
|
|
|
// NOTE: Write the subsequent digits and every 3rd digit, add the seperator
|
|
DN_USize digit_counter = 0;
|
|
for (; temp_index < temp.size; temp_index++, digit_counter++) {
|
|
if (separator && temp_index && (digit_counter % 3 == 0))
|
|
result.data[result.size++] = separator;
|
|
result.data[result.size++] = temp.data[temp_index];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
DN_API bool DN_Str8IsAll(DN_Str8 string, DN_Str8IsAllType is_all)
|
|
{
|
|
bool result = string.size;
|
|
if (!result)
|
|
return result;
|
|
|
|
switch (is_all) {
|
|
case DN_Str8IsAllType_Digits: {
|
|
for (DN_USize index = 0; result && index < string.size; index++)
|
|
result = string.data[index] >= '0' && string.data[index] <= '9';
|
|
} break;
|
|
|
|
case DN_Str8IsAllType_Hex: {
|
|
DN_Str8 trimmed = DN_Str8TrimPrefix(string, DN_Str8Lit("0x"), DN_Str8EqCase_Insensitive);
|
|
for (DN_USize index = 0; result && index < trimmed.size; index++) {
|
|
char ch = trimmed.data[index];
|
|
result = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
|
|
}
|
|
} break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API char *DN_Str8End(DN_Str8 string)
|
|
{
|
|
char *result = string.data + string.size;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8Slice(DN_Str8 string, DN_USize offset, DN_USize size)
|
|
{
|
|
DN_Str8 result = DN_Str8FromPtr(string.data, 0);
|
|
if (string.size == 0)
|
|
return result;
|
|
|
|
DN_USize capped_offset = DN_Min(offset, string.size);
|
|
DN_USize max_size = string.size - capped_offset;
|
|
DN_USize capped_size = DN_Min(size, max_size);
|
|
result = DN_Str8FromPtr(string.data + capped_offset, capped_size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8Advance(DN_Str8 string, DN_USize amount)
|
|
{
|
|
DN_Str8 result = DN_Str8Slice(string, amount, DN_USIZE_MAX);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8NextLine(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = DN_Str8BSplit(string, DN_Str8Lit("\n")).rhs;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8BSplitResult DN_Str8BSplitArray(DN_Str8 string, DN_Str8 const *find, DN_USize find_size)
|
|
{
|
|
DN_Str8BSplitResult result = {};
|
|
if (string.size == 0 || !find || find_size == 0)
|
|
return result;
|
|
|
|
result.lhs = string;
|
|
for (size_t index = 0; !result.rhs.data && index < string.size; index++) {
|
|
for (DN_USize find_index = 0; find_index < find_size; find_index++) {
|
|
DN_Str8 find_item = find[find_index];
|
|
DN_Str8 string_slice = DN_Str8Slice(string, index, find_item.size);
|
|
if (DN_Str8Eq(string_slice, find_item)) {
|
|
result.lhs.size = index;
|
|
result.rhs.data = string_slice.data + find_item.size;
|
|
result.rhs.size = string.size - (index + find_item.size);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8BSplitResult DN_Str8BSplit(DN_Str8 string, DN_Str8 find)
|
|
{
|
|
DN_Str8BSplitResult result = DN_Str8BSplitArray(string, &find, 1);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8BSplitResult DN_Str8BSplitLastArray(DN_Str8 string, DN_Str8 const *find, DN_USize find_size)
|
|
{
|
|
DN_Str8BSplitResult result = {};
|
|
if (string.size == 0 || !find || find_size == 0)
|
|
return result;
|
|
|
|
result.lhs = string;
|
|
for (size_t index = string.size - 1; !result.rhs.data && index < string.size; index--) {
|
|
for (DN_USize find_index = 0; find_index < find_size; find_index++) {
|
|
DN_Str8 find_item = find[find_index];
|
|
DN_Str8 string_slice = DN_Str8Slice(string, index, find_item.size);
|
|
if (DN_Str8Eq(string_slice, find_item)) {
|
|
result.lhs.size = index;
|
|
result.rhs.data = string_slice.data + find_item.size;
|
|
result.rhs.size = string.size - (index + find_item.size);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8BSplitResult DN_Str8BSplitLast(DN_Str8 string, DN_Str8 find)
|
|
{
|
|
DN_Str8BSplitResult result = DN_Str8BSplitLastArray(string, &find, 1);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_Str8Split(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 (string.size == 0 || delimiter.size == 0 || delimiter.size <= 0)
|
|
return result;
|
|
|
|
DN_Str8BSplitResult split = {};
|
|
DN_Str8 first = string;
|
|
do {
|
|
split = DN_Str8BSplit(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_Str8SplitResult DN_Str8SplitArena(DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode)
|
|
{
|
|
DN_Str8SplitResult result = {};
|
|
DN_USize count = DN_Str8Split(string, delimiter, /*splits*/ nullptr, /*count*/ 0, mode);
|
|
result.data = DN_ArenaNewArray(arena, DN_Str8, count, DN_ZMem_No);
|
|
if (result.data) {
|
|
result.count = DN_Str8Split(string, delimiter, result.data, count, mode);
|
|
DN_Assert(count == result.count);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8FindResult DN_Str8FindStr8Array(DN_Str8 string, DN_Str8 const *find, DN_USize find_size, DN_Str8EqCase eq_case)
|
|
{
|
|
DN_Str8FindResult result = {};
|
|
for (DN_USize index = 0; !result.found && index < string.size; index++) {
|
|
for (DN_USize find_index = 0; find_index < find_size; find_index++) {
|
|
DN_Str8 find_item = find[find_index];
|
|
DN_Str8 string_slice = DN_Str8Slice(string, index, find_item.size);
|
|
if (DN_Str8Eq(string_slice, find_item, eq_case)) {
|
|
result.found = true;
|
|
result.index = index;
|
|
result.start_to_before_match = DN_Str8FromPtr(string.data, index);
|
|
result.match = DN_Str8FromPtr(string.data + index, find_item.size);
|
|
result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - index);
|
|
result.after_match_to_end_of_buffer = DN_Str8Advance(result.match_to_end_of_buffer, find_item.size);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8FindResult DN_Str8FindStr8(DN_Str8 string, DN_Str8 find, DN_Str8EqCase eq_case)
|
|
{
|
|
DN_Str8FindResult result = DN_Str8FindStr8Array(string, &find, 1, eq_case);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8FindResult DN_Str8Find(DN_Str8 string, uint32_t flags)
|
|
{
|
|
DN_Str8FindResult result = {};
|
|
for (size_t index = 0; !result.found && index < string.size; index++) {
|
|
result.found |= ((flags & DN_Str8FindFlag_Digit) && DN_CharIsDigit(string.data[index]));
|
|
result.found |= ((flags & DN_Str8FindFlag_Alphabet) && DN_CharIsAlphabet(string.data[index]));
|
|
result.found |= ((flags & DN_Str8FindFlag_Whitespace) && DN_CharIsWhitespace(string.data[index]));
|
|
result.found |= ((flags & DN_Str8FindFlag_Plus) && string.data[index] == '+');
|
|
result.found |= ((flags & DN_Str8FindFlag_Minus) && string.data[index] == '-');
|
|
if (result.found) {
|
|
result.index = index;
|
|
result.match = DN_Str8FromPtr(string.data + index, 1);
|
|
result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - index);
|
|
result.after_match_to_end_of_buffer = DN_Str8Advance(result.match_to_end_of_buffer, 1);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8Segment(DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char)
|
|
{
|
|
if (!segment_size || src.size == 0) {
|
|
DN_Str8 result = DN_Str8FromStr8Arena(arena, src);
|
|
return result;
|
|
}
|
|
|
|
DN_USize segments = src.size / segment_size;
|
|
if (src.size % segment_size == 0)
|
|
segments--;
|
|
|
|
DN_USize segment_counter = 0;
|
|
DN_Str8 result = DN_Str8FromArena(arena, src.size + segments, DN_ZMem_Yes);
|
|
DN_USize write_index = 0;
|
|
for (DN_ForIndexU(src_index, src.size)) {
|
|
result.data[write_index++] = src.data[src_index];
|
|
if ((src_index + 1) % segment_size == 0 && segment_counter < segments) {
|
|
result.data[write_index++] = segment_char;
|
|
segment_counter++;
|
|
}
|
|
DN_AssertF(write_index <= result.size, "result.size=%zu, write_index=%zu", result.size, write_index);
|
|
}
|
|
|
|
DN_AssertF(write_index == result.size, "result.size=%zu, write_index=%zu", result.size, write_index);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8ReverseSegment(DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char)
|
|
{
|
|
if (!segment_size || src.size == 0) {
|
|
DN_Str8 result = DN_Str8FromStr8Arena(arena, src);
|
|
return result;
|
|
}
|
|
|
|
DN_USize segments = src.size / segment_size;
|
|
if (src.size % segment_size == 0)
|
|
segments--;
|
|
|
|
DN_USize write_counter = 0;
|
|
DN_USize segment_counter = 0;
|
|
DN_Str8 result = DN_Str8FromArena(arena, src.size + segments, DN_ZMem_Yes);
|
|
DN_USize write_index = result.size - 1;
|
|
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(6293) // NOTE: Ill-defined loop
|
|
for (size_t src_index = src.size - 1; src_index < src.size; src_index--) {
|
|
DN_MSVC_WARNING_POP
|
|
result.data[write_index--] = src.data[src_index];
|
|
if (++write_counter % segment_size == 0 && segment_counter < segments) {
|
|
result.data[write_index--] = segment_char;
|
|
segment_counter++;
|
|
}
|
|
}
|
|
|
|
DN_Assert(write_index == SIZE_MAX);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8Eq(DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case)
|
|
{
|
|
if (lhs.size != rhs.size)
|
|
return false;
|
|
bool result = true;
|
|
switch (eq_case) {
|
|
case DN_Str8EqCase_Sensitive: {
|
|
result = (DN_Memcmp(lhs.data, rhs.data, lhs.size) == 0);
|
|
} break;
|
|
|
|
case DN_Str8EqCase_Insensitive: {
|
|
for (DN_USize index = 0; index < lhs.size && result; index++)
|
|
result = (DN_CharToLower(lhs.data[index]) == DN_CharToLower(rhs.data[index]));
|
|
} break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8EqInsensitive(DN_Str8 lhs, DN_Str8 rhs)
|
|
{
|
|
bool result = DN_Str8Eq(lhs, rhs, DN_Str8EqCase_Insensitive);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8StartsWith(DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case)
|
|
{
|
|
DN_Str8 substring = {string.data, DN_Min(prefix.size, string.size)};
|
|
bool result = DN_Str8Eq(substring, prefix, eq_case);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8StartsWithInsensitive(DN_Str8 string, DN_Str8 prefix)
|
|
{
|
|
bool result = DN_Str8StartsWith(string, prefix, DN_Str8EqCase_Insensitive);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8EndsWith(DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case)
|
|
{
|
|
DN_Str8 substring = {string.data + string.size - suffix.size, DN_Min(string.size, suffix.size)};
|
|
bool result = DN_Str8Eq(substring, suffix, eq_case);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8EndsWithInsensitive(DN_Str8 string, DN_Str8 suffix)
|
|
{
|
|
bool result = DN_Str8EndsWith(string, suffix, DN_Str8EqCase_Insensitive);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8HasChar(DN_Str8 string, char ch)
|
|
{
|
|
bool result = false;
|
|
for (DN_USize index = 0; !result && index < string.size; index++)
|
|
result = string.data[index] == ch;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8TrimPrefix(DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case)
|
|
{
|
|
DN_Str8 result = string;
|
|
if (DN_Str8StartsWith(string, prefix, eq_case)) {
|
|
result.data += prefix.size;
|
|
result.size -= prefix.size;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8TrimHexPrefix(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = DN_Str8TrimPrefix(string, DN_Str8Lit("0x"), DN_Str8EqCase_Insensitive);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8TrimSuffix(DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case)
|
|
{
|
|
DN_Str8 result = string;
|
|
if (DN_Str8EndsWith(string, suffix, eq_case))
|
|
result.size -= suffix.size;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8TrimAround(DN_Str8 string, DN_Str8 trim_string)
|
|
{
|
|
DN_Str8 result = DN_Str8TrimPrefix(string, trim_string);
|
|
result = DN_Str8TrimSuffix(result, trim_string);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8TrimHeadWhitespace(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = string;
|
|
if (string.size == 0)
|
|
return result;
|
|
|
|
char const *start = string.data;
|
|
char const *end = string.data + string.size;
|
|
while (start < end && DN_CharIsWhitespace(start[0]))
|
|
start++;
|
|
|
|
result = DN_Str8FromPtr(start, end - start);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8TrimTailWhitespace(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = string;
|
|
if (string.size == 0)
|
|
return result;
|
|
|
|
char const *start = string.data;
|
|
char const *end = string.data + string.size;
|
|
while (end > start && DN_CharIsWhitespace(end[-1]))
|
|
end--;
|
|
|
|
result = DN_Str8FromPtr(start, end - start);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8TrimWhitespaceAround(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = DN_Str8TrimHeadWhitespace(string);
|
|
result = DN_Str8TrimTailWhitespace(result);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8TrimByteOrderMark(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = string;
|
|
if (result.size == 0)
|
|
return result;
|
|
|
|
// TODO(dn): This is little endian
|
|
DN_Str8 UTF8_BOM = DN_Str8Lit("\xEF\xBB\xBF");
|
|
DN_Str8 UTF16_BOM_BE = DN_Str8Lit("\xEF\xFF");
|
|
DN_Str8 UTF16_BOM_LE = DN_Str8Lit("\xFF\xEF");
|
|
DN_Str8 UTF32_BOM_BE = DN_Str8Lit("\x00\x00\xFE\xFF");
|
|
DN_Str8 UTF32_BOM_LE = DN_Str8Lit("\xFF\xFE\x00\x00");
|
|
|
|
result = DN_Str8TrimPrefix(result, UTF8_BOM, DN_Str8EqCase_Sensitive);
|
|
result = DN_Str8TrimPrefix(result, UTF16_BOM_BE, DN_Str8EqCase_Sensitive);
|
|
result = DN_Str8TrimPrefix(result, UTF16_BOM_LE, DN_Str8EqCase_Sensitive);
|
|
result = DN_Str8TrimPrefix(result, UTF32_BOM_BE, DN_Str8EqCase_Sensitive);
|
|
result = DN_Str8TrimPrefix(result, UTF32_BOM_LE, DN_Str8EqCase_Sensitive);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FileNameFromPath(DN_Str8 path)
|
|
{
|
|
DN_Str8 separators[] = {DN_Str8Lit("/"), DN_Str8Lit("\\")};
|
|
DN_Str8BSplitResult split = DN_Str8BSplitLastArray(path, separators, DN_ArrayCountU(separators));
|
|
DN_Str8 result = split.rhs.size ? split.rhs : split.lhs;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FileNameNoExtension(DN_Str8 path)
|
|
{
|
|
DN_Str8 file_name = DN_Str8FileNameFromPath(path);
|
|
DN_Str8 result = DN_Str8FilePathNoExtension(file_name);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FilePathNoExtension(DN_Str8 path)
|
|
{
|
|
DN_Str8BSplitResult split = DN_Str8BSplitLast(path, DN_Str8Lit("."));
|
|
DN_Str8 result = split.lhs;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FileExtension(DN_Str8 path)
|
|
{
|
|
DN_Str8BSplitResult split = DN_Str8BSplitLast(path, DN_Str8Lit("."));
|
|
DN_Str8 result = split.rhs;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FileDirectoryFromPath(DN_Str8 path)
|
|
{
|
|
DN_Str8 separators[] = {DN_Str8Lit("/"), DN_Str8Lit("\\")};
|
|
DN_Str8BSplitResult split = DN_Str8BSplitLastArray(path, separators, DN_ArrayCountU(separators));
|
|
DN_Str8 result = split.lhs;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8AppendF(DN_Arena *arena, DN_Str8 string, char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Str8 result = DN_Str8AppendFV(arena, string, fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8AppendFV(DN_Arena *arena, DN_Str8 string, char const *fmt, va_list args)
|
|
{
|
|
// TODO: Calculate size and write into one buffer instead of 2 appends
|
|
DN_Str8 append = DN_Str8FromFmtVArena(arena, fmt, args);
|
|
DN_Str8 result = DN_Str8FromArena(arena, string.size + append.size, DN_ZMem_No);
|
|
DN_Memcpy(result.data, string.data, string.size);
|
|
DN_Memcpy(result.data + string.size, append.data, append.size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FillF(DN_Arena *arena, DN_USize count, char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Str8 result = DN_Str8FillFV(arena, count, fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FillFV(DN_Arena *arena, DN_USize count, char const *fmt, va_list args)
|
|
{
|
|
DN_Str8 fill = DN_Str8FromFmtVArena(arena, fmt, args);
|
|
DN_Str8 result = DN_Str8FromArena(arena, count * fill.size, DN_ZMem_No);
|
|
for (DN_USize index = 0; index < count; index++) {
|
|
void *dest = result.data + (index * fill.size);
|
|
DN_Memcpy(dest, fill.data, fill.size);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_Str8Remove(DN_Str8 *string, DN_USize offset, DN_USize size)
|
|
{
|
|
if (!string || string->size)
|
|
return;
|
|
|
|
char *end = string->data + string->size;
|
|
char *dest = DN_Min(string->data + offset, end);
|
|
char *src = DN_Min(string->data + offset + size, end);
|
|
DN_USize bytes_to_move = end - src;
|
|
DN_Memmove(dest, src, bytes_to_move);
|
|
string->size -= bytes_to_move;
|
|
}
|
|
|
|
DN_API DN_Str8TruncateResult DN_Str8TruncateMiddle(DN_Arena *arena, DN_Str8 str8, DN_U32 side_size, DN_Str8 truncator)
|
|
{
|
|
DN_Str8TruncateResult result = {};
|
|
if (str8.size <= (side_size * 2)) {
|
|
result.str8 = DN_Str8FromStr8Arena(arena, str8);
|
|
return result;
|
|
}
|
|
|
|
DN_Str8 head = DN_Str8Slice(str8, 0, side_size);
|
|
DN_Str8 tail = DN_Str8Slice(str8, str8.size - side_size, side_size);
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(6284) // Object passed as _Param_(3) when a string is required in call to 'DN_Str8FromFmtArena' Actual type: 'struct DN_Str8'
|
|
result.str8 = DN_Str8FromFmtArena(arena, "%S%S%S", head, truncator, tail);
|
|
DN_MSVC_WARNING_POP
|
|
result.truncated = true;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8Lower(DN_Arena *arena, DN_Str8 string)
|
|
{
|
|
DN_Str8 result = DN_Str8FromStr8Arena(arena, string);
|
|
for (DN_ForIndexU(index, result.size))
|
|
result.data[index] = DN_CharToLower(result.data[index]);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8Upper(DN_Arena *arena, DN_Str8 string)
|
|
{
|
|
DN_Str8 result = DN_Str8FromStr8Arena(arena, string);
|
|
for (DN_ForIndexU(index, result.size))
|
|
result.data[index] = DN_CharToUpper(result.data[index]);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromArena(DN_Arena *arena)
|
|
{
|
|
DN_Str8Builder result = {};
|
|
result.arena = arena;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrRef(DN_Arena *arena, DN_Str8 const *strings, DN_USize size)
|
|
{
|
|
DN_Str8Builder result = DN_Str8BuilderFromArena(arena);
|
|
DN_Str8BuilderAppendArrayRef(&result, strings, size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrCopy(DN_Arena *arena, DN_Str8 const *strings, DN_USize size)
|
|
{
|
|
DN_Str8Builder result = DN_Str8BuilderFromArena(arena);
|
|
DN_Str8BuilderAppendArrayCopy(&result, strings, size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromBuilder(DN_Arena *arena, DN_Str8Builder const *builder)
|
|
{
|
|
DN_Str8Builder result = DN_Str8BuilderFromArena(arena);
|
|
DN_Str8BuilderAppendBuilderCopy(&result, builder);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderAddArrayRef(DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize size, DN_Str8BuilderAdd add)
|
|
{
|
|
if (!builder)
|
|
return false;
|
|
|
|
if (!strings || size <= 0)
|
|
return true;
|
|
|
|
DN_Str8Link *links = DN_ArenaNewArray(builder->arena, DN_Str8Link, size, DN_ZMem_No);
|
|
if (!links)
|
|
return false;
|
|
|
|
if (add == DN_Str8BuilderAdd_Append) {
|
|
for (DN_ForIndexU(index, size)) {
|
|
DN_Str8 string = strings[index];
|
|
DN_Str8Link *link = links + index;
|
|
|
|
link->string = string;
|
|
link->next = NULL;
|
|
|
|
if (builder->head)
|
|
builder->tail->next = link;
|
|
else
|
|
builder->head = link;
|
|
|
|
builder->tail = link;
|
|
builder->count++;
|
|
builder->string_size += string.size;
|
|
}
|
|
} else {
|
|
DN_Assert(add == DN_Str8BuilderAdd_Prepend);
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(6293) // NOTE: Ill-defined loop
|
|
for (DN_USize index = size - 1; index < size; index--) {
|
|
DN_MSVC_WARNING_POP
|
|
DN_Str8 string = strings[index];
|
|
DN_Str8Link *link = links + index;
|
|
link->string = string;
|
|
link->next = builder->head;
|
|
builder->head = link;
|
|
if (!builder->tail)
|
|
builder->tail = link;
|
|
builder->count++;
|
|
builder->string_size += string.size;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderAddArrayCopy(DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize size, DN_Str8BuilderAdd add)
|
|
{
|
|
if (!builder)
|
|
return false;
|
|
|
|
if (!strings || size <= 0)
|
|
return true;
|
|
|
|
DN_ArenaTempMem tmp_mem = DN_ArenaTempMemBegin(builder->arena);
|
|
bool result = true;
|
|
DN_Str8 *strings_copy = DN_ArenaNewArray(builder->arena, DN_Str8, size, DN_ZMem_No);
|
|
for (DN_ForIndexU(index, size)) {
|
|
strings_copy[index] = DN_Str8FromStr8Arena(builder->arena, strings[index]);
|
|
if (strings_copy[index].size != strings[index].size) {
|
|
result = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result)
|
|
result = DN_Str8BuilderAddArrayRef(builder, strings_copy, size, add);
|
|
|
|
if (!result)
|
|
DN_ArenaTempMemEnd(tmp_mem);
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderAddFV(DN_Str8Builder *builder, DN_Str8BuilderAdd add, DN_FMT_ATTRIB char const *fmt, va_list args)
|
|
{
|
|
DN_Str8 string = DN_Str8FromFmtVArena(builder->arena, fmt, args);
|
|
DN_ArenaTempMem temp_mem = DN_ArenaTempMemBegin(builder->arena);
|
|
bool result = DN_Str8BuilderAddArrayRef(builder, &string, 1, add);
|
|
if (!result)
|
|
DN_ArenaTempMemEnd(temp_mem);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderAppendRef(DN_Str8Builder *builder, DN_Str8 string)
|
|
{
|
|
bool result = DN_Str8BuilderAddArrayRef(builder, &string, 1, DN_Str8BuilderAdd_Append);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderAppendCopy(DN_Str8Builder *builder, DN_Str8 string)
|
|
{
|
|
bool result = DN_Str8BuilderAddArrayCopy(builder, &string, 1, DN_Str8BuilderAdd_Append);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderAppendF(DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
bool result = DN_Str8BuilderAppendFV(builder, fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderAppendBytesRef(DN_Str8Builder *builder, void const *ptr, DN_USize size)
|
|
{
|
|
DN_Str8 input = DN_Str8FromPtr(ptr, size);
|
|
bool result = DN_Str8BuilderAppendRef(builder, input);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderAppendBytesCopy(DN_Str8Builder *builder, void const *ptr, DN_USize size)
|
|
{
|
|
DN_Str8 input = DN_Str8FromPtr(ptr, size);
|
|
bool result = DN_Str8BuilderAppendCopy(builder, input);
|
|
return result;
|
|
}
|
|
|
|
static bool DN_Str8BuilderAppendBuilder_(DN_Str8Builder *dest, DN_Str8Builder const *src, bool copy)
|
|
{
|
|
if (!dest)
|
|
return false;
|
|
if (!src)
|
|
return true;
|
|
|
|
DN_ArenaTempMemBegin(dest->arena);
|
|
DN_Str8Link *links = DN_ArenaNewArray(dest->arena, DN_Str8Link, src->count, DN_ZMem_No);
|
|
if (!links)
|
|
return false;
|
|
|
|
DN_Str8Link *first = nullptr;
|
|
DN_Str8Link *last = nullptr;
|
|
DN_USize link_index = 0;
|
|
bool result = true;
|
|
for (DN_Str8Link const *it = src->head; it; it = it->next) {
|
|
DN_Str8Link *link = links + link_index++;
|
|
link->next = nullptr;
|
|
link->string = it->string;
|
|
|
|
if (copy) {
|
|
link->string = DN_Str8FromStr8Arena(dest->arena, it->string);
|
|
if (link->string.size != it->string.size) {
|
|
result = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (last)
|
|
last->next = link;
|
|
else
|
|
first = link;
|
|
last = link;
|
|
}
|
|
|
|
if (result) {
|
|
if (dest->head)
|
|
dest->tail->next = first;
|
|
else
|
|
dest->head = first;
|
|
dest->tail = last;
|
|
dest->count += src->count;
|
|
dest->string_size += src->string_size;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderAppendBuilderRef(DN_Str8Builder *dest, DN_Str8Builder const *src)
|
|
{
|
|
bool result = DN_Str8BuilderAppendBuilder_(dest, src, false);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderAppendBuilderCopy(DN_Str8Builder *dest, DN_Str8Builder const *src)
|
|
{
|
|
bool result = DN_Str8BuilderAppendBuilder_(dest, src, true);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderPrependRef(DN_Str8Builder *builder, DN_Str8 string)
|
|
{
|
|
bool result = DN_Str8BuilderAddArrayRef(builder, &string, 1, DN_Str8BuilderAdd_Prepend);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderPrependCopy(DN_Str8Builder *builder, DN_Str8 string)
|
|
{
|
|
bool result = DN_Str8BuilderAddArrayCopy(builder, &string, 1, DN_Str8BuilderAdd_Prepend);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderPrependF(DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
bool result = DN_Str8BuilderPrependFV(builder, fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Str8BuilderErase(DN_Str8Builder *builder, DN_Str8 string)
|
|
{
|
|
for (DN_Str8Link **it = &builder->head; *it; it = &((*it)->next)) {
|
|
if (DN_Str8Eq((*it)->string, string)) {
|
|
*it = (*it)->next;
|
|
builder->string_size -= string.size;
|
|
builder->count -= 1;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8BuilderBuild(DN_Str8Builder const *builder, DN_Arena *arena)
|
|
{
|
|
DN_Str8 result = DN_Str8BuilderBuildDelimited(builder, DN_Str8Lit(""), arena);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8BuilderBuildDelimited(DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena)
|
|
{
|
|
DN_Str8 result = DN_ZeroInit;
|
|
if (!builder || builder->string_size <= 0 || builder->count <= 0)
|
|
return result;
|
|
|
|
DN_USize size_for_delimiter = delimiter.size ? ((builder->count - 1) * delimiter.size) : 0;
|
|
result.data = DN_ArenaNewArray(arena,
|
|
char,
|
|
builder->string_size + size_for_delimiter + 1 /*null terminator*/,
|
|
DN_ZMem_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;
|
|
if (link->next && delimiter.size) {
|
|
DN_Memcpy(result.data + result.size, delimiter.data, delimiter.size);
|
|
result.size += delimiter.size;
|
|
}
|
|
}
|
|
|
|
result.data[result.size] = 0;
|
|
DN_Assert(result.size == builder->string_size + size_for_delimiter);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Slice<DN_Str8> DN_Str8BuilderBuildSlice(DN_Str8Builder const *builder, DN_Arena *arena)
|
|
{
|
|
DN_Slice<DN_Str8> result = DN_ZeroInit;
|
|
if (!builder || builder->string_size <= 0 || builder->count <= 0)
|
|
return result;
|
|
|
|
result = DN_Slice_Alloc<DN_Str8>(arena, builder->count, DN_ZMem_No);
|
|
if (!result.data)
|
|
return result;
|
|
|
|
DN_USize slice_index = 0;
|
|
for (DN_Str8Link *link = builder->head; link; link = link->next)
|
|
result.data[slice_index++] = DN_Str8FromStr8Arena(arena, link->string);
|
|
|
|
DN_Assert(slice_index == builder->count);
|
|
return result;
|
|
}
|
|
|
|
// NOTE: DN_Char ///////////////////////////////////////////////////////////////////////////////////
|
|
// NOTE: DN_UTF ////////////////////////////////////////////////////////////////////////////////////
|
|
DN_API int DN_UTF8_EncodeCodepoint(uint8_t utf8[4], uint32_t codepoint)
|
|
{
|
|
// NOTE: Table from https://www.reedbeta.com/blog/programmers-intro-to-unicode/
|
|
// ----------------------------------------+----------------------------+--------------------+
|
|
// UTF-8 (binary) | Code point (binary) | Range |
|
|
// ----------------------------------------+----------------------------+--------------------+
|
|
// 0xxx'xxxx | xxx'xxxx | U+0000 - U+007F |
|
|
// 110x'xxxx 10yy'yyyy | xxx'xxyy'yyyy | U+0080 - U+07FF |
|
|
// 1110'xxxx 10yy'yyyy 10zz'zzzz | xxxx'yyyy'yyzz'zzzz | U+0800 - U+FFFF |
|
|
// 1111'0xxx 10yy'yyyy 10zz'zzzz 10ww'wwww | x'xxyy'yyyy'zzzz'zzww'wwww | U+10000 - U+10FFFF |
|
|
// ----------------------------------------+----------------------------+--------------------+
|
|
|
|
if (codepoint <= 0b0111'1111) {
|
|
utf8[0] = DN_Cast(uint8_t) codepoint;
|
|
return 1;
|
|
}
|
|
|
|
if (codepoint <= 0b0111'1111'1111) {
|
|
utf8[0] = (0b1100'0000 | ((codepoint >> 6) & 0b01'1111)); // x
|
|
utf8[1] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // y
|
|
return 2;
|
|
}
|
|
|
|
if (codepoint <= 0b1111'1111'1111'1111) {
|
|
utf8[0] = (0b1110'0000 | ((codepoint >> 12) & 0b00'1111)); // x
|
|
utf8[1] = (0b1000'0000 | ((codepoint >> 6) & 0b11'1111)); // y
|
|
utf8[2] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // z
|
|
return 3;
|
|
}
|
|
|
|
if (codepoint <= 0b1'1111'1111'1111'1111'1111) {
|
|
utf8[0] = (0b1111'0000 | ((codepoint >> 18) & 0b00'0111)); // x
|
|
utf8[1] = (0b1000'0000 | ((codepoint >> 12) & 0b11'1111)); // y
|
|
utf8[2] = (0b1000'0000 | ((codepoint >> 6) & 0b11'1111)); // z
|
|
utf8[3] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // w
|
|
return 4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DN_API int DN_UTF16_EncodeCodepoint(uint16_t utf16[2], uint32_t codepoint)
|
|
{
|
|
// NOTE: Table from https://www.reedbeta.com/blog/programmers-intro-to-unicode/
|
|
// ----------------------------------------+------------------------------------+------------------+
|
|
// UTF-16 (binary) | Code point (binary) | Range |
|
|
// ----------------------------------------+------------------------------------+------------------+
|
|
// xxxx'xxxx'xxxx'xxxx | xxxx'xxxx'xxxx'xxxx | U+0000???U+FFFF |
|
|
// 1101'10xx'xxxx'xxxx 1101'11yy'yyyy'yyyy | xxxx'xxxx'xxyy'yyyy'yyyy + 0x10000 | U+10000???U+10FFFF |
|
|
// ----------------------------------------+------------------------------------+------------------+
|
|
|
|
if (codepoint <= 0b1111'1111'1111'1111) {
|
|
utf16[0] = DN_Cast(uint16_t) codepoint;
|
|
return 1;
|
|
}
|
|
|
|
if (codepoint <= 0b1111'1111'1111'1111'1111) {
|
|
uint32_t surrogate_codepoint = codepoint + 0x10000;
|
|
utf16[0] = 0b1101'1000'0000'0000 | ((surrogate_codepoint >> 10) & 0b11'1111'1111); // x
|
|
utf16[1] = 0b1101'1100'0000'0000 | ((surrogate_codepoint >> 0) & 0b11'1111'1111); // y
|
|
return 2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
DN_API DN_U8 DN_U8FromHexNibble(char hex)
|
|
{
|
|
bool digit = hex >= '0' && hex <= '9';
|
|
bool upper = hex >= 'A' && hex <= 'F';
|
|
bool lower = hex >= 'a' && hex <= 'f';
|
|
DN_U8 result = 0xFF;
|
|
if (digit)
|
|
result = hex - '0';
|
|
if (upper)
|
|
result = hex - 'A' + 10;
|
|
if (lower)
|
|
result = hex - 'a' + 10;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_NibbleFromU8Result DN_NibbleFromU8(DN_U8 u8)
|
|
{
|
|
static char const *table = "0123456789abcdef";
|
|
DN_U8 lhs = (u8 >> 0) & 0xF;
|
|
DN_U8 rhs = (u8 >> 4) & 0xF;
|
|
DN_NibbleFromU8Result result = {};
|
|
result.nibble0 = table[rhs];
|
|
result.nibble1 = table[lhs];
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_BytesFromHexPtr(void const *hex, DN_USize hex_count, void *bytes, DN_USize bytes_count)
|
|
{
|
|
DN_USize result = 0;
|
|
DN_U8 const *hex_u8 = DN_Cast(DN_U8 const *) hex;
|
|
if (hex_count >= 2 && hex_u8[0] == '0' && (hex_u8[1] == 'x' || hex_u8[1] == 'X')) {
|
|
hex_u8 += 2;
|
|
hex_count -= 2;
|
|
}
|
|
|
|
if (hex_count > (bytes_count * 2))
|
|
return result;
|
|
|
|
DN_U8 *ptr = DN_Cast(DN_U8 *)bytes;
|
|
for (DN_USize index = 0; index < hex_count; index += 2) {
|
|
DN_U8 nibble0 = DN_U8FromHexNibble(hex_u8[index + 0]);
|
|
DN_U8 nibble1 = DN_U8FromHexNibble(hex_u8[index + 1]);
|
|
if (nibble0 == 0xFF || nibble1 == 0xFF)
|
|
return result;
|
|
*ptr++ = nibble0 << 4 | nibble1 << 0;
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_BytesFromHexPtrArena(void const *hex, DN_USize hex_size, DN_Arena *arena)
|
|
{
|
|
DN_Assert(hex_size % 2 == 0);
|
|
DN_Str8 result = {};
|
|
result.data = DN_ArenaNewArray(arena, char, hex_size / 2, DN_ZMem_No);
|
|
if (result.data)
|
|
result.size = DN_BytesFromHexPtr(hex, hex_size, result.data, hex_size / 2);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_BytesFromHexStr8(DN_Str8 hex, void *dest, DN_USize dest_count)
|
|
{
|
|
DN_USize result = DN_BytesFromHexPtr(hex.data, hex.size, dest, dest_count);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_BytesFromHexStr8Arena(DN_Str8 hex, DN_Arena *arena)
|
|
{
|
|
DN_Str8 result = DN_BytesFromHexPtrArena(hex.data, hex.size, arena);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U8x16 DN_BytesFromHex32Ptr(void const *hex, DN_USize hex_count)
|
|
{
|
|
DN_U8x16 result = {};
|
|
DN_Assert(hex_count / 2 == sizeof result.data);
|
|
DN_USize bytes_written = DN_BytesFromHexPtr(hex, hex_count, result.data, sizeof result);
|
|
DN_Assert(bytes_written == sizeof result.data);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U8x32 DN_BytesFromHex64Ptr(void const *hex, DN_USize hex_count)
|
|
{
|
|
DN_U8x32 result = {};
|
|
DN_Assert(hex_count / 2 == sizeof result.data);
|
|
DN_USize bytes_written = DN_BytesFromHexPtr(hex, hex_count, result.data, sizeof result);
|
|
DN_Assert(bytes_written == sizeof result.data);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_HexU64Str8 DN_HexFromU64(DN_U64 value, DN_HexFromU64Type type)
|
|
{
|
|
DN_HexU64Str8 result = {};
|
|
DN_HexFromBytesPtr(&value, sizeof(value), result.data, sizeof(result.data));
|
|
if (type == DN_HexFromU64Type_Uppercase) {
|
|
for (DN_USize index = 0; index < result.size; index++)
|
|
result.data[index] = DN_CharToUpper(result.data[index]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_HexFromBytesPtr(void const *bytes, DN_USize bytes_count, void *hex, DN_USize hex_count)
|
|
{
|
|
DN_USize result = 0;
|
|
if ((bytes_count * 2) != hex_count)
|
|
return result;
|
|
DN_U8 const *src_u8 = DN_Cast(DN_U8 const *)bytes;
|
|
DN_U8 *ptr = DN_Cast(DN_U8 *)hex;
|
|
for (DN_USize index = 0; index < bytes_count; index++) {
|
|
DN_NibbleFromU8Result to_nibbles = DN_NibbleFromU8(src_u8[index]);
|
|
*ptr++ = to_nibbles.nibble0;
|
|
*ptr++ = to_nibbles.nibble1;
|
|
result += 2;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_HexFromBytesPtrArena(void const *bytes, DN_USize bytes_count, DN_Arena *arena)
|
|
{
|
|
DN_Str8 result = {};
|
|
result.data = DN_ArenaNewArray(arena, char, bytes_count * 2, DN_ZMem_No);
|
|
if (result.data)
|
|
result.size = DN_HexFromBytesPtr(bytes, bytes_count, result.data, bytes_count * 2);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Hex32 DN_HexFromBytes16Ptr(void const *bytes, DN_USize bytes_count)
|
|
{
|
|
DN_Hex32 result = {};
|
|
DN_Assert(bytes_count * 2 == sizeof result.data);
|
|
DN_USize hex_written = DN_HexFromBytesPtr(bytes, bytes_count, result.data, sizeof result.data);
|
|
DN_Assert(hex_written == sizeof result.data);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Hex64 DN_HexFromBytes32Ptr(void const *bytes, DN_USize bytes_count)
|
|
{
|
|
DN_Hex64 result = {};
|
|
DN_Assert(bytes_count * 2 == sizeof result.data);
|
|
DN_USize hex_written = DN_HexFromBytesPtr(bytes, bytes_count, result.data, sizeof result.data);
|
|
DN_Assert(hex_written == sizeof result.data);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Hex128 DN_HexFromBytes64Ptr(void const *bytes, DN_USize bytes_count)
|
|
{
|
|
DN_Hex128 result = {};
|
|
DN_Assert(bytes_count * 2 == sizeof result.data);
|
|
DN_USize hex_written = DN_HexFromBytesPtr(bytes, bytes_count, result.data, sizeof result.data);
|
|
DN_Assert(hex_written == sizeof result.data);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x128 DN_AgeStr8FromMsU64(DN_U64 duration_ms, DN_AgeUnit units)
|
|
{
|
|
DN_Str8x128 result = {};
|
|
DN_U64 remainder_ms = duration_ms;
|
|
if (units & DN_AgeUnit_FractionalSec) {
|
|
units |= DN_AgeUnit_Sec;
|
|
units &= ~DN_AgeUnit_Ms;
|
|
}
|
|
|
|
if (units & DN_AgeUnit_Year) {
|
|
DN_USize value_usize = remainder_ms / (DN_SecFromYears(1) * 1000);
|
|
remainder_ms -= DN_SecFromYears(value_usize) * 1000;
|
|
if (value_usize)
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), "%s%zuyr", result.size ? " " : "", value_usize);
|
|
}
|
|
|
|
if (units & DN_AgeUnit_Week) {
|
|
DN_USize value_usize = remainder_ms / (DN_SecFromWeeks(1) * 1000);
|
|
remainder_ms -= DN_SecFromWeeks(value_usize) * 1000;
|
|
if (value_usize)
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), "%s%zuw", result.size ? " " : "", value_usize);
|
|
}
|
|
|
|
if (units & DN_AgeUnit_Day) {
|
|
DN_USize value_usize = remainder_ms / (DN_SecFromDays(1) * 1000);
|
|
remainder_ms -= DN_SecFromDays(value_usize) * 1000;
|
|
if (value_usize)
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), "%s%zud", result.size ? " " : "", value_usize);
|
|
}
|
|
|
|
if (units & DN_AgeUnit_Hr) {
|
|
DN_USize value_usize = remainder_ms / (DN_SecFromHours(1) * 1000);
|
|
remainder_ms -= DN_SecFromHours(value_usize) * 1000;
|
|
if (value_usize)
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), "%s%zuh", result.size ? " " : "", value_usize);
|
|
}
|
|
|
|
if (units & DN_AgeUnit_Min) {
|
|
DN_USize value_usize = remainder_ms / (DN_SecFromMins(1) * 1000);
|
|
remainder_ms -= DN_SecFromMins(value_usize) * 1000;
|
|
if (value_usize)
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), "%s%zum", result.size ? " " : "", value_usize);
|
|
}
|
|
|
|
if (units & DN_AgeUnit_Sec) {
|
|
if (units & DN_AgeUnit_FractionalSec) {
|
|
DN_F64 remainder_s = remainder_ms / 1000.0;
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), "%s%.3fs", result.size ? " " : "", remainder_s);
|
|
remainder_ms = 0;
|
|
} else {
|
|
DN_USize value_usize = remainder_ms / 1000;
|
|
remainder_ms -= DN_Cast(DN_USize)(value_usize * 1000);
|
|
if (value_usize)
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), "%s%zus", result.size ? " " : "", value_usize);
|
|
}
|
|
}
|
|
|
|
if (units & DN_AgeUnit_Ms) {
|
|
DN_Assert((units & DN_AgeUnit_FractionalSec) == 0);
|
|
DN_USize value_usize = remainder_ms;
|
|
remainder_ms -= value_usize;
|
|
if (value_usize || result.size == 0)
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), "%s%zums", result.size ? " " : "", value_usize);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x128 DN_AgeStr8FromSecU64(DN_U64 duration_s, DN_AgeUnit units)
|
|
{
|
|
DN_U64 duration_ms = duration_s * 1000;
|
|
DN_Str8x128 result = DN_AgeStr8FromMsU64(duration_ms, units);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x128 DN_AgeStr8FromSecF64(DN_F64 duration_s, DN_AgeUnit units)
|
|
{
|
|
DN_U64 duration_ms = DN_Cast(DN_U64)(duration_s * 1000.0);
|
|
DN_Str8x128 result = DN_AgeStr8FromMsU64(duration_ms, units);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromByteCountType(DN_ByteCountType type)
|
|
{
|
|
DN_Str8 result = DN_Str8Lit("");
|
|
switch (type) {
|
|
case DN_ByteCountType_B: result = DN_Str8Lit("B"); break;
|
|
case DN_ByteCountType_KiB: result = DN_Str8Lit("KiB"); break;
|
|
case DN_ByteCountType_MiB: result = DN_Str8Lit("MiB"); break;
|
|
case DN_ByteCountType_GiB: result = DN_Str8Lit("GiB"); break;
|
|
case DN_ByteCountType_TiB: result = DN_Str8Lit("TiB"); break;
|
|
case DN_ByteCountType_Count: result = DN_Str8Lit(""); break;
|
|
case DN_ByteCountType_Auto: result = DN_Str8Lit(""); break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_ByteCountResult DN_ByteCountFromType(DN_U64 bytes, DN_ByteCountType type)
|
|
{
|
|
DN_Assert(type != DN_ByteCountType_Count);
|
|
DN_ByteCountResult result = {};
|
|
result.bytes = DN_Cast(DN_F64) bytes;
|
|
if (type == DN_ByteCountType_Auto)
|
|
for (; result.type < DN_ByteCountType_Count && result.bytes >= 1024.0; result.type = DN_Cast(DN_ByteCountType)(DN_Cast(DN_USize) result.type + 1))
|
|
result.bytes /= 1024.0;
|
|
else
|
|
for (; result.type < type; result.type = DN_Cast(DN_ByteCountType)(DN_Cast(DN_USize) result.type + 1))
|
|
result.bytes /= 1024.0;
|
|
result.suffix = DN_Str8FromByteCountType(result.type);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x32 DN_ByteCountStr8x32FromType(DN_U64 bytes, DN_ByteCountType type)
|
|
{
|
|
DN_ByteCountResult byte_count = DN_ByteCountFromType(bytes, type);
|
|
DN_Str8x32 result = DN_Str8x32FromFmt("%.2f%.*s", byte_count.bytes, DN_Str8PrintFmt(byte_count.suffix));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Profiler DN_ProfilerInit(DN_ProfilerAnchor *anchors, DN_USize count, DN_USize anchors_per_frame, DN_ProfilerTSCNowFunc *tsc_now, DN_U64 tsc_frequency)
|
|
{
|
|
DN_Profiler result = {};
|
|
result.anchors = anchors;
|
|
result.anchors_count = count;
|
|
result.anchors_per_frame = anchors_per_frame;
|
|
result.tsc_now = tsc_now;
|
|
result.tsc_frequency = tsc_frequency;
|
|
|
|
DN_AssertF(result.tsc_frequency != 0,
|
|
"You must set this to the frequency of the timestamp counter function (TSC) (e.g. how "
|
|
"many ticks occur between timestamps). We use this to determine the duration between "
|
|
"each zone's recorded TSC. For example if the 'tsc_now' was set to Window's "
|
|
"QueryPerformanceCounter then 'tsc_frequency' would be set to the value of "
|
|
"QueryPerformanceFrequency which is typically 10mhz (e.g. The duration between two "
|
|
"consecutive TSC's is 10mhz)."
|
|
""
|
|
"Hence frequency can't be zero otherwise it's a divide by 0. If you don't have a TSC "
|
|
"function and pass in null, the profiler defaults to rdtsc() and you must measure the "
|
|
"frequency of rdtsc yourself. The reason for this is that measuring rdtsc requires "
|
|
"having some alternate timing mechanism to measure the duration between the TSCs "
|
|
"provided by rdtsc and this profiler makes no assumption about what timing primitives "
|
|
"are available other than rdtsc which is a CPU builtin available on basically all "
|
|
"platforms or have an equivalent (e.g. __builtin_readcyclecounter)"
|
|
""
|
|
"This codebase provides DN_OS_EstimateTSCPerSecond() as an example of how to that for "
|
|
"convenience and is available if compiling with the OS layer. Some platforms like "
|
|
"Emscripten don't support rdtsc() so you should use an alternative method like "
|
|
"emscripten_get_now() or clock_gettime with CLOCK_MONOTONIC.");
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_USize DN_ProfilerFrameCount(DN_Profiler const *profiler)
|
|
{
|
|
DN_USize result = profiler->anchors_count / profiler->anchors_per_frame;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchorsFromIndex(DN_Profiler *profiler, DN_USize frame_index)
|
|
{
|
|
DN_ProfilerAnchorArray result = {};
|
|
DN_USize anchor_offset = frame_index * profiler->anchors_per_frame;
|
|
result.data = profiler->anchors + anchor_offset;
|
|
result.count = profiler->anchors_per_frame;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchors(DN_Profiler *profiler)
|
|
{
|
|
DN_ProfilerAnchorArray result = DN_ProfilerFrameAnchorsFromIndex(profiler, profiler->frame_index);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_ProfilerZone DN_ProfilerBeginZone(DN_Profiler *profiler, DN_Str8 name, DN_U16 anchor_index)
|
|
{
|
|
DN_ProfilerZone result = {};
|
|
if (profiler->paused)
|
|
return result;
|
|
|
|
DN_Assert(anchor_index < profiler->anchors_per_frame);
|
|
DN_ProfilerAnchor *anchor = DN_ProfilerFrameAnchors(profiler).data + anchor_index;
|
|
anchor->name = name;
|
|
|
|
// 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 (anchor->name.size && anchor->name != name)
|
|
DN_AssertF(name == anchor->name, "Potentially overwriting a zone by accident? Anchor is '%.*s', name is '%.*s'", DN_Str8PrintFmt(anchor->name), DN_Str8PrintFmt(name));
|
|
#endif
|
|
|
|
result.begin_tsc = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTSC();
|
|
result.anchor_index = anchor_index;
|
|
result.parent_zone = profiler->parent_zone;
|
|
result.elapsed_tsc_at_zone_start = anchor->tsc_inclusive;
|
|
profiler->parent_zone = anchor_index;
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_ProfilerEndZone(DN_Profiler *profiler, DN_ProfilerZone zone)
|
|
{
|
|
if (profiler->paused)
|
|
return;
|
|
|
|
DN_Assert(zone.anchor_index < profiler->anchors_per_frame);
|
|
DN_Assert(zone.parent_zone < profiler->anchors_per_frame);
|
|
|
|
DN_ProfilerAnchorArray array = DN_ProfilerFrameAnchors(profiler);
|
|
DN_ProfilerAnchor *anchor = array.data + zone.anchor_index;
|
|
DN_U64 tsc_now = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTSC();
|
|
DN_U64 elapsed_tsc = tsc_now - zone.begin_tsc;
|
|
|
|
anchor->hit_count++;
|
|
anchor->tsc_inclusive = zone.elapsed_tsc_at_zone_start + elapsed_tsc;
|
|
anchor->tsc_exclusive += elapsed_tsc;
|
|
|
|
if (zone.parent_zone != zone.anchor_index) {
|
|
DN_ProfilerAnchor *parent_anchor = array.data + zone.parent_zone;
|
|
parent_anchor->tsc_exclusive -= elapsed_tsc;
|
|
}
|
|
profiler->parent_zone = zone.parent_zone;
|
|
}
|
|
|
|
DN_API void DN_ProfilerNewFrame(DN_Profiler *profiler)
|
|
{
|
|
if (profiler->paused)
|
|
return;
|
|
|
|
// NOTE: End the frame's zone
|
|
DN_ProfilerEndZone(profiler, profiler->frame_zone);
|
|
DN_ProfilerAnchorArray old_frame_anchors = DN_ProfilerFrameAnchors(profiler);
|
|
DN_ProfilerAnchor old_frame_anchor = old_frame_anchors.data[0];
|
|
profiler->frame_avg_tsc = (profiler->frame_avg_tsc + old_frame_anchor.tsc_inclusive) / 2.f;
|
|
|
|
// NOTE: Bump to the next frame
|
|
DN_USize frame_count = profiler->anchors_count / profiler->anchors_per_frame;
|
|
profiler->frame_index = (profiler->frame_index + 1) % frame_count;
|
|
|
|
// NOTE: Zero out the anchors
|
|
DN_ProfilerAnchorArray next_anchors = DN_ProfilerFrameAnchors(profiler);
|
|
DN_Memset(next_anchors.data, 0, sizeof(*profiler->anchors) * next_anchors.count);
|
|
|
|
// NOTE: Start the frame's zone
|
|
profiler->frame_zone = DN_ProfilerBeginZone(profiler, DN_Str8Lit("Profiler Frame"), 0);
|
|
}
|
|
|
|
DN_API void DN_ProfilerDump(DN_Profiler *profiler)
|
|
{
|
|
if (profiler->frame_index == 0)
|
|
return;
|
|
|
|
DN_USize frame_index = profiler->frame_index - 1;
|
|
DN_Assert(profiler->frame_index < profiler->anchors_per_frame);
|
|
|
|
DN_ProfilerAnchor *anchors = profiler->anchors + (frame_index * profiler->anchors_per_frame);
|
|
for (DN_USize index = 1; index < profiler->anchors_per_frame; index++) {
|
|
DN_ProfilerAnchor const *anchor = anchors + index;
|
|
if (!anchor->hit_count)
|
|
continue;
|
|
|
|
DN_U64 tsc_exclusive = anchor->tsc_exclusive;
|
|
DN_U64 tsc_inclusive = anchor->tsc_inclusive;
|
|
DN_F64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DN_Cast(DN_F64) profiler->tsc_frequency;
|
|
if (tsc_exclusive == tsc_inclusive) {
|
|
DN_OS_PrintOutLnF("%.*s[%u]: %.1fms", DN_Str8PrintFmt(anchor->name), anchor->hit_count, tsc_exclusive_milliseconds);
|
|
} else {
|
|
DN_F64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DN_Cast(DN_F64) profiler->tsc_frequency;
|
|
DN_OS_PrintOutLnF("%.*s[%u]: %.1f/%.1fms",
|
|
DN_Str8PrintFmt(anchor->name),
|
|
anchor->hit_count,
|
|
tsc_exclusive_milliseconds,
|
|
tsc_inclusive_milliseconds);
|
|
}
|
|
}
|
|
}
|
|
|
|
DN_API DN_F64 DN_ProfilerSecFromTSC(DN_Profiler *profiler, DN_U64 duration_tsc)
|
|
{
|
|
DN_F64 result = DN_Cast(DN_F64)duration_tsc / profiler->tsc_frequency;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_F64 DN_ProfilerMsFromTSC(DN_Profiler *profiler, DN_U64 duration_tsc)
|
|
{
|
|
DN_F64 result = DN_Cast(DN_F64)duration_tsc / profiler->tsc_frequency * 1000.0;
|
|
return result;
|
|
}
|
|
// DN: Single header generator inlined this file => #include "Base/dn_base_containers.cpp"
|
|
#define DN_CONTAINERS_CPP
|
|
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
|
|
DN_API void *DN_CArray2_InsertArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count)
|
|
{
|
|
void *result = nullptr;
|
|
if (!data || !size || !items || count <= 0 || ((*size + count) > max))
|
|
return result;
|
|
|
|
DN_USize clamped_index = DN_Min(index, *size);
|
|
if (clamped_index != *size) {
|
|
char const *src = DN_Cast(char *)data + (clamped_index * elem_size);
|
|
char const *dest = DN_Cast(char *)data + ((clamped_index + count) * elem_size);
|
|
char const *end = DN_Cast(char *)data + (size[0] * elem_size);
|
|
DN_USize bytes_to_move = end - src;
|
|
DN_Memmove(DN_Cast(void *) dest, src, bytes_to_move);
|
|
}
|
|
|
|
result = DN_Cast(char *)data + (clamped_index * elem_size);
|
|
DN_Memcpy(result, items, elem_size * count);
|
|
*size += count;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_ArrayEraseResult DN_CArray2_EraseRange(void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase)
|
|
{
|
|
DN_ArrayEraseResult result = {};
|
|
if (!data || !size || *size == 0 || count == 0)
|
|
return result;
|
|
|
|
// Compute the range to erase
|
|
DN_USize start = 0, end = 0;
|
|
if (count < 0) {
|
|
DN_USize abs_count = DN_Abs(count);
|
|
start = begin_index >= abs_count ? begin_index - abs_count + 1 : 0;
|
|
end = begin_index >= abs_count ? begin_index + 1 : 0;
|
|
} else {
|
|
start = begin_index;
|
|
end = begin_index + count;
|
|
}
|
|
|
|
// Clamp indices to valid bounds
|
|
start = DN_Min(start, *size);
|
|
end = DN_Min(end, *size);
|
|
|
|
// Erase the range [start, end)
|
|
DN_USize erase_count = end > start ? end - start : 0;
|
|
if (erase_count) {
|
|
char *dest = (char *)data + (elem_size * start);
|
|
char *array_end = (char *)data + (elem_size * *size);
|
|
char *src = dest + (elem_size * erase_count);
|
|
if (erase == DN_ArrayErase_Stable) {
|
|
DN_USize move_size = array_end - src;
|
|
DN_Memmove(dest, src, move_size);
|
|
} else {
|
|
char *unstable_src = array_end - (elem_size * erase_count);
|
|
DN_USize move_size = array_end - unstable_src;
|
|
DN_Memcpy(dest, unstable_src, move_size);
|
|
}
|
|
*size -= erase_count;
|
|
}
|
|
|
|
result.items_erased = erase_count;
|
|
result.it_index = start;
|
|
return result;
|
|
}
|
|
|
|
DN_API void *DN_CArray2_MakeArray(void *data, DN_USize *size, DN_USize max, DN_USize data_size, DN_USize make_size, DN_ZMem z_mem)
|
|
{
|
|
void *result = nullptr;
|
|
DN_USize new_size = *size + make_size;
|
|
if (new_size <= max) {
|
|
result = DN_Cast(char *) data + (data_size * size[0]);
|
|
*size = new_size;
|
|
if (z_mem == DN_ZMem_Yes)
|
|
DN_Memset(result, 0, data_size * make_size);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API void *DN_CArray2_AddArray(void *data, DN_USize *size, DN_USize max, DN_USize data_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add)
|
|
{
|
|
void *result = DN_CArray2_MakeArray(data, size, max, data_size, elems_count, DN_ZMem_No);
|
|
if (result) {
|
|
if (add == DN_ArrayAdd_Append) {
|
|
DN_Memcpy(result, elems, elems_count * data_size);
|
|
} else {
|
|
char *move_dest = DN_Cast(char *)data + (elems_count * data_size); // Shift elements forward
|
|
char *move_src = DN_Cast(char *)data;
|
|
DN_Memmove(move_dest, move_src, data_size * size[0]);
|
|
DN_Memcpy(data, elems, data_size * elems_count);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_CArray2_ResizeFromPool(void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max)
|
|
{
|
|
bool result = true;
|
|
if (new_max != *max) {
|
|
DN_USize bytes_to_alloc = data_size * new_max;
|
|
void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc);
|
|
if (buffer) {
|
|
DN_USize bytes_to_copy = data_size * DN_Min(*size, new_max);
|
|
DN_Memcpy(buffer, *data, bytes_to_copy);
|
|
DN_PoolDealloc(pool, *data);
|
|
*data = buffer;
|
|
*max = new_max;
|
|
*size = DN_Min(*size, new_max);
|
|
} else {
|
|
result = false;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_CArray2_GrowFromPool(void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max)
|
|
{
|
|
bool result = true;
|
|
if (new_max > *max)
|
|
result = DN_CArray2_ResizeFromPool(data, &size, max, data_size, pool, new_max);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_CArray2_GrowIfNeededFromPool(void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize add_count)
|
|
{
|
|
bool result = true;
|
|
DN_USize new_size = size + add_count;
|
|
if (new_size > *max) {
|
|
DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8);
|
|
result = DN_CArray2_ResizeFromPool(data, &size, max, data_size, pool, new_max);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API void *DN_CSLList_Detach(void **link, void **next)
|
|
{
|
|
void *result = *link;
|
|
if (*link) {
|
|
*link = *next;
|
|
*next = nullptr;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Ring_HasSpace(DN_Ring const *ring, DN_U64 size)
|
|
{
|
|
DN_U64 avail = ring->write_pos - ring->read_pos;
|
|
DN_U64 space = ring->size - avail;
|
|
bool result = space >= size;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Ring_HasData(DN_Ring const *ring, DN_U64 size)
|
|
{
|
|
DN_U64 data = ring->write_pos - ring->read_pos;
|
|
bool result = data >= size;
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_Ring_Write(DN_Ring *ring, void const *src, DN_U64 src_size)
|
|
{
|
|
DN_Assert(src_size <= ring->size);
|
|
DN_U64 offset = ring->write_pos % ring->size;
|
|
DN_U64 bytes_before_split = ring->size - offset;
|
|
DN_U64 pre_split_bytes = DN_Min(bytes_before_split, src_size);
|
|
DN_U64 post_split_bytes = src_size - pre_split_bytes;
|
|
void const *pre_split_data = src;
|
|
void const *post_split_data = ((char *)src + pre_split_bytes);
|
|
DN_Memcpy(ring->base + offset, pre_split_data, pre_split_bytes);
|
|
DN_Memcpy(ring->base, post_split_data, post_split_bytes);
|
|
ring->write_pos += src_size;
|
|
}
|
|
|
|
DN_API void DN_Ring_Read(DN_Ring *ring, void *dest, DN_U64 dest_size)
|
|
{
|
|
DN_Assert(dest_size <= ring->size);
|
|
DN_U64 offset = ring->read_pos % ring->size;
|
|
DN_U64 bytes_before_split = ring->size - offset;
|
|
DN_U64 pre_split_bytes = DN_Min(bytes_before_split, dest_size);
|
|
DN_U64 post_split_bytes = dest_size - pre_split_bytes;
|
|
DN_Memcpy(dest, ring->base + offset, pre_split_bytes);
|
|
DN_Memcpy((char *)dest + pre_split_bytes, ring->base, post_split_bytes);
|
|
ring->read_pos += dest_size;
|
|
}
|
|
|
|
// NOTE: DN_CArray /////////////////////////////////////////////////////////////////////////////////
|
|
template <typename T>
|
|
DN_ArrayEraseResult DN_CArray_EraseRange(T *data, DN_USize *size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase)
|
|
{
|
|
DN_ArrayEraseResult result = {};
|
|
if (!data || !size || *size == 0 || count == 0)
|
|
return result;
|
|
|
|
DN_AssertF(count != -1, "There's a bug with negative element erases, see the DN_VArray section in dn_docs.cpp");
|
|
|
|
// NOTE: Caculate the end index of the erase range
|
|
DN_ISize abs_count = DN_Abs(count);
|
|
DN_USize end_index = 0;
|
|
if (count < 0) {
|
|
end_index = begin_index - (abs_count - 1);
|
|
if (end_index > begin_index)
|
|
end_index = 0;
|
|
} else {
|
|
end_index = begin_index + (abs_count - 1);
|
|
if (end_index < begin_index)
|
|
end_index = (*size) - 1;
|
|
}
|
|
|
|
// NOTE: Ensure begin_index < one_past_end_index
|
|
if (end_index < begin_index) {
|
|
DN_USize tmp = begin_index;
|
|
begin_index = end_index;
|
|
end_index = tmp;
|
|
}
|
|
|
|
// NOTE: Ensure indexes are within valid bounds
|
|
begin_index = DN_Min(begin_index, *size);
|
|
end_index = DN_Min(end_index, *size - 1);
|
|
|
|
// NOTE: Erase the items in the range [begin_index, one_past_end_index)
|
|
DN_USize one_past_end_index = end_index + 1;
|
|
DN_USize erase_count = one_past_end_index - begin_index;
|
|
if (erase_count) {
|
|
T *end = data + *size;
|
|
T *dest = data + begin_index;
|
|
if (erase == DN_ArrayErase_Stable) {
|
|
T *src = dest + erase_count;
|
|
DN_Memmove(dest, src, (end - src) * sizeof(T));
|
|
} else {
|
|
T *src = end - erase_count;
|
|
DN_Memcpy(dest, src, (end - src) * sizeof(T));
|
|
}
|
|
*size -= erase_count;
|
|
}
|
|
|
|
result.items_erased = erase_count;
|
|
result.it_index = begin_index;
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T *DN_CArray_MakeArray(T *data, DN_USize *size, DN_USize max, DN_USize count, DN_ZMem z_mem)
|
|
{
|
|
if (!data || !size || count == 0)
|
|
return nullptr;
|
|
|
|
if (!DN_CheckF((*size + count) <= max, "Array is out of space (user requested +%zu items, array has %zu/%zu items)", count, *size, max))
|
|
return nullptr;
|
|
|
|
// TODO: Use placement new? Why doesn't this work?
|
|
T *result = data + *size;
|
|
*size += count;
|
|
if (z_mem == DN_ZMem_Yes)
|
|
DN_Memset(result, 0, sizeof(*result) * count);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T *DN_CArray_InsertArray(T *data, DN_USize *size, DN_USize max, DN_USize index, T const *items, DN_USize count)
|
|
{
|
|
T *result = nullptr;
|
|
if (!data || !size || !items || count <= 0 || ((*size + count) > max))
|
|
return result;
|
|
|
|
DN_USize clamped_index = DN_Min(index, *size);
|
|
if (clamped_index != *size) {
|
|
char const *src = DN_Cast(char *)(data + clamped_index);
|
|
char const *dest = DN_Cast(char *)(data + (clamped_index + count));
|
|
char const *end = DN_Cast(char *)(data + (*size));
|
|
DN_USize bytes_to_move = end - src;
|
|
DN_Memmove(DN_Cast(void *) dest, src, bytes_to_move);
|
|
}
|
|
|
|
result = data + clamped_index;
|
|
DN_Memcpy(result, items, sizeof(T) * count);
|
|
*size += count;
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T DN_CArray_PopFront(T *data, DN_USize *size, DN_USize count)
|
|
{
|
|
T result = {};
|
|
if (!data || !size || *size <= 0)
|
|
return result;
|
|
|
|
result = data[0];
|
|
DN_USize pop_count = DN_Min(count, *size);
|
|
DN_Memmove(data, data + pop_count, (*size - pop_count) * sizeof(T));
|
|
*size -= pop_count;
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T DN_CArray_PopBack(T *data, DN_USize *size, DN_USize count)
|
|
{
|
|
T result = {};
|
|
if (!data || !size || *size <= 0)
|
|
return result;
|
|
|
|
DN_USize pop_count = DN_Min(count, *size);
|
|
result = data[(*size - 1)];
|
|
*size -= pop_count;
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_ArrayFindResult<T> DN_CArray_Find(T *data, DN_USize size, T const &value)
|
|
{
|
|
DN_ArrayFindResult<T> result = {};
|
|
if (!data || size <= 0)
|
|
return result;
|
|
|
|
for (DN_USize index = 0; !result.data && index < size; index++) {
|
|
T *item = data + index;
|
|
if (*item == value) {
|
|
result.data = item;
|
|
result.index = index;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#if !defined(DN_NO_SARRAY)
|
|
// NOTE: DN_SArray /////////////////////////////////////////////////////////////////////////////////
|
|
template <typename T>
|
|
DN_SArray<T> DN_SArray_Init(DN_Arena *arena, DN_USize size, DN_ZMem z_mem)
|
|
{
|
|
DN_SArray<T> result = {};
|
|
if (!arena || !size)
|
|
return result;
|
|
result.data = DN_ArenaNewArray(arena, T, size, z_mem);
|
|
if (result.data)
|
|
result.max = size;
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_SArray<T> DN_SArray_InitSlice(DN_Arena *arena, DN_Slice<T> slice, DN_USize size, DN_ZMem z_mem)
|
|
{
|
|
DN_USize max = DN_Max(slice.size, size);
|
|
DN_SArray<T> result = DN_SArray_Init<T>(arena, max, DN_ZMem_No);
|
|
if (DN_SArray_IsValid(&result)) {
|
|
DN_SArray_AddArray(&result, slice.data, slice.size);
|
|
if (z_mem == DN_ZMem_Yes)
|
|
DN_Memset(result.data + result.size, 0, (result.max - result.size) * sizeof(T));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename T, size_t N>
|
|
DN_SArray<T> DN_SArray_InitCArray(DN_Arena *arena, T const (&array)[N], DN_USize size, DN_ZMem z_mem)
|
|
{
|
|
DN_SArray<T> result = DN_SArray_InitSlice(arena, DN_Slice_Init(DN_Cast(T *) array, N), size, z_mem);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_SArray<T> DN_SArray_InitBuffer(T *buffer, DN_USize size)
|
|
{
|
|
DN_SArray<T> result = {};
|
|
result.data = buffer;
|
|
result.max = size;
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
bool DN_SArray_IsValid(DN_SArray<T> const *array)
|
|
{
|
|
bool result = array && array->data && array->size <= array->max;
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_Slice<T> DN_SArray_Slice(DN_SArray<T> const *array)
|
|
{
|
|
DN_Slice<T> result = {};
|
|
if (array)
|
|
result = DN_Slice_Init<T>(DN_Cast(T *) array->data, array->size);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T *DN_SArray_MakeArray(DN_SArray<T> *array, DN_USize count, DN_ZMem z_mem)
|
|
{
|
|
if (!DN_SArray_IsValid(array))
|
|
return nullptr;
|
|
T *result = DN_CArray_MakeArray(array->data, &array->size, array->max, count, z_mem);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T *DN_SArray_Make(DN_SArray<T> *array, DN_ZMem z_mem)
|
|
{
|
|
T *result = DN_SArray_MakeArray(array, 1, z_mem);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T *DN_SArray_AddArray(DN_SArray<T> *array, T const *items, DN_USize count)
|
|
{
|
|
T *result = DN_SArray_MakeArray(array, count, DN_ZMem_No);
|
|
if (result)
|
|
DN_Memcpy(result, items, count * sizeof(T));
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
T *DN_SArray_AddCArray(DN_SArray<T> *array, T const (&items)[N])
|
|
{
|
|
T *result = DN_SArray_AddArray(array, items, N);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T *DN_SArray_Add(DN_SArray<T> *array, T const &item)
|
|
{
|
|
T *result = DN_SArray_AddArray(array, &item, 1);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T *DN_SArray_InsertArray(DN_SArray<T> *array, DN_USize index, T const *items, DN_USize count)
|
|
{
|
|
T *result = nullptr;
|
|
if (!DN_SArray_IsValid(array))
|
|
return result;
|
|
result = DN_CArray_InsertArray(array->data, &array->size, array->max, index, items, count);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
T *DN_SArray_InsertCArray(DN_SArray<T> *array, DN_USize index, T const (&items)[N])
|
|
{
|
|
T *result = DN_SArray_InsertArray(array, index, items, N);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T *DN_SArray_Insert(DN_SArray<T> *array, DN_USize index, T const &item)
|
|
{
|
|
T *result = DN_SArray_InsertArray(array, index, &item, 1);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T DN_SArray_PopFront(DN_SArray<T> *array, DN_USize count)
|
|
{
|
|
T result = DN_CArray_PopFront(array->data, &array->size, count);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T DN_SArray_PopBack(DN_SArray<T> *array, DN_USize count)
|
|
{
|
|
T result = DN_CArray_PopBack(array->data, &array->size, count);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_ArrayEraseResult DN_SArray_EraseRange(DN_SArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase)
|
|
{
|
|
DN_ArrayEraseResult result = {};
|
|
if (!DN_SArray_IsValid(array) || array->size == 0 || count == 0)
|
|
return result;
|
|
result = DN_CArray_EraseRange(array->data, &array->size, begin_index, count, erase);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
void DN_SArray_Clear(DN_SArray<T> *array)
|
|
{
|
|
if (array)
|
|
array->size = 0;
|
|
}
|
|
#endif // !defined(DN_NO_SARRAY)
|
|
|
|
#if !defined(DN_NO_FARRAY)
|
|
// NOTE: DN_FArray /////////////////////////////////////////////////////////////////////////////////
|
|
template <typename T, DN_USize N>
|
|
DN_FArray<T, N> DN_FArray_Init(T const *array, DN_USize count)
|
|
{
|
|
DN_FArray<T, N> result = {};
|
|
bool added = DN_FArray_AddArray(&result, array, count);
|
|
DN_Assert(added);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
DN_FArray<T, N> DN_FArray_InitSlice(DN_Slice<T> slice)
|
|
{
|
|
DN_FArray<T, N> result = DN_FArray_Init(slice.data, slice.size);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N, DN_USize K>
|
|
DN_FArray<T, N> DN_FArray_InitCArray(T const (&items)[K])
|
|
{
|
|
DN_FArray<T, N> result = DN_FArray_Init<T, N>(items, K);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
bool DN_FArray_IsValid(DN_FArray<T, N> const *array)
|
|
{
|
|
bool result = array && array->size <= DN_ArrayCountU(array->data);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
DN_Slice<T> DN_FArray_Slice(DN_FArray<T, N> const *array)
|
|
{
|
|
DN_Slice<T> result = {};
|
|
if (array)
|
|
result = DN_Slice_Init<T>(DN_Cast(T *) array->data, array->size);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
T *DN_FArray_AddArray(DN_FArray<T, N> *array, T const *items, DN_USize count)
|
|
{
|
|
T *result = DN_FArray_MakeArray(array, count, DN_ZMem_No);
|
|
if (result)
|
|
DN_Memcpy(result, items, count * sizeof(T));
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N, DN_USize K>
|
|
T *DN_FArray_AddCArray(DN_FArray<T, N> *array, T const (&items)[K])
|
|
{
|
|
T *result = DN_FArray_MakeArray(array, K, DN_ZMem_No);
|
|
if (result)
|
|
DN_Memcpy(result, items, K * sizeof(T));
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
T *DN_FArray_Add(DN_FArray<T, N> *array, T const &item)
|
|
{
|
|
T *result = DN_FArray_AddArray(array, &item, 1);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
T *DN_FArray_MakeArray(DN_FArray<T, N> *array, DN_USize count, DN_ZMem z_mem)
|
|
{
|
|
if (!DN_FArray_IsValid(array))
|
|
return nullptr;
|
|
T *result = DN_CArray_MakeArray(array->data, &array->size, N, count, z_mem);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
T *DN_FArray_Make(DN_FArray<T, N> *array, DN_ZMem z_mem)
|
|
{
|
|
T *result = DN_FArray_MakeArray(array, 1, z_mem);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
T *DN_FArray_InsertArray(DN_FArray<T, N> *array, DN_USize index, T const *items, DN_USize count)
|
|
{
|
|
T *result = nullptr;
|
|
if (!DN_FArray_IsValid(array))
|
|
return result;
|
|
result = DN_CArray_InsertArray(array->data, &array->size, N, index, items, count);
|
|
return result;
|
|
}
|
|
|
|
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])
|
|
{
|
|
T *result = DN_FArray_InsertArray(array, index, items, K);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
T *DN_FArray_Insert(DN_FArray<T, N> *array, DN_USize index, T const &item)
|
|
{
|
|
T *result = DN_FArray_InsertArray(array, index, &item, 1);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
T DN_FArray_PopFront(DN_FArray<T, N> *array, DN_USize count)
|
|
{
|
|
T result = DN_CArray_PopFront(array->data, &array->size, count);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
T DN_FArray_PopBack(DN_FArray<T, N> *array, DN_USize count)
|
|
{
|
|
T result = DN_CArray_PopBack(array->data, &array->size, count);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
DN_ArrayFindResult<T> DN_FArray_Find(DN_FArray<T, N> *array, T const &find)
|
|
{
|
|
DN_ArrayFindResult<T> result = DN_CArray_Find<T>(array->data, array->size, find);
|
|
return result;
|
|
}
|
|
|
|
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)
|
|
{
|
|
DN_ArrayEraseResult result = {};
|
|
if (!DN_FArray_IsValid(array) || array->size == 0 || count == 0)
|
|
return result;
|
|
result = DN_CArray_EraseRange(array->data, &array->size, begin_index, count, erase);
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
void DN_FArray_Clear(DN_FArray<T, N> *array)
|
|
{
|
|
if (array)
|
|
array->size = 0;
|
|
}
|
|
#endif // !defined(DN_NO_FARRAY)
|
|
|
|
#if !defined(DN_NO_SLICE)
|
|
template <typename T>
|
|
DN_Slice<T> DN_Slice_Init(T *const data, DN_USize size)
|
|
{
|
|
DN_Slice<T> result = {};
|
|
if (data) {
|
|
result.data = data;
|
|
result.size = size;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename T, DN_USize N>
|
|
DN_Slice<T> DN_Slice_InitCArrayCopy(DN_Arena *arena, T const (&array)[N])
|
|
{
|
|
DN_Slice<T> result = DN_Slice_Alloc<T>(arena, N, DN_ZMem_No);
|
|
if (result.data)
|
|
DN_Memcpy(result.data, array, sizeof(T) * N);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_Slice<T> DN_Slice_CopyPtr(DN_Arena *arena, T *const data, DN_USize size)
|
|
{
|
|
T *copy = DN_ArenaNewArrayCopy(arena, T, data, size);
|
|
DN_Slice<T> result = DN_Slice_Init(copy, copy ? size : 0);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_Slice<T> DN_Slice_Copy(DN_Arena *arena, DN_Slice<T> slice)
|
|
{
|
|
DN_Slice<T> result = DN_Slice_CopyPtr(arena, slice.data, slice.size);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_Slice<T> DN_Slice_Alloc(DN_Arena *arena, DN_USize size, DN_ZMem z_mem)
|
|
{
|
|
DN_Slice<T> result = {};
|
|
if (!arena || size == 0)
|
|
return result;
|
|
result.data = DN_ArenaNewArray(arena, T, size, z_mem);
|
|
if (result.data)
|
|
result.size = size;
|
|
return result;
|
|
}
|
|
|
|
#endif // !defined(DN_NO_SLICE)
|
|
|
|
#if !defined(DN_NO_DSMAP)
|
|
// NOTE: DN_DSMap //////////////////////////////////////////////////////////////////////////////////
|
|
DN_U32 const DN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49;
|
|
DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0;
|
|
|
|
template <typename T>
|
|
DN_DSMap<T> DN_DSMap_Init(DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags)
|
|
{
|
|
DN_DSMap<T> result = {};
|
|
if (!DN_CheckF(DN_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size))
|
|
return result;
|
|
if (size <= 0)
|
|
return result;
|
|
if (!DN_Check(arena))
|
|
return result;
|
|
result.arena = arena;
|
|
result.pool = DN_PoolFromArena(arena, DN_POOL_DEFAULT_ALIGN);
|
|
result.hash_to_slot = DN_ArenaNewArray(result.arena, DN_U32, size, DN_ZMem_Yes);
|
|
result.slots = DN_ArenaNewArray(result.arena, DN_DSMapSlot<T>, size, DN_ZMem_Yes);
|
|
result.occupied = 1; // For sentinel
|
|
result.size = size;
|
|
result.initial_size = size;
|
|
result.flags = flags;
|
|
DN_AssertF(result.hash_to_slot && result.slots, "We pre-allocated a block of memory sufficient in size for the 2 arrays. Maybe the pointers needed extra space because of natural alignment?");
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
void DN_DSMap_Deinit(DN_DSMap<T> *map, DN_ZMem z_mem)
|
|
{
|
|
if (!map)
|
|
return;
|
|
// TODO(doyle): Use z_mem
|
|
(void)z_mem;
|
|
DN_ArenaDeinit(map->arena);
|
|
*map = {};
|
|
}
|
|
|
|
template <typename T>
|
|
bool DN_DSMap_IsValid(DN_DSMap<T> const *map)
|
|
{
|
|
bool result = map &&
|
|
map->arena &&
|
|
map->hash_to_slot && // Hash to slot mapping array must be allocated
|
|
map->slots && // Slots array must be allocated
|
|
(map->size & (map->size - 1)) == 0 && // Must be power of two size
|
|
map->occupied >= 1; // DN_DS_MAP_SENTINEL_SLOT takes up one slot
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_U32 DN_DSMap_Hash(DN_DSMap<T> const *map, DN_DSMapKey key)
|
|
{
|
|
DN_U32 result = 0;
|
|
if (!map)
|
|
return result;
|
|
|
|
if (key.type == DN_DSMapKeyType_U64NoHash) {
|
|
result = DN_Cast(DN_U32) key.u64;
|
|
return result;
|
|
}
|
|
|
|
if (key.type == DN_DSMapKeyType_BufferAsU64NoHash) {
|
|
result = key.hash;
|
|
return result;
|
|
}
|
|
|
|
DN_U32 seed = map->hash_seed ? map->hash_seed : DN_DS_MAP_DEFAULT_HASH_SEED;
|
|
if (map->hash_function) {
|
|
map->hash_function(key, seed);
|
|
} else {
|
|
// NOTE: Courtesy of Demetri Spanos (which this hash table was inspired
|
|
// from), the following is a hashing function snippet provided for
|
|
// reliable, quick and simple quality hashing functions for hash table
|
|
// use.
|
|
// Source: https://github.com/demetri/scribbles/blob/c475464756c104c91bab83ed4e14badefef12ab5/hashing/ub_aware_hash_functions.c
|
|
|
|
char const *key_ptr = nullptr;
|
|
DN_U32 len = 0;
|
|
DN_U32 h = seed;
|
|
switch (key.type) {
|
|
case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/
|
|
case DN_DSMapKeyType_U64NoHash: DN_InvalidCodePath; /*FALLTHRU*/
|
|
case DN_DSMapKeyType_Invalid: break;
|
|
|
|
case DN_DSMapKeyType_Buffer:
|
|
key_ptr = DN_Cast(char const *) key.buffer_data;
|
|
len = key.buffer_size;
|
|
break;
|
|
|
|
case DN_DSMapKeyType_U64:
|
|
key_ptr = DN_Cast(char const *) & key.u64;
|
|
len = sizeof(key.u64);
|
|
break;
|
|
}
|
|
|
|
// Murmur3 32-bit without UB unaligned accesses
|
|
// DN_U32 mur3_32_no_UB(const void *key, int len, DN_U32 h)
|
|
|
|
// main body, work on 32-bit blocks at a time
|
|
for (DN_U32 i = 0; i < len / 4; i++) {
|
|
DN_U32 k;
|
|
memcpy(&k, &key_ptr[i * 4], sizeof(k));
|
|
|
|
k *= 0xcc9e2d51;
|
|
k = ((k << 15) | (k >> 17)) * 0x1b873593;
|
|
h = (((h ^ k) << 13) | ((h ^ k) >> 19)) * 5 + 0xe6546b64;
|
|
}
|
|
|
|
// load/mix up to 3 remaining tail bytes into a tail block
|
|
DN_U32 t = 0;
|
|
uint8_t *tail = ((uint8_t *)key_ptr) + 4 * (len / 4);
|
|
switch (len & 3) {
|
|
case 3: t ^= tail[2] << 16;
|
|
case 2: t ^= tail[1] << 8;
|
|
case 1: {
|
|
t ^= tail[0] << 0;
|
|
h ^= ((0xcc9e2d51 * t << 15) | (0xcc9e2d51 * t >> 17)) * 0x1b873593;
|
|
}
|
|
}
|
|
|
|
// finalization mix, including key length
|
|
h = ((h ^ len) ^ ((h ^ len) >> 16)) * 0x85ebca6b;
|
|
h = (h ^ (h >> 13)) * 0xc2b2ae35;
|
|
result = h ^ (h >> 16);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_U32 DN_DSMap_HashToSlotIndex(DN_DSMap<T> const *map, DN_DSMapKey key)
|
|
{
|
|
DN_Assert(key.type != DN_DSMapKeyType_Invalid);
|
|
DN_U32 result = DN_DS_MAP_SENTINEL_SLOT;
|
|
if (!DN_DSMap_IsValid(map))
|
|
return result;
|
|
|
|
result = key.hash & (map->size - 1);
|
|
for (;;) {
|
|
if (result == DN_DS_MAP_SENTINEL_SLOT) // Sentinel is reserved
|
|
result++;
|
|
|
|
if (map->hash_to_slot[result] == DN_DS_MAP_SENTINEL_SLOT) // Slot is vacant, can use
|
|
return result;
|
|
|
|
DN_DSMapSlot<T> *slot = map->slots + map->hash_to_slot[result];
|
|
if (slot->key.type == DN_DSMapKeyType_Invalid || (slot->key.hash == key.hash && slot->key == key))
|
|
return result;
|
|
|
|
result = (result + 1) & (map->size - 1);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapResult<T> DN_DSMap_Find(DN_DSMap<T> const *map, DN_DSMapKey key)
|
|
{
|
|
DN_DSMapResult<T> result = {};
|
|
if (DN_DSMap_IsValid(map)) {
|
|
DN_U32 index = DN_DSMap_HashToSlotIndex(map, key);
|
|
if (index != DN_DS_MAP_SENTINEL_SLOT && map->hash_to_slot[index] == DN_DS_MAP_SENTINEL_SLOT) {
|
|
result.slot = map->slots; // NOTE: Set to sentinel value
|
|
} else {
|
|
result.slot = map->slots + map->hash_to_slot[index];
|
|
result.found = true;
|
|
}
|
|
result.value = &result.slot->value;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapResult<T> DN_DSMap_Make(DN_DSMap<T> *map, DN_DSMapKey key)
|
|
{
|
|
DN_DSMapResult<T> result = {};
|
|
if (!DN_DSMap_IsValid(map))
|
|
return result;
|
|
|
|
DN_U32 index = DN_DSMap_HashToSlotIndex(map, key);
|
|
if (map->hash_to_slot[index] == DN_DS_MAP_SENTINEL_SLOT) {
|
|
// NOTE: Create the slot
|
|
if (index != DN_DS_MAP_SENTINEL_SLOT)
|
|
map->hash_to_slot[index] = map->occupied++;
|
|
|
|
// NOTE: Check if resize is required
|
|
bool map_is_75pct_full = (map->occupied * 4) > (map->size * 3);
|
|
if (map_is_75pct_full) {
|
|
if (!DN_DSMap_Resize(map, map->size * 2))
|
|
return result;
|
|
result = DN_DSMap_Make(map, key);
|
|
} else {
|
|
result.slot = map->slots + map->hash_to_slot[index];
|
|
result.slot->key = key; // NOTE: Assign key to new slot
|
|
if ((key.type == DN_DSMapKeyType_Buffer ||
|
|
key.type == DN_DSMapKeyType_BufferAsU64NoHash) &&
|
|
!key.no_copy_buffer)
|
|
result.slot->key.buffer_data = DN_PoolNewArrayCopy(&map->pool, char, key.buffer_data, key.buffer_size);
|
|
}
|
|
} else {
|
|
result.slot = map->slots + map->hash_to_slot[index];
|
|
result.found = true;
|
|
}
|
|
|
|
result.value = &result.slot->value;
|
|
DN_Assert(result.slot->key.type != DN_DSMapKeyType_Invalid);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapResult<T> DN_DSMap_Set(DN_DSMap<T> *map, DN_DSMapKey key, T const &value)
|
|
{
|
|
DN_DSMapResult<T> result = {};
|
|
if (!DN_DSMap_IsValid(map))
|
|
return result;
|
|
|
|
result = DN_DSMap_Make(map, key);
|
|
result.slot->value = value;
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapResult<T> DN_DSMap_FindKeyU64(DN_DSMap<T> const *map, DN_U64 key)
|
|
{
|
|
DN_DSMapKey map_key = DN_DSMap_KeyU64(map, key);
|
|
DN_DSMapResult<T> result = DN_DSMap_Find(map, map_key);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapResult<T> DN_DSMap_MakeKeyU64(DN_DSMap<T> *map, DN_U64 key)
|
|
{
|
|
DN_DSMapKey map_key = DN_DSMap_KeyU64(map, key);
|
|
DN_DSMapResult<T> result = DN_DSMap_Make(map, map_key);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapResult<T> DN_DSMap_SetKeyU64(DN_DSMap<T> *map, DN_U64 key, T const &value)
|
|
{
|
|
DN_DSMapKey map_key = DN_DSMap_KeyU64(map, key);
|
|
DN_DSMapResult<T> result = DN_DSMap_Set(map, map_key, value);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapResult<T> DN_DSMap_FindKeyStr8(DN_DSMap<T> const *map, DN_Str8 key)
|
|
{
|
|
DN_DSMapKey map_key = DN_DSMap_KeyStr8(map, key);
|
|
DN_DSMapResult<T> result = DN_DSMap_Find(map, map_key);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapResult<T> DN_DSMap_MakeKeyStr8(DN_DSMap<T> *map, DN_Str8 key)
|
|
{
|
|
DN_DSMapKey map_key = DN_DSMap_KeyStr8(map, key);
|
|
DN_DSMapResult<T> result = DN_DSMap_Make(map, map_key);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapResult<T> DN_DSMap_SetKeyStr8(DN_DSMap<T> *map, DN_Str8 key, T const &value)
|
|
{
|
|
DN_DSMapKey map_key = DN_DSMap_KeyStr8(map, key);
|
|
DN_DSMapResult<T> result = DN_DSMap_Set(map, map_key);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
bool DN_DSMap_Resize(DN_DSMap<T> *map, DN_U32 size)
|
|
{
|
|
if (!DN_DSMap_IsValid(map) || size < map->occupied || size < map->initial_size)
|
|
return false;
|
|
|
|
DN_Arena *prev_arena = map->arena;
|
|
DN_Arena new_arena = {};
|
|
new_arena.mem_funcs = prev_arena->mem_funcs;
|
|
new_arena.flags = prev_arena->flags;
|
|
new_arena.label = prev_arena->label;
|
|
new_arena.prev = prev_arena->prev;
|
|
new_arena.next = prev_arena->next;
|
|
|
|
DN_DSMap<T> new_map = DN_DSMap_Init<T>(&new_arena, size, map->flags);
|
|
if (!DN_DSMap_IsValid(&new_map))
|
|
return false;
|
|
|
|
new_map.initial_size = map->initial_size;
|
|
for (DN_U32 old_index = 1 /*Sentinel*/; old_index < map->occupied; old_index++) {
|
|
DN_DSMapSlot<T> *old_slot = map->slots + old_index;
|
|
DN_DSMapKey old_key = old_slot->key;
|
|
if (old_key.type == DN_DSMapKeyType_Invalid)
|
|
continue;
|
|
DN_DSMap_Set(&new_map, old_key, old_slot->value);
|
|
}
|
|
|
|
if ((map->flags & DN_DSMapFlags_DontFreeArenaOnResize) == 0)
|
|
DN_DSMap_Deinit(map, DN_ZMem_No);
|
|
*map = new_map; // Update the map inplace
|
|
map->arena = prev_arena; // Restore the previous arena pointer, it's been de-init-ed
|
|
*map->arena = new_arena; // Re-init the old arena with the new data
|
|
map->pool.arena = map->arena;
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
bool DN_DSMap_Erase(DN_DSMap<T> *map, DN_DSMapKey key)
|
|
{
|
|
if (!DN_DSMap_IsValid(map))
|
|
return false;
|
|
|
|
DN_U32 index = DN_DSMap_HashToSlotIndex(map, key);
|
|
if (index == 0)
|
|
return true;
|
|
|
|
DN_U32 slot_index = map->hash_to_slot[index];
|
|
if (slot_index == DN_DS_MAP_SENTINEL_SLOT)
|
|
return false;
|
|
|
|
// NOTE: Mark the slot as unoccupied
|
|
map->hash_to_slot[index] = DN_DS_MAP_SENTINEL_SLOT;
|
|
|
|
DN_DSMapSlot<T> *slot = map->slots + slot_index;
|
|
if (!slot->key.no_copy_buffer)
|
|
DN_PoolDealloc(&map->pool, DN_Cast(void *) slot->key.buffer_data);
|
|
*slot = {}; // TODO: Optional?
|
|
|
|
if (map->occupied > 1 /*Sentinel*/) {
|
|
// NOTE: Repair the hash chain, e.g. rehash all the items after the removed
|
|
// element and reposition them if necessary.
|
|
for (DN_U32 probe_index = index;;) {
|
|
probe_index = (probe_index + 1) & (map->size - 1);
|
|
if (map->hash_to_slot[probe_index] == DN_DS_MAP_SENTINEL_SLOT)
|
|
break;
|
|
|
|
DN_DSMapSlot<T> *probe = map->slots + map->hash_to_slot[probe_index];
|
|
DN_U32 new_index = probe->key.hash & (map->size - 1);
|
|
if (index <= probe_index) {
|
|
if (index < new_index && new_index <= probe_index)
|
|
continue;
|
|
} else {
|
|
if (index < new_index || new_index <= probe_index)
|
|
continue;
|
|
}
|
|
|
|
map->hash_to_slot[index] = map->hash_to_slot[probe_index];
|
|
map->hash_to_slot[probe_index] = DN_DS_MAP_SENTINEL_SLOT;
|
|
index = probe_index;
|
|
}
|
|
|
|
// NOTE: We have erased a slot from the hash table, this leaves a gap
|
|
// in our contiguous array. After repairing the chain, the hash mapping
|
|
// is correct.
|
|
// We will now fill in the vacant spot that we erased using the last
|
|
// element in the slot list.
|
|
if (map->occupied >= 3 /*Ignoring sentinel, at least 2 other elements to unstable erase*/) {
|
|
DN_U32 last_index = map->occupied - 1;
|
|
if (last_index != slot_index) {
|
|
// NOTE: Copy in last slot to the erase slot
|
|
DN_DSMapSlot<T> *last_slot = map->slots + last_index;
|
|
map->slots[slot_index] = *last_slot;
|
|
|
|
// NOTE: Update the hash-to-slot mapping for the value that was copied in
|
|
DN_U32 hash_to_slot_index = DN_DSMap_HashToSlotIndex(map, last_slot->key);
|
|
map->hash_to_slot[hash_to_slot_index] = slot_index;
|
|
*last_slot = {}; // TODO: Optional?
|
|
}
|
|
}
|
|
}
|
|
|
|
map->occupied--;
|
|
bool map_is_below_25pct_full = (map->occupied * 4) < (map->size * 1);
|
|
if (map_is_below_25pct_full && (map->size / 2) >= map->initial_size)
|
|
DN_DSMap_Resize(map, map->size / 2);
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
bool DN_DSMap_EraseKeyU64(DN_DSMap<T> *map, DN_U64 key)
|
|
{
|
|
DN_DSMapKey map_key = DN_DSMap_KeyU64(map, key);
|
|
bool result = DN_DSMap_Erase(map, map_key);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
bool DN_DSMap_EraseKeyStr8(DN_DSMap<T> *map, DN_Str8 key)
|
|
{
|
|
DN_DSMapKey map_key = DN_DSMap_KeyStr8(map, key);
|
|
bool result = DN_DSMap_Erase(map, map_key);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapKey DN_DSMap_KeyBuffer(DN_DSMap<T> const *map, void const *data, DN_USize size)
|
|
{
|
|
DN_Assert(size > 0 && size <= UINT32_MAX);
|
|
DN_DSMapKey result = {};
|
|
result.type = DN_DSMapKeyType_Buffer;
|
|
result.buffer_data = data;
|
|
result.buffer_size = DN_Cast(DN_U32) size;
|
|
result.hash = DN_DSMap_Hash(map, result);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapKey DN_DSMap_KeyBufferAsU64NoHash(DN_DSMap<T> const *map, void const *data, DN_U32 size)
|
|
{
|
|
DN_DSMapKey result = {};
|
|
result.type = DN_DSMapKeyType_BufferAsU64NoHash;
|
|
result.buffer_data = data;
|
|
result.buffer_size = DN_Cast(DN_U32) size;
|
|
DN_Assert(size >= sizeof(result.hash));
|
|
DN_Memcpy(&result.hash, data, sizeof(result.hash));
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapKey DN_DSMap_KeyU64(DN_DSMap<T> const *map, DN_U64 u64)
|
|
{
|
|
DN_DSMapKey result = {};
|
|
result.type = DN_DSMapKeyType_U64;
|
|
result.u64 = u64;
|
|
result.hash = DN_DSMap_Hash(map, result);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_DSMapKey DN_DSMap_KeyStr8(DN_DSMap<T> const *map, DN_Str8 string)
|
|
{
|
|
DN_DSMapKey result = DN_DSMap_KeyBuffer(map, string.data, string.size);
|
|
return result;
|
|
}
|
|
#endif // !defined(DN_NO_DSMAP)
|
|
|
|
#if !defined(DN_NO_LIST)
|
|
// NOTE: DN_List ///////////////////////////////////////////////////////////////////////////////////
|
|
template <typename T>
|
|
DN_List<T> DN_List_Init(DN_USize chunk_size)
|
|
{
|
|
DN_List<T> result = {};
|
|
result.chunk_size = chunk_size;
|
|
return result;
|
|
}
|
|
|
|
template <typename T, size_t N>
|
|
DN_List<T> DN_List_InitCArray(DN_Arena *arena, DN_USize chunk_size, T const (&array)[N])
|
|
{
|
|
DN_List<T> result = DN_List_Init<T>(arena, chunk_size);
|
|
for (DN_ForIndexU(index, N))
|
|
DN_List_Add(&result, array[index]);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_List<T> DN_List_InitSliceCopy(DN_Arena *arena, DN_USize chunk_size, DN_Slice<T> slice)
|
|
{
|
|
DN_List<T> result = DN_List_Init<T>(arena, chunk_size);
|
|
for (DN_ForIndexU(index, slice.size))
|
|
DN_List_Add(&result, slice.data[index]);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_API bool DN_List_AttachTail_(DN_List<T> *list, DN_ListChunk<T> *tail)
|
|
{
|
|
if (!tail)
|
|
return false;
|
|
|
|
if (list->tail) {
|
|
list->tail->next = tail;
|
|
tail->prev = list->tail;
|
|
}
|
|
|
|
list->tail = tail;
|
|
|
|
if (!list->head)
|
|
list->head = list->tail;
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_API DN_ListChunk<T> *DN_List_AllocArena_(DN_List<T> *list, DN_Arena *arena, DN_USize count)
|
|
{
|
|
auto *result = DN_ArenaNew(arena, DN_ListChunk<T>, DN_ZMem_Yes);
|
|
DN_ArenaTempMem tmem = DN_ArenaTempMemBegin(arena);
|
|
if (!result)
|
|
return nullptr;
|
|
|
|
DN_USize items = DN_Max(list->chunk_size, count);
|
|
result->data = DN_ArenaNewArray(arena, T, items, DN_ZMem_Yes);
|
|
result->size = items;
|
|
if (!result->data) {
|
|
DN_ArenaTempMemEnd(tmem);
|
|
result = nullptr;
|
|
}
|
|
|
|
DN_List_AttachTail_(list, result);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_API DN_ListChunk<T> *DN_List_AllocPool_(DN_List<T> *list, DN_Pool *pool, DN_USize count)
|
|
{
|
|
auto *result = DN_PoolNew(pool, DN_ListChunk<T>);
|
|
if (!result)
|
|
return nullptr;
|
|
|
|
DN_USize items = DN_Max(list->chunk_size, count);
|
|
result->data = DN_PoolNewArray(pool, T, items);
|
|
result->size = items;
|
|
if (!result->data) {
|
|
DN_PoolDealloc(result);
|
|
result = nullptr;
|
|
}
|
|
|
|
DN_List_AttachTail_(list, result);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_API T *DN_List_MakeArena(DN_List<T> *list, DN_Arena *arena, DN_USize count)
|
|
{
|
|
if (list->chunk_size == 0)
|
|
list->chunk_size = 128;
|
|
|
|
if (!list->tail || (list->tail->count + count) > list->tail->size) {
|
|
if (!DN_List_AllocArena_(list, arena, count))
|
|
return nullptr;
|
|
}
|
|
|
|
T *result = list->tail->data + list->tail->count;
|
|
list->tail->count += count;
|
|
list->count += count;
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_API T *DN_List_MakePool(DN_List<T> *list, DN_Pool *pool, DN_USize count)
|
|
{
|
|
if (list->chunk_size == 0)
|
|
list->chunk_size = 128;
|
|
|
|
if (!list->tail || (list->tail->count + count) > list->tail->size) {
|
|
if (!DN_List_AllocPool_(list, pool, count))
|
|
return nullptr;
|
|
}
|
|
|
|
T *result = list->tail->data + list->tail->count;
|
|
list->tail->count += count;
|
|
list->count += count;
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_API T *DN_List_AddArena(DN_List<T> *list, DN_Arena *arena, T const &value)
|
|
{
|
|
T *result = DN_List_MakeArena(list, arena, 1);
|
|
*result = value;
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_API T *DN_List_AddPool(DN_List<T> *list, DN_Pool *pool, T const &value)
|
|
{
|
|
T *result = DN_List_MakePool(list, pool, 1);
|
|
*result = value;
|
|
return result;
|
|
}
|
|
|
|
template <typename T, size_t N>
|
|
DN_API bool DN_List_AddCArray(DN_List<T> *list, T const (&array)[N])
|
|
{
|
|
if (!list)
|
|
return false;
|
|
for (T const &item : array)
|
|
if (!DN_List_Add(list, item))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_API void DN_List_AddListArena(DN_List<T> *list, DN_Arena *arena, DN_List<T> other)
|
|
{
|
|
if (!list)
|
|
return;
|
|
// TODO(doyle): Copy chunk by chunk
|
|
for (DN_ListIterator<T> it = {}; DN_List_Iterate(&other, &it, 0 /*start_index*/);)
|
|
DN_List_AddArena(list, arena, *it.data);
|
|
}
|
|
|
|
template <typename T>
|
|
DN_API void DN_List_AddListPool(DN_List<T> *list, DN_Pool *pool, DN_List<T> other)
|
|
{
|
|
if (!list)
|
|
return;
|
|
// TODO(doyle): Copy chunk by chunk
|
|
for (DN_ListIterator<T> it = {}; DN_List_Iterate(&other, &it, 0 /*start_index*/);)
|
|
DN_List_AddPool(list, pool, *it.data);
|
|
}
|
|
|
|
template <typename T>
|
|
void DN_List_Clear(DN_List<T> *list)
|
|
{
|
|
if (!list)
|
|
return;
|
|
list->head = list->tail = nullptr;
|
|
list->count = 0;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_API bool DN_List_Iterate(DN_List<T> *list, DN_ListIterator<T> *it, DN_USize start_index)
|
|
{
|
|
bool result = false;
|
|
if (!list || !it || list->chunk_size <= 0)
|
|
return result;
|
|
|
|
if (it->init) {
|
|
it->index++;
|
|
} else {
|
|
*it = {};
|
|
if (start_index == 0) {
|
|
it->chunk = list->head;
|
|
} else {
|
|
DN_List_At(list, start_index, &it->chunk);
|
|
if (list->chunk_size > 0)
|
|
it->chunk_data_index = start_index % list->chunk_size;
|
|
}
|
|
|
|
it->init = true;
|
|
}
|
|
|
|
if (it->chunk) {
|
|
if (it->chunk_data_index >= it->chunk->count) {
|
|
it->chunk = it->chunk->next;
|
|
it->chunk_data_index = 0;
|
|
}
|
|
|
|
if (it->chunk) {
|
|
it->data = it->chunk->data + it->chunk_data_index++;
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
if (!it->chunk)
|
|
DN_Assert(result == false);
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_API T *DN_List_At(DN_List<T> *list, DN_USize index, DN_ListChunk<T> **at_chunk)
|
|
{
|
|
if (!list || index >= list->count || !list->head)
|
|
return nullptr;
|
|
|
|
// NOTE: Scan forwards to the chunk we need. We don't have random access to chunks
|
|
DN_ListChunk<T> **chunk = &list->head;
|
|
DN_USize running_index = index;
|
|
for (; (*chunk) && running_index >= (*chunk)->size; chunk = &((*chunk)->next))
|
|
running_index -= (*chunk)->size;
|
|
|
|
T *result = nullptr;
|
|
if (*chunk)
|
|
result = (*chunk)->data + running_index;
|
|
|
|
if (result && at_chunk)
|
|
*at_chunk = *chunk;
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
DN_Slice<T> DN_List_ToSliceCopy(DN_List<T> const *list, DN_Arena *arena)
|
|
{
|
|
// TODO(doyle): Chunk memcopies is much faster
|
|
DN_Slice<T> result = DN_Slice_Alloc<T>(arena, list->count, DN_ZMem_No);
|
|
if (result.size) {
|
|
DN_USize slice_index = 0;
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(6011) // Dereferencing NULL pointer 'x'
|
|
for (DN_ListIterator<T> it = {}; DN_List_Iterate<T>(DN_Cast(DN_List<T> *) list, &it, 0);)
|
|
result.data[slice_index++] = *it.data;
|
|
DN_MSVC_WARNING_POP
|
|
DN_Assert(slice_index == result.size);
|
|
}
|
|
return result;
|
|
}
|
|
#endif // !defined(DN_NO_LIST)
|
|
|
|
// NOTE: DN_Slice //////////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_Str8 DN_Slice_Str8Render(DN_Arena *arena, DN_Slice<DN_Str8> array, DN_Str8 separator)
|
|
{
|
|
DN_Str8 result = {};
|
|
if (!arena)
|
|
return result;
|
|
|
|
DN_USize total_size = 0;
|
|
for (DN_USize index = 0; index < array.size; index++) {
|
|
if (index)
|
|
total_size += separator.size;
|
|
DN_Str8 item = array.data[index];
|
|
total_size += item.size;
|
|
}
|
|
|
|
result = DN_Str8FromArena(arena, total_size, DN_ZMem_No);
|
|
if (result.data) {
|
|
DN_USize write_index = 0;
|
|
for (DN_USize index = 0; index < array.size; index++) {
|
|
if (index) {
|
|
DN_Memcpy(result.data + write_index, separator.data, separator.size);
|
|
write_index += separator.size;
|
|
}
|
|
DN_Str8 item = array.data[index];
|
|
DN_Memcpy(result.data + write_index, item.data, item.size);
|
|
write_index += item.size;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Slice_Str8RenderSpaceSeparated(DN_Arena *arena, DN_Slice<DN_Str8> array)
|
|
{
|
|
DN_Str8 result = DN_Slice_Str8Render(arena, array, DN_Str8Lit(" "));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str16 DN_Slice_Str16Render(DN_Arena *arena, DN_Slice<DN_Str16> array, DN_Str16 separator)
|
|
{
|
|
DN_Str16 result = {};
|
|
if (!arena)
|
|
return result;
|
|
|
|
DN_USize total_size = 0;
|
|
for (DN_USize index = 0; index < array.size; index++) {
|
|
if (index)
|
|
total_size += separator.size;
|
|
DN_Str16 item = array.data[index];
|
|
total_size += item.size;
|
|
}
|
|
|
|
result = {DN_ArenaNewArray(arena, wchar_t, total_size + 1, DN_ZMem_No), total_size};
|
|
if (result.data) {
|
|
DN_USize write_index = 0;
|
|
for (DN_USize index = 0; index < array.size; index++) {
|
|
if (index) {
|
|
DN_Memcpy(result.data + write_index, separator.data, separator.size * sizeof(result.data[0]));
|
|
write_index += separator.size;
|
|
}
|
|
DN_Str16 item = array.data[index];
|
|
DN_Memcpy(result.data + write_index, item.data, item.size * sizeof(result.data[0]));
|
|
write_index += item.size;
|
|
}
|
|
}
|
|
|
|
result.data[total_size] = 0;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str16 DN_Slice_Str16RenderSpaceSeparated(DN_Arena *arena, DN_Slice<DN_Str16> array)
|
|
{
|
|
DN_Str16 result = DN_Slice_Str16Render(arena, array, DN_Str16Lit(L" "));
|
|
return result;
|
|
}
|
|
|
|
#if !defined(DN_NO_DSMAP)
|
|
// NOTE: DN_DSMap //////////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_DSMapKey DN_DSMap_KeyU64NoHash(DN_U64 u64)
|
|
{
|
|
DN_DSMapKey result = {};
|
|
result.type = DN_DSMapKeyType_U64NoHash;
|
|
result.u64 = u64;
|
|
result.hash = DN_Cast(DN_U32) u64;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_DSMap_KeyEquals(DN_DSMapKey lhs, DN_DSMapKey rhs)
|
|
{
|
|
bool result = false;
|
|
if (lhs.type == rhs.type && lhs.hash == rhs.hash) {
|
|
switch (lhs.type) {
|
|
case DN_DSMapKeyType_Invalid: result = true; break;
|
|
case DN_DSMapKeyType_U64NoHash: result = true; break;
|
|
case DN_DSMapKeyType_U64: result = lhs.u64 == rhs.u64; break;
|
|
|
|
case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/
|
|
case DN_DSMapKeyType_Buffer: {
|
|
if (lhs.buffer_size == rhs.buffer_size)
|
|
result = DN_Memcmp(lhs.buffer_data, rhs.buffer_data, lhs.buffer_size) == 0;
|
|
} break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator==(DN_DSMapKey lhs, DN_DSMapKey rhs)
|
|
{
|
|
bool result = DN_DSMap_KeyEquals(lhs, rhs);
|
|
return result;
|
|
}
|
|
#endif // !defined(DN_NO_DSMAP)
|
|
// DN: Single header generator inlined this file => #include "Base/dn_base_log.cpp"
|
|
#define DN_BASE_LOG_CPP
|
|
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.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_Str8Lit("DEBUG"); break;
|
|
case DN_LOGType_Info: type_str8 = DN_Str8Lit("INFO "); break;
|
|
case DN_LOGType_Warning: type_str8 = DN_Str8Lit("WARN"); break;
|
|
case DN_LOGType_Error: type_str8 = DN_Str8Lit("ERROR"); break;
|
|
case DN_LOGType_Count: type_str8 = DN_Str8Lit("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_Str8Lit(DN_LOG_BoldEscapeCode);
|
|
reset_esc = DN_Str8Lit(DN_LOG_ResetEscapeCode);
|
|
colour_esc = DN_LOG_ColourEscapeCodeStr8FromRGB(DN_LOGColourType_Fg, style.r, style.g, style.b);
|
|
}
|
|
|
|
DN_Str8 file_name = DN_Str8FileNameFromPath(call_site.file);
|
|
DN_GCC_WARNING_PUSH
|
|
DN_GCC_WARNING_DISABLE(-Wformat)
|
|
DN_GCC_WARNING_DISABLE(-Wformat-extra-args)
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(4477)
|
|
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_MSVC_WARNING_POP // '%S' requires an argument of type 'wchar_t *', but variadic argument 7 has type 'DN_Str8'
|
|
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;
|
|
}
|
|
// DN: Single header generator inlined this file => #include "Base/dn_base_leak.cpp"
|
|
#define DN_BASE_LEAK_CPP
|
|
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
|
|
DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, bool leak_permitted)
|
|
{
|
|
if (!ptr)
|
|
return;
|
|
|
|
DN_TicketMutex_Begin(&leak->alloc_table_mutex);
|
|
DN_DEFER
|
|
{
|
|
DN_TicketMutex_End(&leak->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_StackTraceWalkStr8FromHeap(128, 3 /*skip*/);
|
|
DN_DSMap<DN_LeakAlloc> *alloc_table = &leak->alloc_table;
|
|
DN_DSMapResult<DN_LeakAlloc> alloc_entry = DN_DSMap_MakeKeyU64(alloc_table, DN_Cast(DN_U64) ptr);
|
|
DN_LeakAlloc *alloc = alloc_entry.value;
|
|
if (alloc_entry.found) {
|
|
if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) {
|
|
DN_Str8x32 alloc_size = DN_ByteCountStr8x32(alloc->size);
|
|
DN_Str8x32 new_alloc_size = DN_ByteCountStr8x32(size);
|
|
DN_HardAssertF(
|
|
alloc->flags & DN_LeakAllocFlag_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_LeakTrackDealloc()\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_Str8PrintFmt(alloc_size),
|
|
DN_Str8PrintFmt(alloc->stack_trace),
|
|
DN_Str8PrintFmt(new_alloc_size),
|
|
DN_Str8PrintFmt(stack_trace));
|
|
}
|
|
|
|
// NOTE: Pointer was reused, clean up the prior entry
|
|
leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.size;
|
|
leak->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_LeakAllocFlag_LeakPermitted : 0;
|
|
leak->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.size;
|
|
}
|
|
|
|
DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr)
|
|
{
|
|
if (!ptr)
|
|
return;
|
|
|
|
DN_TicketMutex_Begin(&leak->alloc_table_mutex);
|
|
DN_DEFER
|
|
{
|
|
DN_TicketMutex_End(&leak->alloc_table_mutex);
|
|
};
|
|
|
|
DN_Str8 stack_trace = DN_StackTraceWalkStr8FromHeap(128, 3 /*skip*/);
|
|
DN_DSMap<DN_LeakAlloc> *alloc_table = &leak->alloc_table;
|
|
DN_DSMapResult<DN_LeakAlloc> 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_LeakAlloc *alloc = alloc_entry.value;
|
|
if (alloc->flags & DN_LeakAllocFlag_Freed) {
|
|
DN_Str8x32 freed_size = DN_ByteCountStr8x32(alloc->freed_size);
|
|
DN_HardAssertF((alloc->flags & DN_LeakAllocFlag_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_Str8PrintFmt(freed_size),
|
|
DN_Str8PrintFmt(alloc->stack_trace),
|
|
DN_Str8PrintFmt(alloc->freed_stack_trace),
|
|
DN_Str8PrintFmt(stack_trace));
|
|
}
|
|
|
|
DN_Assert(alloc->freed_stack_trace.size == 0);
|
|
alloc->flags |= DN_LeakAllocFlag_Freed;
|
|
alloc->freed_stack_trace = stack_trace;
|
|
leak->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.size;
|
|
}
|
|
|
|
DN_API void DN_LeakDump_(DN_LeakTracker *leak)
|
|
{
|
|
DN_U64 leak_count = 0;
|
|
DN_U64 leaked_bytes = 0;
|
|
for (DN_USize index = 1; index < leak->alloc_table.occupied; index++) {
|
|
DN_DSMapSlot<DN_LeakAlloc> *slot = leak->alloc_table.slots + index;
|
|
DN_LeakAlloc *alloc = &slot->value;
|
|
bool alloc_leaked = (alloc->flags & DN_LeakAllocFlag_Freed) == 0;
|
|
bool leak_permitted = (alloc->flags & DN_LeakAllocFlag_LeakPermitted);
|
|
if (alloc_leaked && !leak_permitted) {
|
|
leaked_bytes += alloc->size;
|
|
leak_count++;
|
|
DN_Str8x32 alloc_size = DN_ByteCountStr8x32(alloc->size);
|
|
DN_LOG_WarningF(
|
|
"Pointer (0x%p) leaked %.*s at:\n"
|
|
"%.*s",
|
|
alloc->ptr,
|
|
DN_Str8PrintFmt(alloc_size),
|
|
DN_Str8PrintFmt(alloc->stack_trace));
|
|
}
|
|
}
|
|
|
|
if (leak_count) {
|
|
DN_Str8x32 leak_size = DN_ByteCountStr8x32(leaked_bytes);
|
|
DN_LOG_WarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DN_Str8PrintFmt(leak_size));
|
|
}
|
|
}
|
|
#define DN_OS_INC_CPP
|
|
|
|
// DN: Single header generator inlined this file => #include "OS/dn_os_tls.cpp"
|
|
#define DN_OS_TLSCPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "dn_os_tls.h"
|
|
#if !defined(DN_OS_TLS_H)
|
|
#define DN_OS_TLS_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_os_inc.h"
|
|
#endif
|
|
|
|
// 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;
|
|
};
|
|
|
|
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)
|
|
|
|
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)
|
|
#endif
|
|
|
|
// 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_ArenaTempMemBegin(arena);
|
|
destructed = false;
|
|
push_arena = push_tmem;
|
|
if (push_arena)
|
|
DN_OS_TLSPushArena(arena);
|
|
}
|
|
|
|
DN_OSTLSTMem::~DN_OSTLSTMem()
|
|
{
|
|
DN_Assert(destructed == false);
|
|
DN_ArenaTempMemEnd(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.
|
|
for (DN_ForItCArray(it, DN_Arena, tls->arenas)) {
|
|
DN_Arena *arena = it.data;
|
|
switch (DN_Cast(DN_OSTLSArena) it.index) {
|
|
default: *arena = DN_ArenaFromVMem(reserve, commit, DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_NoAllocTrack); break;
|
|
case DN_OSTLSArena_ErrorSink: *arena = DN_ArenaFromVMem(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 = {};
|
|
for (DN_ForItCArray(it, DN_Arena, tls->arenas))
|
|
DN_ArenaDeinit(it.data);
|
|
}
|
|
|
|
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_RawAssert(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_ArenaPos(result->arena);
|
|
|
|
if (tls->err_sink.stack_size == DN_ArrayCountU(err->stack)) {
|
|
DN_Str8Builder builder = DN_Str8BuilderFromTLS();
|
|
DN_USize counter = 0;
|
|
for (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_Str8BuilderAppendF' Actual type: 'struct DN_Str8'.
|
|
DN_Str8BuilderAppendF(&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_Str8BuilderBuildFromTLS(&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_ArenaPopTo(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_ArenaNew(arena, DN_OSErrSinkMsg, DN_ZMem_Yes);
|
|
entry->msg = DN_Str8FromStr8Arena(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_ArenaPopTo(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_Str8FileNameFromPath(it->call_site.file);
|
|
DN_Str8BuilderAppendF(builder,
|
|
"%.*s:%05I32u:%.*s %.*s",
|
|
DN_Str8PrintFmt(file_name),
|
|
it->call_site.line,
|
|
DN_Str8PrintFmt(it->call_site.function),
|
|
DN_Str8PrintFmt(it->msg));
|
|
} else {
|
|
// NOTE: More than one message
|
|
for (DN_OSErrSinkMsg *it = msg; it != end; it = it->next) {
|
|
DN_Str8 file_name = DN_Str8FileNameFromPath(it->call_site.file);
|
|
DN_Str8BuilderAppendF(builder,
|
|
"%s - %.*s:%05I32u:%.*s%s%.*s",
|
|
it == msg ? "" : "\n",
|
|
DN_Str8PrintFmt(file_name),
|
|
it->call_site.line,
|
|
DN_Str8PrintFmt(it->call_site.function),
|
|
it->msg.size ? " " : "",
|
|
DN_Str8PrintFmt(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_Str8BuilderFromTLS();
|
|
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_ArenaPopTo(err->arena, arena_pos);
|
|
|
|
result = DN_Str8BuilderBuild(&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_Str8BuilderFromTLS();
|
|
if (err_msg.size) {
|
|
DN_Str8BuilderAppendRef(&builder, err_msg);
|
|
DN_Str8BuilderAppendRef(&builder, DN_Str8Lit(":"));
|
|
} else {
|
|
DN_Str8BuilderAppendRef(&builder, DN_Str8Lit("Error(s) encountered:"));
|
|
}
|
|
|
|
if (msg->next) // NOTE: More than 1 message
|
|
DN_Str8BuilderAppendRef(&builder, DN_Str8Lit("\n"));
|
|
DN_OS_ErrSinkAddMsgToStr8Builder(&builder, msg, nullptr);
|
|
|
|
DN_Str8 log = DN_Str8BuilderBuildFromTLS(&builder);
|
|
DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Error), call_site, "%.*s", DN_Str8PrintFmt(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_Str8FromFmtVArena(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_Str8FromFmtVArena(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_ArenaNew(err->arena, DN_OSErrSinkMsg, DN_ZMem_Yes);
|
|
if (DN_Check(msg)) {
|
|
msg->msg = DN_Str8FromFmtVArena(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);
|
|
}
|
|
|
|
// DN: Single header generator inlined this file => #include "OS/dn_os.cpp"
|
|
#define DN_OS_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_os_inc.h"
|
|
#endif
|
|
|
|
#if defined(DN_PLATFORM_POSIX)
|
|
#include <sys/sysinfo.h> // get_nprocs
|
|
#include <unistd.h> // getpagesize
|
|
#endif
|
|
|
|
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_Str8FromPtr(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_Str8Lit("\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_Str8FromPtr(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_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_Str8PrintFmt(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_ArenaStatStr(&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_ArenaStatStr(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_Str8PrintFmt(file_path));
|
|
#else
|
|
(void)file_path;
|
|
#endif // #if defined(DN_DEBUG_THREAD_CONTEXT)
|
|
}
|
|
|
|
DN_API DN_Str8 DN_OS_BytesFromHexPtrArenaFrame(void const *hex, DN_USize hex_count)
|
|
{
|
|
DN_Arena *frame_arena = DN_OS_TLSFrameArena();
|
|
DN_Str8 result = DN_BytesFromHexPtrArena(hex, hex_count, frame_arena);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_OS_BytesFromHexStr8ArenaFrame(DN_Str8 hex)
|
|
{
|
|
DN_Str8 result = DN_OS_BytesFromHexPtrArenaFrame(hex.data, hex.size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_OS_HexFromBytesPtrArenaFrame(void const *bytes, DN_USize bytes_count)
|
|
{
|
|
DN_Arena *frame_arena = DN_OS_TLSFrameArena();
|
|
DN_Str8 result = DN_HexFromBytesPtrArena(bytes, bytes_count, frame_arena);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_OS_HexFromBytesPtrArenaTLS(void const *bytes, DN_USize bytes_count)
|
|
{
|
|
DN_Arena *tls_arena = DN_OS_TLSArena();
|
|
DN_Str8 result = DN_HexFromBytesPtrArena(bytes, bytes_count, tls_arena);
|
|
return result;
|
|
}
|
|
|
|
// 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 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_Str8Lit("/"), DN_Str8Lit("\\")};
|
|
DN_Str8BSplitResult split = DN_Str8BSplitLastArray(exe_path, separators, DN_ArrayCountU(separators));
|
|
result = DN_Str8FromStr8Arena(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_CPUGetTSC();
|
|
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_CPUGetTSC();
|
|
uint64_t tsc_elapsed = tsc_end - tsc_begin;
|
|
result = tsc_elapsed / os_elapsed * os_frequency;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathIsOlderThan(DN_Str8 path, DN_Str8 check_against)
|
|
{
|
|
DN_OSPathInfo file_info = DN_OS_PathInfo(path);
|
|
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;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_OS_FileReadAll(DN_Allocator alloc_type, void *allocator, DN_Str8 path, DN_OSErrSink *err)
|
|
{
|
|
// NOTE: Query file size
|
|
DN_Str8 result = {};
|
|
DN_OSPathInfo path_info = DN_OS_PathInfo(path);
|
|
if (!path_info.exists) {
|
|
DN_OS_ErrSinkAppendF(err, 1, "File does not exist/could not be queried for reading '%.*s'", DN_Str8PrintFmt(path));
|
|
return result;
|
|
}
|
|
|
|
// NOTE: Allocate
|
|
DN_ArenaTempMem arena_tmp = {};
|
|
if (alloc_type == DN_Allocator_Arena) {
|
|
DN_Arena *arena = DN_Cast(DN_Arena *) allocator;
|
|
arena_tmp = DN_ArenaTempMemBegin(arena);
|
|
result = DN_Str8FromArena(arena, path_info.size, DN_ZMem_No);
|
|
} else {
|
|
DN_Pool *pool = DN_Cast(DN_Pool *) allocator;
|
|
result = DN_Str8FromPool(pool, path_info.size);
|
|
}
|
|
|
|
if (!result.data) {
|
|
DN_Str8x32 bytes_str = DN_ByteCountStr8x32(path_info.size);
|
|
DN_OS_ErrSinkAppendF(err, 1 /*err_code*/, "Failed to allocate %.*s for reading file '%.*s'", DN_Str8PrintFmt(bytes_str), DN_Str8PrintFmt(path));
|
|
return result;
|
|
}
|
|
|
|
// NOTE: Read all
|
|
DN_OSFile file = DN_OS_FileOpen(path, DN_OSFileOpen_OpenIfExist, DN_OSFileAccess_Read, err);
|
|
DN_OSFileRead read = DN_OS_FileRead(&file, result.data, result.size, err);
|
|
if (file.error || !read.success) {
|
|
if (alloc_type == DN_Allocator_Arena) {
|
|
DN_ArenaTempMemEnd(arena_tmp);
|
|
} else {
|
|
DN_Pool *pool = DN_Cast(DN_Pool *) allocator;
|
|
DN_PoolDealloc(pool, result.data);
|
|
}
|
|
result = {};
|
|
}
|
|
|
|
DN_OS_FileClose(&file);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_OS_FileReadAllArena(DN_Arena *arena, DN_Str8 path, DN_OSErrSink *err)
|
|
{
|
|
DN_Str8 result = DN_OS_FileReadAll(DN_Allocator_Arena, arena, path, err);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_OS_FileReadAllPool(DN_Pool *pool, DN_Str8 path, DN_OSErrSink *err)
|
|
{
|
|
DN_Str8 result = DN_OS_FileReadAll(DN_Allocator_Pool, pool, path, err);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_OS_FileReadAllTLS(DN_Str8 path, DN_OSErrSink *err)
|
|
{
|
|
DN_Str8 result = DN_OS_FileReadAll(DN_Allocator_Arena, DN_OS_TLSArena(), path, err);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileWriteAll(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_FileWriteAllFV(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_Str8FromFmtVArena(tmem.arena, fmt, args);
|
|
bool result = DN_OS_FileWriteAll(file_path, buffer, error);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileWriteAllF(DN_Str8 file_path, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
bool result = DN_OS_FileWriteAllFV(file_path, error, fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileWriteAllSafe(DN_Str8 path, DN_Str8 buffer, DN_OSErrSink *error)
|
|
{
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str8 tmp_path = DN_Str8FromFmtArena(tmem.arena, "%.*s.tmp", DN_Str8PrintFmt(path));
|
|
if (!DN_OS_FileWriteAll(tmp_path, buffer, error))
|
|
return false;
|
|
if (!DN_OS_FileCopy(tmp_path, path, true /*overwrite*/, error))
|
|
return false;
|
|
if (!DN_OS_PathDelete(tmp_path))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileWriteAllSafeFV(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_Str8FromFmtVArena(tmem.arena, fmt, args);
|
|
bool result = DN_OS_FileWriteAllSafe(path, buffer, error);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileWriteAllSafeF(DN_Str8 path, DN_OSErrSink *error, DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
bool result = DN_OS_FileWriteAllSafeFV(path, error, fmt, args);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathAddRef(DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path)
|
|
{
|
|
if (!arena || !fs_path || path.size == 0)
|
|
return false;
|
|
|
|
if (path.size <= 0)
|
|
return true;
|
|
|
|
DN_Str8 const delimiter_array[] = {
|
|
DN_Str8Lit("\\"),
|
|
DN_Str8Lit("/")};
|
|
|
|
if (fs_path->links_size == 0)
|
|
fs_path->has_prefix_path_separator = (path.data[0] == '/');
|
|
|
|
for (;;) {
|
|
DN_Str8BSplitResult delimiter = DN_Str8BSplitArray(path, delimiter_array, DN_ArrayCountU(delimiter_array));
|
|
for (; delimiter.lhs.data; delimiter = DN_Str8BSplitArray(delimiter.rhs, delimiter_array, DN_ArrayCountU(delimiter_array))) {
|
|
if (delimiter.lhs.size <= 0)
|
|
continue;
|
|
|
|
DN_OSPathLink *link = DN_ArenaNew(arena, DN_OSPathLink, DN_ZMem_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_PathAddRefTLS(DN_OSPath *fs_path, DN_Str8 path)
|
|
{
|
|
bool result = DN_OS_PathAddRef(DN_OS_TLSTopArena(), fs_path, path);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathAddRefFrame(DN_OSPath *fs_path, DN_Str8 path)
|
|
{
|
|
bool result = DN_OS_PathAddRef(DN_OS_TLSFrameArena(), fs_path, path);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathAdd(DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path)
|
|
{
|
|
DN_Str8 copy = DN_Str8FromStr8Arena(arena, path);
|
|
bool result = copy.size ? 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_Str8FromFmtVArena(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_Str8FromFmtVArena(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_Str8FromFmtVArena(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_Str8FromArena(arena, string_size, DN_ZMem_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_W32_ThreadSetName(name);
|
|
#else
|
|
DN_Posix_ThreadSetName(name);
|
|
#endif
|
|
}
|
|
|
|
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
|
|
DN_API void DN_OS_HttpRequestWait(DN_OSHttpResponse *response)
|
|
{
|
|
if (response && response->on_complete_semaphore.handle != 0)
|
|
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;
|
|
}
|
|
// DN: Single header generator inlined this file => #include "OS/dn_os_allocator.cpp"
|
|
#define DN_OS_ALLOCATOR_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_os_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_inc.h"
|
|
#endif
|
|
|
|
static void *DN_ArenaBasicAllocFromOSHeap(DN_USize size)
|
|
{
|
|
void *result = DN_OS_MemAlloc(size, DN_ZMem_Yes);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Arena DN_ArenaFromHeap(DN_U64 size, DN_ArenaFlags flags)
|
|
{
|
|
DN_ArenaMemFuncs mem_funcs = {};
|
|
mem_funcs.type = DN_ArenaMemFuncType_Basic;
|
|
mem_funcs.basic_alloc = DN_ArenaBasicAllocFromOSHeap;
|
|
mem_funcs.basic_dealloc = DN_OS_MemDealloc;
|
|
DN_Arena result = DN_ArenaFromMemFuncs(size, size, flags, mem_funcs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Arena DN_ArenaFromVMem(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.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_ArenaFromMemFuncs(reserve, commit, flags, mem_funcs);
|
|
return result;
|
|
}
|
|
|
|
// DN: Single header generator inlined this file => #include "OS/dn_os_containers.cpp"
|
|
#define DN_OS_CONTAINERS_CPP
|
|
|
|
// DN: Single header generator commented out this header => #include "dn_os_containers.h"
|
|
#if !defined(DN_OS_CONTAINERS_H)
|
|
#define DN_OS_CONTAINERS_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
// 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_ZMem z_mem);
|
|
template <typename T> T * DN_VArray_Make (DN_VArray<T> *array, DN_ZMem z_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_ZMem z_mem);
|
|
#endif // !defined(DN_OS_CONTAINERS_H)
|
|
|
|
/*
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
|
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ __$$\ \_$$ _|$$$\ $$ |$$ _____|$$ __$$\ $$ __$$\
|
|
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ / $$ | $$ | $$$$\ $$ |$$ | $$ | $$ |$$ / \__|
|
|
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$$$$ | $$ | $$ $$\$$ |$$$$$\ $$$$$$$ |\$$$$$$\
|
|
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __$$ | $$ | $$ \$$$$ |$$ __| $$ __$$< \____$$\
|
|
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ | $$ |$$\ $$ |
|
|
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$ | $$ |$$$$$$\ $$ | \$$ |$$$$$$$$\ $$ | $$ |\$$$$$$ |
|
|
// \______/ \______/ \__| \__| \__| \__| \__|\______|\__| \__|\________|\__| \__| \______/
|
|
//
|
|
// 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_ZMem_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_ZMem z_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 (z_mem == DN_ZMem_Yes)
|
|
DN_Memset(result, 0, count * sizeof(T));
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
T *DN_VArray_Make(DN_VArray<T> *array, DN_ZMem z_mem)
|
|
{
|
|
T *result = DN_VArray_MakeArray(array, 1, z_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_ZMem z_mem)
|
|
{
|
|
if (array) {
|
|
if (z_mem == DN_ZMem_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.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;
|
|
}
|
|
// DN: Single header generator inlined this file => #include "OS/dn_os_print.cpp"
|
|
#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_Str8PrintFmt(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_Str8Lit(DN_LOG_BoldEscapeCode));
|
|
DN_OS_Print(dest, string);
|
|
if (style.colour || style.bold == DN_LOGBold_Yes)
|
|
DN_OS_Print(dest, DN_Str8Lit(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_Str8Lit(DN_LOG_BoldEscapeCode));
|
|
DN_OS_PrintFV(dest, fmt, args);
|
|
if (style.colour || style.bold == DN_LOGBold_Yes)
|
|
DN_OS_Print(dest, DN_Str8Lit(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_Str8Lit("\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_Str8Lit("\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_Str8Lit("\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_Str8Lit("\n"));
|
|
}
|
|
// DN: Single header generator inlined this file => #include "OS/dn_os_string.cpp"
|
|
#define DN_OS_STRING_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_os_inc.h"
|
|
#endif
|
|
|
|
// NOTE: DN_Str8
|
|
DN_API DN_Str8 DN_Str8FromFmtArenaFrame(DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Arena *frame_arena = DN_OS_TLSGet()->frame_arena;
|
|
DN_Str8 result = DN_Str8FromFmtVArena(frame_arena, fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromFmtVArenaFrame(DN_FMT_ATTRIB char const *fmt, va_list args)
|
|
{
|
|
DN_Arena *frame_arena = DN_OS_TLSGet()->frame_arena;
|
|
DN_Str8 result = DN_Str8FromFmtVArena(frame_arena, fmt, args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromArenaFrame(DN_USize size, DN_ZMem z_mem)
|
|
{
|
|
DN_Arena *frame_arena = DN_OS_TLSGet()->frame_arena;
|
|
DN_Str8 result = DN_Str8FromArena(frame_arena, size, z_mem);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromHeapF(DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_USize size = DN_FmtVSize(fmt, args);
|
|
DN_Str8 result = DN_Str8FromHeap(size, DN_ZMem_No);
|
|
DN_VSNPrintF(result.data, DN_Cast(int)(result.size + 1), fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromHeap(DN_USize size, DN_ZMem z_mem)
|
|
{
|
|
DN_Str8 result = {};
|
|
result.data = DN_Cast(char *)DN_OS_MemAlloc(size + 1, z_mem);
|
|
if (result.data) {
|
|
result.size = size;
|
|
result.data[result.size] = 0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromTLSFV(DN_FMT_ATTRIB char const *fmt, va_list args)
|
|
{
|
|
DN_Str8 result = DN_Str8FromFmtVArena(DN_OS_TLSTopArena(), fmt, args);
|
|
return result;
|
|
}
|
|
|
|
|
|
DN_API DN_Str8 DN_Str8FromTLSF(DN_FMT_ATTRIB char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Str8 result = DN_Str8FromFmtVArena(DN_OS_TLSTopArena(), fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromTLS(DN_USize size, DN_ZMem z_mem)
|
|
{
|
|
DN_Str8 result = DN_Str8FromArena(DN_OS_TLSTopArena(), size, z_mem);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromStr8Frame(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = DN_Str8FromStr8Arena(DN_OS_TLSGet()->frame_arena, string);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FromStr8TLS(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = DN_Str8FromStr8Arena(DN_OS_TLSTopArena(), string);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8SplitResult DN_Str8SplitFromFrame(DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode)
|
|
{
|
|
DN_Str8SplitResult result = DN_Str8SplitArena(DN_OS_TLSGet()->frame_arena, string, delimiter, mode);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8SplitResult DN_Str8SplitFromTLS(DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode)
|
|
{
|
|
DN_Str8SplitResult result = DN_Str8SplitArena(DN_OS_TLSTopArena(), string, delimiter, mode);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8SegmentFromFrame(DN_Str8 src, DN_USize segment_size, char segment_char)
|
|
{
|
|
DN_Str8 result = DN_Str8Segment(DN_OS_TLSGet()->frame_arena, src, segment_size, segment_char);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8SegmentFromTLS(DN_Str8 src, DN_USize segment_size, char segment_char)
|
|
{
|
|
DN_Str8 result = DN_Str8Segment(DN_OS_TLSTopArena(), src, segment_size, segment_char);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8ReverseSegmentFromFrame(DN_Str8 src, DN_USize segment_size, char segment_char)
|
|
{
|
|
DN_Str8 result = DN_Str8ReverseSegment(DN_OS_TLSGet()->frame_arena, src, segment_size, segment_char);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8ReverseSegmentFromTLS(DN_Str8 src, DN_USize segment_size, char segment_char)
|
|
{
|
|
DN_Str8 result = DN_Str8ReverseSegment(DN_OS_TLSTopArena(), src, segment_size, segment_char);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8AppendFFromFrame(DN_Str8 string, char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Str8 result = DN_Str8AppendFV(DN_OS_TLSGet()->frame_arena, string, fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8AppendFFromTLS(DN_Str8 string, char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Str8 result = DN_Str8AppendFV(DN_OS_TLSTopArena(), string, fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FillFFromFrame(DN_USize count, char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Str8 result = DN_Str8FillFV(DN_OS_TLSGet()->frame_arena, count, fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8FillFFromTLS(DN_USize count, char const *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
DN_Str8 result = DN_Str8FillFV(DN_OS_TLSTopArena(), count, fmt, args);
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8TruncateResult DN_Str8TruncateMiddleArenaFrame(DN_Str8 str8, uint32_t side_size, DN_Str8 truncator)
|
|
{
|
|
DN_Str8TruncateResult result = DN_Str8TruncateMiddle(DN_OS_TLSGet()->frame_arena, str8, side_size, truncator);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8TruncateResult DN_Str8TruncateMiddleArenaTLS(DN_Str8 str8, uint32_t side_size, DN_Str8 truncator)
|
|
{
|
|
DN_Str8TruncateResult result = DN_Str8TruncateMiddle(DN_OS_TLSTopArena(), str8, side_size, truncator);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8PadNewLines(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_Str8BuilderFromTLS();
|
|
|
|
DN_Str8BSplitResult split = DN_Str8BSplit(src, DN_Str8Lit("\n"));
|
|
while (split.lhs.size) {
|
|
DN_Str8BuilderAppendRef(&builder, pad);
|
|
DN_Str8BuilderAppendRef(&builder, split.lhs);
|
|
split = DN_Str8BSplit(split.rhs, DN_Str8Lit("\n"));
|
|
if (split.lhs.size)
|
|
DN_Str8BuilderAppendRef(&builder, DN_Str8Lit("\n"));
|
|
}
|
|
|
|
DN_Str8 result = DN_Str8BuilderBuild(&builder, arena);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8PadNewLinesFromFrame(DN_Str8 src, DN_Str8 pad)
|
|
{
|
|
DN_Str8 result = DN_Str8PadNewLines(DN_OS_TLSGet()->frame_arena, src, pad);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8PadNewLinesFromTLS(DN_Str8 src, DN_Str8 pad)
|
|
{
|
|
DN_Str8 result = DN_Str8PadNewLines(DN_OS_TLSTopArena(), src, pad);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8UpperFromFrame(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = DN_Str8Upper(DN_OS_TLSGet()->frame_arena, string);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8UpperFromTLS(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = DN_Str8Upper(DN_OS_TLSTopArena(), string);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8LowerFromFrame(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = DN_Str8Lower(DN_OS_TLSGet()->frame_arena, string);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8LowerFromTLS(DN_Str8 string)
|
|
{
|
|
DN_Str8 result = DN_Str8Lower(DN_OS_TLSTopArena(), string);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8Replace(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 (string.size == 0 || find.size == 0 || find.size > string.size || find.size == 0 || string.size == 0) {
|
|
result = DN_Str8FromStr8Arena(arena, string);
|
|
return result;
|
|
}
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
|
DN_Str8Builder string_builder = DN_Str8BuilderFromArena(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_Str8Slice(string, tail, find.size);
|
|
if (!DN_Str8Eq(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_Str8FromPtr(string.data, head);
|
|
DN_Str8BuilderAppendRef(&string_builder, slice);
|
|
}
|
|
|
|
DN_Str8 range = DN_Str8Slice(string, head, (tail - head));
|
|
DN_Str8BuilderAppendRef(&string_builder, range);
|
|
DN_Str8BuilderAppendRef(&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_Str8FromStr8Arena(arena, string);
|
|
} else {
|
|
DN_Str8 remainder = DN_Str8FromPtr(string.data + head, string.size - head);
|
|
DN_Str8BuilderAppendRef(&string_builder, remainder);
|
|
result = DN_Str8BuilderBuild(&string_builder, arena);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_Str8ReplaceInsensitive(DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena)
|
|
{
|
|
DN_Str8 result = DN_Str8Replace(string, find, replace, start_index, arena, DN_Str8EqCase_Insensitive);
|
|
return result;
|
|
}
|
|
|
|
// NOTE: DN_Str8Builder ////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_Str8 DN_Str8BuilderBuildFromOSHeap(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_ZMem_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;
|
|
}
|
|
|
|
#if defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_EMSCRIPTEN)
|
|
// DN: Single header generator inlined this file => #include "OS/dn_os_posix.cpp"
|
|
#define DN_OS_POSIX_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_os_inc.h"
|
|
#endif
|
|
|
|
#include <dirent.h> // readdir, opendir, closedir
|
|
#include <sys/statvfs.h>
|
|
|
|
// NOTE: DN_OSMem
|
|
static DN_U32 DN_OS_MemConvertPageToOSFlags_(DN_U32 protect)
|
|
{
|
|
DN_Assert((protect & ~DN_MemPage_All) == 0);
|
|
DN_Assert(protect != 0);
|
|
DN_U32 result = 0;
|
|
|
|
if (protect & (DN_MemPage_NoAccess | DN_MemPage_Guard)) {
|
|
result = PROT_NONE;
|
|
} else {
|
|
if (protect & DN_MemPage_Read)
|
|
result = PROT_READ;
|
|
if (protect & DN_MemPage_Write)
|
|
result = PROT_WRITE;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_flags)
|
|
{
|
|
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
|
|
|
|
if (commit == DN_MemCommit_Yes)
|
|
os_page_flags |= (PROT_READ | PROT_WRITE);
|
|
|
|
void *result = mmap(nullptr, size, os_page_flags, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
DN_AtomicAddU64(&g_dn_->os.mem_allocs_total, 1);
|
|
DN_AtomicAddU64(&g_dn_->os.mem_allocs_frame, 1);
|
|
if (result == MAP_FAILED)
|
|
result = nullptr;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags)
|
|
{
|
|
bool result = false;
|
|
if (!ptr || size == 0)
|
|
return false;
|
|
|
|
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
|
|
result = mprotect(ptr, size, os_page_flags) == 0;
|
|
DN_AtomicAddU64(&g_dn_->os.mem_allocs_total, 1);
|
|
DN_AtomicAddU64(&g_dn_->os.mem_allocs_frame, 1);
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size)
|
|
{
|
|
mprotect(ptr, size, PROT_NONE);
|
|
madvise(ptr, size, MADV_FREE);
|
|
}
|
|
|
|
DN_API void DN_OS_MemRelease(void *ptr, DN_USize size)
|
|
{
|
|
munmap(ptr, size);
|
|
}
|
|
|
|
DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags)
|
|
{
|
|
if (!ptr || size == 0)
|
|
return 0;
|
|
|
|
static DN_Str8 const ALIGNMENT_ERROR_MSG = DN_Str8Lit(
|
|
"Page protection requires pointers to be page aligned because we "
|
|
"can only guard memory at a multiple of the page boundary.");
|
|
DN_AssertF(DN_IsPowerOfTwoAligned(DN_Cast(uintptr_t) ptr, g_dn_->os.page_size),
|
|
"%s",
|
|
ALIGNMENT_ERROR_MSG.data);
|
|
DN_AssertF(
|
|
DN_IsPowerOfTwoAligned(size, g_dn_->os.page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
|
|
|
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
|
|
int result = mprotect(ptr, size, os_page_flags);
|
|
DN_AssertF(result == 0, "mprotect failed (%d)", errno);
|
|
return result;
|
|
}
|
|
|
|
DN_API void *DN_OS_MemAlloc(DN_USize size, DN_ZMem z_mem)
|
|
{
|
|
void *result = z_mem == DN_ZMem_Yes ? calloc(1, size) : malloc(size);
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_MemDealloc(void *ptr)
|
|
{
|
|
free(ptr);
|
|
}
|
|
|
|
// NOTE: Date //////////////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_OSDateTime DN_OS_DateLocalTimeNow()
|
|
{
|
|
DN_OSDateTime result = {};
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
|
|
// NOTE: localtime_r is used because it is thread safe
|
|
// See: https://linux.die.net/man/3/localtime
|
|
// According to POSIX.1-2004, localtime() is required to behave as though
|
|
// tzset(3) was called, while localtime_r() does not have this requirement.
|
|
// For portable code tzset(3) should be called before localtime_r().
|
|
for (static bool once = true; once; once = false)
|
|
tzset();
|
|
|
|
struct tm time = {};
|
|
localtime_r(&ts.tv_sec, &time);
|
|
|
|
result.hour = time.tm_hour;
|
|
result.minutes = time.tm_min;
|
|
result.seconds = time.tm_sec;
|
|
|
|
result.day = DN_Cast(uint8_t) time.tm_mday;
|
|
result.month = DN_Cast(uint8_t) time.tm_mon + 1;
|
|
result.year = 1900 + DN_Cast(int16_t) time.tm_year;
|
|
return result;
|
|
}
|
|
|
|
DN_API uint64_t DN_OS_DateUnixTimeNs()
|
|
{
|
|
struct timespec ts = {};
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
uint64_t result = (ts.tv_sec * 1000 /*ms*/ * 1000 /*us*/ * 1000 /*ns*/) + ts.tv_nsec;
|
|
return result;
|
|
}
|
|
|
|
DN_API uint64_t DN_OS_DateLocalToUnixTimeS(DN_OSDateTime)
|
|
{
|
|
DN_AssertOnce(!"Unimplemented");
|
|
uint64_t result = 0;
|
|
return result;
|
|
}
|
|
|
|
DN_API uint64_t DN_OS_DateToUnixTimeS(DN_OSDateTime date)
|
|
{
|
|
DN_Assert(DN_OS_DateIsValid(date));
|
|
struct tm timeinfo = {};
|
|
timeinfo.tm_year = date.year - 1900;
|
|
timeinfo.tm_mon = date.month - 1;
|
|
timeinfo.tm_mday = date.day;
|
|
timeinfo.tm_hour = date.hour;
|
|
timeinfo.tm_min = date.minutes;
|
|
timeinfo.tm_sec = date.seconds;
|
|
uint64_t result = mktime(&timeinfo);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_OSDateTime DN_OS_DateUnixTimeSToDate(uint64_t time)
|
|
{
|
|
time_t posix_time = DN_Cast(time_t) time;
|
|
struct tm posix_date = *gmtime(&posix_time);
|
|
DN_OSDateTime result = {};
|
|
result.year = posix_date.tm_year + 1900;
|
|
result.month = posix_date.tm_mon + 1;
|
|
result.day = posix_date.tm_mday;
|
|
result.hour = posix_date.tm_hour;
|
|
result.minutes = posix_date.tm_min;
|
|
result.seconds = posix_date.tm_sec;
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size)
|
|
{
|
|
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
DN_InvalidCodePath;
|
|
(void)buffer;
|
|
(void)size;
|
|
#else
|
|
DN_Assert(buffer && size);
|
|
DN_USize bytes_written = 0;
|
|
while (bytes_written < size) {
|
|
DN_USize bytes_remaining = size - bytes_written;
|
|
DN_USize need_amount = DN_Min(bytes_remaining, 32);
|
|
DN_USize bytes_read = 0;
|
|
do {
|
|
bytes_read = getrandom((DN_U8 *)buffer + bytes_written, need_amount, 0);
|
|
} while (bytes_read != need_amount || errno == EAGAIN || errno == EINTR);
|
|
bytes_written += bytes_read;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
DN_API bool DN_OS_SetEnvVar(DN_Str8 name, DN_Str8 value)
|
|
{
|
|
DN_AssertFOnce(false, "Unimplemented");
|
|
(void)name;
|
|
(void)value;
|
|
bool result = false;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_OSDiskSpace DN_OS_DiskSpace(DN_Str8 path)
|
|
{
|
|
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(nullptr);
|
|
DN_OSDiskSpace result = {};
|
|
DN_Str8 path_z_terminated = DN_Str8FromStr8Arena(tmem.arena, path);
|
|
|
|
struct statvfs info = {};
|
|
if (statvfs(path_z_terminated.data, &info) != 0)
|
|
return result;
|
|
|
|
result.success = true;
|
|
result.avail = info.f_bavail * info.f_frsize;
|
|
result.size = info.f_blocks * info.f_frsize;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_OS_EXEPath(DN_Arena *arena)
|
|
{
|
|
DN_Str8 result = {};
|
|
if (!arena)
|
|
return result;
|
|
|
|
int required_size_wo_null_terminator = 0;
|
|
for (int try_size = 128;; try_size *= 2) {
|
|
auto scoped_arena = DN_ArenaTempMemScope(arena);
|
|
char *try_buf = DN_ArenaNewArray(arena, char, try_size, DN_ZMem_No);
|
|
int bytes_written = readlink("/proc/self/exe", try_buf, try_size);
|
|
if (bytes_written == -1) {
|
|
// Failed, we're unable to determine the executable directory
|
|
break;
|
|
} else if (bytes_written == try_size) {
|
|
// Try again, if returned size was equal- we may of prematurely
|
|
// truncated according to the man pages
|
|
continue;
|
|
} else {
|
|
// readlink will give us the path to the executable. Once we
|
|
// determine the correct buffer size required to get the full file
|
|
// path, we do some post-processing on said string and extract just
|
|
// the directory.
|
|
|
|
// TODO(dn): It'd be nice if there's some way of keeping this
|
|
// try_buf around, memcopy the byte and trash the try_buf from the
|
|
// arena. Instead we just get the size and redo the call one last
|
|
// time after this "calculate" step.
|
|
DN_AssertF(bytes_written < try_size,
|
|
"bytes_written can never be greater than the try size, function writes at "
|
|
"most try_size");
|
|
required_size_wo_null_terminator = bytes_written;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (required_size_wo_null_terminator) {
|
|
DN_ArenaTempMem temp_mem = DN_ArenaTempMemBegin(arena);
|
|
char *exe_path =
|
|
DN_ArenaNewArray(arena, char, required_size_wo_null_terminator + 1, DN_ZMem_No);
|
|
exe_path[required_size_wo_null_terminator] = 0;
|
|
|
|
int bytes_written = readlink("/proc/self/exe", exe_path, required_size_wo_null_terminator);
|
|
if (bytes_written == -1) {
|
|
// Note that if read-link fails again can be because there's
|
|
// a potential race condition here, our exe or directory could have
|
|
// been deleted since the last call, so we need to be careful.
|
|
DN_ArenaTempMemEnd(temp_mem);
|
|
} else {
|
|
result = DN_Str8FromPtr(exe_path, required_size_wo_null_terminator);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_SleepMs(DN_UInt milliseconds)
|
|
{
|
|
struct timespec ts;
|
|
ts.tv_sec = milliseconds / 1000;
|
|
ts.tv_nsec = (milliseconds % 1000) * 1'000'000; // Convert remaining milliseconds to nanoseconds
|
|
// nanosleep can fail if interrupted by a signal, so we loop until the full sleep time has passed
|
|
while (nanosleep(&ts, &ts) == -1 && errno == EINTR)
|
|
;
|
|
}
|
|
|
|
DN_API DN_U64 DN_OS_PerfCounterFrequency()
|
|
{
|
|
// NOTE: On Linux we use clock_gettime(CLOCK_MONOTONIC_RAW) (or CLOCK_MONOTONIC) which
|
|
// increments at nanosecond granularity.
|
|
DN_U64 result = 1'000'000'000;
|
|
return result;
|
|
}
|
|
|
|
static DN_POSIXCore *DN_OS_GetPOSIXCore_()
|
|
{
|
|
DN_Assert(g_dn_ && g_dn_->os.platform_context);
|
|
DN_POSIXCore *result = DN_Cast(DN_POSIXCore *)g_dn_->os.platform_context;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64 DN_OS_PerfCounterNow()
|
|
{
|
|
DN_POSIXCore *posix = DN_OS_GetPOSIXCore_();
|
|
struct timespec ts;
|
|
clock_gettime(posix->clock_monotonic_raw ? CLOCK_MONOTONIC_RAW : CLOCK_MONOTONIC, &ts);
|
|
DN_U64 result = DN_Cast(DN_U64) ts.tv_sec * 1'000'000'000 + DN_Cast(DN_U64) ts.tv_nsec;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileCopy(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_OSErrSink *error)
|
|
{
|
|
bool result = false;
|
|
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
DN_OS_ErrSinkAppendF(error, 1, "Unsupported on Emscripten because of their VFS model");
|
|
#else
|
|
int src_fd = open(src.data, O_RDONLY);
|
|
if (src_fd == -1) {
|
|
int error_code = errno;
|
|
DN_OS_ErrSinkAppendF(error,
|
|
error_code,
|
|
"Failed to open file '%.*s' for copying: (%d) %s",
|
|
DN_Str8PrintFmt(src),
|
|
error_code,
|
|
strerror(error_code));
|
|
return result;
|
|
}
|
|
DN_DEFER
|
|
{
|
|
close(src_fd);
|
|
};
|
|
|
|
// NOTE: File permission is set to read/write by owner, read by others
|
|
int dest_fd = open(dest.data, O_WRONLY | O_CREAT | (overwrite ? O_TRUNC : 0), 0644);
|
|
if (dest_fd == -1) {
|
|
int error_code = errno;
|
|
DN_OS_ErrSinkAppendF(error,
|
|
error_code,
|
|
"Failed to open file destination '%.*s' for copying to: (%d) %s",
|
|
DN_Str8PrintFmt(src),
|
|
error_code,
|
|
strerror(error_code));
|
|
return result;
|
|
}
|
|
DN_DEFER
|
|
{
|
|
close(dest_fd);
|
|
};
|
|
|
|
struct stat stat_existing;
|
|
int fstat_result = fstat(src_fd, &stat_existing);
|
|
if (fstat_result == -1) {
|
|
int error_code = errno;
|
|
DN_OS_ErrSinkAppendF(error,
|
|
error_code,
|
|
"Failed to query file size of '%.*s' for copying: (%d) %s",
|
|
DN_Str8PrintFmt(src),
|
|
error_code,
|
|
strerror(error_code));
|
|
return result;
|
|
}
|
|
|
|
ssize_t bytes_written = sendfile64(dest_fd, src_fd, 0, stat_existing.st_size);
|
|
result = (bytes_written == stat_existing.st_size);
|
|
if (!result) {
|
|
int error_code = errno;
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str8 file_size_str8 = DN_Str8FromByteCount(tmem.arena, stat_existing.st_size, DN_ByteCountType_Auto);
|
|
DN_Str8 bytes_written_str8 = DN_Str8FromByteCount(tmem.arena, bytes_written, DN_ByteCountType_Auto);
|
|
DN_OS_ErrSinkAppendF(error,
|
|
error_code,
|
|
"Failed to copy file '%.*s' to '%.*s', we copied %.*s but the file "
|
|
"size is %.*s: (%d) %s",
|
|
DN_Str8PrintFmt(src),
|
|
DN_Str8PrintFmt(dest),
|
|
DN_Str8PrintFmt(bytes_written_str8),
|
|
DN_Str8PrintFmt(file_size_str8),
|
|
error_code,
|
|
strerror(error_code));
|
|
}
|
|
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileMove(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_OSErrSink *error)
|
|
{
|
|
// See: https://github.com/gingerBill/gb/blob/master/gb.h
|
|
bool result = false;
|
|
bool file_moved = true;
|
|
if (link(src.data, dest.data) == -1) {
|
|
// NOTE: Link can fail if we're trying to link across different volumes
|
|
// so we fall back to a binary directory.
|
|
file_moved |= DN_OS_FileCopy(src, dest, overwrite, error);
|
|
}
|
|
|
|
if (file_moved) {
|
|
result = true;
|
|
int unlink_result = unlink(src.data);
|
|
if (unlink_result == -1) {
|
|
int error_code = errno;
|
|
DN_OS_ErrSinkAppendF(
|
|
error,
|
|
error_code,
|
|
"File '%.*s' was moved but failed to be unlinked from old location: (%d) %s",
|
|
DN_Str8PrintFmt(src),
|
|
error_code,
|
|
strerror(error_code));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path,
|
|
DN_OSFileOpen open_mode,
|
|
DN_OSFileAccess access,
|
|
DN_OSErrSink *error)
|
|
{
|
|
DN_OSFile result = {};
|
|
if (path.size == 0 || path.size <= 0)
|
|
return result;
|
|
|
|
if ((access & ~(DN_OSFileAccess_All) || ((access & DN_OSFileAccess_All) == 0))) {
|
|
DN_InvalidCodePath;
|
|
return result;
|
|
}
|
|
|
|
if (access & DN_OSFileAccess_Execute) {
|
|
result.error = true;
|
|
DN_OS_ErrSinkAppendF(
|
|
error,
|
|
1,
|
|
"Failed to open file '%.*s': File access flag 'execute' is not supported",
|
|
DN_Str8PrintFmt(path));
|
|
DN_InvalidCodePath; // TODO: Not supported via fopen
|
|
return result;
|
|
}
|
|
|
|
// NOTE: fopen interface is not as expressive as the Win32
|
|
// We will fopen the file beforehand to setup the state/check for validity
|
|
// before closing and reopening it with the correct request access
|
|
// permissions.
|
|
{
|
|
FILE *handle = nullptr;
|
|
switch (open_mode) {
|
|
case DN_OSFileOpen_CreateAlways: handle = fopen(path.data, "w"); break;
|
|
case DN_OSFileOpen_OpenIfExist: handle = fopen(path.data, "r"); break;
|
|
case DN_OSFileOpen_OpenAlways: handle = fopen(path.data, "a"); break;
|
|
default: DN_InvalidCodePath; break;
|
|
}
|
|
|
|
if (!handle) { // TODO(doyle): FileOpen flag to string
|
|
result.error = true;
|
|
DN_OS_ErrSinkAppendF(error,
|
|
1,
|
|
"Failed to open file '%.*s': File could not be opened in requested "
|
|
"mode 'DN_OSFileOpen' flag %d",
|
|
DN_Str8PrintFmt(path),
|
|
open_mode);
|
|
return result;
|
|
}
|
|
fclose(handle);
|
|
}
|
|
|
|
char const *fopen_mode = nullptr;
|
|
if (access & DN_OSFileAccess_AppendOnly)
|
|
fopen_mode = "a+";
|
|
else if (access & DN_OSFileAccess_Write)
|
|
fopen_mode = "w+";
|
|
else if (access & DN_OSFileAccess_Read)
|
|
fopen_mode = "r";
|
|
|
|
FILE *handle = fopen(path.data, fopen_mode);
|
|
if (!handle) {
|
|
result.error = true;
|
|
DN_OS_ErrSinkAppendF(error,
|
|
1,
|
|
"Failed to open file '%S': File could not be opened with requested "
|
|
"access mode 'DN_OSFileAccess' %d",
|
|
path,
|
|
fopen_mode);
|
|
return result;
|
|
}
|
|
result.handle = handle;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size, DN_OSErrSink *err)
|
|
{
|
|
DN_OSFileRead result = {};
|
|
if (!file || !file->handle || file->error || !buffer || size <= 0)
|
|
return result;
|
|
|
|
result.bytes_read = fread(buffer, 1, size, DN_Cast(FILE *) file->handle);
|
|
if (feof(DN_Cast(FILE*)file->handle)) {
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str8x32 buffer_size_str8 = DN_ByteCountStr8x32(size);
|
|
DN_OS_ErrSinkAppendF(err, 1, "Failed to read %S from file", buffer_size_str8);
|
|
return result;
|
|
}
|
|
|
|
result.success = true;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileWritePtr(DN_OSFile *file, void const *buffer, DN_USize size, DN_OSErrSink *err)
|
|
{
|
|
if (!file || !file->handle || file->error || !buffer || size <= 0)
|
|
return false;
|
|
bool result =
|
|
fwrite(buffer, DN_Cast(DN_USize) size, 1 /*count*/, DN_Cast(FILE *) file->handle) ==
|
|
1 /*count*/;
|
|
if (!result) {
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str8x32 buffer_size_str8 = DN_ByteCountStr8x32(size);
|
|
DN_OS_ErrSinkAppendF(err, 1, "Failed to write buffer (%s) to file handle", DN_Str8PrintFmt(buffer_size_str8));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileFlush(DN_OSFile *file, DN_OSErrSink *err)
|
|
{
|
|
// TODO: errno is not thread safe
|
|
int fd = fileno(DN_Cast(FILE *) file->handle);
|
|
if (fd == -1) {
|
|
DN_OS_ErrSinkAppendF(err, errno, "Failed to flush file buffer to disk, file handle could not be converted to descriptor (%d): %s", fd, strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
int fsync_result = fsync(fd);
|
|
if (fsync_result == -1) {
|
|
DN_OS_ErrSinkAppendF(err, errno, "Failed to flush file buffer to disk (%d): %s", fsync_result, strerror(errno));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
DN_API void DN_OS_FileClose(DN_OSFile *file)
|
|
{
|
|
if (!file || !file->handle || file->error)
|
|
return;
|
|
fclose(DN_Cast(FILE *) file->handle);
|
|
*file = {};
|
|
}
|
|
|
|
DN_API DN_OSPathInfo DN_OS_PathInfo(DN_Str8 path)
|
|
{
|
|
DN_OSPathInfo result = {};
|
|
if (path.size == 0)
|
|
return result;
|
|
|
|
struct stat file_stat;
|
|
if (lstat(path.data, &file_stat) != -1) {
|
|
result.exists = true;
|
|
result.size = file_stat.st_size;
|
|
result.last_access_time_in_s = file_stat.st_atime;
|
|
result.last_write_time_in_s = file_stat.st_mtime;
|
|
// TODO(dn): Seems linux does not support creation time via stat. We
|
|
// shoddily deal with this.
|
|
result.create_time_in_s = DN_Min(result.last_access_time_in_s, result.last_write_time_in_s);
|
|
|
|
if (S_ISDIR(file_stat.st_mode))
|
|
result.type = DN_OSPathInfoType_Directory;
|
|
else if (S_ISREG(file_stat.st_mode))
|
|
result.type = DN_OSPathInfoType_File;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathDelete(DN_Str8 path)
|
|
{
|
|
bool result = false;
|
|
if (path.size)
|
|
result = remove(path.data) == 0;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathIsFile(DN_Str8 path)
|
|
{
|
|
bool result = false;
|
|
if (path.size == 0)
|
|
return result;
|
|
|
|
struct stat stat_result;
|
|
if (lstat(path.data, &stat_result) != -1)
|
|
result = S_ISREG(stat_result.st_mode) || S_ISLNK(stat_result.st_mode);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathIsDir(DN_Str8 path)
|
|
{
|
|
bool result = false;
|
|
if (path.size == 0)
|
|
return result;
|
|
|
|
struct stat stat_result;
|
|
if (lstat(path.data, &stat_result) != -1)
|
|
result = S_ISDIR(stat_result.st_mode);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathMakeDir(DN_Str8 path)
|
|
{
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
bool result = true;
|
|
|
|
// TODO(doyle): Implement this without using the path indexes, it's not
|
|
// necessary. See Windows implementation.
|
|
DN_USize path_indexes_size = 0;
|
|
uint16_t path_indexes[64] = {};
|
|
|
|
DN_Str8 copy = DN_Str8FromStr8Arena(tmem.arena, path);
|
|
for (DN_USize index = copy.size - 1; index < copy.size; index--) {
|
|
bool first_char = index == (copy.size - 1);
|
|
char ch = copy.data[index];
|
|
if (ch == '/' || first_char) {
|
|
char temp = copy.data[index];
|
|
|
|
if (!first_char)
|
|
copy.data[index] = 0; // Temporarily null terminate it
|
|
|
|
bool is_file = DN_OS_PathIsFile(copy);
|
|
|
|
if (!first_char)
|
|
copy.data[index] = temp; // Undo null termination
|
|
|
|
if (is_file) {
|
|
// NOTE: There's something that exists in at this path, but
|
|
// it's not a directory. This request to make a directory is
|
|
// invalid.
|
|
return false;
|
|
} else if (DN_OS_PathIsDir(copy)) {
|
|
// NOTE: We found a directory, we can stop here and start
|
|
// building up all the directories that didn't exist up to
|
|
// this point.
|
|
break;
|
|
} else {
|
|
// NOTE: There's nothing that exists at this path, we can
|
|
// create a directory here
|
|
path_indexes[path_indexes_size++] = DN_Cast(uint16_t) index;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (DN_USize index = path_indexes_size - 1; result && index < path_indexes_size; index--) {
|
|
uint16_t path_index = path_indexes[index];
|
|
char temp = copy.data[path_index];
|
|
|
|
if (index != 0)
|
|
copy.data[path_index] = 0;
|
|
result |= mkdir(copy.data, 0774) == 0;
|
|
if (index != 0)
|
|
copy.data[path_index] = temp;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathIterateDir(DN_Str8 path, DN_OSDirIterator *it)
|
|
{
|
|
if (!it->handle) {
|
|
it->handle = opendir(path.data);
|
|
if (!it->handle)
|
|
return false;
|
|
}
|
|
|
|
struct dirent *entry;
|
|
for (;;) {
|
|
entry = readdir(DN_Cast(DIR *) it->handle);
|
|
if (entry == NULL)
|
|
break;
|
|
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
|
continue;
|
|
|
|
DN_USize name_size = DN_CStr8Size(entry->d_name);
|
|
DN_USize clamped_size = DN_Min(sizeof(it->buffer) - 1, name_size);
|
|
DN_AssertF(name_size == clamped_size, "name: %s, name_size: %zu, clamped_size: %zu", entry->d_name, name_size, clamped_size);
|
|
DN_Memcpy(it->buffer, entry->d_name, clamped_size);
|
|
it->buffer[clamped_size] = 0;
|
|
it->file_name = DN_Str8FromPtr(it->buffer, clamped_size);
|
|
return true;
|
|
}
|
|
|
|
closedir(DN_Cast(DIR *) it->handle);
|
|
it->handle = NULL;
|
|
it->file_name = {};
|
|
it->buffer[0] = 0;
|
|
return false;
|
|
}
|
|
|
|
DN_API void DN_OS_Exit(int32_t exit_code)
|
|
{
|
|
exit(DN_Cast(int) exit_code);
|
|
}
|
|
|
|
enum DN_OSPipeType_
|
|
{
|
|
DN_OSPipeType__Read,
|
|
DN_OSPipeType__Write,
|
|
DN_OSPipeType__Count,
|
|
};
|
|
|
|
DN_API DN_OSExecResult DN_OS_ExecWait(DN_OSExecAsyncHandle handle,
|
|
DN_Arena *arena,
|
|
DN_OSErrSink *error)
|
|
{
|
|
DN_OSExecResult result = {};
|
|
if (!handle.process || handle.os_error_code || handle.exit_code) {
|
|
if (handle.os_error_code)
|
|
result.os_error_code = handle.os_error_code;
|
|
else
|
|
result.exit_code = handle.exit_code;
|
|
|
|
DN_Assert(!handle.stdout_read);
|
|
DN_Assert(!handle.stdout_write);
|
|
DN_Assert(!handle.stderr_read);
|
|
DN_Assert(!handle.stderr_write);
|
|
return result;
|
|
}
|
|
|
|
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
DN_InvalidCodePathF("Unsupported operation");
|
|
#endif
|
|
|
|
static_assert(sizeof(pid_t) <= sizeof(handle.process),
|
|
"We store the PID opaquely in a register sized pointer");
|
|
pid_t process = {};
|
|
DN_Memcpy(&process, &handle.process, sizeof(process));
|
|
for (;;) {
|
|
int status = 0;
|
|
if (waitpid(process, &status, 0) < 0) {
|
|
result.os_error_code = errno;
|
|
break;
|
|
}
|
|
|
|
if (WIFEXITED(status)) {
|
|
result.exit_code = WEXITSTATUS(status);
|
|
break;
|
|
}
|
|
|
|
if (WIFSIGNALED(status)) {
|
|
result.os_error_code = WTERMSIG(status);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int stdout_pipe[DN_OSPipeType__Count] = {};
|
|
int stderr_pipe[DN_OSPipeType__Count] = {};
|
|
DN_Memcpy(&stdout_pipe[DN_OSPipeType__Read],
|
|
&handle.stdout_read,
|
|
sizeof(stdout_pipe[DN_OSPipeType__Read]));
|
|
DN_Memcpy(&stdout_pipe[DN_OSPipeType__Write],
|
|
&handle.stdout_write,
|
|
sizeof(stdout_pipe[DN_OSPipeType__Write]));
|
|
DN_Memcpy(&stderr_pipe[DN_OSPipeType__Read],
|
|
&handle.stderr_read,
|
|
sizeof(stderr_pipe[DN_OSPipeType__Read]));
|
|
DN_Memcpy(&stderr_pipe[DN_OSPipeType__Write],
|
|
&handle.stderr_write,
|
|
sizeof(stderr_pipe[DN_OSPipeType__Write]));
|
|
|
|
// NOTE: Process has finished, stop the write end of the pipe
|
|
close(stdout_pipe[DN_OSPipeType__Write]);
|
|
close(stderr_pipe[DN_OSPipeType__Write]);
|
|
|
|
// NOTE: Read the data from the read end of the pipe
|
|
if (result.os_error_code == 0) {
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
|
if (arena && handle.stdout_read) {
|
|
char buffer[4096];
|
|
DN_Str8Builder builder = DN_Str8BuilderFromArena(tmem.arena);
|
|
for (;;) {
|
|
ssize_t bytes_read =
|
|
read(stdout_pipe[DN_OSPipeType__Read], buffer, sizeof(buffer));
|
|
if (bytes_read <= 0)
|
|
break;
|
|
DN_Str8BuilderAppendF(&builder, "%.*s", bytes_read, buffer);
|
|
}
|
|
|
|
result.stdout_text = DN_Str8BuilderBuild(&builder, arena);
|
|
}
|
|
|
|
if (arena && handle.stderr_read) {
|
|
char buffer[4096];
|
|
DN_Str8Builder builder = DN_Str8BuilderFromArena(tmem.arena);
|
|
for (;;) {
|
|
ssize_t bytes_read =
|
|
read(stderr_pipe[DN_OSPipeType__Read], buffer, sizeof(buffer));
|
|
if (bytes_read <= 0)
|
|
break;
|
|
DN_Str8BuilderAppendF(&builder, "%.*s", bytes_read, buffer);
|
|
}
|
|
|
|
result.stderr_text = DN_Str8BuilderBuild(&builder, arena);
|
|
}
|
|
}
|
|
|
|
close(stdout_pipe[DN_OSPipeType__Read]);
|
|
close(stderr_pipe[DN_OSPipeType__Read]);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Slice<DN_Str8> cmd_line,
|
|
DN_OSExecArgs *args,
|
|
DN_OSErrSink *error)
|
|
{
|
|
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
DN_InvalidCodePathF("Unsupported operation");
|
|
#endif
|
|
DN_AssertFOnce(args->environment.size == 0, "Unimplemented in POSIX");
|
|
|
|
DN_OSExecAsyncHandle result = {};
|
|
if (cmd_line.size == 0)
|
|
return result;
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str8 cmd_rendered = DN_Slice_Str8Render(tmem.arena, cmd_line, DN_Str8Lit(" "));
|
|
int stdout_pipe[DN_OSPipeType__Count] = {};
|
|
int stderr_pipe[DN_OSPipeType__Count] = {};
|
|
|
|
// NOTE: Open stdout pipe //////////////////////////////////////////////////////////////////////
|
|
if (DN_BitIsSet(args->flags, DN_OSExecFlags_SaveStdout)) {
|
|
if (pipe(stdout_pipe) == -1) {
|
|
result.os_error_code = errno;
|
|
DN_OS_ErrSinkAppendF(
|
|
error,
|
|
result.os_error_code,
|
|
"Failed to create stdout pipe to redirect the output of the command '%.*s': %s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
strerror(result.os_error_code));
|
|
return result;
|
|
}
|
|
DN_Assert(stdout_pipe[DN_OSPipeType__Read] != 0);
|
|
DN_Assert(stdout_pipe[DN_OSPipeType__Write] != 0);
|
|
}
|
|
|
|
DN_DEFER
|
|
{
|
|
if (result.os_error_code == 0 && result.exit_code == 0)
|
|
return;
|
|
close(stdout_pipe[DN_OSPipeType__Read]);
|
|
close(stdout_pipe[DN_OSPipeType__Write]);
|
|
};
|
|
|
|
// NOTE: Open stderr pipe //////////////////////////////////////////////////////////////////////
|
|
if (DN_BitIsSet(args->flags, DN_OSExecFlags_SaveStderr)) {
|
|
if (DN_BitIsSet(args->flags, DN_OSExecFlags_MergeStderrToStdout)) {
|
|
stderr_pipe[DN_OSPipeType__Read] = stdout_pipe[DN_OSPipeType__Read];
|
|
stderr_pipe[DN_OSPipeType__Write] = stdout_pipe[DN_OSPipeType__Write];
|
|
} else if (pipe(stderr_pipe) == -1) {
|
|
result.os_error_code = errno;
|
|
DN_OS_ErrSinkAppendF(
|
|
error,
|
|
result.os_error_code,
|
|
"Failed to create stderr pipe to redirect the output of the command '%.*s': %s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
strerror(result.os_error_code));
|
|
return result;
|
|
}
|
|
DN_Assert(stderr_pipe[DN_OSPipeType__Read] != 0);
|
|
DN_Assert(stderr_pipe[DN_OSPipeType__Write] != 0);
|
|
}
|
|
|
|
DN_DEFER
|
|
{
|
|
if (result.os_error_code == 0 && result.exit_code == 0)
|
|
return;
|
|
close(stderr_pipe[DN_OSPipeType__Read]);
|
|
close(stderr_pipe[DN_OSPipeType__Write]);
|
|
};
|
|
|
|
pid_t child_pid = fork();
|
|
if (child_pid < 0) {
|
|
result.os_error_code = errno;
|
|
DN_OS_ErrSinkAppendF(
|
|
error,
|
|
result.os_error_code,
|
|
"Failed to fork process to execute the command '%.*s': %s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
strerror(result.os_error_code));
|
|
return result;
|
|
}
|
|
|
|
if (child_pid == 0) { // Child process
|
|
if (DN_BitIsSet(args->flags, DN_OSExecFlags_SaveStdout) &&
|
|
(dup2(stdout_pipe[DN_OSPipeType__Write], STDOUT_FILENO) == -1)) {
|
|
result.os_error_code = errno;
|
|
DN_OS_ErrSinkAppendF(
|
|
error,
|
|
result.os_error_code,
|
|
"Failed to redirect stdout 'write' pipe for output of command '%.*s': %s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
strerror(result.os_error_code));
|
|
return result;
|
|
}
|
|
|
|
if (DN_BitIsSet(args->flags, DN_OSExecFlags_SaveStderr) &&
|
|
(dup2(stderr_pipe[DN_OSPipeType__Write], STDERR_FILENO) == -1)) {
|
|
result.os_error_code = errno;
|
|
DN_OS_ErrSinkAppendF(
|
|
error,
|
|
result.os_error_code,
|
|
"Failed to redirect stderr 'read' pipe for output of command '%.*s': %s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
strerror(result.os_error_code));
|
|
return result;
|
|
}
|
|
|
|
// NOTE: Convert the command into something suitable for execvp
|
|
char **argv =
|
|
DN_ArenaNewArray(tmem.arena, char *, cmd_line.size + 1 /*null*/, DN_ZMem_Yes);
|
|
if (!argv) {
|
|
result.exit_code = -1;
|
|
DN_OS_ErrSinkAppendF(
|
|
error,
|
|
result.os_error_code,
|
|
"Failed to create argument values from command line '%.*s': Out of memory",
|
|
DN_Str8PrintFmt(cmd_rendered));
|
|
return result;
|
|
}
|
|
|
|
for (DN_ForIndexU(arg_index, cmd_line.size)) {
|
|
DN_Str8 arg = cmd_line.data[arg_index];
|
|
argv[arg_index] = DN_Str8FromStr8Arena(tmem.arena, arg).data; // NOTE: Copy string to guarantee it is null-terminated
|
|
}
|
|
|
|
// NOTE: Change the working directory if there is one
|
|
char *prev_working_dir = nullptr;
|
|
DN_DEFER
|
|
{
|
|
if (!prev_working_dir)
|
|
return;
|
|
if (result.os_error_code == 0) {
|
|
int chdir_result = chdir(prev_working_dir);
|
|
(void)chdir_result;
|
|
}
|
|
free(prev_working_dir);
|
|
};
|
|
|
|
if (args->working_dir.size) {
|
|
prev_working_dir = get_current_dir_name();
|
|
DN_Str8 working_dir = DN_Str8FromStr8Arena(tmem.arena, args->working_dir);
|
|
if (chdir(working_dir.data) == -1) {
|
|
result.os_error_code = errno;
|
|
DN_OS_ErrSinkAppendF(
|
|
error,
|
|
result.os_error_code,
|
|
"Failed to create argument values from command line '%.*s': %s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
strerror(result.os_error_code));
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// NOTE: Execute the command. We reuse argv because the first arg, the
|
|
// binary to execute is guaranteed to be null-terminated.
|
|
if (execvp(argv[0], argv) < 0) {
|
|
result.os_error_code = errno;
|
|
DN_OS_ErrSinkAppendF(
|
|
error,
|
|
result.os_error_code,
|
|
"Failed to execute command'%.*s': %s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
strerror(result.os_error_code));
|
|
return result;
|
|
}
|
|
}
|
|
|
|
DN_Assert(result.os_error_code == 0);
|
|
DN_Memcpy(&result.stdout_read,
|
|
&stdout_pipe[DN_OSPipeType__Read],
|
|
sizeof(stdout_pipe[DN_OSPipeType__Read]));
|
|
DN_Memcpy(&result.stdout_write,
|
|
&stdout_pipe[DN_OSPipeType__Write],
|
|
sizeof(stdout_pipe[DN_OSPipeType__Write]));
|
|
|
|
if (DN_BitIsSet(args->flags, DN_OSExecFlags_SaveStderr) && DN_BitIsNotSet(args->flags, DN_OSExecFlags_MergeStderrToStdout)) {
|
|
DN_Memcpy(&result.stderr_read,
|
|
&stderr_pipe[DN_OSPipeType__Read],
|
|
sizeof(stderr_pipe[DN_OSPipeType__Read]));
|
|
DN_Memcpy(&result.stderr_write,
|
|
&stderr_pipe[DN_OSPipeType__Write],
|
|
sizeof(stderr_pipe[DN_OSPipeType__Write]));
|
|
}
|
|
result.exec_flags = args->flags;
|
|
DN_Memcpy(&result.process, &child_pid, sizeof(child_pid));
|
|
return result;
|
|
}
|
|
|
|
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_InvalidCodePath;
|
|
DN_OSExecResult result = {};
|
|
return result;
|
|
}
|
|
|
|
static DN_POSIXSyncPrimitive *DN_OS_U64ToPOSIXSyncPrimitive_(DN_U64 u64)
|
|
{
|
|
DN_POSIXSyncPrimitive *result = nullptr;
|
|
DN_Memcpy(&result, &u64, sizeof(result));
|
|
return result;
|
|
}
|
|
|
|
static DN_U64 DN_POSIX_SyncPrimitiveToU64(DN_POSIXSyncPrimitive *primitive)
|
|
{
|
|
DN_U64 result = 0;
|
|
static_assert(sizeof(result) >= sizeof(primitive), "Pointer size mis-match");
|
|
DN_Memcpy(&result, &primitive, sizeof(result));
|
|
return result;
|
|
}
|
|
|
|
static DN_POSIXSyncPrimitive *DN_POSIX_AllocSyncPrimitive_()
|
|
{
|
|
DN_POSIXCore *posix = DN_OS_GetPOSIXCore_();
|
|
DN_POSIXSyncPrimitive *result = nullptr;
|
|
pthread_mutex_lock(&posix->sync_primitive_free_list_mutex);
|
|
{
|
|
if (posix->sync_primitive_free_list) {
|
|
result = posix->sync_primitive_free_list;
|
|
posix->sync_primitive_free_list = posix->sync_primitive_free_list->next;
|
|
result->next = nullptr;
|
|
} else {
|
|
DN_OSCore *os = &g_dn_->os;
|
|
result = DN_ArenaNew(&os->arena, DN_POSIXSyncPrimitive, DN_ZMem_Yes);
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&posix->sync_primitive_free_list_mutex);
|
|
return result;
|
|
}
|
|
|
|
static void DN_POSIX_DeallocSyncPrimitive_(DN_POSIXSyncPrimitive *primitive)
|
|
{
|
|
if (primitive) {
|
|
DN_POSIXCore *posix = DN_OS_GetPOSIXCore_();
|
|
pthread_mutex_lock(&posix->sync_primitive_free_list_mutex);
|
|
primitive->next = posix->sync_primitive_free_list;
|
|
posix->sync_primitive_free_list = primitive;
|
|
pthread_mutex_unlock(&posix->sync_primitive_free_list_mutex);
|
|
}
|
|
}
|
|
|
|
// NOTE: DN_OSSemaphore ////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_OSSemaphore DN_OS_SemaphoreInit(DN_U32 initial_count)
|
|
{
|
|
DN_OSSemaphore result = {};
|
|
DN_POSIXSyncPrimitive *primitive = DN_POSIX_AllocSyncPrimitive_();
|
|
if (primitive) {
|
|
int pshared = 0; // Share the semaphore across all threads in the process
|
|
if (sem_init(&primitive->sem, pshared, initial_count) == 0)
|
|
result.handle = DN_POSIX_SyncPrimitiveToU64(primitive);
|
|
else
|
|
DN_POSIX_DeallocSyncPrimitive_(primitive);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_SemaphoreDeinit(DN_OSSemaphore *semaphore)
|
|
{
|
|
if (semaphore && semaphore->handle != 0) {
|
|
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(semaphore->handle);
|
|
sem_destroy(&primitive->sem);
|
|
DN_POSIX_DeallocSyncPrimitive_(primitive);
|
|
*semaphore = {};
|
|
}
|
|
}
|
|
|
|
DN_API void DN_OS_SemaphoreIncrement(DN_OSSemaphore *semaphore, DN_U32 amount)
|
|
{
|
|
if (semaphore && semaphore->handle != 0) {
|
|
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(semaphore->handle);
|
|
#if defined(DN_OS_WIN32)
|
|
sem_post_multiple(&primitive->sem, amount); // mingw extension
|
|
#else
|
|
for (DN_ForIndexU(index, amount))
|
|
sem_post(&primitive->sem);
|
|
#endif // !defined(DN_OS_WIN32)
|
|
}
|
|
}
|
|
|
|
DN_API DN_OSSemaphoreWaitResult DN_OS_SemaphoreWait(DN_OSSemaphore *semaphore,
|
|
DN_U32 timeout_ms)
|
|
{
|
|
DN_OSSemaphoreWaitResult result = {};
|
|
if (!semaphore || semaphore->handle == 0)
|
|
return result;
|
|
|
|
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(semaphore->handle);
|
|
if (timeout_ms == DN_OS_SEMAPHORE_INFINITE_TIMEOUT) {
|
|
int wait_result = 0;
|
|
do {
|
|
wait_result = sem_wait(&primitive->sem);
|
|
} while (wait_result == -1 && errno == EINTR);
|
|
|
|
if (wait_result == 0)
|
|
result = DN_OSSemaphoreWaitResult_Success;
|
|
} else {
|
|
DN_U64 now_ms = DN_OS_DateUnixTimeMs();
|
|
DN_U64 end_ts_ms = now_ms + timeout_ms;
|
|
|
|
struct timespec abs_timeout = {};
|
|
abs_timeout.tv_sec = end_ts_ms / 1'000;
|
|
abs_timeout.tv_nsec = 1'000'000 * (end_ts_ms - (end_ts_ms / 1'000) * 1'000);
|
|
if (sem_timedwait(&primitive->sem, &abs_timeout) == 0)
|
|
result = DN_OSSemaphoreWaitResult_Success;
|
|
else if (errno == ETIMEDOUT)
|
|
result = DN_OSSemaphoreWaitResult_Timeout;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// NOTE: DN_OSMutex ////////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_OSMutex DN_OS_MutexInit()
|
|
{
|
|
DN_POSIXSyncPrimitive *primitive = DN_POSIX_AllocSyncPrimitive_();
|
|
DN_OSMutex result = {};
|
|
if (primitive) {
|
|
if (pthread_mutex_init(&primitive->mutex, nullptr) == 0)
|
|
result.handle = DN_POSIX_SyncPrimitiveToU64(primitive);
|
|
else
|
|
DN_POSIX_DeallocSyncPrimitive_(primitive);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_MutexDeinit(DN_OSMutex *mutex)
|
|
{
|
|
if (mutex && mutex->handle != 0) {
|
|
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(mutex->handle);
|
|
pthread_mutex_destroy(&primitive->mutex);
|
|
DN_POSIX_DeallocSyncPrimitive_(primitive);
|
|
*mutex = {};
|
|
}
|
|
}
|
|
|
|
DN_API void DN_OS_MutexLock(DN_OSMutex *mutex)
|
|
{
|
|
if (mutex && mutex->handle != 0) {
|
|
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(mutex->handle);
|
|
pthread_mutex_lock(&primitive->mutex);
|
|
}
|
|
}
|
|
|
|
DN_API void DN_OS_MutexUnlock(DN_OSMutex *mutex)
|
|
{
|
|
if (mutex && mutex->handle != 0) {
|
|
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(mutex->handle);
|
|
pthread_mutex_unlock(&primitive->mutex);
|
|
}
|
|
}
|
|
|
|
DN_API DN_OSConditionVariable DN_OS_ConditionVariableInit()
|
|
{
|
|
DN_POSIXSyncPrimitive *primitive = DN_POSIX_AllocSyncPrimitive_();
|
|
DN_OSConditionVariable result = {};
|
|
if (primitive) {
|
|
if (pthread_cond_init(&primitive->cv, nullptr) == 0)
|
|
result.handle = DN_POSIX_SyncPrimitiveToU64(primitive);
|
|
else
|
|
DN_POSIX_DeallocSyncPrimitive_(primitive);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_ConditionVariableDeinit(DN_OSConditionVariable *cv)
|
|
{
|
|
if (cv && cv->handle != 0) {
|
|
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(cv->handle);
|
|
pthread_cond_destroy(&primitive->cv);
|
|
DN_POSIX_DeallocSyncPrimitive_(primitive);
|
|
*cv = {};
|
|
}
|
|
}
|
|
|
|
DN_API bool DN_OS_ConditionVariableWaitUntil(DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 end_ts_ms)
|
|
{
|
|
bool result = false;
|
|
if (cv && mutex && mutex->handle != 0 && cv->handle != 0) {
|
|
DN_POSIXSyncPrimitive *cv_primitive = DN_OS_U64ToPOSIXSyncPrimitive_(cv->handle);
|
|
DN_POSIXSyncPrimitive *mutex_primitive = DN_OS_U64ToPOSIXSyncPrimitive_(mutex->handle);
|
|
|
|
struct timespec time = {};
|
|
time.tv_sec = end_ts_ms / 1'000;
|
|
time.tv_nsec = 1'000'000 * (end_ts_ms - (end_ts_ms / 1'000) * 1'000);
|
|
int wait_result = pthread_cond_timedwait(&cv_primitive->cv, &mutex_primitive->mutex, &time);
|
|
result = (wait_result != ETIMEDOUT);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_ConditionVariableWait(DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 sleep_ms)
|
|
{
|
|
DN_U64 end_ts_ms = DN_OS_DateUnixTimeMs() + sleep_ms;
|
|
bool result = DN_OS_ConditionVariableWaitUntil(cv, mutex, end_ts_ms);
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_ConditionVariableSignal(DN_OSConditionVariable *cv)
|
|
{
|
|
if (cv && cv->handle != 0) {
|
|
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(cv->handle);
|
|
pthread_cond_signal(&primitive->cv);
|
|
}
|
|
}
|
|
|
|
DN_API void DN_OS_ConditionVariableBroadcast(DN_OSConditionVariable *cv)
|
|
{
|
|
if (cv && cv->handle != 0) {
|
|
DN_POSIXSyncPrimitive *primitive = DN_OS_U64ToPOSIXSyncPrimitive_(cv->handle);
|
|
pthread_cond_broadcast(&primitive->cv);
|
|
}
|
|
}
|
|
|
|
// NOTE: DN_OSThread ///////////////////////////////////////////////////////////////////////////////
|
|
static void *DN_OS_ThreadFunc_(void *user_context)
|
|
{
|
|
DN_OS_ThreadExecute_(user_context);
|
|
return nullptr;
|
|
}
|
|
|
|
DN_API bool DN_OS_ThreadInit(DN_OSThread *thread, DN_OSThreadFunc *func, void *user_context)
|
|
{
|
|
bool result = false;
|
|
if (!thread)
|
|
return result;
|
|
|
|
thread->func = func;
|
|
thread->user_context = user_context;
|
|
thread->init_semaphore = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
|
|
|
// TODO(doyle): Check if semaphore is valid
|
|
// NOTE: pthread_t is essentially the thread ID. In Windows, the handle and
|
|
// the ID are different things. For pthreads then we just duplicate the
|
|
// thread ID to both variables
|
|
pthread_t p_thread = {};
|
|
static_assert(sizeof(p_thread) <= sizeof(thread->handle),
|
|
"We store the thread handle opaquely in our abstraction, "
|
|
"there must be enough bytes to store pthread's structure");
|
|
static_assert(sizeof(p_thread) <= sizeof(thread->thread_id),
|
|
"We store the thread handle opaquely in our abstraction, "
|
|
"there must be enough bytes to store pthread's structure");
|
|
|
|
pthread_attr_t attribs = {};
|
|
pthread_attr_init(&attribs);
|
|
result = pthread_create(&p_thread, &attribs, DN_OS_ThreadFunc_, thread) == 0;
|
|
pthread_attr_destroy(&attribs);
|
|
|
|
if (result) {
|
|
DN_Memcpy(&thread->handle, &p_thread, sizeof(p_thread));
|
|
DN_Memcpy(&thread->thread_id, &p_thread, sizeof(p_thread));
|
|
}
|
|
|
|
if (result) {
|
|
DN_OS_SemaphoreIncrement(&thread->init_semaphore, 1);
|
|
} else {
|
|
DN_OS_SemaphoreDeinit(&thread->init_semaphore);
|
|
*thread = {};
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_ThreadDeinit(DN_OSThread *thread)
|
|
{
|
|
if (!thread || !thread->handle)
|
|
return;
|
|
|
|
pthread_t thread_id = {};
|
|
DN_Memcpy(&thread_id, &thread->thread_id, sizeof(thread_id));
|
|
|
|
void *return_val = nullptr;
|
|
pthread_join(thread_id, &return_val);
|
|
thread->handle = {};
|
|
thread->thread_id = {};
|
|
}
|
|
|
|
DN_API DN_U32 DN_OS_ThreadID()
|
|
{
|
|
pid_t result = gettid();
|
|
DN_Assert(gettid() >= 0);
|
|
return DN_Cast(DN_U32) result;
|
|
}
|
|
|
|
DN_API void DN_Posix_Init(DN_POSIXCore *posix)
|
|
{
|
|
int mutex_init = pthread_mutex_init(&posix->sync_primitive_free_list_mutex, nullptr);
|
|
DN_Assert(mutex_init == 0);
|
|
|
|
struct timespec ts;
|
|
posix->clock_monotonic_raw = clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != -1;
|
|
if (!posix->clock_monotonic_raw) {
|
|
int get_result = clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
DN_AssertF(get_result != -1, "CLOCK_MONOTONIC_RAW and CLOCK_MONOTONIC are not supported by this platform");
|
|
}
|
|
}
|
|
|
|
DN_API void DN_Posix_ThreadSetName(DN_Str8 name)
|
|
{
|
|
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
(void)name;
|
|
#else
|
|
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(nullptr);
|
|
DN_Str8 copy = DN_Str8FromStr8Arena(tmem.arena, name);
|
|
pthread_t thread = pthread_self();
|
|
pthread_setname_np(thread, (char *)copy.data);
|
|
#endif
|
|
}
|
|
|
|
DN_API DN_POSIXProcSelfStatus DN_Posix_ProcSelfStatus()
|
|
{
|
|
DN_POSIXProcSelfStatus result = {};
|
|
|
|
// NOTE: Example
|
|
//
|
|
// ...
|
|
// VmPeak: 3352 kB
|
|
// VmSize: 3352 kB
|
|
// VmLck: 0 kB
|
|
// ...
|
|
//
|
|
// VmSize is the total virtual memory used
|
|
DN_OSFile file = DN_OS_FileOpen(DN_Str8Lit("/proc/self/status"), DN_OSFileOpen_OpenIfExist, DN_OSFileAccess_Read, nullptr);
|
|
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(nullptr);
|
|
|
|
if (!file.error) {
|
|
char buf[256];
|
|
DN_Str8Builder builder = DN_Str8BuilderFromTLS();
|
|
for (;;) {
|
|
DN_OSFileRead read = DN_OS_FileRead(&file, buf, sizeof(buf), nullptr);
|
|
if (!read.success || read.bytes_read == 0)
|
|
break;
|
|
DN_Str8BuilderAppendF(&builder, "%.*s", DN_Cast(int)read.bytes_read, buf);
|
|
}
|
|
|
|
DN_Str8 const NAME = DN_Str8Lit("Name:");
|
|
DN_Str8 const PID = DN_Str8Lit("Pid:");
|
|
DN_Str8 const VM_PEAK = DN_Str8Lit("VmPeak:");
|
|
DN_Str8 const VM_SIZE = DN_Str8Lit("VmSize:");
|
|
DN_Str8 status_buf = DN_Str8BuilderBuildFromTLS(&builder);
|
|
DN_Str8SplitResult lines = DN_Str8SplitFromTLS(status_buf, DN_Str8Lit("\n"), DN_Str8SplitIncludeEmptyStrings_No);
|
|
|
|
for (DN_ForItSize(line_it, DN_Str8, lines.data, lines.count)) {
|
|
DN_Str8 line = DN_Str8TrimWhitespaceAround(*line_it.data);
|
|
if (DN_Str8StartsWith(line, NAME, DN_Str8EqCase_Insensitive)) {
|
|
DN_Str8 str8 = DN_Str8TrimWhitespaceAround(DN_Str8Slice(line, NAME.size, line.size));
|
|
result.name_size = DN_Min(str8.size, sizeof(result.name));
|
|
DN_Memcpy(result.name, str8.data, result.name_size);
|
|
} else if (DN_Str8StartsWith(line, PID, DN_Str8EqCase_Insensitive)) {
|
|
DN_Str8 str8 = DN_Str8TrimWhitespaceAround(DN_Str8Slice(line, PID.size, line.size));
|
|
DN_U64FromResult to_u64 = DN_U64FromStr8(str8, 0);
|
|
result.pid = to_u64.value;
|
|
DN_Assert(to_u64.success);
|
|
} else if (DN_Str8StartsWith(line, VM_SIZE, DN_Str8EqCase_Insensitive)) {
|
|
DN_Str8 size_with_kb = DN_Str8TrimWhitespaceAround(DN_Str8Slice(line, VM_SIZE.size, line.size));
|
|
DN_Assert(DN_Str8EndsWith(size_with_kb, DN_Str8Lit("kB")));
|
|
DN_Str8 vm_size = DN_Str8BSplit(size_with_kb, DN_Str8Lit(" ")).lhs;
|
|
DN_U64FromResult to_u64 = DN_U64FromStr8(vm_size, 0);
|
|
result.vm_size = DN_Kilobytes(to_u64.value);
|
|
DN_Assert(to_u64.success);
|
|
} else if (DN_Str8StartsWith(line, VM_PEAK, DN_Str8EqCase_Insensitive)) {
|
|
DN_Str8 size_with_kb = DN_Str8TrimWhitespaceAround(DN_Str8Slice(line, VM_PEAK.size, line.size));
|
|
DN_Assert(DN_Str8EndsWith(size_with_kb, DN_Str8Lit("kB")));
|
|
DN_Str8 vm_size = DN_Str8BSplit(size_with_kb, DN_Str8Lit(" ")).lhs;
|
|
DN_U64FromResult to_u64 = DN_U64FromStr8(vm_size, 0);
|
|
result.vm_peak = DN_Kilobytes(to_u64.value);
|
|
DN_Assert(to_u64.success);
|
|
}
|
|
}
|
|
}
|
|
DN_OS_FileClose(&file);
|
|
return result;
|
|
}
|
|
|
|
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
|
|
#if 0 // TODO(doyle): Implement websockets for Windows and Emscripten
|
|
static EM_BOOL EMWebSocketOnOpenCallback(int type, const EmscriptenWebSocketOpenEvent *event, void *user_context)
|
|
{
|
|
(void)user_context;
|
|
(void)type;
|
|
(void)event;
|
|
// EMSCRIPTEN_RESULT result = emscripten_websocket_send_utf8_text(event->socket, R"({"jsonrpc":"2.0","id":1,"method": "eth_subscribe","params":["newHeads"]})");
|
|
// if (result)
|
|
// DN_LOG_InfoF("Failed to emscripten_websocket_send_utf8_text(): %d\n", result);
|
|
return EM_TRUE;
|
|
}
|
|
|
|
static EM_BOOL EMWebSocketOnMsgCallback(int type, const EmscriptenWebSocketMessageEvent *event __attribute__((nonnull)), void *user_context)
|
|
{
|
|
(void)type;
|
|
(void)user_context;
|
|
(void)event;
|
|
if (event->isText) {
|
|
DN_LOG_InfoF("Received: %.*s", event->numBytes, event->data);
|
|
} else {
|
|
DN_LOG_InfoF("Received: %d bytes", event->numBytes);
|
|
}
|
|
return EM_TRUE;
|
|
}
|
|
|
|
static EM_BOOL EMWebSocketOnErrorCallback(int type, const EmscriptenWebSocketErrorEvent *event, void *user_context)
|
|
{
|
|
(void)user_context;
|
|
(void)type;
|
|
(void)event;
|
|
return EM_TRUE;
|
|
}
|
|
|
|
static EM_BOOL EMWebSocketOnCloseCallback(int type, const EmscriptenWebSocketCloseEvent *event, void *user_context)
|
|
{
|
|
(void)user_context;
|
|
(void)type;
|
|
(void)event;
|
|
return EM_TRUE;
|
|
}
|
|
#endif
|
|
|
|
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
static void DN_OS_HttpRequestEMFetchOnSuccessCallback(emscripten_fetch_t *fetch)
|
|
{
|
|
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData;
|
|
if (!DN_Check(response))
|
|
return;
|
|
|
|
response->http_status = DN_Cast(DN_U32) fetch->status;
|
|
response->body = DN_Str8FromArena(response->arena, fetch->numBytes, DN_ZMem_No);
|
|
if (response->body.data)
|
|
DN_Memcpy(response->body.data, fetch->data, fetch->numBytes);
|
|
|
|
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
|
|
DN_AtomicAddU32(&response->done, 1);
|
|
}
|
|
|
|
static void DN_OS_HttpRequestEMFetchOnErrorCallback(emscripten_fetch_t *fetch)
|
|
{
|
|
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData;
|
|
if (!DN_Check(response))
|
|
return;
|
|
|
|
response->http_status = DN_Cast(DN_U32) fetch->status;
|
|
response->body = DN_Str8FromArena(response->arena, fetch->numBytes, DN_ZMem_No);
|
|
if (response->body.size)
|
|
DN_Memcpy(response->body.data, fetch->data, fetch->numBytes);
|
|
|
|
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
|
|
DN_AtomicAddU32(&response->done, 1);
|
|
}
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
if (!response || !arena)
|
|
return;
|
|
|
|
response->arena = arena;
|
|
response->builder.arena =
|
|
response->tmem_arena ? response->tmem_arena : &response->tmp_arena;
|
|
|
|
DN_Arena *tmem = response->tmem_arena;
|
|
DN_OSTLSTMem tmem_ = DN_OS_TLSTMem(arena);
|
|
if (!tmem)
|
|
tmem = tmem_.arena;
|
|
|
|
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
emscripten_fetch_attr_t fetch_attribs = {};
|
|
emscripten_fetch_attr_init(&fetch_attribs);
|
|
|
|
if (method.size >= sizeof(fetch_attribs.requestMethod)) {
|
|
response->error_msg =
|
|
DN_Str8FromFmtArena(arena,
|
|
"Request method in EM has a size limit of 31 characters, method was "
|
|
"'%.*s' which is %zu characters long",
|
|
DN_Str8PrintFmt(method),
|
|
method.size);
|
|
DN_CheckF(method.size < sizeof(fetch_attribs.requestMethod),
|
|
"%.*s",
|
|
DN_Str8PrintFmt(response->error_msg));
|
|
response->error_code = DN_Cast(DN_U32) - 1;
|
|
DN_AtomicAddU32(&response->done, 1);
|
|
return;
|
|
}
|
|
|
|
DN_Memcpy(fetch_attribs.requestMethod, method.data, method.size);
|
|
|
|
fetch_attribs.requestData = body.data;
|
|
fetch_attribs.requestDataSize = body.size;
|
|
fetch_attribs.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
|
|
fetch_attribs.onsuccess = DN_OS_HttpRequestEMFetchOnSuccessCallback;
|
|
fetch_attribs.onerror = DN_OS_HttpRequestEMFetchOnErrorCallback;
|
|
fetch_attribs.userData = response;
|
|
|
|
DN_Str8 url = DN_Str8FromFmtArena(tmem, "%.*s%.*s", DN_Str8PrintFmt(host), DN_Str8PrintFmt(path));
|
|
DN_LOG_InfoF("Initiating HTTP '%s' request to '%.*s' with payload '%.*s'",
|
|
fetch_attribs.requestMethod,
|
|
DN_Str8PrintFmt(url),
|
|
DN_Str8PrintFmt(body));
|
|
response->on_complete_semaphore = DN_OS_SemaphoreInit(0);
|
|
response->em_handle = emscripten_fetch(&fetch_attribs, url.data);
|
|
#else // #elif defined(DN_OS_WIN32)
|
|
DN_InvalidCodePathF("Unimplemented function");
|
|
#endif
|
|
}
|
|
|
|
DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response)
|
|
{
|
|
// NOTE: Cleanup
|
|
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
if (response->em_handle) {
|
|
emscripten_fetch_close(response->em_handle);
|
|
response->em_handle = nullptr;
|
|
}
|
|
#endif // #elif defined(DN_OS_WIN32)
|
|
|
|
DN_ArenaDeinit(&response->tmp_arena);
|
|
DN_OS_SemaphoreDeinit(&response->on_complete_semaphore);
|
|
*response = {};
|
|
}
|
|
#elif defined(DN_PLATFORM_WIN32)
|
|
// DN: Single header generator inlined this file => #include "OS/dn_os_w32.cpp"
|
|
#define DN_OS_WIN32_CPP
|
|
|
|
/*
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
|
|
// $$ __$$\ $$ __$$\ $$ | $\ $$ |\_$$ _|$$$\ $$ |$$ ___$$\ $$ __$$\
|
|
// $$ / $$ |$$ / \__| $$ |$$$\ $$ | $$ | $$$$\ $$ |\_/ $$ |\__/ $$ |
|
|
// $$ | $$ |\$$$$$$\ $$ $$ $$\$$ | $$ | $$ $$\$$ | $$$$$ / $$$$$$ |
|
|
// $$ | $$ | \____$$\ $$$$ _$$$$ | $$ | $$ \$$$$ | \___$$\ $$ ____/
|
|
// $$ | $$ |$$\ $$ | $$$ / \$$$ | $$ | $$ |\$$$ |$$\ $$ |$$ |
|
|
// $$$$$$ |\$$$$$$ | $$ / \$$ |$$$$$$\ $$ | \$$ |\$$$$$$ |$$$$$$$$\
|
|
// \______/ \______/ \__/ \__|\______|\__| \__| \______/ \________|
|
|
//
|
|
// dn_os_w32.cpp
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
*/
|
|
|
|
// NOTE: DN_Mem ///////////////////////////////////////////////////////////////////////////
|
|
static DN_U32 DN_OS_MemConvertPageToOSFlags_(DN_U32 protect)
|
|
{
|
|
DN_Assert((protect & ~DN_MemPage_All) == 0);
|
|
DN_Assert(protect != 0);
|
|
DN_U32 result = 0;
|
|
|
|
if (protect & DN_MemPage_NoAccess) {
|
|
result = PAGE_NOACCESS;
|
|
} else if (protect & DN_MemPage_ReadWrite) {
|
|
result = PAGE_READWRITE;
|
|
} else if (protect & DN_MemPage_Read) {
|
|
result = PAGE_READONLY;
|
|
} else if (protect & DN_MemPage_Write) {
|
|
DN_LOG_WarningF("Windows does not support write-only pages, granting read+write access");
|
|
result = PAGE_READWRITE;
|
|
}
|
|
|
|
if (protect & DN_MemPage_Guard)
|
|
result |= PAGE_GUARD;
|
|
|
|
DN_AssertF(result != PAGE_GUARD, "Page guard is a modifier, you must also specify a page permission like read or/and write");
|
|
return result;
|
|
}
|
|
|
|
DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_flags)
|
|
{
|
|
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
|
|
unsigned long flags = MEM_RESERVE;
|
|
if (commit == DN_MemCommit_Yes)
|
|
flags |= MEM_COMMIT;
|
|
|
|
void *result = VirtualAlloc(nullptr, size, flags, os_page_flags);
|
|
if (flags & MEM_COMMIT) {
|
|
DN_Assert(g_dn_);
|
|
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_total, 1);
|
|
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_frame, 1);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags)
|
|
{
|
|
bool result = false;
|
|
if (!ptr || size == 0)
|
|
return false;
|
|
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
|
|
result = VirtualAlloc(ptr, size, MEM_COMMIT, os_page_flags) != nullptr;
|
|
DN_Assert(g_dn_);
|
|
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_total, 1);
|
|
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_frame, 1);
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_MemDecommit(void *ptr, DN_USize size)
|
|
{
|
|
// NOTE: This is a decommit call, which is explicitly saying to free the
|
|
// pages but not the address space, you would use OS_MemRelease to release
|
|
// everything.
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(6250) // Calling 'VirtualFree' without the MEM_RELEASE flag might free memory but not address descriptors (VADs). This causes address space leaks.
|
|
VirtualFree(ptr, size, MEM_DECOMMIT);
|
|
DN_MSVC_WARNING_POP
|
|
}
|
|
|
|
DN_API void DN_OS_MemRelease(void *ptr, DN_USize size)
|
|
{
|
|
(void)size;
|
|
VirtualFree(ptr, 0, MEM_RELEASE);
|
|
}
|
|
|
|
DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags)
|
|
{
|
|
if (!ptr || size == 0)
|
|
return 0;
|
|
|
|
static DN_Str8 const ALIGNMENT_ERROR_MSG =
|
|
DN_Str8Lit("Page protection requires pointers to be page aligned because we can only guard memory at a multiple of the page boundary.");
|
|
DN_AssertF(DN_IsPowerOfTwoAligned(DN_Cast(uintptr_t) ptr, g_dn_->os.page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
|
DN_AssertF(DN_IsPowerOfTwoAligned(size, g_dn_->os.page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
|
|
|
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
|
|
unsigned long prev_flags = 0;
|
|
int result = VirtualProtect(ptr, size, os_page_flags, &prev_flags);
|
|
|
|
(void)prev_flags;
|
|
if (result == 0)
|
|
DN_AssertF(result, "VirtualProtect failed");
|
|
return result;
|
|
}
|
|
|
|
DN_API void *DN_OS_MemAlloc(DN_USize size, DN_ZMem z_mem)
|
|
{
|
|
DN_RawAssert(g_dn_->init_flags & DN_InitFlags_OS && "DN must be initialised with the OS flag");
|
|
DN_U32 flags = z_mem == DN_ZMem_Yes ? HEAP_ZERO_MEMORY : 0;
|
|
DN_Assert(size <= DN_Cast(DWORD)(-1));
|
|
void *result = HeapAlloc(GetProcessHeap(), flags, DN_Cast(DWORD) size);
|
|
DN_Assert(g_dn_);
|
|
DN_AtomicAddU64(&g_dn_->os.mem_allocs_total, 1);
|
|
DN_AtomicAddU64(&g_dn_->os.mem_allocs_frame, 1);
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_MemDealloc(void *ptr)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, ptr);
|
|
}
|
|
|
|
// NOTE: Date //////////////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_OSDateTime DN_OS_DateLocalTimeNow()
|
|
{
|
|
SYSTEMTIME sys_time;
|
|
GetLocalTime(&sys_time);
|
|
|
|
DN_OSDateTime result = {};
|
|
result.hour = DN_Cast(uint8_t) sys_time.wHour;
|
|
result.minutes = DN_Cast(uint8_t) sys_time.wMinute;
|
|
result.seconds = DN_Cast(uint8_t) sys_time.wSecond;
|
|
result.day = DN_Cast(uint8_t) sys_time.wDay;
|
|
result.month = DN_Cast(uint8_t) sys_time.wMonth;
|
|
result.year = DN_Cast(int16_t) sys_time.wYear;
|
|
return result;
|
|
}
|
|
|
|
const DN_U64 DN_OS_WIN32_UNIX_TIME_START = 0x019DB1DED53E8000; // January 1, 1970 (start of Unix epoch) in "ticks"
|
|
const DN_U64 DN_OS_WIN32_FILE_TIME_TICKS_PER_SECOND = 10'000'000; // Filetime returned is in intervals of 100 nanoseconds
|
|
|
|
DN_API DN_U64 DN_OS_DateUnixTimeNs()
|
|
{
|
|
FILETIME file_time;
|
|
GetSystemTimeAsFileTime(&file_time);
|
|
|
|
// NOTE: Filetime returned is in intervals of 100 nanoeseconds so we
|
|
// multiply by 100 to get nanoseconds.
|
|
LARGE_INTEGER date_time;
|
|
date_time.u.LowPart = file_time.dwLowDateTime;
|
|
date_time.u.HighPart = file_time.dwHighDateTime;
|
|
DN_U64 result = (date_time.QuadPart - DN_OS_WIN32_UNIX_TIME_START) * 100;
|
|
return result;
|
|
}
|
|
|
|
static SYSTEMTIME DN_OS_DateToSystemTime_(DN_OSDateTime date)
|
|
{
|
|
SYSTEMTIME result = {};
|
|
result.wYear = date.year;
|
|
result.wMonth = date.month;
|
|
result.wDay = date.day;
|
|
result.wHour = date.hour;
|
|
result.wMinute = date.minutes;
|
|
result.wSecond = date.seconds;
|
|
return result;
|
|
}
|
|
|
|
static DN_U64 DN_OS_SystemTimeToUnixTimeS_(SYSTEMTIME *sys_time)
|
|
{
|
|
FILETIME file_time = {};
|
|
SystemTimeToFileTime(sys_time, &file_time);
|
|
|
|
LARGE_INTEGER date_time;
|
|
date_time.u.LowPart = file_time.dwLowDateTime;
|
|
date_time.u.HighPart = file_time.dwHighDateTime;
|
|
DN_U64 result = (date_time.QuadPart - DN_OS_WIN32_UNIX_TIME_START) / DN_OS_WIN32_FILE_TIME_TICKS_PER_SECOND;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64 DN_OS_DateLocalToUnixTimeS(DN_OSDateTime date)
|
|
{
|
|
SYSTEMTIME local_time = DN_OS_DateToSystemTime_(date);
|
|
SYSTEMTIME sys_time = {};
|
|
TzSpecificLocalTimeToSystemTime(nullptr, &local_time, &sys_time);
|
|
DN_U64 result = DN_OS_SystemTimeToUnixTimeS_(&sys_time);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64 DN_OS_DateToUnixTimeS(DN_OSDateTime date)
|
|
{
|
|
DN_Assert(DN_OS_DateIsValid(date));
|
|
|
|
SYSTEMTIME sys_time = DN_OS_DateToSystemTime_(date);
|
|
DN_U64 result = DN_OS_SystemTimeToUnixTimeS_(&sys_time);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_OSDateTime DN_OS_DateUnixTimeSToDate(DN_U64 time)
|
|
{
|
|
// NOTE: Windows epoch time starts from Jan 1, 1601 and counts in
|
|
// 100-nanoseconds intervals.
|
|
//
|
|
// See: https://devblogs.microsoft.com/oldnewthing/20090306-00/?p=18913
|
|
|
|
DN_U64 w32_time = 116'444'736'000'000'000 + (time * 10'000'000);
|
|
SYSTEMTIME sys_time = {};
|
|
FILETIME file_time = {};
|
|
file_time.dwLowDateTime = (DWORD)w32_time;
|
|
file_time.dwHighDateTime = w32_time >> 32;
|
|
FileTimeToSystemTime(&file_time, &sys_time);
|
|
|
|
DN_OSDateTime result = {};
|
|
result.year = DN_Cast(uint16_t) sys_time.wYear;
|
|
result.month = DN_Cast(uint8_t) sys_time.wMonth;
|
|
result.day = DN_Cast(uint8_t) sys_time.wDay;
|
|
result.hour = DN_Cast(uint8_t) sys_time.wHour;
|
|
result.minutes = DN_Cast(uint8_t) sys_time.wMinute;
|
|
result.seconds = DN_Cast(uint8_t) sys_time.wSecond;
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size)
|
|
{
|
|
DN_Assert(g_dn_);
|
|
DN_W32Core *w32 = DN_Cast(DN_W32Core *) g_dn_->os.platform_context;
|
|
DN_Assert(w32->bcrypt_init_success);
|
|
|
|
long gen_status = BCryptGenRandom(w32->bcrypt_rng_handle, DN_Cast(unsigned char *) buffer, size, 0 /*flags*/);
|
|
// NOTE: This can only fail if the handle is invalid or one or more parameters are invalid. We
|
|
// validate our parameters so this shouldn't be the case.
|
|
DN_Assert(gen_status == 0);
|
|
}
|
|
|
|
DN_API DN_OSDiskSpace DN_OS_DiskSpace(DN_Str8 path)
|
|
{
|
|
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(nullptr);
|
|
DN_OSDiskSpace result = {};
|
|
DN_Str16 path16 = DN_W32_Str8ToStr16(tmem.arena, path);
|
|
|
|
ULARGE_INTEGER free_bytes_avail_to_caller;
|
|
ULARGE_INTEGER total_number_of_bytes;
|
|
ULARGE_INTEGER total_number_of_free_bytes;
|
|
if (!GetDiskFreeSpaceExW(path16.data,
|
|
&free_bytes_avail_to_caller,
|
|
&total_number_of_bytes,
|
|
&total_number_of_free_bytes))
|
|
return result;
|
|
|
|
result.success = true;
|
|
result.avail = free_bytes_avail_to_caller.QuadPart;
|
|
result.size = total_number_of_bytes.QuadPart;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_SetEnvVar(DN_Str8 name, DN_Str8 value)
|
|
{
|
|
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(nullptr);
|
|
DN_Str16 name16 = DN_W32_Str8ToStr16(tmem.arena, name);
|
|
DN_Str16 value16 = DN_W32_Str8ToStr16(tmem.arena, value);
|
|
bool result = SetEnvironmentVariableW(name16.data, value16.data) != 0;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_OS_EXEPath(DN_Arena *arena)
|
|
{
|
|
DN_Str8 result = {};
|
|
if (!arena)
|
|
return result;
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
|
DN_Str16 exe_dir16 = DN_W32_EXEPathW(tmem.arena);
|
|
result = DN_W32_Str16ToStr8(arena, exe_dir16);
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_SleepMs(DN_UInt milliseconds)
|
|
{
|
|
Sleep(milliseconds);
|
|
}
|
|
|
|
DN_API DN_U64 DN_OS_PerfCounterFrequency()
|
|
{
|
|
DN_Assert(g_dn_);
|
|
DN_W32Core *w32 = DN_Cast(DN_W32Core *) g_dn_->os.platform_context;
|
|
DN_Assert(w32->qpc_frequency.QuadPart);
|
|
DN_U64 result = w32->qpc_frequency.QuadPart;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64 DN_OS_PerfCounterNow()
|
|
{
|
|
LARGE_INTEGER integer = {};
|
|
QueryPerformanceCounter(&integer);
|
|
DN_U64 result = integer.QuadPart;
|
|
return result;
|
|
}
|
|
|
|
static DN_U64 DN_W32_FileTimeToSeconds_(FILETIME const *time)
|
|
{
|
|
ULARGE_INTEGER time_large_int = {};
|
|
time_large_int.u.LowPart = time->dwLowDateTime;
|
|
time_large_int.u.HighPart = time->dwHighDateTime;
|
|
DN_U64 result = (time_large_int.QuadPart / 10000000ULL) - 11644473600ULL;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileCopy(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_OSErrSink *err)
|
|
{
|
|
bool result = false;
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str16 src16 = DN_W32_Str8ToStr16(tmem.arena, src);
|
|
DN_Str16 dest16 = DN_W32_Str8ToStr16(tmem.arena, dest);
|
|
|
|
int fail_if_exists = overwrite == false;
|
|
result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0;
|
|
|
|
if (!result) {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
DN_OS_ErrSinkAppendF(err,
|
|
win_error.code,
|
|
"Failed to copy file '%.*s' to '%.*s': (%u) %.*s",
|
|
DN_Str8PrintFmt(src),
|
|
DN_Str8PrintFmt(dest),
|
|
win_error.code,
|
|
DN_Str8PrintFmt(win_error.msg));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileMove(DN_Str8 src, DN_Str8 dest, bool overwrite, DN_OSErrSink *err)
|
|
{
|
|
bool result = false;
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str16 src16 = DN_W32_Str8ToStr16(tmem.arena, src);
|
|
DN_Str16 dest16 = DN_W32_Str8ToStr16(tmem.arena, dest);
|
|
|
|
unsigned long flags = MOVEFILE_COPY_ALLOWED;
|
|
if (overwrite)
|
|
flags |= MOVEFILE_REPLACE_EXISTING;
|
|
|
|
result = MoveFileExW(src16.data, dest16.data, flags) != 0;
|
|
if (!result) {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
DN_OS_ErrSinkAppendF(err,
|
|
win_error.code,
|
|
"Failed to move file '%.*s' to '%.*s': (%u) %.*s",
|
|
DN_Str8PrintFmt(src),
|
|
DN_Str8PrintFmt(dest),
|
|
win_error.code,
|
|
DN_Str8PrintFmt(win_error.msg));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_OSFile DN_OS_FileOpen(DN_Str8 path, DN_OSFileOpen open_mode, DN_U32 access, DN_OSErrSink *err)
|
|
{
|
|
DN_OSFile result = {};
|
|
if (path.size == 0 || path.size <= 0)
|
|
return result;
|
|
|
|
if ((access & ~DN_OSFileAccess_All) || ((access & DN_OSFileAccess_All) == 0)) {
|
|
DN_InvalidCodePath;
|
|
return result;
|
|
}
|
|
|
|
unsigned long create_flag = 0;
|
|
switch (open_mode) {
|
|
case DN_OSFileOpen_CreateAlways: create_flag = CREATE_ALWAYS; break;
|
|
case DN_OSFileOpen_OpenIfExist: create_flag = OPEN_EXISTING; break;
|
|
case DN_OSFileOpen_OpenAlways: create_flag = OPEN_ALWAYS; break;
|
|
default: DN_InvalidCodePath; return result;
|
|
}
|
|
|
|
unsigned long access_mode = 0;
|
|
if (access & DN_OSFileAccess_AppendOnly) {
|
|
DN_AssertF((access & ~DN_OSFileAccess_AppendOnly) == 0,
|
|
"Append can only be applied exclusively to the file, other access modes not permitted");
|
|
access_mode = FILE_APPEND_DATA;
|
|
} else {
|
|
if (access & DN_OSFileAccess_Read)
|
|
access_mode |= GENERIC_READ;
|
|
if (access & DN_OSFileAccess_Write)
|
|
access_mode |= GENERIC_WRITE;
|
|
if (access & DN_OSFileAccess_Execute)
|
|
access_mode |= GENERIC_EXECUTE;
|
|
}
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str16 path16 = DN_W32_Str8ToStr16(tmem.arena, path);
|
|
void *handle = CreateFileW(/*LPCWSTR lpFileName*/ path16.data,
|
|
/*DWORD dwDesiredAccess*/ access_mode,
|
|
/*DWORD dwShareMode*/ FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
/*LPSECURITY_ATTRIBUTES lpSecurityAttributes*/ nullptr,
|
|
/*DWORD dwCreationDisposition*/ create_flag,
|
|
/*DWORD dwFlagsAndAttributes*/ FILE_ATTRIBUTE_NORMAL,
|
|
/*HANDLE hTemplateFile*/ nullptr);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
result.error = true;
|
|
DN_OS_ErrSinkAppendF(err, win_error.code, "Failed to open file at '%.*s': '%.*s'", DN_Str8PrintFmt(path), DN_Str8PrintFmt(win_error.msg));
|
|
return result;
|
|
}
|
|
|
|
result.handle = handle;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size, DN_OSErrSink *err)
|
|
{
|
|
DN_OSFileRead result = {};
|
|
if (!file || !file->handle || file->error || !buffer || size <= 0)
|
|
return result;
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
if (!DN_Check(size <= (unsigned long)-1)) {
|
|
DN_Str8x32 buffer_size_str8 = DN_ByteCountStr8x32(size);
|
|
DN_OS_ErrSinkAppendF(
|
|
err,
|
|
1 /*error_code*/,
|
|
"Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO",
|
|
DN_Str8PrintFmt(buffer_size_str8));
|
|
return result;
|
|
}
|
|
|
|
unsigned long bytes_read = 0;
|
|
unsigned long read_result = ReadFile(/*HANDLE hFile*/ file->handle,
|
|
/*LPVOID lpBuffer*/ buffer,
|
|
/*DWORD nNumberOfBytesToRead*/ DN_Cast(unsigned long) size,
|
|
/*LPDWORD lpNumberOfByesRead*/ &bytes_read,
|
|
/*LPOVERLAPPED lpOverlapped*/ nullptr);
|
|
if (read_result == 0) {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
DN_OS_ErrSinkAppendF(err, win_error.code, "Failed to read data from file: (%u) %.*s", win_error.code, DN_Str8PrintFmt(win_error.msg));
|
|
return result;
|
|
}
|
|
|
|
if (bytes_read != size) {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
DN_OS_ErrSinkAppendF(
|
|
err,
|
|
win_error.code,
|
|
"Failed to read the desired number of bytes from file, we read %uB but we expected %uB: (%u) %.*s",
|
|
bytes_read,
|
|
DN_Cast(unsigned long) size,
|
|
win_error.code,
|
|
DN_Str8PrintFmt(win_error.msg));
|
|
return result;
|
|
}
|
|
|
|
result.bytes_read = bytes_read;
|
|
result.success = true;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileWritePtr(DN_OSFile *file, void const *buffer, DN_USize size, DN_OSErrSink *err)
|
|
{
|
|
if (!file || !file->handle || file->error || !buffer || size <= 0)
|
|
return false;
|
|
|
|
bool result = true;
|
|
char const *end = DN_Cast(char *) buffer + size;
|
|
for (char const *ptr = DN_Cast(char const *) buffer; result && ptr != end;) {
|
|
unsigned long write_size = DN_Cast(unsigned long) DN_Min((unsigned long)-1, end - ptr);
|
|
unsigned long bytes_written = 0;
|
|
result = WriteFile(file->handle, ptr, write_size, &bytes_written, nullptr /*lpOverlapped*/) != 0;
|
|
ptr += bytes_written;
|
|
}
|
|
|
|
if (!result) {
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
DN_Str8x32 buffer_size_str8 = DN_ByteCountStr8x32(size);
|
|
DN_OS_ErrSinkAppendF(err, win_error.code, "Failed to write buffer (%.*s) to file handle: %.*s", DN_Str8PrintFmt(buffer_size_str8), DN_Str8PrintFmt(win_error.msg));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_FileFlush(DN_OSFile *file, DN_OSErrSink *err)
|
|
{
|
|
if (!file || !file->handle || file->error)
|
|
return false;
|
|
|
|
BOOL result = FlushFileBuffers(DN_Cast(HANDLE) file->handle);
|
|
if (!result) {
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
DN_OS_ErrSinkAppendF(err, win_error.code, "Failed to flush file buffer to disk: %.*s", DN_Str8PrintFmt(win_error.msg));
|
|
}
|
|
|
|
return DN_Cast(bool) result;
|
|
}
|
|
|
|
DN_API void DN_OS_FileClose(DN_OSFile *file)
|
|
{
|
|
if (!file || !file->handle || file->error)
|
|
return;
|
|
CloseHandle(file->handle);
|
|
*file = {};
|
|
}
|
|
|
|
DN_API DN_OSPathInfo DN_OS_PathInfo(DN_Str8 path)
|
|
{
|
|
DN_OSPathInfo result = {};
|
|
if (path.size == 0)
|
|
return result;
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str16 path16 = DN_W32_Str8ToStr16(tmem.arena, path);
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
|
|
if (!GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data))
|
|
return result;
|
|
|
|
result.exists = true;
|
|
result.create_time_in_s = DN_W32_FileTimeToSeconds_(&attrib_data.ftCreationTime);
|
|
result.last_access_time_in_s = DN_W32_FileTimeToSeconds_(&attrib_data.ftLastAccessTime);
|
|
result.last_write_time_in_s = DN_W32_FileTimeToSeconds_(&attrib_data.ftLastWriteTime);
|
|
|
|
LARGE_INTEGER large_int = {};
|
|
large_int.u.HighPart = DN_Cast(int32_t) attrib_data.nFileSizeHigh;
|
|
large_int.u.LowPart = attrib_data.nFileSizeLow;
|
|
result.size = (DN_U64)large_int.QuadPart;
|
|
|
|
if (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) {
|
|
if (attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
result.type = DN_OSPathInfoType_Directory;
|
|
else
|
|
result.type = DN_OSPathInfoType_File;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathDelete(DN_Str8 path)
|
|
{
|
|
bool result = false;
|
|
if (path.size == 0)
|
|
return result;
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str16 path16 = DN_W32_Str8ToStr16(tmem.arena, path);
|
|
if (path16.size) {
|
|
result = DeleteFileW(path16.data);
|
|
if (!result)
|
|
result = RemoveDirectoryW(path16.data);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathIsFile(DN_Str8 path)
|
|
{
|
|
bool result = false;
|
|
if (path.size == 0)
|
|
return result;
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str16 path16 = DN_W32_Str8ToStr16(tmem.arena, path);
|
|
if (path16.size) {
|
|
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
|
|
if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data))
|
|
result = (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) &&
|
|
!(attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathIsDir(DN_Str8 path)
|
|
{
|
|
bool result = false;
|
|
if (path.size == 0)
|
|
return result;
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str16 path16 = DN_W32_Str8ToStr16(tmem.arena, path);
|
|
if (path16.size) {
|
|
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
|
|
if (GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data))
|
|
result = (attrib_data.dwFileAttributes != INVALID_FILE_ATTRIBUTES) &&
|
|
(attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathMakeDir(DN_Str8 path)
|
|
{
|
|
bool result = true;
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str16 path16 = DN_W32_Str8ToStr16(tmem.arena, path);
|
|
|
|
// NOTE: Go back from the end of the string to all the directories in the
|
|
// string, and try to create them. Since Win32 API cannot create
|
|
// intermediate directories that don't exist in a path we need to go back
|
|
// and record all the directories until we encounter one that exists.
|
|
//
|
|
// From that point onwards go forwards and make all the directories
|
|
// inbetween by null-terminating the string temporarily, creating the
|
|
// directory and so forth until we reach the end.
|
|
//
|
|
// If we find a file at some point in the path we fail out because the
|
|
// series of directories can not be made if a file exists with the same
|
|
// name.
|
|
for (DN_USize index = 0; index < path16.size; index++) {
|
|
bool first_char = index == (path16.size - 1);
|
|
wchar_t ch = path16.data[index];
|
|
if (ch == '/' || ch == '\\' || first_char) {
|
|
wchar_t temp = path16.data[index];
|
|
if (!first_char)
|
|
path16.data[index] = 0; // Temporarily null terminate it
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA attrib_data = {};
|
|
bool successful = GetFileAttributesExW(path16.data, GetFileExInfoStandard, &attrib_data); // Check
|
|
|
|
if (successful) {
|
|
if (attrib_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
// NOTE: The directory exists, continue iterating the path
|
|
} else {
|
|
// NOTE: There's some kind of file that exists at the path
|
|
// but it's not a directory. This request to make a
|
|
// directory is invalid.
|
|
return false;
|
|
}
|
|
} else {
|
|
// NOTE: There's nothing that exists at this path, we can create
|
|
// a directory here
|
|
result |= (CreateDirectoryW(path16.data, nullptr) == 0);
|
|
}
|
|
|
|
if (!first_char)
|
|
path16.data[index] = temp; // Undo null termination
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_PathIterateDir(DN_Str8 path, DN_OSDirIterator *it)
|
|
{
|
|
if (path.size == 0 || !it || path.size <= 0)
|
|
return false;
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_W32FolderIteratorW wide_it = {};
|
|
DN_Str16 path16 = {};
|
|
if (it->handle) {
|
|
wide_it.handle = it->handle;
|
|
} else {
|
|
bool needs_asterisks = DN_Str8EndsWith(path, DN_Str8Lit("\\")) ||
|
|
DN_Str8EndsWith(path, DN_Str8Lit("/"));
|
|
bool has_glob = DN_Str8EndsWith(path, DN_Str8Lit("\\*")) ||
|
|
DN_Str8EndsWith(path, DN_Str8Lit("/*"));
|
|
|
|
DN_Str8 adjusted_path = path;
|
|
if (!has_glob) {
|
|
// NOTE: We are missing the glob for enumerating the files, we will
|
|
// add those characters in this branch, so overwrite the null
|
|
// character, add the glob and re-null terminate the buffer.
|
|
if (needs_asterisks)
|
|
adjusted_path = DN_OS_PathF(tmem.arena, "%.*s*", DN_Str8PrintFmt(path));
|
|
else
|
|
adjusted_path = DN_OS_PathF(tmem.arena, "%.*s/*", DN_Str8PrintFmt(path));
|
|
}
|
|
|
|
path16 = DN_W32_Str8ToStr16(tmem.arena, adjusted_path);
|
|
if (path16.size <= 0) // Conversion error
|
|
return false;
|
|
}
|
|
|
|
bool result = DN_W32_DirWIterate(path16, &wide_it);
|
|
it->handle = wide_it.handle;
|
|
if (result) {
|
|
int size = DN_W32_Str16ToStr8Buffer(wide_it.file_name, it->buffer, DN_ArrayCountU(it->buffer));
|
|
it->file_name = DN_Str8FromPtr(it->buffer, size);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
// NOTE: DN_OSExec /////////////////////////////////////////////////////////////////////////////////
|
|
DN_API void DN_OS_Exit(int32_t exit_code)
|
|
{
|
|
ExitProcess(DN_Cast(UINT) exit_code);
|
|
}
|
|
|
|
DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle,
|
|
char *stdout_buffer,
|
|
DN_USize *stdout_size,
|
|
char *stderr_buffer,
|
|
DN_USize *stderr_size,
|
|
DN_U32 timeout_ms,
|
|
DN_OSErrSink *err)
|
|
{
|
|
DN_OSExecResult result = {};
|
|
size_t stdout_buffer_size = 0;
|
|
size_t stderr_buffer_size = 0;
|
|
if (stdout_size) {
|
|
stdout_buffer_size = *stdout_size;
|
|
*stdout_size = 0;
|
|
}
|
|
|
|
if (stderr_size) {
|
|
stderr_buffer_size = *stderr_size;
|
|
*stderr_size = 0;
|
|
}
|
|
|
|
if (!handle.process || handle.os_error_code || handle.exit_code) {
|
|
if (handle.os_error_code)
|
|
result.os_error_code = handle.os_error_code;
|
|
else
|
|
result.exit_code = handle.exit_code;
|
|
|
|
DN_Assert(!handle.stdout_read);
|
|
DN_Assert(!handle.stdout_write);
|
|
DN_Assert(!handle.stderr_read);
|
|
DN_Assert(!handle.stderr_write);
|
|
DN_Assert(!handle.process);
|
|
return result;
|
|
}
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DWORD stdout_bytes_available = 0;
|
|
DWORD stderr_bytes_available = 0;
|
|
PeekNamedPipe(handle.stdout_read, nullptr, 0, nullptr, &stdout_bytes_available, nullptr);
|
|
PeekNamedPipe(handle.stderr_read, nullptr, 0, nullptr, &stderr_bytes_available, nullptr);
|
|
|
|
DWORD exec_result = WAIT_TIMEOUT;
|
|
if (stdout_bytes_available == 0 && stderr_bytes_available == 0)
|
|
exec_result = WaitForSingleObject(handle.process, timeout_ms);
|
|
|
|
if (exec_result == WAIT_FAILED) {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
result.os_error_code = win_error.code;
|
|
DN_OS_ErrSinkAppendF(err, result.os_error_code, "Executed command failed to terminate: %.*s", DN_Str8PrintFmt(win_error.msg));
|
|
} else if (DN_Check(exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0)) {
|
|
// NOTE: Read stdout from process
|
|
// If the pipes are full, the process will block. We periodically
|
|
// flush the pipes to make sure this doesn't happen
|
|
char sink[DN_Kilobytes(8)];
|
|
stdout_bytes_available = 0;
|
|
if (PeekNamedPipe(handle.stdout_read, nullptr, 0, nullptr, &stdout_bytes_available, nullptr)) {
|
|
if (stdout_bytes_available) {
|
|
DWORD bytes_read = 0;
|
|
char *dest_buffer = handle.stdout_write && stdout_buffer ? stdout_buffer : sink;
|
|
DN_USize dest_size = handle.stdout_write && stdout_buffer ? stdout_buffer_size : DN_ArrayCountU(sink);
|
|
BOOL success = ReadFile(handle.stdout_read, dest_buffer, DN_Cast(DWORD) dest_size, &bytes_read, NULL);
|
|
if (success) {
|
|
if (stdout_size)
|
|
*stdout_size = bytes_read;
|
|
} else {
|
|
DN_OS_ErrSinkAppendF(err, 1, "Failed to read bytes from stdout");
|
|
}
|
|
}
|
|
}
|
|
|
|
// NOTE: Read stderr from process
|
|
stderr_bytes_available = 0;
|
|
if (PeekNamedPipe(handle.stderr_read, nullptr, 0, nullptr, &stderr_bytes_available, nullptr)) {
|
|
if (stderr_bytes_available) {
|
|
char *dest_buffer = handle.stderr_write && stderr_buffer ? stderr_buffer : sink;
|
|
size_t dest_size = handle.stderr_write && stderr_buffer ? stderr_buffer_size : DN_ArrayCountU(sink);
|
|
DWORD bytes_read = 0;
|
|
BOOL success = ReadFile(handle.stderr_read, dest_buffer, DN_Cast(DWORD) dest_size, &bytes_read, NULL);
|
|
if (success) {
|
|
if (stderr_size)
|
|
*stderr_size = bytes_read;
|
|
} else {
|
|
DN_OS_ErrSinkAppendF(err, 1, "Failed to read bytes from stderr");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
result.finished = exec_result == WAIT_OBJECT_0 || exec_result == WAIT_FAILED;
|
|
if (exec_result == WAIT_OBJECT_0) {
|
|
DWORD exit_status;
|
|
if (GetExitCodeProcess(handle.process, &exit_status)) {
|
|
result.exit_code = exit_status;
|
|
} else {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
result.os_error_code = win_error.code;
|
|
DN_OS_ErrSinkAppendF(err,
|
|
result.os_error_code,
|
|
"Failed to retrieve command exit code: %.*s",
|
|
DN_Str8PrintFmt(win_error.msg));
|
|
}
|
|
|
|
// NOTE: Cleanup
|
|
if (handle.stdout_write)
|
|
CloseHandle(handle.stdout_write);
|
|
if (handle.stderr_write)
|
|
CloseHandle(handle.stderr_write);
|
|
if (handle.stdout_read)
|
|
CloseHandle(handle.stdout_read);
|
|
if (handle.stderr_read)
|
|
CloseHandle(handle.stderr_read);
|
|
if (handle.process)
|
|
CloseHandle(handle.process);
|
|
}
|
|
|
|
result.stdout_text = DN_Str8FromPtr(stdout_buffer, stdout_size ? *stdout_size : 0);
|
|
result.stderr_text = DN_Str8FromPtr(stderr_buffer, stderr_size ? *stderr_size : 0);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_OSExecResult DN_OS_ExecWait(DN_OSExecAsyncHandle handle, DN_Arena *arena, DN_OSErrSink *err)
|
|
{
|
|
DN_OSExecResult result = {};
|
|
if (!handle.process || handle.os_error_code || handle.exit_code) {
|
|
result.finished = true;
|
|
if (handle.os_error_code)
|
|
result.os_error_code = handle.os_error_code;
|
|
else
|
|
result.exit_code = handle.exit_code;
|
|
|
|
DN_Assert(!handle.stdout_read);
|
|
DN_Assert(!handle.stdout_write);
|
|
DN_Assert(!handle.stderr_read);
|
|
DN_Assert(!handle.stderr_write);
|
|
DN_Assert(!handle.process);
|
|
return result;
|
|
}
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
|
DN_Str8Builder stdout_builder = {};
|
|
DN_Str8Builder stderr_builder = {};
|
|
if (arena) {
|
|
stdout_builder.arena = tmem.arena;
|
|
stderr_builder.arena = tmem.arena;
|
|
}
|
|
|
|
DN_U32 const SLOW_WAIT_TIME_MS = 100;
|
|
DN_U32 const FAST_WAIT_TIME_MS = 20;
|
|
DN_U32 wait_ms = FAST_WAIT_TIME_MS;
|
|
while (!result.finished) {
|
|
size_t stdout_size = DN_Kilobytes(8);
|
|
size_t stderr_size = DN_Kilobytes(8);
|
|
char *stdout_buffer = DN_ArenaNewArray(tmem.arena, char, stdout_size, DN_ZMem_No);
|
|
char *stderr_buffer = DN_ArenaNewArray(tmem.arena, char, stderr_size, DN_ZMem_No);
|
|
result = DN_OS_ExecPump(handle, stdout_buffer, &stdout_size, stderr_buffer, &stderr_size, wait_ms, err);
|
|
DN_Str8BuilderAppendCopy(&stdout_builder, result.stdout_text);
|
|
DN_Str8BuilderAppendCopy(&stderr_builder, result.stderr_text);
|
|
wait_ms = (result.stdout_text.size || result.stderr_text.size) ? FAST_WAIT_TIME_MS : SLOW_WAIT_TIME_MS;
|
|
}
|
|
|
|
// NOTE: Get stdout/stderr. If no arena is passed this is a no-op //////////////////////////////
|
|
result.stdout_text = DN_Str8BuilderBuild(&stdout_builder, arena);
|
|
result.stderr_text = DN_Str8BuilderBuild(&stderr_builder, arena);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Slice<DN_Str8> cmd_line, DN_OSExecArgs *args, DN_OSErrSink *err)
|
|
{
|
|
// NOTE: Pre-amble /////////////////////////////////////////////////////////////////////////////
|
|
DN_OSExecAsyncHandle result = {};
|
|
if (cmd_line.size == 0)
|
|
return result;
|
|
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Str8 cmd_rendered = DN_Slice_Str8Render(tmem.arena, cmd_line, DN_Str8Lit(" "));
|
|
DN_Str16 cmd16 = DN_W32_Str8ToStr16(tmem.arena, cmd_rendered);
|
|
DN_Str16 working_dir16 = DN_W32_Str8ToStr16(tmem.arena, args->working_dir);
|
|
|
|
DN_Str8Builder env_builder = DN_Str8BuilderFromTLS();
|
|
DN_Str8BuilderAppendArrayRef(&env_builder, args->environment.data, args->environment.size);
|
|
if (env_builder.string_size)
|
|
DN_Str8BuilderAppendRef(&env_builder, DN_Str8Lit("\0"));
|
|
|
|
DN_Str8 env_block8 = DN_Str8BuilderBuildDelimitedFromTLS(&env_builder, DN_Str8Lit("\0"));
|
|
DN_Str16 env_block16 = {};
|
|
if (env_block8.size)
|
|
env_block16 = DN_W32_Str8ToStr16(tmem.arena, env_block8);
|
|
|
|
// NOTE: Stdout/err security attributes ////////////////////////////////////////////////////////
|
|
SECURITY_ATTRIBUTES save_std_security_attribs = {};
|
|
save_std_security_attribs.nLength = sizeof(save_std_security_attribs);
|
|
save_std_security_attribs.bInheritHandle = true;
|
|
|
|
// NOTE: Redirect stdout ///////////////////////////////////////////////////////////////////////
|
|
HANDLE stdout_read = {};
|
|
HANDLE stdout_write = {};
|
|
DN_DEFER
|
|
{
|
|
if (result.os_error_code || result.exit_code) {
|
|
CloseHandle(stdout_read);
|
|
CloseHandle(stdout_write);
|
|
}
|
|
};
|
|
|
|
if (DN_BitIsSet(args->flags, DN_OSExecFlags_SaveStdout)) {
|
|
if (!CreatePipe(&stdout_read, &stdout_write, &save_std_security_attribs, /*nSize*/ 0)) {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
result.os_error_code = win_error.code;
|
|
DN_OS_ErrSinkAppendF(
|
|
err,
|
|
result.os_error_code,
|
|
"Failed to create stdout pipe to redirect the output of the command '%.*s': %.*s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
DN_Str8PrintFmt(win_error.msg));
|
|
return result;
|
|
}
|
|
|
|
if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
result.os_error_code = win_error.code;
|
|
DN_OS_ErrSinkAppendF(err,
|
|
result.os_error_code,
|
|
"Failed to make stdout 'read' pipe non-inheritable when trying to "
|
|
"execute command '%.*s': %.*s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
DN_Str8PrintFmt(win_error.msg));
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// NOTE: Redirect stderr ///////////////////////////////////////////////////////////////////////
|
|
HANDLE stderr_read = {};
|
|
HANDLE stderr_write = {};
|
|
DN_DEFER
|
|
{
|
|
if (result.os_error_code || result.exit_code) {
|
|
CloseHandle(stderr_read);
|
|
CloseHandle(stderr_write);
|
|
}
|
|
};
|
|
|
|
if (DN_BitIsSet(args->flags, DN_OSExecFlags_SaveStderr)) {
|
|
if (DN_BitIsSet(args->flags, DN_OSExecFlags_MergeStderrToStdout)) {
|
|
stderr_read = stdout_read;
|
|
stderr_write = stdout_write;
|
|
} else {
|
|
if (!CreatePipe(&stderr_read, &stderr_write, &save_std_security_attribs, /*nSize*/ 0)) {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
result.os_error_code = win_error.code;
|
|
DN_OS_ErrSinkAppendF(
|
|
err,
|
|
result.os_error_code,
|
|
"Failed to create stderr pipe to redirect the output of the command '%.*s': %.*s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
DN_Str8PrintFmt(win_error.msg));
|
|
return result;
|
|
}
|
|
|
|
if (!SetHandleInformation(stderr_read, HANDLE_FLAG_INHERIT, 0)) {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
result.os_error_code = win_error.code;
|
|
DN_OS_ErrSinkAppendF(err,
|
|
result.os_error_code,
|
|
"Failed to make stderr 'read' pipe non-inheritable when trying to "
|
|
"execute command '%.*s': %.*s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
DN_Str8PrintFmt(win_error.msg));
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
// NOTE: Execute command ///////////////////////////////////////////////////////////////////////
|
|
PROCESS_INFORMATION proc_info = {};
|
|
STARTUPINFOW startup_info = {};
|
|
startup_info.cb = sizeof(STARTUPINFOW);
|
|
startup_info.hStdError = stderr_write ? stderr_write : GetStdHandle(STD_ERROR_HANDLE);
|
|
startup_info.hStdOutput = stdout_write ? stdout_write : GetStdHandle(STD_OUTPUT_HANDLE);
|
|
startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
|
startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
|
BOOL create_result = CreateProcessW(nullptr,
|
|
cmd16.data,
|
|
nullptr,
|
|
nullptr,
|
|
true,
|
|
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
|
|
env_block16.data,
|
|
working_dir16.data,
|
|
&startup_info,
|
|
&proc_info);
|
|
if (!create_result) {
|
|
DN_W32Error win_error = DN_W32_LastError(tmem.arena);
|
|
result.os_error_code = win_error.code;
|
|
DN_OS_ErrSinkAppendF(err,
|
|
result.os_error_code,
|
|
"Failed to execute command '%.*s': %.*s",
|
|
DN_Str8PrintFmt(cmd_rendered),
|
|
DN_Str8PrintFmt(win_error.msg));
|
|
return result;
|
|
}
|
|
|
|
// NOTE: Post-amble ////////////////////////////////////////////////////////////////////////////
|
|
CloseHandle(proc_info.hThread);
|
|
result.process = proc_info.hProcess;
|
|
result.stdout_read = stdout_read;
|
|
result.stdout_write = stdout_write;
|
|
if (DN_BitIsSet(args->flags, DN_OSExecFlags_SaveStderr) && DN_BitIsNotSet(args->flags, DN_OSExecFlags_MergeStderrToStdout)) {
|
|
result.stderr_read = stderr_read;
|
|
result.stderr_write = stderr_write;
|
|
}
|
|
result.exec_flags = args->flags;
|
|
return result;
|
|
}
|
|
|
|
static DN_W32Core *DN_OS_GetW32Core_()
|
|
{
|
|
DN_Assert(g_dn_ && g_dn_->os.platform_context);
|
|
DN_W32Core *result = DN_Cast(DN_W32Core *)g_dn_->os.platform_context;
|
|
return result;
|
|
}
|
|
|
|
static DN_W32SyncPrimitive *DN_OS_U64ToW32SyncPrimitive_(DN_U64 u64)
|
|
{
|
|
DN_W32SyncPrimitive *result = nullptr;
|
|
DN_Memcpy(&result, &u64, sizeof(u64));
|
|
return result;
|
|
}
|
|
|
|
static DN_U64 DN_W32_SyncPrimitiveToU64(DN_W32SyncPrimitive *primitive)
|
|
{
|
|
DN_U64 result = 0;
|
|
static_assert(sizeof(result) == sizeof(primitive), "Pointer size mis-match");
|
|
DN_Memcpy(&result, &primitive, sizeof(result));
|
|
return result;
|
|
}
|
|
|
|
static DN_W32SyncPrimitive *DN_W32_AllocSyncPrimitive_()
|
|
{
|
|
DN_W32Core *w32 = DN_OS_GetW32Core_();
|
|
DN_W32SyncPrimitive *result = nullptr;
|
|
EnterCriticalSection(&w32->sync_primitive_free_list_mutex);
|
|
{
|
|
if (w32->sync_primitive_free_list) {
|
|
result = w32->sync_primitive_free_list;
|
|
w32->sync_primitive_free_list = w32->sync_primitive_free_list->next;
|
|
result->next = nullptr;
|
|
} else {
|
|
DN_OSCore *os = &g_dn_->os;
|
|
result = DN_ArenaNew(&os->arena, DN_W32SyncPrimitive, DN_ZMem_Yes);
|
|
}
|
|
}
|
|
LeaveCriticalSection(&w32->sync_primitive_free_list_mutex);
|
|
return result;
|
|
}
|
|
|
|
static void DN_W32_DeallocSyncPrimitive_(DN_W32SyncPrimitive *primitive)
|
|
{
|
|
if (primitive) {
|
|
DN_W32Core *w32 = DN_OS_GetW32Core_();
|
|
EnterCriticalSection(&w32->sync_primitive_free_list_mutex);
|
|
primitive->next = w32->sync_primitive_free_list;
|
|
w32->sync_primitive_free_list = primitive;
|
|
LeaveCriticalSection(&w32->sync_primitive_free_list_mutex);
|
|
}
|
|
}
|
|
|
|
// NOTE: DN_OSSemaphore ////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_OSSemaphore DN_OS_SemaphoreInit(DN_U32 initial_count)
|
|
{
|
|
DN_OSSemaphore result = {};
|
|
DN_W32SyncPrimitive *primitive = DN_W32_AllocSyncPrimitive_();
|
|
if (primitive) {
|
|
SECURITY_ATTRIBUTES security_attribs = {};
|
|
primitive->sem = CreateSemaphoreA(&security_attribs, initial_count, INT32_MAX, nullptr /*name*/);
|
|
if (primitive->sem)
|
|
result.handle = DN_W32_SyncPrimitiveToU64(primitive);
|
|
if (!primitive->sem)
|
|
DN_W32_DeallocSyncPrimitive_(primitive);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_SemaphoreDeinit(DN_OSSemaphore *semaphore)
|
|
{
|
|
if (semaphore && semaphore->handle != 0) {
|
|
DN_W32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(semaphore->handle);
|
|
CloseHandle(primitive->sem);
|
|
DN_W32_DeallocSyncPrimitive_(primitive);
|
|
*semaphore = {};
|
|
}
|
|
}
|
|
|
|
DN_API void DN_OS_SemaphoreIncrement(DN_OSSemaphore *semaphore, DN_U32 amount)
|
|
{
|
|
if (semaphore && semaphore->handle != 0) {
|
|
DN_W32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(semaphore->handle);
|
|
LONG prev_count = 0;
|
|
ReleaseSemaphore(primitive->sem, amount, &prev_count);
|
|
}
|
|
}
|
|
|
|
DN_API DN_OSSemaphoreWaitResult DN_OS_SemaphoreWait(DN_OSSemaphore *semaphore, DN_U32 timeout_ms)
|
|
{
|
|
DN_OSSemaphoreWaitResult result = {};
|
|
if (semaphore && semaphore->handle != 0) {
|
|
DN_W32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(semaphore->handle);
|
|
DWORD wait_result = WaitForSingleObject(primitive->sem, timeout_ms == DN_OS_SEMAPHORE_INFINITE_TIMEOUT ? INFINITE : timeout_ms);
|
|
if (wait_result == WAIT_TIMEOUT)
|
|
result = DN_OSSemaphoreWaitResult_Timeout;
|
|
else if (wait_result == WAIT_OBJECT_0)
|
|
result = DN_OSSemaphoreWaitResult_Success;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// NOTE: DN_OSMutex ////////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_OSMutex DN_OS_MutexInit()
|
|
{
|
|
DN_W32SyncPrimitive *primitive = DN_W32_AllocSyncPrimitive_();
|
|
if (primitive)
|
|
InitializeCriticalSection(&primitive->mutex);
|
|
DN_OSMutex result = {};
|
|
result.handle = DN_W32_SyncPrimitiveToU64(primitive);
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_MutexDeinit(DN_OSMutex *mutex)
|
|
{
|
|
if (mutex && mutex->handle != 0) {
|
|
DN_W32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(mutex->handle);
|
|
DeleteCriticalSection(&primitive->mutex);
|
|
DN_W32_DeallocSyncPrimitive_(primitive);
|
|
*mutex = {};
|
|
}
|
|
}
|
|
|
|
DN_API void DN_OS_MutexLock(DN_OSMutex *mutex)
|
|
{
|
|
if (mutex && mutex->handle != 0) {
|
|
DN_W32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(mutex->handle);
|
|
EnterCriticalSection(&primitive->mutex);
|
|
}
|
|
}
|
|
|
|
DN_API void DN_OS_MutexUnlock(DN_OSMutex *mutex)
|
|
{
|
|
if (mutex && mutex->handle != 0) {
|
|
DN_W32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(mutex->handle);
|
|
LeaveCriticalSection(&primitive->mutex);
|
|
}
|
|
}
|
|
|
|
// NOTE: DN_OSConditionVariable ////////////////////////////////////////////////////////////////////
|
|
DN_API DN_OSConditionVariable DN_OS_ConditionVariableInit()
|
|
{
|
|
DN_W32SyncPrimitive *primitive = DN_W32_AllocSyncPrimitive_();
|
|
if (primitive)
|
|
InitializeConditionVariable(&primitive->cv);
|
|
DN_OSConditionVariable result = {};
|
|
result.handle = DN_W32_SyncPrimitiveToU64(primitive);
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_ConditionVariableDeinit(DN_OSConditionVariable *cv)
|
|
{
|
|
if (cv && cv->handle != 0) {
|
|
DN_W32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(cv->handle);
|
|
DN_W32_DeallocSyncPrimitive_(primitive);
|
|
*cv = {};
|
|
}
|
|
}
|
|
|
|
DN_API bool DN_OS_ConditionVariableWaitUntil(DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 end_ts_ms)
|
|
{
|
|
bool result = false;
|
|
DN_U64 now_ms = DN_OS_DateUnixTimeNs() / (1000 * 1000);
|
|
if (now_ms < end_ts_ms) {
|
|
DN_U64 sleep_ms = end_ts_ms - now_ms;
|
|
result = DN_OS_ConditionVariableWait(cv, mutex, sleep_ms);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_OS_ConditionVariableWait(DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 sleep_ms)
|
|
{
|
|
bool result = false;
|
|
if (mutex && cv && mutex->handle != 0 && cv->handle != 0 && sleep_ms > 0) {
|
|
DN_W32SyncPrimitive *mutex_primitive = DN_OS_U64ToW32SyncPrimitive_(mutex->handle);
|
|
DN_W32SyncPrimitive *cv_primitive = DN_OS_U64ToW32SyncPrimitive_(cv->handle);
|
|
result = SleepConditionVariableCS(&cv_primitive->cv, &mutex_primitive->mutex, DN_Cast(DWORD) sleep_ms);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_ConditionVariableSignal(DN_OSConditionVariable *cv)
|
|
{
|
|
if (cv && cv->handle != 0) {
|
|
DN_W32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(cv->handle);
|
|
WakeConditionVariable(&primitive->cv);
|
|
}
|
|
}
|
|
|
|
DN_API void DN_OS_ConditionVariableBroadcast(DN_OSConditionVariable *cv)
|
|
{
|
|
if (cv && cv->handle != 0) {
|
|
DN_W32SyncPrimitive *primitive = DN_OS_U64ToW32SyncPrimitive_(cv->handle);
|
|
WakeAllConditionVariable(&primitive->cv);
|
|
}
|
|
}
|
|
|
|
// NOTE: DN_OSThread ///////////////////////////////////////////////////////////////////////////////
|
|
static DWORD __stdcall DN_OS_ThreadFunc_(void *user_context)
|
|
{
|
|
DN_OS_ThreadExecute_(user_context);
|
|
return 0;
|
|
}
|
|
|
|
DN_API bool DN_OS_ThreadInit(DN_OSThread *thread, DN_OSThreadFunc *func, void *user_context)
|
|
{
|
|
bool result = false;
|
|
if (!thread)
|
|
return result;
|
|
|
|
thread->func = func;
|
|
thread->user_context = user_context;
|
|
thread->init_semaphore = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
|
|
|
// TODO(doyle): Check if semaphore is valid
|
|
DWORD thread_id = 0;
|
|
SECURITY_ATTRIBUTES security_attribs = {};
|
|
thread->handle = CreateThread(&security_attribs,
|
|
0 /*stack_size*/,
|
|
DN_OS_ThreadFunc_,
|
|
thread,
|
|
0 /*creation_flags*/,
|
|
&thread_id);
|
|
|
|
result = thread->handle != INVALID_HANDLE_VALUE;
|
|
if (result)
|
|
thread->thread_id = thread_id;
|
|
|
|
// NOTE: Ensure that thread_id is set before 'thread->func' is called.
|
|
if (result) {
|
|
DN_OS_SemaphoreIncrement(&thread->init_semaphore, 1);
|
|
} else {
|
|
DN_OS_SemaphoreDeinit(&thread->init_semaphore);
|
|
*thread = {};
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_OS_ThreadDeinit(DN_OSThread *thread)
|
|
{
|
|
if (!thread || !thread->handle)
|
|
return;
|
|
|
|
WaitForSingleObject(thread->handle, INFINITE);
|
|
CloseHandle(thread->handle);
|
|
thread->handle = INVALID_HANDLE_VALUE;
|
|
thread->thread_id = {};
|
|
DN_OS_TLSDeinit(&thread->tls);
|
|
}
|
|
|
|
DN_API DN_U32 DN_OS_ThreadID()
|
|
{
|
|
unsigned long result = GetCurrentThreadId();
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_W32_ThreadSetName(DN_Str8 name)
|
|
{
|
|
DN_OSTLS *tls = DN_OS_TLSGet();
|
|
DN_ArenaTempMem tmem = DN_ArenaTempMemBegin(tls->arenas + DN_OSTLSArena_TMem0);
|
|
|
|
// NOTE: SetThreadDescription is only available in
|
|
// Windows Server 2016, Windows 10 LTSB 2016 and Windows 10 version 1607
|
|
//
|
|
// See: https://learn.microsoft.com/en-us/windows/w32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription
|
|
DN_W32Core *w32 = DN_OS_GetW32Core_();
|
|
if (w32->set_thread_description) {
|
|
DN_Str16 name16 = DN_W32_Str8ToStr16(tmem.arena, name);
|
|
w32->set_thread_description(GetCurrentThread(), (WCHAR *)name16.data);
|
|
DN_ArenaTempMemEnd(tmem);
|
|
return;
|
|
}
|
|
|
|
// NOTE: Fallback to throw-exception method to set thread name
|
|
#pragma pack(push, 8)
|
|
|
|
struct DN_W32ThreadNameInfo
|
|
{
|
|
DN_U32 dwType;
|
|
char *szName;
|
|
DN_U32 dwThreadID;
|
|
DN_U32 dwFlags;
|
|
};
|
|
|
|
#pragma pack(pop)
|
|
|
|
DN_Str8 copy = DN_Str8FromStr8Arena(tmem.arena, name);
|
|
DN_W32ThreadNameInfo info = {};
|
|
info.dwType = 0x1000;
|
|
info.szName = (char *)copy.data;
|
|
info.dwThreadID = DN_OS_ThreadID();
|
|
|
|
// TODO: Review warning 6320
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(6320) // Exception-filter expression is the constant EXCEPTION_EXECUTE_HANDLER. This might mask exceptions that were not intended to be handled
|
|
DN_MSVC_WARNING_DISABLE(6322) // Empty _except block
|
|
__try {
|
|
RaiseException(0x406D1388, 0, sizeof(info) / sizeof(void *), (const ULONG_PTR *)&info);
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
DN_MSVC_WARNING_POP
|
|
|
|
DN_ArenaTempMemEnd(tmem);
|
|
}
|
|
|
|
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
|
|
void DN_OS_HttpRequestWin32Callback(HINTERNET session, DWORD *dwContext, DWORD dwInternetStatus, VOID *lpvStatusInformation, DWORD dwStatusInformationLength)
|
|
{
|
|
(void)session;
|
|
(void)dwStatusInformationLength;
|
|
|
|
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) dwContext;
|
|
HINTERNET request = DN_Cast(HINTERNET) response->w32_request_handle;
|
|
DN_W32Error error = {};
|
|
DWORD const READ_BUFFER_SIZE = DN_Megabytes(1);
|
|
|
|
if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESOLVING_NAME) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_NAME_RESOLVED) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDING_REQUEST) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_SENT) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CREATED) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DETECTING_PROXY) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REDIRECT) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE) {
|
|
DWORD status = 0;
|
|
DWORD status_size = sizeof(status_size);
|
|
if (WinHttpQueryHeaders(request,
|
|
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
|
|
WINHTTP_HEADER_NAME_BY_INDEX,
|
|
&status,
|
|
&status_size,
|
|
WINHTTP_NO_HEADER_INDEX)) {
|
|
response->http_status = DN_Cast(uint16_t) status;
|
|
|
|
// NOTE: You can normally call into WinHttpQueryDataAvailable which means the kernel
|
|
// will buffer the response into a single buffer and return us the full size of the
|
|
// request.
|
|
//
|
|
// or
|
|
//
|
|
// You may call WinHttpReadData directly to write the memory into our buffer directly.
|
|
// This is advantageous to avoid a copy from the kernel buffer into our buffer. If the
|
|
// end user application knows the typical payload size then they can optimise for this
|
|
// to prevent unnecessary allocation on the user side.
|
|
void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No);
|
|
if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr))
|
|
error = DN_W32_LastError(&response->tmp_arena);
|
|
} else {
|
|
error = DN_W32_LastError(&response->tmp_arena);
|
|
}
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE) {
|
|
DWORD bytes_read = dwStatusInformationLength;
|
|
if (bytes_read) {
|
|
DN_Str8 prev_buffer = DN_Str8FromPtr(DN_Cast(char *) lpvStatusInformation, bytes_read);
|
|
DN_Str8BuilderAppendRef(&response->builder, prev_buffer);
|
|
|
|
void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No);
|
|
if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr))
|
|
error = DN_W32_LastError(&response->tmp_arena);
|
|
}
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE) {
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR) {
|
|
WINHTTP_ASYNC_RESULT *async_result = DN_Cast(WINHTTP_ASYNC_RESULT *) lpvStatusInformation;
|
|
error = DN_W32_ErrorCodeToMsg(&response->tmp_arena, DN_Cast(DN_U32) async_result->dwError);
|
|
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE) {
|
|
if (!WinHttpReceiveResponse(request, 0))
|
|
error = DN_W32_LastError(&response->tmp_arena);
|
|
}
|
|
|
|
// NOTE: If the request handle is missing, then, the response has been freed.
|
|
// MSDN says that this callback can still be called after closing the handle
|
|
// and trigger the WINHTTP_CALLBACK_STATUS_REQUEST_ERROR.
|
|
if (request) {
|
|
bool read_complete = dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE && dwStatusInformationLength == 0;
|
|
if (read_complete)
|
|
response->body = DN_Str8BuilderBuild(&response->builder, response->arena);
|
|
|
|
if (read_complete || dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR || error.code) {
|
|
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
|
|
DN_AtomicAddU32(&response->done, 1);
|
|
}
|
|
|
|
if (error.code) {
|
|
response->error_code = error.code;
|
|
response->error_msg = error.msg;
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (!response || !arena)
|
|
return;
|
|
|
|
response->arena = arena;
|
|
response->builder.arena = response->tmem_arena ? response->tmem_arena : &response->tmp_arena;
|
|
|
|
DN_Arena *tmem_arena = response->tmem_arena;
|
|
DN_OSTLSTMem tmem_ = DN_OS_TLSTMem(arena);
|
|
if (!tmem_arena)
|
|
tmem_arena = tmem_.arena;
|
|
|
|
DN_W32Error error = {};
|
|
DN_DEFER
|
|
{
|
|
response->error_msg = error.msg;
|
|
response->error_code = error.code;
|
|
if (error.code) {
|
|
// NOTE: 'Wait' handles failures gracefully, skipping the wait and
|
|
// cleans up the request
|
|
DN_OS_HttpRequestWait(response);
|
|
DN_AtomicAddU32(&response->done, 1);
|
|
}
|
|
};
|
|
|
|
response->w32_request_session = WinHttpOpen(nullptr /*user agent*/, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC);
|
|
if (!response->w32_request_session) {
|
|
error = DN_W32_LastError(&response->tmp_arena);
|
|
return;
|
|
}
|
|
|
|
DWORD callback_flags = WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE |
|
|
WINHTTP_CALLBACK_STATUS_READ_COMPLETE |
|
|
WINHTTP_CALLBACK_STATUS_REQUEST_ERROR |
|
|
WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE;
|
|
if (WinHttpSetStatusCallback(response->w32_request_session,
|
|
DN_Cast(WINHTTP_STATUS_CALLBACK) DN_OS_HttpRequestWin32Callback,
|
|
callback_flags,
|
|
DN_Cast(DWORD_PTR) nullptr /*dwReserved*/) == WINHTTP_INVALID_STATUS_CALLBACK) {
|
|
error = DN_W32_LastError(&response->tmp_arena);
|
|
return;
|
|
}
|
|
|
|
DN_Str16 host16 = DN_W32_Str8ToStr16(tmem_arena, host);
|
|
response->w32_request_connection = WinHttpConnect(response->w32_request_session, host16.data, secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0 /*reserved*/);
|
|
if (!response->w32_request_connection) {
|
|
error = DN_W32_LastError(&response->tmp_arena);
|
|
return;
|
|
}
|
|
|
|
DN_Str16 method16 = DN_W32_Str8ToStr16(tmem_arena, method);
|
|
DN_Str16 path16 = DN_W32_Str8ToStr16(tmem_arena, path);
|
|
response->w32_request_handle = WinHttpOpenRequest(response->w32_request_connection,
|
|
method16.data,
|
|
path16.data,
|
|
nullptr /*version*/,
|
|
nullptr /*referrer*/,
|
|
nullptr /*accept types*/,
|
|
secure ? WINHTTP_FLAG_SECURE : 0);
|
|
if (!response->w32_request_handle) {
|
|
error = DN_W32_LastError(&response->tmp_arena);
|
|
return;
|
|
}
|
|
|
|
DN_Str16 headers16 = DN_W32_Str8ToStr16(tmem_arena, headers);
|
|
response->on_complete_semaphore = DN_OS_SemaphoreInit(0);
|
|
if (!WinHttpSendRequest(response->w32_request_handle,
|
|
headers16.data,
|
|
DN_Cast(DWORD) headers16.size,
|
|
body.data /*optional data*/,
|
|
DN_Cast(DWORD) body.size /*optional length*/,
|
|
DN_Cast(DWORD) body.size /*total content length*/,
|
|
DN_Cast(DWORD_PTR) response)) {
|
|
error = DN_W32_LastError(&response->tmp_arena);
|
|
return;
|
|
}
|
|
}
|
|
|
|
DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response)
|
|
{
|
|
// NOTE: Cleanup
|
|
// NOTE: These calls are synchronous even when the HTTP request is async.
|
|
WinHttpCloseHandle(response->w32_request_handle);
|
|
WinHttpCloseHandle(response->w32_request_connection);
|
|
WinHttpCloseHandle(response->w32_request_session);
|
|
|
|
response->w32_request_session = nullptr;
|
|
response->w32_request_connection = nullptr;
|
|
response->w32_request_handle = nullptr;
|
|
DN_ArenaDeinit(&response->tmp_arena);
|
|
DN_OS_SemaphoreDeinit(&response->on_complete_semaphore);
|
|
|
|
*response = {};
|
|
}
|
|
|
|
// NOTE: DN_W32 ////////////////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_Str16 DN_W32_ErrorCodeToMsg16Alloc(DN_U32 error_code)
|
|
{
|
|
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
|
|
void *module_to_get_errors_from = nullptr;
|
|
if (error_code >= 12000 && error_code <= 12175) {
|
|
flags |= FORMAT_MESSAGE_FROM_HMODULE;
|
|
module_to_get_errors_from = GetModuleHandleA("winhttp.dll");
|
|
}
|
|
|
|
wchar_t *result16 = nullptr;
|
|
DWORD size = FormatMessageW(/*DWORD dwFlags */ flags | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
/*LPCVOID lpSource */ module_to_get_errors_from,
|
|
/*DWORD dwMessageId */ error_code,
|
|
/*DWORD dwLanguageId*/ 0,
|
|
/*LPWSTR lpBuffer */ (LPWSTR)&result16,
|
|
/*DWORD nSize */ 0,
|
|
/*va_list *Arguments */ nullptr);
|
|
|
|
DN_Str16 result = {};
|
|
result.data = result16;
|
|
result.size = size;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_W32Error DN_W32_ErrorCodeToMsgAlloc(DN_U32 error_code)
|
|
{
|
|
DN_W32Error result = {};
|
|
result.code = error_code;
|
|
DN_Str16 error16 = DN_W32_ErrorCodeToMsg16Alloc(error_code);
|
|
if (error16.size)
|
|
result.msg = DN_W32_Str16ToStr8FromHeap(error16);
|
|
if (error16.data)
|
|
LocalFree(error16.data);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_W32Error DN_W32_ErrorCodeToMsg(DN_Arena *arena, DN_U32 error_code)
|
|
{
|
|
DN_W32Error result = {};
|
|
result.code = error_code;
|
|
if (arena) {
|
|
DN_Str16 error16 = DN_W32_ErrorCodeToMsg16Alloc(error_code);
|
|
if (error16.size)
|
|
result.msg = DN_W32_Str16ToStr8(arena, error16);
|
|
if (error16.data)
|
|
LocalFree(error16.data);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_W32Error DN_W32_LastError(DN_Arena *arena)
|
|
{
|
|
DN_W32Error result = DN_W32_ErrorCodeToMsg(arena, GetLastError());
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_W32Error DN_W32_LastErrorAlloc()
|
|
{
|
|
DN_W32Error result = DN_W32_ErrorCodeToMsgAlloc(GetLastError());
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_W32_MakeProcessDPIAware()
|
|
{
|
|
typedef bool SetProcessDpiAwareProc(void);
|
|
typedef bool SetProcessDpiAwarenessProc(DPI_AWARENESS);
|
|
typedef bool SetProcessDpiAwarenessContextProc(void * /*DPI_AWARENESS_CONTEXT*/);
|
|
|
|
// NOTE(doyle): Taken from cmuratori/refterm snippet on DPI awareness. It
|
|
// appears we can make this robust by just loading user32.dll and using
|
|
// GetProcAddress on the DPI function. If it's not there, we're on an old
|
|
// version of windows, so we can call an older version of the API.
|
|
void *lib_handle = LoadLibraryA("user32.dll");
|
|
if (!lib_handle)
|
|
return;
|
|
|
|
if (auto *set_process_dpi_awareness_context = DN_Cast(SetProcessDpiAwarenessContextProc *) GetProcAddress(DN_Cast(HMODULE) lib_handle, "SetProcessDpiAwarenessContext"))
|
|
set_process_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
|
else if (auto *set_process_dpi_awareness = DN_Cast(SetProcessDpiAwarenessProc *) GetProcAddress(DN_Cast(HMODULE) lib_handle, "SetProcessDpiAwareness"))
|
|
set_process_dpi_awareness(DPI_AWARENESS_PER_MONITOR_AWARE);
|
|
else if (auto *set_process_dpi_aware = DN_Cast(SetProcessDpiAwareProc *) GetProcAddress(DN_Cast(HMODULE) lib_handle, "SetProcessDpiAware"))
|
|
set_process_dpi_aware();
|
|
}
|
|
|
|
// NOTE: Windows UTF8 to Str16 //////////////////////////////////////////////
|
|
DN_API DN_Str16 DN_W32_Str8ToStr16(DN_Arena *arena, DN_Str8 src)
|
|
{
|
|
DN_Str16 result = {};
|
|
if (!arena || src.size == 0)
|
|
return result;
|
|
|
|
int required_size = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, nullptr /*dest*/, 0 /*dest size*/);
|
|
if (required_size <= 0)
|
|
return result;
|
|
|
|
wchar_t *buffer = DN_ArenaNewArray(arena, wchar_t, required_size + 1, DN_ZMem_No);
|
|
if (!buffer)
|
|
return result;
|
|
|
|
int chars_written = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, buffer, required_size);
|
|
if (DN_Check(chars_written == required_size)) {
|
|
result.data = buffer;
|
|
result.size = chars_written;
|
|
result.data[result.size] = 0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API int DN_W32_Str8ToStr16Buffer(DN_Str8 src, wchar_t *dest, int dest_size)
|
|
{
|
|
int result = 0;
|
|
if (src.size == 0)
|
|
return result;
|
|
|
|
result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, nullptr /*dest*/, 0 /*dest size*/);
|
|
if (result <= 0 || result > dest_size || !dest)
|
|
return result;
|
|
|
|
result = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, dest, DN_Cast(int) dest_size);
|
|
dest[DN_Min(result, dest_size - 1)] = 0;
|
|
return result;
|
|
}
|
|
|
|
// NOTE: Windows Str16 To UTF8 //////////////////////////////////////////////////////////////////
|
|
DN_API int DN_W32_Str16ToStr8Buffer(DN_Str16 src, char *dest, int dest_size)
|
|
{
|
|
int result = 0;
|
|
if (src.size == 0)
|
|
return result;
|
|
|
|
int src_size = DN_SaturateCastISizeToInt(src.size);
|
|
if (src_size <= 0)
|
|
return result;
|
|
|
|
result = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, nullptr /*dest*/, 0 /*dest size*/, nullptr, nullptr);
|
|
if (result <= 0 || result > dest_size || !dest)
|
|
return result;
|
|
|
|
result = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, dest, DN_Cast(int) dest_size, nullptr, nullptr);
|
|
dest[DN_Min(result, dest_size - 1)] = 0;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_W32_Str16ToStr8(DN_Arena *arena, DN_Str16 src)
|
|
{
|
|
DN_Str8 result = {};
|
|
if (!arena || src.size == 0)
|
|
return result;
|
|
|
|
int src_size = DN_SaturateCastISizeToInt(src.size);
|
|
if (src_size <= 0)
|
|
return result;
|
|
|
|
int required_size = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, nullptr /*dest*/, 0 /*dest size*/, nullptr, nullptr);
|
|
if (required_size <= 0)
|
|
return result;
|
|
|
|
// NOTE: Str8 allocate ensures there's one extra byte for
|
|
// null-termination already so no-need to +1 the required size
|
|
DN_ArenaTempMemScope temp_mem = DN_ArenaTempMemScope(arena);
|
|
DN_Str8 buffer = DN_Str8FromArena(arena, required_size, DN_ZMem_No);
|
|
if (buffer.size == 0)
|
|
return result;
|
|
|
|
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr);
|
|
if (DN_Check(chars_written == required_size)) {
|
|
result = buffer;
|
|
result.data[result.size] = 0;
|
|
temp_mem.mem = {};
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_W32_Str16ToStr8FromHeap(DN_Str16 src)
|
|
{
|
|
DN_Str8 result = {};
|
|
if (src.size == 0)
|
|
return result;
|
|
|
|
int src_size = DN_SaturateCastISizeToInt(src.size);
|
|
if (src_size <= 0)
|
|
return result;
|
|
|
|
int required_size = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, nullptr /*dest*/, 0 /*dest size*/, nullptr, nullptr);
|
|
if (required_size <= 0)
|
|
return result;
|
|
|
|
// NOTE: Str8 allocate ensures there's one extra byte for
|
|
// null-termination already so no-need to +1 the required size
|
|
DN_Str8 buffer = DN_Str8FromHeap(required_size, DN_ZMem_No);
|
|
if (buffer.size == 0)
|
|
return result;
|
|
|
|
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr);
|
|
if (DN_Check(chars_written == required_size)) {
|
|
result = buffer;
|
|
result.data[result.size] = 0;
|
|
} else {
|
|
DN_OS_MemDealloc(buffer.data);
|
|
buffer = {};
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// NOTE: Windows Executable Directory //////////////////////////////////////////
|
|
DN_API DN_Str16 DN_W32_EXEPathW(DN_Arena *arena)
|
|
{
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
|
DN_Str16 result = {};
|
|
DN_USize module_size = 0;
|
|
wchar_t *module_path = nullptr;
|
|
do {
|
|
module_size += 256;
|
|
module_path = DN_ArenaNewArray(tmem.arena, wchar_t, module_size, DN_ZMem_No);
|
|
if (!module_path)
|
|
return result;
|
|
module_size = DN_Cast(DN_USize) GetModuleFileNameW(nullptr /*module*/, module_path, DN_Cast(int) module_size);
|
|
} while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
DN_USize index_of_last_slash = 0;
|
|
for (DN_USize index = module_size - 1; !index_of_last_slash && index < module_size; index--)
|
|
index_of_last_slash = module_path[index] == '\\' ? index : 0;
|
|
|
|
result.data = DN_ArenaNewArray(arena, wchar_t, module_size + 1, DN_ZMem_No);
|
|
result.size = module_size;
|
|
DN_Memcpy(result.data, module_path, sizeof(wchar_t) * result.size);
|
|
result.data[result.size] = 0;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str16 DN_W32_EXEDirW(DN_Arena *arena)
|
|
{
|
|
// TODO(doyle): Implement a DN_Str16_BinarySearchReverse
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
|
DN_Str16 result = {};
|
|
DN_USize module_size = 0;
|
|
wchar_t *module_path = nullptr;
|
|
do {
|
|
module_size += 256;
|
|
module_path = DN_ArenaNewArray(tmem.arena, wchar_t, module_size, DN_ZMem_No);
|
|
if (!module_path)
|
|
return result;
|
|
module_size = DN_Cast(DN_USize) GetModuleFileNameW(nullptr /*module*/, module_path, DN_Cast(int) module_size);
|
|
} while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);
|
|
|
|
DN_USize index_of_last_slash = 0;
|
|
for (DN_USize index = module_size - 1; !index_of_last_slash && index < module_size; index--)
|
|
index_of_last_slash = module_path[index] == '\\' ? index : 0;
|
|
|
|
result.data = DN_ArenaNewArray(arena, wchar_t, index_of_last_slash + 1, DN_ZMem_No);
|
|
result.size = index_of_last_slash;
|
|
DN_Memcpy(result.data, module_path, sizeof(wchar_t) * result.size);
|
|
result.data[result.size] = 0;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_W32_WorkingDir(DN_Arena *arena, DN_Str8 suffix)
|
|
{
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
|
DN_Str16 suffix16 = DN_W32_Str8ToStr16(tmem.arena, suffix);
|
|
DN_Str16 dir16 = DN_W32_WorkingDirW(tmem.arena, suffix16);
|
|
DN_Str8 result = DN_W32_Str16ToStr8(arena, dir16);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str16 DN_W32_WorkingDirW(DN_Arena *arena, DN_Str16 suffix)
|
|
{
|
|
DN_Assert(suffix.size >= 0);
|
|
DN_Str16 result = {};
|
|
|
|
// NOTE: required_size is the size required *including* the null-terminator
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
|
unsigned long required_size = GetCurrentDirectoryW(0, nullptr);
|
|
unsigned long desired_size = required_size + DN_Cast(unsigned long) suffix.size;
|
|
|
|
wchar_t *tmem_w_path = DN_ArenaNewArray(tmem.arena, wchar_t, desired_size, DN_ZMem_No);
|
|
if (!tmem_w_path)
|
|
return result;
|
|
|
|
unsigned long bytes_written_wo_null_terminator = GetCurrentDirectoryW(desired_size, tmem_w_path);
|
|
if ((bytes_written_wo_null_terminator + 1) != required_size) {
|
|
// TODO(dn): Error
|
|
return result;
|
|
}
|
|
|
|
wchar_t *w_path = DN_ArenaNewArray(arena, wchar_t, desired_size, DN_ZMem_No);
|
|
if (!w_path)
|
|
return result;
|
|
|
|
if (suffix.size) {
|
|
DN_Memcpy(w_path, tmem_w_path, sizeof(*tmem_w_path) * bytes_written_wo_null_terminator);
|
|
DN_Memcpy(w_path + bytes_written_wo_null_terminator, suffix.data, sizeof(suffix.data[0]) * suffix.size);
|
|
w_path[desired_size] = 0;
|
|
}
|
|
|
|
result = DN_Str16{w_path, DN_Cast(DN_USize)(desired_size - 1)};
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_W32_DirWIterate(DN_Str16 path, DN_W32FolderIteratorW *it)
|
|
{
|
|
WIN32_FIND_DATAW find_data = {};
|
|
if (it->handle) {
|
|
if (FindNextFileW(it->handle, &find_data) == 0) {
|
|
FindClose(it->handle);
|
|
return false;
|
|
}
|
|
} else {
|
|
it->handle = FindFirstFileExW(path.data, /*LPCWSTR lpFileName,*/
|
|
FindExInfoStandard, /*FINDEX_INFO_LEVELS fInfoLevelId,*/
|
|
&find_data, /*LPVOID lpFindFileData,*/
|
|
FindExSearchNameMatch, /*FINDEX_SEARCH_OPS fSearchOp,*/
|
|
nullptr, /*LPVOID lpSearchFilter,*/
|
|
FIND_FIRST_EX_LARGE_FETCH /*unsigned long dwAdditionalFlags)*/);
|
|
|
|
if (it->handle == INVALID_HANDLE_VALUE)
|
|
return false;
|
|
}
|
|
|
|
it->file_name_buf[0] = 0;
|
|
it->file_name = DN_Str16{it->file_name_buf, 0};
|
|
|
|
do {
|
|
if (find_data.cFileName[0] == '.' || (find_data.cFileName[0] == '.' && find_data.cFileName[1] == '.'))
|
|
continue;
|
|
|
|
it->file_name.size = DN_CStr16Size(find_data.cFileName);
|
|
DN_Assert(it->file_name.size < (DN_ArrayCountU(it->file_name_buf) - 1));
|
|
DN_Memcpy(it->file_name.data, find_data.cFileName, it->file_name.size * sizeof(wchar_t));
|
|
it->file_name_buf[it->file_name.size] = 0;
|
|
break;
|
|
} while (FindNextFileW(it->handle, &find_data) != 0);
|
|
|
|
bool result = it->file_name.size > 0;
|
|
if (!result)
|
|
FindClose(it->handle);
|
|
return result;
|
|
}
|
|
#else
|
|
#error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
|
|
#endif
|
|
|
|
// DN: Single header generator inlined this file => #include "OS/dn_os_stacktrace.cpp"
|
|
#define DN_CORE_DEBUG_CPP
|
|
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_os_inc.h"
|
|
|
|
DN_API DN_StackTraceWalkResult DN_StackTraceWalk(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();
|
|
|
|
DN_W32Core *w32 = DN_OS_GetW32Core_();
|
|
if (!w32->sym_initialised) {
|
|
w32->sym_initialised = true;
|
|
SymSetOptions(SYMOPT_LOAD_LINES);
|
|
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) {
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
|
|
DN_W32Error error = DN_W32_LastError(tmem.arena);
|
|
DN_LOG_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DN_Str8PrintFmt(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_ArenaNewArray(arena, uint64_t, raw_frames.size, DN_ZMem_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_StackTraceAddWalkToStr8Builder(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_StackTraceRawFrameToFrame(builder->arena, raw_frame);
|
|
DN_Str8BuilderAppendF(builder, "%.*s(%zu): %.*s%s", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name), (DN_Cast(int) index == walk->size - 1) ? "" : "\n");
|
|
}
|
|
}
|
|
|
|
DN_API bool DN_StackTraceWalkResultIterate(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_StackTraceWalkResultToStr8(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_Str8BuilderFromArena(tmem.arena);
|
|
DN_StackTraceAddWalkToStr8Builder(walk, &builder, skip);
|
|
result = DN_Str8BuilderBuild(&builder, arena);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_StackTraceWalkStr8(DN_Arena *arena, uint16_t limit, uint16_t skip)
|
|
{
|
|
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(arena);
|
|
DN_StackTraceWalkResult walk = DN_StackTraceWalk(tmem.arena, limit);
|
|
DN_Str8 result = DN_StackTraceWalkResultToStr8(arena, &walk, skip);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8 DN_StackTraceWalkStr8FromHeap(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_ArenaFromHeap(DN_Kilobytes(64), DN_ArenaFlags_NoAllocTrack);
|
|
DN_Str8Builder builder = DN_Str8BuilderFromArena(&arena);
|
|
DN_StackTraceWalkResult walk = DN_StackTraceWalk(&arena, limit);
|
|
DN_StackTraceAddWalkToStr8Builder(&walk, &builder, skip);
|
|
DN_Str8 result = DN_Str8BuilderBuildFromOSHeap(&builder);
|
|
DN_ArenaDeinit(&arena);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTraceGetFrames(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_StackTraceWalk(tmem.arena, limit);
|
|
if (!walk.size)
|
|
return result;
|
|
|
|
DN_USize slice_index = 0;
|
|
result = DN_Slice_Alloc<DN_StackTraceFrame>(arena, walk.size, DN_ZMem_No);
|
|
for (DN_StackTraceWalkResultIterator it = {}; DN_StackTraceWalkResultIterate(&it, &walk);)
|
|
result.data[slice_index++] = DN_StackTraceRawFrameToFrame(arena, it.raw_frame);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_StackTraceFrame DN_StackTraceRawFrameToFrame(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_CStr16Size(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_W32_Str16ToStr8(arena, file_name16);
|
|
result.function_name = DN_W32_Str16ToStr8(arena, function_name16);
|
|
|
|
if (result.function_name.size == 0)
|
|
result.function_name = DN_Str8Lit("<unknown function>");
|
|
if (result.file_name.size == 0)
|
|
result.file_name = DN_Str8Lit("<unknown file>");
|
|
#else
|
|
DN_StackTraceFrame result = {};
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
DN_API void DN_StackTracePrint(uint16_t limit)
|
|
{
|
|
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
|
|
DN_Slice<DN_StackTraceFrame> stack_trace = DN_StackTraceGetFrames(tmem.arena, limit);
|
|
for (DN_StackTraceFrame &frame : stack_trace)
|
|
DN_OS_PrintErrLnF("%.*s(%I64u): %.*s", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name));
|
|
}
|
|
|
|
DN_API void DN_StackTraceReloadSymbols()
|
|
{
|
|
#if defined(DN_OS_WIN32)
|
|
HANDLE process = GetCurrentProcess();
|
|
SymRefreshModuleList(process);
|
|
#endif
|
|
}
|
|
#define DN_INC_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator inlined this file => #include "dn_inc.h"
|
|
#if !defined(DN_INC_H)
|
|
#define DN_INC_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "dn_base_inc.h"
|
|
#if !defined(DN_BASE_INC_H)
|
|
#define DN_BASE_INC_H
|
|
|
|
// NOTE: DN configuration
|
|
// All the following configuration options are optional. If omitted, DN has default behaviours to
|
|
// handle the various options.
|
|
//
|
|
// Platform Target
|
|
// Define one of the following directives to configure this library to compile for that platform.
|
|
// By default, the library will auto-detect the current host platform and select that as the
|
|
// target platform.
|
|
//
|
|
// DN_PLATFORM_EMSCRIPTEN
|
|
// DN_PLATFORM_POSIX
|
|
// DN_PLATFORM_WIN32
|
|
//
|
|
// For example
|
|
//
|
|
// #define DN_PLATFORM_WIN32
|
|
//
|
|
// Will ensure that <Windows.h> is included and the OS layer is implemented using Win32
|
|
// primitives.
|
|
//
|
|
// Static functions
|
|
// All public functions in the DN library are prefixed with the macro '#define DN_API'. By
|
|
// default 'DN_API' is not defined to anything. Define
|
|
//
|
|
// DN_STATIC_API
|
|
//
|
|
// To replace all the prefixed with 'static' ensuring that the functions in the library do not
|
|
// export an entry into the linking table and thereby optimise compilation times as the linker
|
|
// will not try to resolve symbols from other translation units from the the unit including the
|
|
// DN library.
|
|
//
|
|
// Using the in-built replacement header for <Windows.h> (requires dn_os_inc.h)
|
|
// If you are building DN for the Windows platform, <Windows.h> is a large legacy header that
|
|
// applications have to link to use Windows syscalls. By default DN library uses a replacement
|
|
// header for all the Windows functions that it uses in the OS layer removing the need to include
|
|
// <Windows.h> to improve compilation times. This can be disabled by defining:
|
|
//
|
|
// DN_NO_WINDOWS_H_REPLACEMENT_HEADER
|
|
//
|
|
// To instead use <Windows.h>. DN automatically detects if <Windows.h> is included in an earlier
|
|
// translation unit and will automatically disable the in-built replacement header in which case
|
|
// this does not need to be defined.
|
|
//
|
|
// Freestanding
|
|
// The base layer can be used without an OS implementation by defining DN_FREESTANDING like:
|
|
//
|
|
// #define DN_FREESTANDING
|
|
//
|
|
// This means functionality that relies on the OS like printing, memory allocation, stack traces
|
|
// and so forth are disabled.
|
|
|
|
// DN: Single header generator commented out this header => #include "Base/dn_base_compiler.h"
|
|
#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)
|
|
// DN: Single header generator commented out this header => #include "Base/dn_base.h"
|
|
#if !defined(DN_BASE_H)
|
|
#define DN_BASE_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
// 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) DN_USize index = 0; index < size; index++
|
|
#define DN_ForIndexI(index, size) DN_ISize index = 0; index < size; index++
|
|
#define DN_ForItSize(it, T, array, size) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (size); it.index++, it.data = (array) + it.index
|
|
#define DN_ForItSizeReverse(it, T, array, size) struct { DN_USize index; T *data; } it = {(size) - 1, &(array)[size - 1]}; it.index < (size); it.index--, it.data = (array) + it.index
|
|
#define DN_ForIt(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->size; it.index++, it.data = ((array)->data) + it.index
|
|
#define DN_ForLinkedListIt(it, T, list) struct { DN_USize index; T *data; } it = {0, list}; it.data; it.index++, it.data = ((it).data->next)
|
|
#define DN_ForItCArray(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < DN_ArrayCountU(array); it.index++, it.data = (array) + it.index
|
|
|
|
#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_MsFromSec(val) ((val) * 1000ULL)
|
|
#define DN_SecFromMins(val) ((val) * 60ULL)
|
|
#define DN_SecFromHours(val) (DN_SecFromMins(val) * 60ULL)
|
|
#define DN_SecFromDays(val) (DN_SecFromHours(val) * 24ULL)
|
|
#define DN_SecFromWeeks(val) (DN_SecFromDays(val) * 7ULL)
|
|
#define DN_SecFromYears(val) (DN_SecFromWeeks(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: 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 uintptr_t DN_UPtr;
|
|
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_ZMem
|
|
{
|
|
DN_ZMem_No, // Memory can be handed out without zero-ing it out
|
|
DN_ZMem_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
|
|
};
|
|
|
|
struct DN_Str8x32 { char data[32]; DN_USize size; };
|
|
struct DN_Str8x64 { char data[64]; DN_USize size; };
|
|
struct DN_Str8x128 { char data[128]; DN_USize size; };
|
|
struct DN_Str8x256 { char data[256]; DN_USize size; };
|
|
|
|
struct DN_Hex32 { char data[32]; };
|
|
struct DN_Hex64 { char data[64]; };
|
|
struct DN_Hex128 { char data[128]; };
|
|
|
|
struct DN_HexU64Str8
|
|
{
|
|
char data[(sizeof(DN_U64) * 2) + 1 /*null-terminator*/];
|
|
DN_U8 size;
|
|
};
|
|
|
|
enum DN_HexFromU64Type
|
|
{
|
|
DN_HexFromU64Type_Nil,
|
|
DN_HexFromU64Type_Uppercase,
|
|
};
|
|
|
|
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
|
|
};
|
|
|
|
struct DN_U8x16 { DN_U8 data[16]; };
|
|
struct DN_U8x32 { DN_U8 data[32]; };
|
|
struct DN_U8x64 { DN_U8 data[64]; };
|
|
|
|
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; }
|
|
};
|
|
|
|
struct DN_CallSite
|
|
{
|
|
DN_Str8 file;
|
|
DN_Str8 function;
|
|
DN_U32 line;
|
|
};
|
|
|
|
#define DN_CALL_SITE DN_CallSite { DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ }
|
|
|
|
// 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_DeferLoop(begin, end) \
|
|
bool DN_UniqueName(once) = (begin, true); \
|
|
DN_UniqueName(once); \
|
|
end, DN_UniqueName(once) = false
|
|
|
|
|
|
// NOTE: Intrinsics
|
|
// NOTE: DN_AtomicAdd/Exchange return the previous value store in the target
|
|
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
|
#include <intrin.h>
|
|
#define DN_AtomicCompareExchange64(dest, desired_val, prev_val) _InterlockedCompareExchange64((__int64 volatile *)dest, desired_val, prev_val)
|
|
#define DN_AtomicCompareExchange32(dest, desired_val, prev_val) _InterlockedCompareExchange((long volatile *)dest, desired_val, prev_val)
|
|
|
|
#define DN_AtomicLoadU64(target) *(target)
|
|
#define DN_AtomicLoadU32(target) *(target)
|
|
#define DN_AtomicAddU32(target, value) _InterlockedExchangeAdd((long volatile *)target, value)
|
|
#define DN_AtomicAddU64(target, value) _InterlockedExchangeAdd64((__int64 volatile *)target, value)
|
|
#define DN_AtomicSubU32(target, value) DN_AtomicAddU32(DN_Cast(long volatile *) target, (long)-value)
|
|
#define DN_AtomicSubU64(target, value) DN_AtomicAddU64(target, (DN_U64) - value)
|
|
|
|
#define DN_CountLeadingZerosU64(value) __lzcnt64(value)
|
|
#define DN_CountLeadingZerosU32(value) __lzcnt(value)
|
|
#define DN_CPUGetTSC() __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)
|
|
#if !defined(__wasm_simd128__)
|
|
#error DN_Base requires -msse2 to be passed to Emscripten
|
|
#endif
|
|
#include <emmintrin.h>
|
|
#else
|
|
#include <x86intrin.h>
|
|
#endif
|
|
|
|
#define DN_AtomicLoadU64(target) __atomic_load_n(x, __ATOMIC_SEQ_CST)
|
|
#define DN_AtomicLoadU32(target) __atomic_load_n(x, __ATOMIC_SEQ_CST)
|
|
#define DN_AtomicAddU32(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
|
|
#define DN_AtomicAddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
|
|
#define DN_AtomicSubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
|
|
#define DN_AtomicSubU64(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
|
|
|
|
#define DN_CountLeadingZerosU64(value) __builtin_clzll(value)
|
|
#define DN_CountLeadingZerosU32(value) __builtin_clzl(value)
|
|
|
|
#if defined(DN_COMPILER_GCC)
|
|
#define DN_CPUGetTSC() __rdtsc()
|
|
#else
|
|
#define DN_CPUGetTSC() __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(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
|
|
#define DN_CountLeadingZerosUSize(value) DN_CountLeadingZerosU64(value)
|
|
#else
|
|
#define DN_CountLeadingZerosUSize(value) DN_CountLeadingZerosU32(value)
|
|
#endif
|
|
|
|
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];
|
|
};
|
|
|
|
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
|
|
};
|
|
|
|
struct DN_U64FromResult
|
|
{
|
|
bool success;
|
|
DN_U64 value;
|
|
};
|
|
|
|
struct DN_I64FromResult
|
|
{
|
|
bool success;
|
|
DN_I64 value;
|
|
};
|
|
|
|
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
|
|
|
|
enum DN_Allocator
|
|
{
|
|
DN_Allocator_Arena,
|
|
DN_Allocator_Pool,
|
|
};
|
|
|
|
struct DN_ArenaBlock
|
|
{
|
|
DN_ArenaBlock *prev;
|
|
DN_U64 used;
|
|
DN_U64 commit;
|
|
DN_U64 reserve;
|
|
DN_U64 reserve_sum;
|
|
};
|
|
|
|
typedef DN_U32 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;
|
|
};
|
|
|
|
struct DN_ArenaStatsStr8x64
|
|
{
|
|
DN_Str8x64 info;
|
|
DN_Str8x64 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);
|
|
|
|
#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];
|
|
DN_U8 align;
|
|
};
|
|
|
|
struct DN_NibbleFromU8Result
|
|
{
|
|
char nibble0;
|
|
char nibble1;
|
|
};
|
|
|
|
enum DN_Str8EqCase
|
|
{
|
|
DN_Str8EqCase_Sensitive,
|
|
DN_Str8EqCase_Insensitive,
|
|
};
|
|
|
|
enum DN_Str8IsAllType
|
|
{
|
|
DN_Str8IsAllType_Digits,
|
|
DN_Str8IsAllType_Hex,
|
|
};
|
|
|
|
struct DN_Str8BSplitResult
|
|
{
|
|
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
|
|
};
|
|
|
|
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,
|
|
};
|
|
|
|
enum DN_Str8SplitIncludeEmptyStrings
|
|
{
|
|
DN_Str8SplitIncludeEmptyStrings_No,
|
|
DN_Str8SplitIncludeEmptyStrings_Yes,
|
|
};
|
|
|
|
struct DN_Str8TruncateResult
|
|
{
|
|
bool truncated;
|
|
DN_Str8 str8;
|
|
};
|
|
|
|
struct DN_Str8SplitResult
|
|
{
|
|
DN_Str8 *data;
|
|
DN_USize count;
|
|
};
|
|
|
|
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_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
|
|
};
|
|
|
|
enum DN_Str8BuilderAdd
|
|
{
|
|
DN_Str8BuilderAdd_Append,
|
|
DN_Str8BuilderAdd_Prepend,
|
|
};
|
|
|
|
typedef DN_U32 DN_AgeUnit;
|
|
enum DN_AgeUnit_
|
|
{
|
|
DN_AgeUnit_Ms = 1 << 0,
|
|
DN_AgeUnit_Sec = 1 << 1,
|
|
DN_AgeUnit_Min = 1 << 2,
|
|
DN_AgeUnit_Hr = 1 << 3,
|
|
DN_AgeUnit_Day = 1 << 4,
|
|
DN_AgeUnit_Week = 1 << 5,
|
|
DN_AgeUnit_Year = 1 << 6,
|
|
DN_AgeUnit_FractionalSec = 1 << 7,
|
|
DN_AgeUnit_HMS = DN_AgeUnit_Sec | DN_AgeUnit_Min | DN_AgeUnit_Hr,
|
|
DN_AgeUnit_All = DN_AgeUnit_Ms | DN_AgeUnit_HMS | DN_AgeUnit_Day | DN_AgeUnit_Week | DN_AgeUnit_Year,
|
|
};
|
|
|
|
enum DN_ByteCountType
|
|
{
|
|
DN_ByteCountType_B,
|
|
DN_ByteCountType_KiB,
|
|
DN_ByteCountType_MiB,
|
|
DN_ByteCountType_GiB,
|
|
DN_ByteCountType_TiB,
|
|
DN_ByteCountType_Count,
|
|
DN_ByteCountType_Auto,
|
|
};
|
|
|
|
struct DN_ByteCountResult
|
|
{
|
|
DN_ByteCountType type;
|
|
DN_Str8 suffix; // "KiB", "MiB", "GiB" .. e.t.c
|
|
DN_F64 bytes;
|
|
};
|
|
|
|
struct DN_FmtAppendResult
|
|
{
|
|
DN_USize size_req;
|
|
DN_Str8 str8;
|
|
bool truncated;
|
|
};
|
|
|
|
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;
|
|
};
|
|
|
|
struct DN_ProfilerAnchorArray
|
|
{
|
|
DN_ProfilerAnchor *data;
|
|
DN_USize count;
|
|
};
|
|
|
|
typedef DN_U64 (DN_ProfilerTSCNowFunc)();
|
|
struct DN_Profiler
|
|
{
|
|
DN_USize frame_index;
|
|
DN_ProfilerAnchor *anchors;
|
|
DN_USize anchors_count;
|
|
DN_USize anchors_per_frame;
|
|
DN_U16 parent_zone;
|
|
bool paused;
|
|
DN_ProfilerTSCNowFunc *tsc_now;
|
|
DN_U64 tsc_frequency;
|
|
DN_ProfilerZone frame_zone;
|
|
DN_F64 frame_avg_tsc;
|
|
};
|
|
|
|
#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)
|
|
// DN: Single header generator commented out this header => #include "../External/stb_sprintf.h"
|
|
// 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
|
|
// - Using defined(__wasm64__) to correctly assign stbsp__uintptr to stbsp__uint64 for a 64bit WASM target.
|
|
|
|
// 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
|
|
#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
|
|
#define stbsp__int32 signed int
|
|
|
|
#ifdef _MSC_VER
|
|
#define stbsp__uint64 unsigned __int64
|
|
#define stbsp__int64 signed __int64
|
|
#else
|
|
#define stbsp__uint64 unsigned long long
|
|
#define stbsp__int64 signed long long
|
|
#endif
|
|
#define stbsp__uint16 unsigned short
|
|
|
|
#ifndef stbsp__uintptr
|
|
#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) || defined(__wasm64__)
|
|
#define stbsp__uintptr stbsp__uint64
|
|
#else
|
|
#define stbsp__uintptr stbsp__uint32
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC)
|
|
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
|
#define STB_SPRINTF_MSVC_MODE
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses
|
|
#define STBSP__UNALIGNED(code)
|
|
#else
|
|
#define STBSP__UNALIGNED(code) code
|
|
#endif
|
|
|
|
#ifndef STB_SPRINTF_NOFLOAT
|
|
// internal float utility functions
|
|
static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits);
|
|
static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value);
|
|
#define STBSP__SPECIAL 0x7000
|
|
#endif
|
|
|
|
static char stbsp__period = '.';
|
|
static char stbsp__comma = ',';
|
|
static struct
|
|
{
|
|
short temp; // force next field to be 2-byte aligned
|
|
char pair[201];
|
|
} stbsp__digitpair =
|
|
{
|
|
0,
|
|
"00010203040506070809101112131415161718192021222324"
|
|
"25262728293031323334353637383940414243444546474849"
|
|
"50515253545556575859606162636465666768697071727374"
|
|
"75767778798081828384858687888990919293949596979899"
|
|
};
|
|
|
|
STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod)
|
|
{
|
|
stbsp__period = pperiod;
|
|
stbsp__comma = pcomma;
|
|
}
|
|
|
|
#define STBSP__LEFTJUST 1
|
|
#define STBSP__LEADINGPLUS 2
|
|
#define STBSP__LEADINGSPACE 4
|
|
#define STBSP__LEADING_0X 8
|
|
#define STBSP__LEADINGZERO 16
|
|
#define STBSP__INTMAX 32
|
|
#define STBSP__TRIPLET_COMMA 64
|
|
#define STBSP__NEGATIVE 128
|
|
#define STBSP__METRIC_SUFFIX 256
|
|
#define STBSP__HALFWIDTH 512
|
|
#define STBSP__METRIC_NOSPACE 1024
|
|
#define STBSP__METRIC_1024 2048
|
|
#define STBSP__METRIC_JEDEC 4096
|
|
|
|
static void stbsp__lead_sign(stbsp__uint32 fl, char *sign)
|
|
{
|
|
sign[0] = 0;
|
|
if (fl & STBSP__NEGATIVE) {
|
|
sign[0] = 1;
|
|
sign[1] = '-';
|
|
} else if (fl & STBSP__LEADINGSPACE) {
|
|
sign[0] = 1;
|
|
sign[1] = ' ';
|
|
} else if (fl & STBSP__LEADINGPLUS) {
|
|
sign[0] = 1;
|
|
sign[1] = '+';
|
|
}
|
|
}
|
|
|
|
static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit)
|
|
{
|
|
char const * sn = s;
|
|
|
|
// get up to 4-byte alignment
|
|
for (;;) {
|
|
if (((stbsp__uintptr)sn & 3) == 0)
|
|
break;
|
|
|
|
if (!limit || *sn == 0)
|
|
return (stbsp__uint32)(sn - s);
|
|
|
|
++sn;
|
|
--limit;
|
|
}
|
|
|
|
// scan over 4 bytes at a time to find terminating 0
|
|
// this will intentionally scan up to 3 bytes past the end of buffers,
|
|
// but becase it works 4B aligned, it will never cross page boundaries
|
|
// (hence the STBSP__ASAN markup; the over-read here is intentional
|
|
// and harmless)
|
|
while (limit >= 4) {
|
|
stbsp__uint32 v = *(stbsp__uint32 *)sn;
|
|
// bit hack to find if there's a 0 byte in there
|
|
if ((v - 0x01010101) & (~v) & 0x80808080UL)
|
|
break;
|
|
|
|
sn += 4;
|
|
limit -= 4;
|
|
}
|
|
|
|
// handle the last few characters to find actual size
|
|
while (limit && *sn) {
|
|
++sn;
|
|
--limit;
|
|
}
|
|
|
|
return (stbsp__uint32)(sn - s);
|
|
}
|
|
|
|
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va)
|
|
{
|
|
static char hex[] = "0123456789abcdefxp";
|
|
static char hexu[] = "0123456789ABCDEFXP";
|
|
char *bf;
|
|
char const *f;
|
|
int tlen = 0;
|
|
|
|
bf = buf;
|
|
f = fmt;
|
|
for (;;) {
|
|
stbsp__int32 fw, pr, tz;
|
|
stbsp__uint32 fl;
|
|
|
|
// macros for the callback buffer stuff
|
|
#define stbsp__chk_cb_bufL(bytes) \
|
|
{ \
|
|
int len = (int)(bf - buf); \
|
|
if ((len + (bytes)) >= STB_SPRINTF_MIN) { \
|
|
tlen += len; \
|
|
if (0 == (bf = buf = callback(buf, user, len))) \
|
|
goto done; \
|
|
} \
|
|
}
|
|
#define stbsp__chk_cb_buf(bytes) \
|
|
{ \
|
|
if (callback) { \
|
|
stbsp__chk_cb_bufL(bytes); \
|
|
} \
|
|
}
|
|
#define stbsp__flush_cb() \
|
|
{ \
|
|
stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \
|
|
} // flush if there is even one byte in the buffer
|
|
#define stbsp__cb_buf_clamp(cl, v) \
|
|
cl = v; \
|
|
if (callback) { \
|
|
int lg = STB_SPRINTF_MIN - (int)(bf - buf); \
|
|
if (cl > lg) \
|
|
cl = lg; \
|
|
}
|
|
|
|
// fast copy everything up to the next % (or end of string)
|
|
for (;;) {
|
|
while (((stbsp__uintptr)f) & 3) {
|
|
schk1:
|
|
if (f[0] == '%')
|
|
goto scandd;
|
|
schk2:
|
|
if (f[0] == 0)
|
|
goto endfmt;
|
|
stbsp__chk_cb_buf(1);
|
|
*bf++ = f[0];
|
|
++f;
|
|
}
|
|
for (;;) {
|
|
// Check if the next 4 bytes contain %(0x25) or end of string.
|
|
// Using the 'hasless' trick:
|
|
// https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
|
|
stbsp__uint32 v, c;
|
|
v = *(stbsp__uint32 *)f;
|
|
c = (~v) & 0x80808080;
|
|
if (((v ^ 0x25252525) - 0x01010101) & c)
|
|
goto schk1;
|
|
if ((v - 0x01010101) & c)
|
|
goto schk2;
|
|
if (callback)
|
|
if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4)
|
|
goto schk1;
|
|
#ifdef STB_SPRINTF_NOUNALIGNED
|
|
if(((stbsp__uintptr)bf) & 3) {
|
|
bf[0] = f[0];
|
|
bf[1] = f[1];
|
|
bf[2] = f[2];
|
|
bf[3] = f[3];
|
|
} else
|
|
#endif
|
|
{
|
|
*(stbsp__uint32 *)bf = v;
|
|
}
|
|
bf += 4;
|
|
f += 4;
|
|
}
|
|
}
|
|
scandd:
|
|
|
|
++f;
|
|
|
|
// ok, we have a percent, read the modifiers first
|
|
fw = 0;
|
|
pr = -1;
|
|
fl = 0;
|
|
tz = 0;
|
|
|
|
// flags
|
|
for (;;) {
|
|
switch (f[0]) {
|
|
// if we have left justify
|
|
case '-':
|
|
fl |= STBSP__LEFTJUST;
|
|
++f;
|
|
continue;
|
|
// if we have leading plus
|
|
case '+':
|
|
fl |= STBSP__LEADINGPLUS;
|
|
++f;
|
|
continue;
|
|
// if we have leading space
|
|
case ' ':
|
|
fl |= STBSP__LEADINGSPACE;
|
|
++f;
|
|
continue;
|
|
// if we have leading 0x
|
|
case '#':
|
|
fl |= STBSP__LEADING_0X;
|
|
++f;
|
|
continue;
|
|
// if we have thousand commas
|
|
case '\'':
|
|
fl |= STBSP__TRIPLET_COMMA;
|
|
++f;
|
|
continue;
|
|
// if we have kilo marker (none->kilo->kibi->jedec)
|
|
case '$':
|
|
if (fl & STBSP__METRIC_SUFFIX) {
|
|
if (fl & STBSP__METRIC_1024) {
|
|
fl |= STBSP__METRIC_JEDEC;
|
|
} else {
|
|
fl |= STBSP__METRIC_1024;
|
|
}
|
|
} else {
|
|
fl |= STBSP__METRIC_SUFFIX;
|
|
}
|
|
++f;
|
|
continue;
|
|
// if we don't want space between metric suffix and number
|
|
case '_':
|
|
fl |= STBSP__METRIC_NOSPACE;
|
|
++f;
|
|
continue;
|
|
// if we have leading zero
|
|
case '0':
|
|
fl |= STBSP__LEADINGZERO;
|
|
++f;
|
|
goto flags_done;
|
|
default: goto flags_done;
|
|
}
|
|
}
|
|
flags_done:
|
|
|
|
// get the field width
|
|
if (f[0] == '*') {
|
|
fw = va_arg(va, stbsp__uint32);
|
|
++f;
|
|
} else {
|
|
while ((f[0] >= '0') && (f[0] <= '9')) {
|
|
fw = fw * 10 + f[0] - '0';
|
|
f++;
|
|
}
|
|
}
|
|
// get the precision
|
|
if (f[0] == '.') {
|
|
++f;
|
|
if (f[0] == '*') {
|
|
pr = va_arg(va, stbsp__uint32);
|
|
++f;
|
|
} else {
|
|
pr = 0;
|
|
while ((f[0] >= '0') && (f[0] <= '9')) {
|
|
pr = pr * 10 + f[0] - '0';
|
|
f++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// handle integer size overrides
|
|
switch (f[0]) {
|
|
// are we halfwidth?
|
|
case 'h':
|
|
fl |= STBSP__HALFWIDTH;
|
|
++f;
|
|
if (f[0] == 'h')
|
|
++f; // QUARTERWIDTH
|
|
break;
|
|
// are we 64-bit (unix style)
|
|
case 'l':
|
|
fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0);
|
|
++f;
|
|
if (f[0] == 'l') {
|
|
fl |= STBSP__INTMAX;
|
|
++f;
|
|
}
|
|
break;
|
|
// are we 64-bit on intmax? (c99)
|
|
case 'j':
|
|
fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0;
|
|
++f;
|
|
break;
|
|
// are we 64-bit on size_t or ptrdiff_t? (c99)
|
|
case 'z':
|
|
fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;
|
|
++f;
|
|
break;
|
|
case 't':
|
|
fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;
|
|
++f;
|
|
break;
|
|
// are we 64-bit (msft style)
|
|
case 'I':
|
|
if ((f[1] == '6') && (f[2] == '4')) {
|
|
fl |= STBSP__INTMAX;
|
|
f += 3;
|
|
} else if ((f[1] == '3') && (f[2] == '2')) {
|
|
f += 3;
|
|
} else {
|
|
fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0);
|
|
++f;
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
// handle each replacement
|
|
switch (f[0]) {
|
|
#define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307
|
|
char num[STBSP__NUMSZ];
|
|
char lead[8];
|
|
char tail[8];
|
|
char *s;
|
|
char const *h;
|
|
stbsp__uint32 l, n, cs;
|
|
stbsp__uint64 n64;
|
|
#ifndef STB_SPRINTF_NOFLOAT
|
|
double fv;
|
|
#endif
|
|
stbsp__int32 dp;
|
|
char const *sn;
|
|
|
|
case 's':
|
|
// get the string
|
|
s = va_arg(va, char *);
|
|
if (s == 0)
|
|
s = (char *)"null";
|
|
// get the length, limited to desired precision
|
|
// always limit to ~0u chars since our counts are 32b
|
|
l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u);
|
|
lead[0] = 0;
|
|
tail[0] = 0;
|
|
pr = 0;
|
|
dp = 0;
|
|
cs = 0;
|
|
// 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;
|
|
*s = (char)va_arg(va, int);
|
|
l = 1;
|
|
lead[0] = 0;
|
|
tail[0] = 0;
|
|
pr = 0;
|
|
dp = 0;
|
|
cs = 0;
|
|
goto scopy;
|
|
|
|
case 'n': // weird write-bytes specifier
|
|
{
|
|
int *d = va_arg(va, int *);
|
|
*d = tlen + (int)(bf - buf);
|
|
} break;
|
|
|
|
#ifdef STB_SPRINTF_NOFLOAT
|
|
case 'A': // float
|
|
case 'a': // hex float
|
|
case 'G': // float
|
|
case 'g': // float
|
|
case 'E': // float
|
|
case 'e': // float
|
|
case 'f': // float
|
|
va_arg(va, double); // eat it
|
|
s = (char *)"No float";
|
|
l = 8;
|
|
lead[0] = 0;
|
|
tail[0] = 0;
|
|
pr = 0;
|
|
cs = 0;
|
|
STBSP__NOTUSED(dp);
|
|
goto scopy;
|
|
#else
|
|
case 'A': // hex float
|
|
case 'a': // hex float
|
|
h = (f[0] == 'A') ? hexu : hex;
|
|
fv = va_arg(va, double);
|
|
if (pr == -1)
|
|
pr = 6; // default is 6
|
|
// read the double into a string
|
|
if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv))
|
|
fl |= STBSP__NEGATIVE;
|
|
|
|
s = num + 64;
|
|
|
|
stbsp__lead_sign(fl, lead);
|
|
|
|
if (dp == -1023)
|
|
dp = (n64) ? -1022 : 0;
|
|
else
|
|
n64 |= (((stbsp__uint64)1) << 52);
|
|
n64 <<= (64 - 56);
|
|
if (pr < 15)
|
|
n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4));
|
|
// add leading chars
|
|
|
|
#ifdef STB_SPRINTF_MSVC_MODE
|
|
*s++ = '0';
|
|
*s++ = 'x';
|
|
#else
|
|
lead[1 + lead[0]] = '0';
|
|
lead[2 + lead[0]] = 'x';
|
|
lead[0] += 2;
|
|
#endif
|
|
*s++ = h[(n64 >> 60) & 15];
|
|
n64 <<= 4;
|
|
if (pr)
|
|
*s++ = stbsp__period;
|
|
sn = s;
|
|
|
|
// print the bits
|
|
n = pr;
|
|
if (n > 13)
|
|
n = 13;
|
|
if (pr > (stbsp__int32)n)
|
|
tz = pr - n;
|
|
pr = 0;
|
|
while (n--) {
|
|
*s++ = h[(n64 >> 60) & 15];
|
|
n64 <<= 4;
|
|
}
|
|
|
|
// print the expo
|
|
tail[1] = h[17];
|
|
if (dp < 0) {
|
|
tail[2] = '-';
|
|
dp = -dp;
|
|
} else
|
|
tail[2] = '+';
|
|
n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3));
|
|
tail[0] = (char)n;
|
|
for (;;) {
|
|
tail[n] = '0' + dp % 10;
|
|
if (n <= 3)
|
|
break;
|
|
--n;
|
|
dp /= 10;
|
|
}
|
|
|
|
dp = (int)(s - sn);
|
|
l = (int)(s - (num + 64));
|
|
s = num + 64;
|
|
cs = 1 + (3 << 24);
|
|
goto scopy;
|
|
|
|
case 'G': // float
|
|
case 'g': // float
|
|
h = (f[0] == 'G') ? hexu : hex;
|
|
fv = va_arg(va, double);
|
|
if (pr == -1)
|
|
pr = 6;
|
|
else if (pr == 0)
|
|
pr = 1; // default is 6
|
|
// read the double into a string
|
|
if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000))
|
|
fl |= STBSP__NEGATIVE;
|
|
|
|
// clamp the precision and delete extra zeros after clamp
|
|
n = pr;
|
|
if (l > (stbsp__uint32)pr)
|
|
l = pr;
|
|
while ((l > 1) && (pr) && (sn[l - 1] == '0')) {
|
|
--pr;
|
|
--l;
|
|
}
|
|
|
|
// should we use %e
|
|
if ((dp <= -4) || (dp > (stbsp__int32)n)) {
|
|
if (pr > (stbsp__int32)l)
|
|
pr = l - 1;
|
|
else if (pr)
|
|
--pr; // when using %e, there is one digit before the decimal
|
|
goto doexpfromg;
|
|
}
|
|
// this is the insane action to get the pr to match %g semantics for %f
|
|
if (dp > 0) {
|
|
pr = (dp < (stbsp__int32)l) ? l - dp : 0;
|
|
} else {
|
|
pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr);
|
|
}
|
|
goto dofloatfromg;
|
|
|
|
case 'E': // float
|
|
case 'e': // float
|
|
h = (f[0] == 'E') ? hexu : hex;
|
|
fv = va_arg(va, double);
|
|
if (pr == -1)
|
|
pr = 6; // default is 6
|
|
// read the double into a string
|
|
if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000))
|
|
fl |= STBSP__NEGATIVE;
|
|
doexpfromg:
|
|
tail[0] = 0;
|
|
stbsp__lead_sign(fl, lead);
|
|
if (dp == STBSP__SPECIAL) {
|
|
s = (char *)sn;
|
|
cs = 0;
|
|
pr = 0;
|
|
goto scopy;
|
|
}
|
|
s = num + 64;
|
|
// handle leading chars
|
|
*s++ = sn[0];
|
|
|
|
if (pr)
|
|
*s++ = stbsp__period;
|
|
|
|
// handle after decimal
|
|
if ((l - 1) > (stbsp__uint32)pr)
|
|
l = pr + 1;
|
|
for (n = 1; n < l; n++)
|
|
*s++ = sn[n];
|
|
// trailing zeros
|
|
tz = pr - (l - 1);
|
|
pr = 0;
|
|
// dump expo
|
|
tail[1] = h[0xe];
|
|
dp -= 1;
|
|
if (dp < 0) {
|
|
tail[2] = '-';
|
|
dp = -dp;
|
|
} else
|
|
tail[2] = '+';
|
|
#ifdef STB_SPRINTF_MSVC_MODE
|
|
n = 5;
|
|
#else
|
|
n = (dp >= 100) ? 5 : 4;
|
|
#endif
|
|
tail[0] = (char)n;
|
|
for (;;) {
|
|
tail[n] = '0' + dp % 10;
|
|
if (n <= 3)
|
|
break;
|
|
--n;
|
|
dp /= 10;
|
|
}
|
|
cs = 1 + (3 << 24); // how many tens
|
|
goto flt_lead;
|
|
|
|
case 'f': // float
|
|
fv = va_arg(va, double);
|
|
doafloat:
|
|
// do kilos
|
|
if (fl & STBSP__METRIC_SUFFIX) {
|
|
double divisor;
|
|
divisor = 1000.0f;
|
|
if (fl & STBSP__METRIC_1024)
|
|
divisor = 1024.0;
|
|
while (fl < 0x4000000) {
|
|
if ((fv < divisor) && (fv > -divisor))
|
|
break;
|
|
fv /= divisor;
|
|
fl += 0x1000000;
|
|
}
|
|
}
|
|
if (pr == -1)
|
|
pr = 6; // default is 6
|
|
// read the double into a string
|
|
if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr))
|
|
fl |= STBSP__NEGATIVE;
|
|
dofloatfromg:
|
|
tail[0] = 0;
|
|
stbsp__lead_sign(fl, lead);
|
|
if (dp == STBSP__SPECIAL) {
|
|
s = (char *)sn;
|
|
cs = 0;
|
|
pr = 0;
|
|
goto scopy;
|
|
}
|
|
s = num + 64;
|
|
|
|
// handle the three decimal varieties
|
|
if (dp <= 0) {
|
|
stbsp__int32 i;
|
|
// handle 0.000*000xxxx
|
|
*s++ = '0';
|
|
if (pr)
|
|
*s++ = stbsp__period;
|
|
n = -dp;
|
|
if ((stbsp__int32)n > pr)
|
|
n = pr;
|
|
i = n;
|
|
while (i) {
|
|
if ((((stbsp__uintptr)s) & 3) == 0)
|
|
break;
|
|
*s++ = '0';
|
|
--i;
|
|
}
|
|
while (i >= 4) {
|
|
*(stbsp__uint32 *)s = 0x30303030;
|
|
s += 4;
|
|
i -= 4;
|
|
}
|
|
while (i) {
|
|
*s++ = '0';
|
|
--i;
|
|
}
|
|
if ((stbsp__int32)(l + n) > pr)
|
|
l = pr - n;
|
|
i = l;
|
|
while (i) {
|
|
*s++ = *sn++;
|
|
--i;
|
|
}
|
|
tz = pr - (n + l);
|
|
cs = 1 + (3 << 24); // how many tens did we write (for commas below)
|
|
} else {
|
|
cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0;
|
|
if ((stbsp__uint32)dp >= l) {
|
|
// handle xxxx000*000.0
|
|
n = 0;
|
|
for (;;) {
|
|
if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
|
|
cs = 0;
|
|
*s++ = stbsp__comma;
|
|
} else {
|
|
*s++ = sn[n];
|
|
++n;
|
|
if (n >= l)
|
|
break;
|
|
}
|
|
}
|
|
if (n < (stbsp__uint32)dp) {
|
|
n = dp - n;
|
|
if ((fl & STBSP__TRIPLET_COMMA) == 0) {
|
|
while (n) {
|
|
if ((((stbsp__uintptr)s) & 3) == 0)
|
|
break;
|
|
*s++ = '0';
|
|
--n;
|
|
}
|
|
while (n >= 4) {
|
|
*(stbsp__uint32 *)s = 0x30303030;
|
|
s += 4;
|
|
n -= 4;
|
|
}
|
|
}
|
|
while (n) {
|
|
if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
|
|
cs = 0;
|
|
*s++ = stbsp__comma;
|
|
} else {
|
|
*s++ = '0';
|
|
--n;
|
|
}
|
|
}
|
|
}
|
|
cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens
|
|
if (pr) {
|
|
*s++ = stbsp__period;
|
|
tz = pr;
|
|
}
|
|
} else {
|
|
// handle xxxxx.xxxx000*000
|
|
n = 0;
|
|
for (;;) {
|
|
if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
|
|
cs = 0;
|
|
*s++ = stbsp__comma;
|
|
} else {
|
|
*s++ = sn[n];
|
|
++n;
|
|
if (n >= (stbsp__uint32)dp)
|
|
break;
|
|
}
|
|
}
|
|
cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens
|
|
if (pr)
|
|
*s++ = stbsp__period;
|
|
if ((l - dp) > (stbsp__uint32)pr)
|
|
l = pr + dp;
|
|
while (n < l) {
|
|
*s++ = sn[n];
|
|
++n;
|
|
}
|
|
tz = pr - (l - dp);
|
|
}
|
|
}
|
|
pr = 0;
|
|
|
|
// handle k,m,g,t
|
|
if (fl & STBSP__METRIC_SUFFIX) {
|
|
char idx;
|
|
idx = 1;
|
|
if (fl & STBSP__METRIC_NOSPACE)
|
|
idx = 0;
|
|
tail[0] = idx;
|
|
tail[1] = ' ';
|
|
{
|
|
if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'.
|
|
if (fl & STBSP__METRIC_1024)
|
|
tail[idx + 1] = "_KMGT"[fl >> 24];
|
|
else
|
|
tail[idx + 1] = "_kMGT"[fl >> 24];
|
|
idx++;
|
|
// If printing kibits and not in jedec, add the 'i'.
|
|
if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) {
|
|
tail[idx + 1] = 'i';
|
|
idx++;
|
|
}
|
|
tail[0] = idx;
|
|
}
|
|
}
|
|
};
|
|
|
|
flt_lead:
|
|
// get the length that we copied
|
|
l = (stbsp__uint32)(s - (num + 64));
|
|
s = num + 64;
|
|
goto scopy;
|
|
#endif
|
|
|
|
case 'B': // upper binary
|
|
case 'b': // lower binary
|
|
h = (f[0] == 'B') ? hexu : hex;
|
|
lead[0] = 0;
|
|
if (fl & STBSP__LEADING_0X) {
|
|
lead[0] = 2;
|
|
lead[1] = '0';
|
|
lead[2] = h[0xb];
|
|
}
|
|
l = (8 << 4) | (1 << 8);
|
|
goto radixnum;
|
|
|
|
case 'o': // octal
|
|
h = hexu;
|
|
lead[0] = 0;
|
|
if (fl & STBSP__LEADING_0X) {
|
|
lead[0] = 1;
|
|
lead[1] = '0';
|
|
}
|
|
l = (3 << 4) | (3 << 8);
|
|
goto radixnum;
|
|
|
|
case 'p': // pointer
|
|
fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0;
|
|
pr = sizeof(void *) * 2;
|
|
fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros
|
|
// fall through - to X
|
|
|
|
case 'X': // upper hex
|
|
case 'x': // lower hex
|
|
h = (f[0] == 'X') ? hexu : hex;
|
|
l = (4 << 4) | (4 << 8);
|
|
lead[0] = 0;
|
|
if (fl & STBSP__LEADING_0X) {
|
|
lead[0] = 2;
|
|
lead[1] = '0';
|
|
lead[2] = h[16];
|
|
}
|
|
radixnum:
|
|
// get the number
|
|
if (fl & STBSP__INTMAX)
|
|
n64 = va_arg(va, stbsp__uint64);
|
|
else
|
|
n64 = va_arg(va, stbsp__uint32);
|
|
|
|
s = num + STBSP__NUMSZ;
|
|
dp = 0;
|
|
// clear tail, and clear leading if value is zero
|
|
tail[0] = 0;
|
|
if (n64 == 0) {
|
|
lead[0] = 0;
|
|
if (pr == 0) {
|
|
l = 0;
|
|
cs = 0;
|
|
goto scopy;
|
|
}
|
|
}
|
|
// convert to string
|
|
for (;;) {
|
|
*--s = h[n64 & ((1 << (l >> 8)) - 1)];
|
|
n64 >>= (l >> 8);
|
|
if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr)))
|
|
break;
|
|
if (fl & STBSP__TRIPLET_COMMA) {
|
|
++l;
|
|
if ((l & 15) == ((l >> 4) & 15)) {
|
|
l &= ~15;
|
|
*--s = stbsp__comma;
|
|
}
|
|
}
|
|
};
|
|
// get the tens and the comma pos
|
|
cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24);
|
|
// get the length that we copied
|
|
l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);
|
|
// copy it
|
|
goto scopy;
|
|
|
|
case 'u': // unsigned
|
|
case 'i':
|
|
case 'd': // integer
|
|
// get the integer and abs it
|
|
if (fl & STBSP__INTMAX) {
|
|
stbsp__int64 i64 = va_arg(va, stbsp__int64);
|
|
n64 = (stbsp__uint64)i64;
|
|
if ((f[0] != 'u') && (i64 < 0)) {
|
|
n64 = (stbsp__uint64)-i64;
|
|
fl |= STBSP__NEGATIVE;
|
|
}
|
|
} else {
|
|
stbsp__int32 i = va_arg(va, stbsp__int32);
|
|
n64 = (stbsp__uint32)i;
|
|
if ((f[0] != 'u') && (i < 0)) {
|
|
n64 = (stbsp__uint32)-i;
|
|
fl |= STBSP__NEGATIVE;
|
|
}
|
|
}
|
|
|
|
#ifndef STB_SPRINTF_NOFLOAT
|
|
if (fl & STBSP__METRIC_SUFFIX) {
|
|
if (n64 < 1024)
|
|
pr = 0;
|
|
else if (pr == -1)
|
|
pr = 1;
|
|
fv = (double)(stbsp__int64)n64;
|
|
goto doafloat;
|
|
}
|
|
#endif
|
|
|
|
// convert to string
|
|
s = num + STBSP__NUMSZ;
|
|
l = 0;
|
|
|
|
for (;;) {
|
|
// do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)
|
|
char *o = s - 8;
|
|
if (n64 >= 100000000) {
|
|
n = (stbsp__uint32)(n64 % 100000000);
|
|
n64 /= 100000000;
|
|
} else {
|
|
n = (stbsp__uint32)n64;
|
|
n64 = 0;
|
|
}
|
|
if ((fl & STBSP__TRIPLET_COMMA) == 0) {
|
|
do {
|
|
s -= 2;
|
|
*(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];
|
|
n /= 100;
|
|
} while (n);
|
|
}
|
|
while (n) {
|
|
if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {
|
|
l = 0;
|
|
*--s = stbsp__comma;
|
|
--o;
|
|
} else {
|
|
*--s = (char)(n % 10) + '0';
|
|
n /= 10;
|
|
}
|
|
}
|
|
if (n64 == 0) {
|
|
if ((s[0] == '0') && (s != (num + STBSP__NUMSZ)))
|
|
++s;
|
|
break;
|
|
}
|
|
while (s != o)
|
|
if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {
|
|
l = 0;
|
|
*--s = stbsp__comma;
|
|
--o;
|
|
} else {
|
|
*--s = '0';
|
|
}
|
|
}
|
|
|
|
tail[0] = 0;
|
|
stbsp__lead_sign(fl, lead);
|
|
|
|
// get the length that we copied
|
|
l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);
|
|
if (l == 0) {
|
|
*--s = '0';
|
|
l = 1;
|
|
}
|
|
cs = l + (3 << 24);
|
|
if (pr < 0)
|
|
pr = 0;
|
|
|
|
scopy:
|
|
// get fw=leading/trailing space, pr=leading zeros
|
|
if (pr < (stbsp__int32)l)
|
|
pr = l;
|
|
n = pr + lead[0] + tail[0] + tz;
|
|
if (fw < (stbsp__int32)n)
|
|
fw = n;
|
|
fw -= n;
|
|
pr -= l;
|
|
|
|
// handle right justify and leading zeros
|
|
if ((fl & STBSP__LEFTJUST) == 0) {
|
|
if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr
|
|
{
|
|
pr = (fw > pr) ? fw : pr;
|
|
fw = 0;
|
|
} else {
|
|
fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas
|
|
}
|
|
}
|
|
|
|
// copy the spaces and/or zeros
|
|
if (fw + pr) {
|
|
stbsp__int32 i;
|
|
stbsp__uint32 c;
|
|
|
|
// copy leading spaces (or when doing %8.4d stuff)
|
|
if ((fl & STBSP__LEFTJUST) == 0)
|
|
while (fw > 0) {
|
|
stbsp__cb_buf_clamp(i, fw);
|
|
fw -= i;
|
|
while (i) {
|
|
if ((((stbsp__uintptr)bf) & 3) == 0)
|
|
break;
|
|
*bf++ = ' ';
|
|
--i;
|
|
}
|
|
while (i >= 4) {
|
|
*(stbsp__uint32 *)bf = 0x20202020;
|
|
bf += 4;
|
|
i -= 4;
|
|
}
|
|
while (i) {
|
|
*bf++ = ' ';
|
|
--i;
|
|
}
|
|
stbsp__chk_cb_buf(1);
|
|
}
|
|
|
|
// copy leader
|
|
sn = lead + 1;
|
|
while (lead[0]) {
|
|
stbsp__cb_buf_clamp(i, lead[0]);
|
|
lead[0] -= (char)i;
|
|
while (i) {
|
|
*bf++ = *sn++;
|
|
--i;
|
|
}
|
|
stbsp__chk_cb_buf(1);
|
|
}
|
|
|
|
// copy leading zeros
|
|
c = cs >> 24;
|
|
cs &= 0xffffff;
|
|
cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0;
|
|
while (pr > 0) {
|
|
stbsp__cb_buf_clamp(i, pr);
|
|
pr -= i;
|
|
if ((fl & STBSP__TRIPLET_COMMA) == 0) {
|
|
while (i) {
|
|
if ((((stbsp__uintptr)bf) & 3) == 0)
|
|
break;
|
|
*bf++ = '0';
|
|
--i;
|
|
}
|
|
while (i >= 4) {
|
|
*(stbsp__uint32 *)bf = 0x30303030;
|
|
bf += 4;
|
|
i -= 4;
|
|
}
|
|
}
|
|
while (i) {
|
|
if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) {
|
|
cs = 0;
|
|
*bf++ = stbsp__comma;
|
|
} else
|
|
*bf++ = '0';
|
|
--i;
|
|
}
|
|
stbsp__chk_cb_buf(1);
|
|
}
|
|
}
|
|
|
|
// copy leader if there is still one
|
|
sn = lead + 1;
|
|
while (lead[0]) {
|
|
stbsp__int32 i;
|
|
stbsp__cb_buf_clamp(i, lead[0]);
|
|
lead[0] -= (char)i;
|
|
while (i) {
|
|
*bf++ = *sn++;
|
|
--i;
|
|
}
|
|
stbsp__chk_cb_buf(1);
|
|
}
|
|
|
|
// copy the string
|
|
n = l;
|
|
while (n) {
|
|
stbsp__int32 i;
|
|
stbsp__cb_buf_clamp(i, n);
|
|
n -= i;
|
|
STBSP__UNALIGNED(while (i >= 4) {
|
|
*(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s;
|
|
bf += 4;
|
|
s += 4;
|
|
i -= 4;
|
|
})
|
|
while (i) {
|
|
*bf++ = *s++;
|
|
--i;
|
|
}
|
|
stbsp__chk_cb_buf(1);
|
|
}
|
|
|
|
// copy trailing zeros
|
|
while (tz) {
|
|
stbsp__int32 i;
|
|
stbsp__cb_buf_clamp(i, tz);
|
|
tz -= i;
|
|
while (i) {
|
|
if ((((stbsp__uintptr)bf) & 3) == 0)
|
|
break;
|
|
*bf++ = '0';
|
|
--i;
|
|
}
|
|
while (i >= 4) {
|
|
*(stbsp__uint32 *)bf = 0x30303030;
|
|
bf += 4;
|
|
i -= 4;
|
|
}
|
|
while (i) {
|
|
*bf++ = '0';
|
|
--i;
|
|
}
|
|
stbsp__chk_cb_buf(1);
|
|
}
|
|
|
|
// copy tail if there is one
|
|
sn = tail + 1;
|
|
while (tail[0]) {
|
|
stbsp__int32 i;
|
|
stbsp__cb_buf_clamp(i, tail[0]);
|
|
tail[0] -= (char)i;
|
|
while (i) {
|
|
*bf++ = *sn++;
|
|
--i;
|
|
}
|
|
stbsp__chk_cb_buf(1);
|
|
}
|
|
|
|
// handle the left justify
|
|
if (fl & STBSP__LEFTJUST)
|
|
if (fw > 0) {
|
|
while (fw) {
|
|
stbsp__int32 i;
|
|
stbsp__cb_buf_clamp(i, fw);
|
|
fw -= i;
|
|
while (i) {
|
|
if ((((stbsp__uintptr)bf) & 3) == 0)
|
|
break;
|
|
*bf++ = ' ';
|
|
--i;
|
|
}
|
|
while (i >= 4) {
|
|
*(stbsp__uint32 *)bf = 0x20202020;
|
|
bf += 4;
|
|
i -= 4;
|
|
}
|
|
while (i--)
|
|
*bf++ = ' ';
|
|
stbsp__chk_cb_buf(1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: // unknown, just copy code
|
|
s = num + STBSP__NUMSZ - 1;
|
|
*s = f[0];
|
|
l = 1;
|
|
fw = fl = 0;
|
|
lead[0] = 0;
|
|
tail[0] = 0;
|
|
pr = 0;
|
|
dp = 0;
|
|
cs = 0;
|
|
goto scopy;
|
|
}
|
|
++f;
|
|
}
|
|
endfmt:
|
|
|
|
if (!callback)
|
|
*bf = 0;
|
|
else
|
|
stbsp__flush_cb();
|
|
|
|
done:
|
|
return tlen + (int)(bf - buf);
|
|
}
|
|
|
|
// cleanup
|
|
#undef STBSP__LEFTJUST
|
|
#undef STBSP__LEADINGPLUS
|
|
#undef STBSP__LEADINGSPACE
|
|
#undef STBSP__LEADING_0X
|
|
#undef STBSP__LEADINGZERO
|
|
#undef STBSP__INTMAX
|
|
#undef STBSP__TRIPLET_COMMA
|
|
#undef STBSP__NEGATIVE
|
|
#undef STBSP__METRIC_SUFFIX
|
|
#undef STBSP__NUMSZ
|
|
#undef stbsp__chk_cb_bufL
|
|
#undef stbsp__chk_cb_buf
|
|
#undef stbsp__flush_cb
|
|
#undef stbsp__cb_buf_clamp
|
|
|
|
// ============================================================================
|
|
// wrapper functions
|
|
|
|
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...)
|
|
{
|
|
int result;
|
|
va_list va;
|
|
va_start(va, fmt);
|
|
result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
|
|
va_end(va);
|
|
return result;
|
|
}
|
|
|
|
typedef struct stbsp__context {
|
|
char *buf;
|
|
int count;
|
|
int length;
|
|
char tmp[STB_SPRINTF_MIN];
|
|
} stbsp__context;
|
|
|
|
static char *stbsp__clamp_callback(const char *buf, void *user, int len)
|
|
{
|
|
stbsp__context *c = (stbsp__context *)user;
|
|
c->length += len;
|
|
|
|
if (len > c->count)
|
|
len = c->count;
|
|
|
|
if (len) {
|
|
if (buf != c->buf) {
|
|
const char *s, *se;
|
|
char *d;
|
|
d = c->buf;
|
|
s = buf;
|
|
se = buf + len;
|
|
do {
|
|
*d++ = *s++;
|
|
} while (s < se);
|
|
}
|
|
c->buf += len;
|
|
c->count -= len;
|
|
}
|
|
|
|
if (c->count <= 0)
|
|
return c->tmp;
|
|
return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can
|
|
}
|
|
|
|
static char * stbsp__count_clamp_callback( const char * buf, void * user, int len )
|
|
{
|
|
stbsp__context * c = (stbsp__context*)user;
|
|
(void) sizeof(buf);
|
|
|
|
c->length += len;
|
|
return c->tmp; // go direct into buffer if you can
|
|
}
|
|
|
|
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va )
|
|
{
|
|
stbsp__context c;
|
|
|
|
if ( (count == 0) && !buf )
|
|
{
|
|
c.length = 0;
|
|
|
|
STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va );
|
|
}
|
|
else
|
|
{
|
|
int l;
|
|
|
|
c.buf = buf;
|
|
c.count = count;
|
|
c.length = 0;
|
|
|
|
STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va );
|
|
|
|
// zero-terminate
|
|
l = (int)( c.buf - buf );
|
|
if ( l >= count ) // should never be greater, only equal (or less) than count
|
|
l = count - 1;
|
|
buf[l] = 0;
|
|
}
|
|
|
|
return c.length;
|
|
}
|
|
|
|
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...)
|
|
{
|
|
int result;
|
|
va_list va;
|
|
va_start(va, fmt);
|
|
|
|
result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va);
|
|
va_end(va);
|
|
|
|
return result;
|
|
}
|
|
|
|
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va)
|
|
{
|
|
return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
|
|
}
|
|
|
|
// =======================================================================
|
|
// low level float utility functions
|
|
|
|
#ifndef STB_SPRINTF_NOFLOAT
|
|
|
|
// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)
|
|
#define STBSP__COPYFP(dest, src) \
|
|
{ \
|
|
int cn; \
|
|
for (cn = 0; cn < 8; cn++) \
|
|
((char *)&dest)[cn] = ((char *)&src)[cn]; \
|
|
}
|
|
|
|
// get float info
|
|
static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value)
|
|
{
|
|
double d;
|
|
stbsp__int64 b = 0;
|
|
|
|
// load value and round at the frac_digits
|
|
d = value;
|
|
|
|
STBSP__COPYFP(b, d);
|
|
|
|
*bits = b & ((((stbsp__uint64)1) << 52) - 1);
|
|
*expo = (stbsp__int32)(((b >> 52) & 2047) - 1023);
|
|
|
|
return (stbsp__int32)((stbsp__uint64) b >> 63);
|
|
}
|
|
|
|
static double const stbsp__bot[23] = {
|
|
1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011,
|
|
1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022
|
|
};
|
|
static double const stbsp__negbot[22] = {
|
|
1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011,
|
|
1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022
|
|
};
|
|
static double const stbsp__negboterr[22] = {
|
|
-5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023,
|
|
4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029,
|
|
-3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035,
|
|
2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039
|
|
};
|
|
static double const stbsp__top[13] = {
|
|
1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299
|
|
};
|
|
static double const stbsp__negtop[13] = {
|
|
1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299
|
|
};
|
|
static double const stbsp__toperr[13] = {
|
|
8388608,
|
|
6.8601809640529717e+028,
|
|
-7.253143638152921e+052,
|
|
-4.3377296974619174e+075,
|
|
-1.5559416129466825e+098,
|
|
-3.2841562489204913e+121,
|
|
-3.7745893248228135e+144,
|
|
-1.7356668416969134e+167,
|
|
-3.8893577551088374e+190,
|
|
-9.9566444326005119e+213,
|
|
6.3641293062232429e+236,
|
|
-5.2069140800249813e+259,
|
|
-5.2504760255204387e+282
|
|
};
|
|
static double const stbsp__negtoperr[13] = {
|
|
3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109,
|
|
-5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201,
|
|
7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293,
|
|
8.0970921678014997e-317
|
|
};
|
|
|
|
#if defined(_MSC_VER) && (_MSC_VER <= 1200)
|
|
static stbsp__uint64 const stbsp__powten[20] = {
|
|
1,
|
|
10,
|
|
100,
|
|
1000,
|
|
10000,
|
|
100000,
|
|
1000000,
|
|
10000000,
|
|
100000000,
|
|
1000000000,
|
|
10000000000,
|
|
100000000000,
|
|
1000000000000,
|
|
10000000000000,
|
|
100000000000000,
|
|
1000000000000000,
|
|
10000000000000000,
|
|
100000000000000000,
|
|
1000000000000000000,
|
|
10000000000000000000U
|
|
};
|
|
#define stbsp__tento19th ((stbsp__uint64)1000000000000000000)
|
|
#else
|
|
static stbsp__uint64 const stbsp__powten[20] = {
|
|
1,
|
|
10,
|
|
100,
|
|
1000,
|
|
10000,
|
|
100000,
|
|
1000000,
|
|
10000000,
|
|
100000000,
|
|
1000000000,
|
|
10000000000ULL,
|
|
100000000000ULL,
|
|
1000000000000ULL,
|
|
10000000000000ULL,
|
|
100000000000000ULL,
|
|
1000000000000000ULL,
|
|
10000000000000000ULL,
|
|
100000000000000000ULL,
|
|
1000000000000000000ULL,
|
|
10000000000000000000ULL
|
|
};
|
|
#define stbsp__tento19th (1000000000000000000ULL)
|
|
#endif
|
|
|
|
#define stbsp__ddmulthi(oh, ol, xh, yh) \
|
|
{ \
|
|
double ahi = 0, alo, bhi = 0, blo; \
|
|
stbsp__int64 bt; \
|
|
oh = xh * yh; \
|
|
STBSP__COPYFP(bt, xh); \
|
|
bt &= ((~(stbsp__uint64)0) << 27); \
|
|
STBSP__COPYFP(ahi, bt); \
|
|
alo = xh - ahi; \
|
|
STBSP__COPYFP(bt, yh); \
|
|
bt &= ((~(stbsp__uint64)0) << 27); \
|
|
STBSP__COPYFP(bhi, bt); \
|
|
blo = yh - bhi; \
|
|
ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \
|
|
}
|
|
|
|
#define stbsp__ddtoS64(ob, xh, xl) \
|
|
{ \
|
|
double ahi = 0, alo, vh, t; \
|
|
ob = (stbsp__int64)xh; \
|
|
vh = (double)ob; \
|
|
ahi = (xh - vh); \
|
|
t = (ahi - xh); \
|
|
alo = (xh - (ahi - t)) - (vh + t); \
|
|
ob += (stbsp__int64)(ahi + alo + xl); \
|
|
}
|
|
|
|
#define stbsp__ddrenorm(oh, ol) \
|
|
{ \
|
|
double s; \
|
|
s = oh + ol; \
|
|
ol = ol - (s - oh); \
|
|
oh = s; \
|
|
}
|
|
|
|
#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh);
|
|
|
|
#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl);
|
|
|
|
static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350
|
|
{
|
|
double ph, pl;
|
|
if ((power >= 0) && (power <= 22)) {
|
|
stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]);
|
|
} else {
|
|
stbsp__int32 e, et, eb;
|
|
double p2h, p2l;
|
|
|
|
e = power;
|
|
if (power < 0)
|
|
e = -e;
|
|
et = (e * 0x2c9) >> 14; /* %23 */
|
|
if (et > 13)
|
|
et = 13;
|
|
eb = e - (et * 23);
|
|
|
|
ph = d;
|
|
pl = 0.0;
|
|
if (power < 0) {
|
|
if (eb) {
|
|
--eb;
|
|
stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]);
|
|
stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]);
|
|
}
|
|
if (et) {
|
|
stbsp__ddrenorm(ph, pl);
|
|
--et;
|
|
stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]);
|
|
stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]);
|
|
ph = p2h;
|
|
pl = p2l;
|
|
}
|
|
} else {
|
|
if (eb) {
|
|
e = eb;
|
|
if (eb > 22)
|
|
eb = 22;
|
|
e -= eb;
|
|
stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]);
|
|
if (e) {
|
|
stbsp__ddrenorm(ph, pl);
|
|
stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]);
|
|
stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl);
|
|
ph = p2h;
|
|
pl = p2l;
|
|
}
|
|
}
|
|
if (et) {
|
|
stbsp__ddrenorm(ph, pl);
|
|
--et;
|
|
stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]);
|
|
stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]);
|
|
ph = p2h;
|
|
pl = p2l;
|
|
}
|
|
}
|
|
}
|
|
stbsp__ddrenorm(ph, pl);
|
|
*ohi = ph;
|
|
*olo = pl;
|
|
}
|
|
|
|
// given a float value, returns the significant bits in bits, and the position of the
|
|
// decimal point in decimal_pos. +/-INF and NAN are specified by special values
|
|
// returned in the decimal_pos parameter.
|
|
// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000
|
|
static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits)
|
|
{
|
|
double d;
|
|
stbsp__int64 bits = 0;
|
|
stbsp__int32 expo, e, ng, tens;
|
|
|
|
d = value;
|
|
STBSP__COPYFP(bits, d);
|
|
expo = (stbsp__int32)((bits >> 52) & 2047);
|
|
ng = (stbsp__int32)((stbsp__uint64) bits >> 63);
|
|
if (ng)
|
|
d = -d;
|
|
|
|
if (expo == 2047) // is nan or inf?
|
|
{
|
|
*start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf";
|
|
*decimal_pos = STBSP__SPECIAL;
|
|
*len = 3;
|
|
return ng;
|
|
}
|
|
|
|
if (expo == 0) // is zero or denormal
|
|
{
|
|
if (((stbsp__uint64) bits << 1) == 0) // do zero
|
|
{
|
|
*decimal_pos = 1;
|
|
*start = out;
|
|
out[0] = '0';
|
|
*len = 1;
|
|
return ng;
|
|
}
|
|
// find the right expo for denormals
|
|
{
|
|
stbsp__int64 v = ((stbsp__uint64)1) << 51;
|
|
while ((bits & v) == 0) {
|
|
--expo;
|
|
v >>= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// find the decimal exponent as well as the decimal bits of the value
|
|
{
|
|
double ph, pl;
|
|
|
|
// log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046
|
|
tens = expo - 1023;
|
|
tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1);
|
|
|
|
// move the significant bits into position and stick them into an int
|
|
stbsp__raise_to_power10(&ph, &pl, d, 18 - tens);
|
|
|
|
// get full as much precision from double-double as possible
|
|
stbsp__ddtoS64(bits, ph, pl);
|
|
|
|
// check if we undershot
|
|
if (((stbsp__uint64)bits) >= stbsp__tento19th)
|
|
++tens;
|
|
}
|
|
|
|
// now do the rounding in integer land
|
|
frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits);
|
|
if ((frac_digits < 24)) {
|
|
stbsp__uint32 dg = 1;
|
|
if ((stbsp__uint64)bits >= stbsp__powten[9])
|
|
dg = 10;
|
|
while ((stbsp__uint64)bits >= stbsp__powten[dg]) {
|
|
++dg;
|
|
if (dg == 20)
|
|
goto noround;
|
|
}
|
|
if (frac_digits < dg) {
|
|
stbsp__uint64 r;
|
|
// add 0.5 at the right position and round
|
|
e = dg - frac_digits;
|
|
if ((stbsp__uint32)e >= 24)
|
|
goto noround;
|
|
r = stbsp__powten[e];
|
|
bits = bits + (r / 2);
|
|
if ((stbsp__uint64)bits >= stbsp__powten[dg])
|
|
++tens;
|
|
bits /= r;
|
|
}
|
|
noround:;
|
|
}
|
|
|
|
// kill long trailing runs of zeros
|
|
if (bits) {
|
|
stbsp__uint32 n;
|
|
for (;;) {
|
|
if (bits <= 0xffffffff)
|
|
break;
|
|
if (bits % 1000)
|
|
goto donez;
|
|
bits /= 1000;
|
|
}
|
|
n = (stbsp__uint32)bits;
|
|
while ((n % 1000) == 0)
|
|
n /= 1000;
|
|
bits = n;
|
|
donez:;
|
|
}
|
|
|
|
// convert to string
|
|
out += 64;
|
|
e = 0;
|
|
for (;;) {
|
|
stbsp__uint32 n;
|
|
char *o = out - 8;
|
|
// do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)
|
|
if (bits >= 100000000) {
|
|
n = (stbsp__uint32)(bits % 100000000);
|
|
bits /= 100000000;
|
|
} else {
|
|
n = (stbsp__uint32)bits;
|
|
bits = 0;
|
|
}
|
|
while (n) {
|
|
out -= 2;
|
|
*(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];
|
|
n /= 100;
|
|
e += 2;
|
|
}
|
|
if (bits == 0) {
|
|
if ((e) && (out[0] == '0')) {
|
|
++out;
|
|
--e;
|
|
}
|
|
break;
|
|
}
|
|
while (out != o) {
|
|
*--out = '0';
|
|
++e;
|
|
}
|
|
}
|
|
|
|
*decimal_pos = tens;
|
|
*start = out;
|
|
*len = e;
|
|
return ng;
|
|
}
|
|
|
|
#undef stbsp__ddmulthi
|
|
#undef stbsp__ddrenorm
|
|
#undef stbsp__ddmultlo
|
|
#undef stbsp__ddmultlos
|
|
#undef STBSP__SPECIAL
|
|
#undef STBSP__COPYFP
|
|
|
|
#endif // STB_SPRINTF_NOFLOAT
|
|
|
|
// clean up
|
|
#undef stbsp__uint16
|
|
#undef stbsp__uint32
|
|
#undef stbsp__int32
|
|
#undef stbsp__uint64
|
|
#undef stbsp__int64
|
|
#undef STBSP__UNALIGNED
|
|
|
|
#endif // STB_SPRINTF_IMPLEMENTATION
|
|
|
|
/*
|
|
------------------------------------------------------------------------------
|
|
This software is available under 2 licenses -- choose whichever you prefer.
|
|
------------------------------------------------------------------------------
|
|
ALTERNATIVE A - MIT License
|
|
Copyright (c) 2017 Sean Barrett
|
|
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.
|
|
------------------------------------------------------------------------------
|
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
|
This is free and unencumbered software released into the public domain.
|
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
|
software, either in source code form or as a compiled binary, for any purpose,
|
|
commercial or non-commercial, and by any means.
|
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
|
software dedicate any and all copyright interest in the software to the public
|
|
domain. We make this dedication for the benefit of the public at large and to
|
|
the detriment of our heirs and successors. We intend this dedication to be an
|
|
overt act of relinquishment in perpetuity of all present and future rights to
|
|
this software under copyright law.
|
|
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 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.
|
|
------------------------------------------------------------------------------
|
|
*/
|
|
DN_GCC_WARNING_POP
|
|
DN_MSVC_WARNING_POP
|
|
|
|
DN_API void DN_BeginFrame ();
|
|
|
|
#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__)
|
|
|
|
DN_API bool DN_MemEq (void const *lhs, DN_USize lhs_size, void const *rhs, DN_USize rhs_size);
|
|
|
|
DN_API DN_U64 DN_AtomicSetValue64 (DN_U64 volatile *target, DN_U64 value);
|
|
DN_API DN_U32 DN_AtomicSetValue32 (DN_U32 volatile *target, DN_U32 value);
|
|
|
|
DN_API DN_CPUIDResult DN_CPUID (DN_CPUIDArgs args);
|
|
DN_API DN_USize DN_CPUHasFeatureArray (DN_CPUReport const *report, DN_CPUFeatureQuery *features, DN_USize features_size);
|
|
DN_API bool DN_CPUHasFeature (DN_CPUReport const *report, DN_CPUFeature feature);
|
|
DN_API bool DN_CPUHasAllFeatures (DN_CPUReport const *report, DN_CPUFeature const *features, DN_USize features_size);
|
|
DN_API void DN_CPUSetFeature (DN_CPUReport *report, DN_CPUFeature feature);
|
|
DN_API DN_CPUReport DN_CPUGetReport ();
|
|
|
|
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);
|
|
|
|
DN_API void DN_BitUnsetInplace (DN_USize *flags, DN_USize bitfield);
|
|
DN_API void DN_BitSetInplace (DN_USize *flags, DN_USize bitfield);
|
|
DN_API bool DN_BitIsSet (DN_USize bits, DN_USize bits_to_set);
|
|
DN_API bool DN_BitIsNotSet (DN_USize bits, DN_USize bits_to_check);
|
|
#define DN_BitClearNextLSB(value) (value) & ((value) - 1)
|
|
|
|
DN_API DN_I64 DN_SafeAddI64 (DN_I64 a, DN_I64 b);
|
|
DN_API DN_I64 DN_SafeMulI64 (DN_I64 a, DN_I64 b);
|
|
|
|
DN_API DN_U64 DN_SafeAddU64 (DN_U64 a, DN_U64 b);
|
|
DN_API DN_U64 DN_SafeMulU64 (DN_U64 a, DN_U64 b);
|
|
|
|
DN_API DN_U64 DN_SafeSubU64 (DN_U64 a, DN_U64 b);
|
|
DN_API DN_U32 DN_SafeSubU32 (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);
|
|
|
|
DN_API void DN_ASanPoisonMemoryRegion (void const volatile *ptr, DN_USize size);
|
|
DN_API void DN_ASanUnpoisonMemoryRegion (void const volatile *ptr, DN_USize size);
|
|
|
|
DN_API DN_F32 DN_EpsilonClampF32 (DN_F32 value, DN_F32 target, DN_F32 epsilon);
|
|
|
|
DN_API DN_Arena DN_ArenaFromBuffer (void *buffer, DN_USize size, DN_ArenaFlags flags);
|
|
DN_API DN_Arena DN_ArenaFromMemFuncs (DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags, DN_ArenaMemFuncs mem_funcs);
|
|
DN_API void DN_ArenaDeinit (DN_Arena *arena);
|
|
DN_API bool DN_ArenaCommit (DN_Arena *arena, DN_U64 size);
|
|
DN_API bool DN_ArenaCommitTo (DN_Arena *arena, DN_U64 pos);
|
|
DN_API bool DN_ArenaGrow (DN_Arena *arena, DN_U64 reserve, DN_U64 commit);
|
|
DN_API void * DN_ArenaAlloc (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem zmem);
|
|
DN_API void * DN_ArenaAllocContiguous (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem zmem);
|
|
DN_API void * DN_ArenaCopy (DN_Arena *arena, void const *data, DN_U64 size, uint8_t align);
|
|
DN_API void DN_ArenaPopTo (DN_Arena *arena, DN_U64 init_used);
|
|
DN_API void DN_ArenaPop (DN_Arena *arena, DN_U64 amount);
|
|
DN_API DN_U64 DN_ArenaPos (DN_Arena const *arena);
|
|
DN_API void DN_ArenaClear (DN_Arena *arena);
|
|
DN_API bool DN_ArenaOwnsPtr (DN_Arena const *arena, void *ptr);
|
|
DN_API DN_Str8x64 DN_ArenaInfoStr8x64 (DN_ArenaInfo info);
|
|
DN_API DN_ArenaStats DN_ArenaSumStatsArray (DN_ArenaStats const *array, DN_USize size);
|
|
DN_API DN_ArenaStats DN_ArenaSumStats (DN_ArenaStats lhs, DN_ArenaStats rhs);
|
|
DN_API DN_ArenaStats DN_ArenaSumArenaArrayToStats(DN_Arena const *array, DN_USize size);
|
|
DN_API DN_ArenaTempMem DN_ArenaTempMemBegin (DN_Arena *arena);
|
|
DN_API void DN_ArenaTempMemEnd (DN_ArenaTempMem mem);
|
|
#define DN_ArenaNew(arena, T, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), zmem)
|
|
#define DN_ArenaNewZ(arena, T) (T *)DN_ArenaAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes)
|
|
|
|
#define DN_ArenaNewContiguous(arena, T, zmem) (T *)DN_ArenaAllocContiguous(arena, sizeof(T), alignof(T), zmem)
|
|
#define DN_ArenaNewContiguousZ(arena, T) (T *)DN_ArenaAllocContiguous(arena, sizeof(T), alignof(T), DN_ZMem_Yes)
|
|
|
|
#define DN_ArenaNewArray(arena, T, count, zmem) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), zmem)
|
|
#define DN_ArenaNewArrayZ(arena, T, count) (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_Yes)
|
|
|
|
#define DN_ArenaNewCopy(arena, T, src) (T *)DN_ArenaCopy (arena, (src), sizeof(T), alignof(T))
|
|
#define DN_ArenaNewArrayCopy(arena, T, src, count) (T *)DN_ArenaCopy (arena, (src), sizeof(T) * (count), alignof(T))
|
|
|
|
DN_API DN_Pool DN_PoolFromArena (DN_Arena *arena, DN_U8 align);
|
|
DN_API bool DN_PoolIsValid (DN_Pool const *pool);
|
|
DN_API void * DN_PoolAlloc (DN_Pool *pool, DN_USize size);
|
|
DN_API void DN_PoolDealloc (DN_Pool *pool, void *ptr);
|
|
DN_API void * DN_PoolCopy (DN_Pool *pool, void const *data, DN_U64 size, uint8_t align);
|
|
#define DN_PoolNew(pool, T) (T *)DN_PoolAlloc(pool, sizeof(T))
|
|
#define DN_PoolNewArray(pool, T, count) (T *)DN_PoolAlloc(pool, count * sizeof(T))
|
|
#define DN_PoolNewCopy(pool, T, src) (T *)DN_PoolCopy (pool, (src), sizeof(T), alignof(T))
|
|
#define DN_PoolNewArrayCopy(pool, T, src, count) (T *)DN_PoolCopy (pool, (src), sizeof(T) * (count), alignof(T))
|
|
|
|
DN_API bool DN_CharIsAlphabet (char ch);
|
|
DN_API bool DN_CharIsDigit (char ch);
|
|
DN_API bool DN_CharIsAlphaNum (char ch);
|
|
DN_API bool DN_CharIsWhitespace (char ch);
|
|
DN_API bool DN_CharIsHex (char ch);
|
|
DN_API char DN_CharToLower (char ch);
|
|
DN_API char DN_CharToUpper (char ch);
|
|
|
|
DN_API DN_U64FromResult DN_U64FromStr8 (DN_Str8 string, char separator);
|
|
DN_API DN_U64FromResult DN_U64FromPtr (void const *data, DN_USize size, char separator);
|
|
DN_API DN_U64 DN_U64FromPtrUnsafe (void const *data, DN_USize size, char separator);
|
|
DN_API DN_U64FromResult DN_U64FromHexPtr (void const *hex, DN_USize hex_count);
|
|
DN_API DN_U64 DN_U64FromHexPtrUnsafe (void const *hex, DN_USize hex_count);
|
|
DN_API DN_U64FromResult DN_U64FromHexStr8 (DN_Str8 hex);
|
|
DN_API DN_U64 DN_U64FromHexStr8Unsafe (DN_Str8 hex);
|
|
DN_API DN_I64FromResult DN_I64FromStr8 (DN_Str8 string, char separator);
|
|
DN_API DN_I64FromResult DN_I64FromPtr (void const *data, DN_USize size, char separator);
|
|
DN_API DN_I64 DN_I64FromPtrUnsafe (void const *data, DN_USize size, char separator);
|
|
|
|
DN_API DN_USize DN_FmtVSize (DN_FMT_ATTRIB char const *fmt, va_list args);
|
|
DN_API DN_USize DN_FmtSize (DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API DN_FmtAppendResult DN_FmtVAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, va_list args);
|
|
DN_API DN_FmtAppendResult DN_FmtAppend (char *buf, DN_USize *buf_size, DN_USize buf_max, char const *fmt, ...);
|
|
DN_API DN_FmtAppendResult DN_FmtAppendTruncate (char *buf, DN_USize *buf_size, DN_USize buf_max, DN_Str8 truncator, char const *fmt, ...);
|
|
DN_API DN_USize DN_CStr8Size (char const *src);
|
|
DN_API DN_USize DN_CStr16Size (wchar_t const *src);
|
|
|
|
#define DN_Str16Lit(string) DN_Str16{(wchar_t *)(string), sizeof(string)/sizeof(string[0]) - 1}
|
|
#define DN_Str8Lit(c_str) DN_Literal(DN_Str8){(char *)(c_str), sizeof(c_str) - 1}
|
|
#define DN_Str8PrintFmt(string) (int)((string).size), (string).data
|
|
#define DN_Str8FromPtr(data, size) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(size)}
|
|
#define DN_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->size)
|
|
DN_API DN_Str8 DN_Str8FromCStr8 (char const *src);
|
|
DN_API DN_Str8 DN_Str8FromArena (DN_Arena *arena, DN_USize size, DN_ZMem z_mem);
|
|
DN_API DN_Str8 DN_Str8FromPool (DN_Pool *pool, DN_USize size);
|
|
DN_API DN_Str8 DN_Str8FromPtrArena (DN_Arena *arena, void const *data, DN_USize size);
|
|
DN_API DN_Str8 DN_Str8FromPtrPool (DN_Pool *pool, void const *data, DN_USize size);
|
|
DN_API DN_Str8 DN_Str8FromStr8Arena (DN_Arena *arena, DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8FromStr8Pool (DN_Pool *pool, DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8FromFmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API DN_Str8 DN_Str8FromFmtVArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args);
|
|
DN_API DN_Str8 DN_Str8FromFmtPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API DN_Str8 DN_Str8FromByteCountType (DN_ByteCountType type);
|
|
DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API DN_Str8x32 DN_Str8x32FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args);
|
|
DN_API DN_Str8x64 DN_Str8x64FromFmt (DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API DN_Str8x64 DN_Str8x64FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args);
|
|
DN_API DN_Str8x128 DN_Str8x128FromFmt (DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API DN_Str8x256 DN_Str8x256FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args);
|
|
DN_API DN_Str8x256 DN_Str8x256FromFmt (DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API DN_Str8x256 DN_Str8x256FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args);
|
|
DN_API DN_Str8x32 DN_Str8x32FromU64 (DN_U64 val, char separator);
|
|
DN_API bool DN_Str8IsAll (DN_Str8 string, DN_Str8IsAllType is_all);
|
|
DN_API char * DN_Str8End (DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8Slice (DN_Str8 string, DN_USize offset, DN_USize size);
|
|
DN_API DN_Str8 DN_Str8Advance (DN_Str8 string, DN_USize amount);
|
|
DN_API DN_Str8 DN_Str8NextLine (DN_Str8 string);
|
|
DN_API DN_Str8BSplitResult DN_Str8BSplitArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size);
|
|
DN_API DN_Str8BSplitResult DN_Str8BSplit (DN_Str8 string, DN_Str8 find);
|
|
DN_API DN_Str8BSplitResult DN_Str8BSplitLastArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size);
|
|
DN_API DN_Str8BSplitResult DN_Str8BSplitLast (DN_Str8 string, DN_Str8 find);
|
|
DN_API DN_USize DN_Str8Split (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitIncludeEmptyStrings mode);
|
|
DN_API DN_Str8SplitResult DN_Str8SplitArena (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
|
|
DN_API DN_Str8FindResult DN_Str8FindStr8Array (DN_Str8 string, DN_Str8 const *find, DN_USize find_size, DN_Str8EqCase eq_case);
|
|
DN_API DN_Str8FindResult DN_Str8FindStr8 (DN_Str8 string, DN_Str8 find, DN_Str8EqCase eq_case);
|
|
DN_API DN_Str8FindResult DN_Str8Find (DN_Str8 string, uint32_t flags);
|
|
DN_API DN_Str8 DN_Str8Segment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char);
|
|
DN_API DN_Str8 DN_Str8ReverseSegment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char);
|
|
DN_API bool DN_Str8Eq (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
|
DN_API bool DN_Str8EqInsensitive (DN_Str8 lhs, DN_Str8 rhs);
|
|
DN_API bool DN_Str8StartsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
|
DN_API bool DN_Str8StartsWithInsensitive(DN_Str8 string, DN_Str8 prefix);
|
|
DN_API bool DN_Str8EndsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
|
DN_API bool DN_Str8EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix);
|
|
DN_API bool DN_Str8HasChar (DN_Str8 string, char ch);
|
|
DN_API DN_Str8 DN_Str8TrimPrefix (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
|
DN_API DN_Str8 DN_Str8TrimHexPrefix (DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
|
DN_API DN_Str8 DN_Str8TrimAround (DN_Str8 string, DN_Str8 trim_string);
|
|
DN_API DN_Str8 DN_Str8TrimHeadWhitespace (DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8TrimTailWhitespace (DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8TrimWhitespaceAround (DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8TrimByteOrderMark (DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path);
|
|
DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path);
|
|
DN_API DN_Str8 DN_Str8FilePathNoExtension (DN_Str8 path);
|
|
DN_API DN_Str8 DN_Str8FileExtension (DN_Str8 path);
|
|
DN_API DN_Str8 DN_Str8FileDirectoryFromPath(DN_Str8 path);
|
|
DN_API DN_Str8 DN_Str8AppendF (DN_Arena *arena, DN_Str8 string, char const *fmt, ...);
|
|
DN_API DN_Str8 DN_Str8AppendFV (DN_Arena *arena, DN_Str8 string, char const *fmt, va_list args);
|
|
DN_API DN_Str8 DN_Str8FillF (DN_Arena *arena, DN_USize count, char const *fmt, ...);
|
|
DN_API DN_Str8 DN_Str8FillFV (DN_Arena *arena, DN_USize count, char const *fmt, va_list args);
|
|
DN_API void DN_Str8Remove (DN_Str8 *string, DN_USize offset, DN_USize size);
|
|
DN_API DN_Str8TruncateResult DN_Str8TruncateMiddle (DN_Arena *arena, DN_Str8 str8, DN_U32 side_size, DN_Str8 truncator);
|
|
DN_API DN_Str8 DN_Str8Lower (DN_Arena *arena, DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8Upper (DN_Arena *arena, DN_Str8 string);
|
|
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromArena (DN_Arena *arena);
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrRef (DN_Arena *arena, DN_Str8 const *strings, DN_USize size);
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrCopy (DN_Arena *arena, DN_Str8 const *strings, DN_USize size);
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromBuilder (DN_Arena *arena, DN_Str8Builder const *builder);
|
|
DN_API bool DN_Str8BuilderAddArrayRef (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize size, DN_Str8BuilderAdd add);
|
|
DN_API bool DN_Str8BuilderAddArrayCopy (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize size, DN_Str8BuilderAdd add);
|
|
DN_API bool DN_Str8BuilderAddFV (DN_Str8Builder *builder, DN_Str8BuilderAdd add, DN_FMT_ATTRIB char const *fmt, va_list args);
|
|
#define DN_Str8BuilderAppendArrayRef(builder, strings, size) DN_Str8BuilderAddArrayRef(builder, strings, size, DN_Str8BuilderAdd_Append)
|
|
#define DN_Str8BuilderAppendArrayCopy(builder, strings, size) DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_Str8BuilderAdd_Append)
|
|
#define DN_Str8BuilderAppendSliceRef(builder, slice) DN_Str8BuilderAddArrayRef(builder, slice.data, slice.size, DN_Str8BuilderAdd_Append)
|
|
#define DN_Str8BuilderAppendSliceCopy(builder, slice) DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.size, DN_Str8BuilderAdd_Append)
|
|
DN_API bool DN_Str8BuilderAppendRef (DN_Str8Builder *builder, DN_Str8 string);
|
|
DN_API bool DN_Str8BuilderAppendCopy (DN_Str8Builder *builder, DN_Str8 string);
|
|
#define DN_Str8BuilderAppendFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_Str8BuilderAdd_Append, fmt, args)
|
|
DN_API bool DN_Str8BuilderAppendF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API bool DN_Str8BuilderAppendBytesRef (DN_Str8Builder *builder, void const *ptr, DN_USize size);
|
|
DN_API bool DN_Str8BuilderAppendBytesCopy (DN_Str8Builder *builder, void const *ptr, DN_USize size);
|
|
DN_API bool DN_Str8BuilderAppendBuilderRef (DN_Str8Builder *dest, DN_Str8Builder const *src);
|
|
DN_API bool DN_Str8BuilderAppendBuilderCopy (DN_Str8Builder *dest, DN_Str8Builder const *src);
|
|
#define DN_Str8BuilderPrependArrayRef(builder, strings, size) DN_Str8BuilderAddArrayRef(builder, strings, size, DN_Str8BuilderAdd_Prepend)
|
|
#define DN_Str8BuilderPrependArrayCopy(builder, strings, size) DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_Str8BuilderAdd_Prepend)
|
|
#define DN_Str8BuilderPrependSliceRef(builder, slice) DN_Str8BuilderAddArrayRef(builder, slice.data, slice.size, DN_Str8BuilderAdd_Prepend)
|
|
#define DN_Str8BuilderPrependSliceCopy(builder, slice) DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.size, DN_Str8BuilderAdd_Prepend)
|
|
DN_API bool DN_Str8BuilderPrependRef (DN_Str8Builder *builder, DN_Str8 string);
|
|
DN_API bool DN_Str8BuilderPrependCopy (DN_Str8Builder *builder, DN_Str8 string);
|
|
#define DN_Str8BuilderPrependFV(builder, fmt, args) DN_Str8BuilderAddFV(builder, DN_Str8BuilderAdd_Prepend, fmt, args)
|
|
DN_API bool DN_Str8BuilderPrependF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API bool DN_Str8BuilderErase (DN_Str8Builder *builder, DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8BuilderBuild (DN_Str8Builder const *builder, DN_Arena *arena);
|
|
DN_API DN_Str8 DN_Str8BuilderBuildDelimited (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena);
|
|
DN_API DN_Slice<DN_Str8> DN_Str8BuilderBuildSlice (DN_Str8Builder const *builder, DN_Arena *arena);
|
|
|
|
DN_API int DN_EncodeUTF8Codepoint (uint8_t utf8[4], uint32_t codepoint);
|
|
DN_API int DN_EncodeUTF16Codepoint (uint16_t utf16[2], uint32_t codepoint);
|
|
|
|
DN_API DN_U8 DN_U8FromHexNibble (char hex);
|
|
DN_API DN_NibbleFromU8Result DN_NibbleFromU8 (DN_U8 u8);
|
|
|
|
DN_API DN_USize DN_BytesFromHexPtr (void const *hex, DN_USize hex_count, void *dest, DN_USize dest_count);
|
|
DN_API DN_Str8 DN_BytesFromHexPtrArena (void const *hex, DN_USize hex_count, DN_Arena *arena);
|
|
DN_API DN_USize DN_BytesFromHexStr8 (DN_Str8 hex, void *dest, DN_USize dest_count);
|
|
DN_API DN_Str8 DN_BytesFromHexStr8Arena (DN_Str8 hex, DN_Arena *arena);
|
|
DN_API DN_U8x16 DN_BytesFromHex32Ptr (void const *hex, DN_USize hex_count);
|
|
DN_API DN_U8x32 DN_BytesFromHex64Ptr (void const *hex, DN_USize hex_count);
|
|
|
|
DN_API DN_HexU64Str8 DN_HexFromU64 (DN_U64 value, DN_HexFromU64Type type);
|
|
DN_API DN_USize DN_HexFromBytesPtr (void const *bytes, DN_USize bytes_count, void *hex, DN_USize hex_count);
|
|
DN_API DN_Str8 DN_HexFromBytesPtrArena (void const *bytes, DN_USize bytes_count, DN_Arena *arena);
|
|
DN_API DN_Hex32 DN_HexFromBytes16Ptr (void const *bytes, DN_USize bytes_count);
|
|
DN_API DN_Hex64 DN_HexFromBytes32Ptr (void const *bytes, DN_USize bytes_count);
|
|
DN_API DN_Hex128 DN_HexFromBytes64Ptr (void const *bytes, DN_USize bytes_count);
|
|
|
|
DN_API DN_Str8x128 DN_AgeStr8FromMsU64 (DN_U64 duration_ms, DN_AgeUnit units);
|
|
DN_API DN_Str8x128 DN_AgeStr8FromSecU64 (DN_U64 duration_ms, DN_AgeUnit units);
|
|
DN_API DN_Str8x128 DN_AgeStr8FromSecF64 (DN_F64 sec, DN_AgeUnit units);
|
|
|
|
DN_API DN_ByteCountResult DN_ByteCountFromType (DN_U64 bytes, DN_ByteCountType type);
|
|
#define DN_ByteCount(bytes) DN_ByteCountFromType(bytes, DN_ByteCountType_Auto)
|
|
DN_API DN_Str8x32 DN_ByteCountStr8x32FromType (DN_U64 bytes, DN_ByteCountType type);
|
|
#define DN_ByteCountStr8x32(bytes) DN_ByteCountStr8x32FromType(bytes, DN_ByteCountType_Auto)
|
|
|
|
#define DN_ProfilerZoneLoop(prof, name, index) \
|
|
DN_ProfilerZone DN_UniqueName(zone_) = DN_ProfilerBeginZone(prof, DN_Str8Lit(name), index), DN_UniqueName(dummy_) = {}; \
|
|
DN_UniqueName(dummy_).begin_tsc == 0; \
|
|
DN_ProfilerEndZone(prof, DN_UniqueName(zone_)), DN_UniqueName(dummy_).begin_tsc = 1
|
|
|
|
#define DN_ProfilerZoneLoopAuto(prof, name) DN_ProfilerZoneLoop(prof, name, __COUNTER__ + 1)
|
|
DN_API DN_Profiler DN_ProfilerInit (DN_ProfilerAnchor *anchors, DN_USize count, DN_USize anchors_per_frame, DN_ProfilerTSCNowFunc *tsc_now, DN_U64 tsc_frequency);
|
|
DN_API DN_ProfilerZone DN_ProfilerBeginZone (DN_Profiler *profiler, DN_Str8 name, DN_U16 anchor_index);
|
|
#define DN_ProfilerBeginZoneAuto(prof, name) DN_ProfilerBeginZone(prof, DN_Str8Lit(name), __COUNTER__ + 1)
|
|
DN_API void DN_ProfilerEndZone (DN_Profiler *profiler, DN_ProfilerZone zone);
|
|
DN_API DN_USize DN_ProfilerFrameCount (DN_Profiler const *profiler);
|
|
DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchorsFromIndex (DN_Profiler *profiler, DN_USize frame_index);
|
|
DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchors (DN_Profiler *profiler);
|
|
DN_API void DN_ProfilerNewFrame (DN_Profiler *profiler);
|
|
DN_API void DN_ProfilerDump (DN_Profiler *profiler);
|
|
DN_API DN_F64 DN_ProfilerSecFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc);
|
|
DN_API DN_F64 DN_ProfilerMsFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc);
|
|
|
|
#endif // !defined(DN_BASE_H)
|
|
// DN: Single header generator commented out this header => #include "Base/dn_base_os.h"
|
|
#if !defined(DN_BASE_OS_H)
|
|
#define DN_BASE_OS_H
|
|
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.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;
|
|
};
|
|
|
|
|
|
#if defined(DN_FREESTANDING)
|
|
#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A")
|
|
#define DN_StackTraceWalk(...)
|
|
#define DN_StackTraceWalkResultIterate(...)
|
|
#define DN_StackTraceWalkResultToStr8(...) DN_Str8Lit("N/A")
|
|
#define DN_StackTraceWalkStr8(...) DN_Str8Lit("N/A")
|
|
#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A")
|
|
#define DN_StackTraceGetFrames(...)
|
|
#define DN_StackTraceRawFrameToFrame(...)
|
|
#define DN_StackTracePrint(...)
|
|
#define DN_StackTraceReloadSymbols(...)
|
|
#else
|
|
DN_API DN_StackTraceWalkResult DN_StackTraceWalk (struct DN_Arena *arena, DN_U16 limit);
|
|
DN_API bool DN_StackTraceWalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk);
|
|
DN_API DN_Str8 DN_StackTraceWalkResultToStr8 (struct DN_Arena *arena, DN_StackTraceWalkResult const *walk, DN_U16 skip);
|
|
DN_API DN_Str8 DN_StackTraceWalkStr8 (struct DN_Arena *arena, DN_U16 limit, DN_U16 skip);
|
|
DN_API DN_Str8 DN_StackTraceWalkStr8FromHeap (DN_U16 limit, DN_U16 skip);
|
|
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTraceGetFrames (struct DN_Arena *arena, DN_U16 limit);
|
|
DN_API DN_StackTraceFrame DN_StackTraceRawFrameToFrame (struct DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
|
|
DN_API void DN_StackTracePrint (DN_U16 limit);
|
|
DN_API void DN_StackTraceReloadSymbols ();
|
|
#endif
|
|
#endif // !defined(DN_BASE_OS_H)
|
|
// DN: Single header generator commented out this header => #include "Base/dn_base_assert.h"
|
|
#if !defined(DN_BASE_ASSERT_H)
|
|
#define DN_BASE_ASSERT_H
|
|
|
|
#define DN_HardAssertF(expr, fmt, ...) \
|
|
do { \
|
|
if (!(expr)) { \
|
|
DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
|
|
DN_LOG_ErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
|
DN_Str8PrintFmt(stack_trace_), \
|
|
##__VA_ARGS__); \
|
|
DN_DebugBreak; \
|
|
} \
|
|
} while (0)
|
|
#define DN_HardAssert(expr) DN_HardAssertF(expr, "")
|
|
|
|
// NOTE: Our default assert requires stack traces which has a bit of a chicken-and-egg problem if
|
|
// we're trying to detect some code related to the DN startup sequence. If we try to assert before
|
|
// the OS layer is initialised stack-traces will try to use temporary memory which requires TLS to
|
|
// be setup which belongs to the OS.
|
|
//
|
|
// This causes recursion errors as they call into each other. We use RawAsserts for these kind of
|
|
// checks.
|
|
#if defined(DN_NO_ASSERT)
|
|
#define DN_RawAssert(...)
|
|
#define DN_Assert(...)
|
|
#define DN_AssertOnce(...)
|
|
#define DN_AssertF(...)
|
|
#define DN_AssertFOnce(...)
|
|
#else
|
|
#define DN_RawAssert(expr) do { if (!(expr)) DN_DebugBreak; } while (0)
|
|
|
|
#define DN_AssertF(expr, fmt, ...) \
|
|
do { \
|
|
if (!(expr)) { \
|
|
DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
|
|
DN_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
|
DN_Str8PrintFmt(stack_trace_), \
|
|
##__VA_ARGS__); \
|
|
DN_DebugBreak; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define DN_AssertFOnce(expr, fmt, ...) \
|
|
do { \
|
|
static bool once = true; \
|
|
if (!(expr) && once) { \
|
|
once = false; \
|
|
DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
|
|
DN_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
|
DN_Str8PrintFmt(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")
|
|
#define DN_StaticAssert(expr) \
|
|
DN_GCC_WARNING_PUSH \
|
|
DN_GCC_WARNING_DISABLE(-Wunused-local-typedefs) \
|
|
typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \
|
|
DN_GCC_WARNING_POP
|
|
|
|
#define DN_Check(expr) DN_CheckF(expr, "")
|
|
#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_StackTracePrint(128 /*limit*/), DN_DebugBreak, false))
|
|
#endif
|
|
|
|
#endif
|
|
// DN: Single header generator commented out this header => #include "Base/dn_base_log.h"
|
|
#if !defined(DN_BASE_LOG_H)
|
|
#define DN_BASE_LOG_H
|
|
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.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)
|
|
// DN: Single header generator commented out this header => #include "Base/dn_base_containers.h"
|
|
#if !defined(DN_CONTAINERS_H)
|
|
#define DN_CONTAINERS_H
|
|
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
|
|
struct DN_Ring
|
|
{
|
|
DN_U64 size;
|
|
char *base;
|
|
DN_U64 write_pos;
|
|
DN_U64 read_pos;
|
|
};
|
|
|
|
// NOTE: DN_CArray /////////////////////////////////////////////////////////////////////////////////
|
|
enum DN_ArrayErase
|
|
{
|
|
DN_ArrayErase_Unstable,
|
|
DN_ArrayErase_Stable,
|
|
};
|
|
|
|
enum DN_ArrayAdd
|
|
{
|
|
DN_ArrayAdd_Append,
|
|
DN_ArrayAdd_Prepend,
|
|
};
|
|
|
|
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)
|
|
|
|
// NOTE: Macros for operating on data structures that are embedded into a C style struct or from a
|
|
// set of defined variables from the available scope. Keep it stupid simple, structs and functions.
|
|
// Minimal amount of container types with flexible construction leads to less duplicated container
|
|
// code and less template meta-programming.
|
|
//
|
|
// LArray => Literal Array
|
|
// Define a C array and size:
|
|
//
|
|
// ```
|
|
// MyStruct buffer[TB_ASType_Count] = {};
|
|
// DN_USize size = 0;
|
|
// MyStruct *item = DN_LArray_Make(buffer, size, DN_ArrayCountU(buffer), DN_ZMem_No);
|
|
// ```
|
|
//
|
|
// IArray => Intrusive Array
|
|
// Define a struct with the members 'data', 'size' and 'max':
|
|
//
|
|
// ```
|
|
// struct MyArray {
|
|
// MyStruct *data;
|
|
// DN_USize size;
|
|
// DN_USize max;
|
|
// } my_array = {};
|
|
//
|
|
// MyStruct *item = DN_IArray_Make(&my_array, MyArray, DN_ZMem_No);
|
|
// ```
|
|
// ISLList => Intrusive Singly Linked List
|
|
// Define a struct with the members 'next':
|
|
//
|
|
// ```
|
|
// struct MyLinkItem {
|
|
// int data;
|
|
// MyLinkItem *next;
|
|
// } my_link = {};
|
|
//
|
|
// MyLinkItem *first_item = DN_ISLList_Detach(&my_link, MyLinkItem);
|
|
// ```
|
|
|
|
#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_ArenaNew(arena, T, DN_ZMem_Yes); \
|
|
DN_DLList_Init(list); \
|
|
} while (0)
|
|
|
|
#define DN_DLList_InitPool(list, T, pool) \
|
|
do { \
|
|
(list) = DN_PoolNew(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, list) \
|
|
auto *it = (list)->next; (it) != (list); (it) = (it)->next
|
|
|
|
#define DN_DoublyLLDetach(head, ptr) \
|
|
do { \
|
|
if ((head) && (head) == (ptr)) \
|
|
(head) = (head)->next; \
|
|
if ((ptr)) { \
|
|
if ((ptr)->next) \
|
|
(ptr)->next->prev = (ptr)->prev; \
|
|
if ((ptr)->prev) \
|
|
(ptr)->prev->next = (ptr)->next; \
|
|
(ptr)->prev = (ptr)->next = 0; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define DN_DoublyLLAppend(head, ptr) \
|
|
do { \
|
|
if ((ptr)) { \
|
|
DN_AssertF((ptr)->prev == 0 && (ptr)->next == 0, "Detach the pointer first"); \
|
|
(ptr)->prev = (head); \
|
|
(ptr)->next = 0; \
|
|
if ((head)) { \
|
|
(ptr)->next = (head)->next; \
|
|
(head)->next = (ptr); \
|
|
} else { \
|
|
(head) = (ptr); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#define DN_DoublyLLPrepend(head, ptr) \
|
|
do { \
|
|
if ((ptr)) { \
|
|
DN_AssertF((ptr)->prev == 0 && (ptr)->next == 0, "Detach the pointer first"); \
|
|
(ptr)->prev = nullptr; \
|
|
(ptr)->next = (head); \
|
|
if ((head)) { \
|
|
(ptr)->prev = (head)->prev; \
|
|
(head)->prev = (ptr); \
|
|
} else { \
|
|
(head) = (ptr); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#define DN_ISLList_Detach(list) (decltype(list))DN_CSLList_Detach((void **)&(list), (void **)&(list)->next)
|
|
|
|
#define DN_LArray_ResizeFromPool(c_array, size, max, pool, new_max) DN_CArray2_ResizeFromPool((void **)&(c_array), size, max, sizeof((c_array)[0]), pool, new_max)
|
|
#define DN_LArray_GrowFromPool(c_array, size, max, pool, new_max) DN_CArray2_GrowFromPool((void **)&(c_array), size, max, sizeof((c_array)[0]), pool, new_max)
|
|
#define DN_LArray_GrowIfNeededFromPool(c_array, size, max, pool, add_count) DN_CArray2_GrowIfNeededFromPool((void **)(c_array), size, max, sizeof((c_array)[0]), pool, add_count)
|
|
#define DN_LArray_MakeArray(c_array, size, max, count, z_mem) (decltype(&(c_array)[0]))DN_CArray2_MakeArray(c_array, size, max, sizeof((c_array)[0]), count, z_mem)
|
|
#define DN_LArray_MakeArrayZ(c_array, size, max, count) (decltype(&(c_array)[0]))DN_CArray2_MakeArray(c_array, size, max, sizeof((c_array)[0]), count, DN_ZMem_Yes)
|
|
#define DN_LArray_Make(c_array, size, max, z_mem) (decltype(&(c_array)[0]))DN_CArray2_MakeArray(c_array, size, max, sizeof((c_array)[0]), 1, z_mem)
|
|
#define DN_LArray_MakeZ(c_array, size, max) (decltype(&(c_array)[0]))DN_CArray2_MakeArray(c_array, size, max, sizeof((c_array)[0]), 1, DN_ZMem_Yes)
|
|
#define DN_LArray_AddArray(c_array, size, max, items, count, add) (decltype(&(c_array)[0]))DN_CArray2_AddArray(c_array, size, max, sizeof((c_array)[0]), items, count, add)
|
|
#define DN_LArray_Add(c_array, size, max, item, add) (decltype(&(c_array)[0]))DN_CArray2_AddArray(c_array, size, max, sizeof((c_array)[0]), &item, 1, add)
|
|
#define DN_LArray_AppendArray(c_array, size, max, items, count) (decltype(&(c_array)[0]))DN_CArray2_AddArray(c_array, size, max, sizeof((c_array)[0]), items, count, DN_ArrayAdd_Append)
|
|
#define DN_LArray_Append(c_array, size, max, item) (decltype(&(c_array)[0]))DN_CArray2_AddArray(c_array, size, max, sizeof((c_array)[0]), &item, 1, DN_ArrayAdd_Append)
|
|
#define DN_LArray_PrependArray(c_array, size, max, items, count) (decltype(&(c_array)[0]))DN_CArray2_AddArray(c_array, size, max, sizeof((c_array)[0]), items, count, DN_ArrayAdd_Prepend)
|
|
#define DN_LArray_Prepend(c_array, size, max, item) (decltype(&(c_array)[0]))DN_CArray2_AddArray(c_array, size, max, sizeof((c_array)[0]), &item, 1, DN_ArrayAdd_Prepend)
|
|
#define DN_LArray_EraseRange(c_array, size, begin_index, count, erase) DN_CArray2_EraseRange(c_array, size, sizeof((c_array)[0]), begin_index, count, erase)
|
|
#define DN_LArray_Erase(c_array, size, index, erase) DN_CArray2_EraseRange(c_array, size, sizeof((c_array)[0]), index, 1, erase)
|
|
#define DN_LArray_InsertArray(c_array, size, max, index, items, count) (decltype(&(c_array)[0]))DN_CArray2_InsertArray(c_array, size, max, sizeof((c_array)[0]), index, items, count)
|
|
#define DN_LArray_Insert(c_array, size, max, index, item) (decltype(&(c_array)[0]))DN_CArray2_InsertArray(c_array, size, max, sizeof((c_array)[0]), index, &item, 1)
|
|
|
|
#define DN_IArray_ResizeFromPool(array, pool, new_max) DN_CArray2_ResizeFromPool((void **)(&(array)->data), &(array)->size, &(array)->max, sizeof((array)->data[0]), pool, new_max)
|
|
#define DN_IArray_GrowFromPool(array, pool, new_max) DN_CArray2_GrowFromPool((void **)(&(array)->data), &(array)->size, &(array)->max, sizeof((array)->data[0]), pool, new_max)
|
|
#define DN_IArray_GrowIfNeededFromPool(array, pool, add_count) DN_CArray2_GrowIfNeededFromPool((void **)(&(array)->data), (array)->size, &(array)->max, sizeof((array)->data[0]), pool, add_count)
|
|
#define DN_IArray_MakeArray(array, count, z_mem) (decltype(&((array)->data)[0]))DN_CArray2_MakeArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), count, z_mem)
|
|
#define DN_IArray_MakeArrayZ(array, count) (decltype(&((array)->data)[0]))DN_CArray2_MakeArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), count, DN_ZMem_Yes)
|
|
#define DN_IArray_Make(array, z_mem) (decltype(&((array)->data)[0]))DN_CArray2_MakeArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), 1, z_mem)
|
|
#define DN_IArray_MakeZ(array) (decltype(&((array)->data)[0]))DN_CArray2_MakeArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), 1, DN_ZMem_Yes)
|
|
#define DN_IArray_AddArray(array, items, count, add) (decltype(&((array)->data)[0]))DN_CArray2_AddArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), items, count, add)
|
|
#define DN_IArray_Add(array, item, add) (decltype(&((array)->data)[0]))DN_CArray2_AddArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), &item, 1, add)
|
|
#define DN_IArray_AppendArray(array, items, count) (decltype(&((array)->data)[0]))DN_CArray2_AddArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), items, count, DN_ArrayAdd_Append)
|
|
#define DN_IArray_Append(array, item) (decltype(&((array)->data)[0]))DN_CArray2_AddArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), &item, 1, DN_ArrayAdd_Append)
|
|
#define DN_IArray_PrependArray(array, items, count) (decltype(&((array)->data)[0]))DN_CArray2_AddArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), items, count, DN_ArrayAdd_Prepend)
|
|
#define DN_IArray_Prepend(array, item) (decltype(&((array)->data)[0]))DN_CArray2_AddArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), &item, 1, DN_ArrayAdd_Prepend)
|
|
#define DN_IArray_EraseRange(array, begin_index, count, erase) DN_CArray2_EraseRange((array)->data, &(array)->size, sizeof(((array)->data)[0]), begin_index, count, erase)
|
|
#define DN_IArray_Erase(array, index, erase) DN_CArray2_EraseRange((array)->data, &(array)->size, sizeof(((array)->data)[0]), index, 1, erase)
|
|
#define DN_IArray_InsertArray(array, index, items, count) (decltype(&((array)->data)[0]))DN_CArray2_InsertArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), index, items, count)
|
|
#define DN_IArray_Insert(array, index, item, count) (decltype(&((array)->data)[0]))DN_CArray2_InsertArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), index, &item, 1)
|
|
|
|
DN_API DN_ArrayEraseResult DN_CArray2_EraseRange (void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
|
DN_API void *DN_CArray2_MakeArray (void *data, DN_USize *size, DN_USize max, DN_USize data_size, DN_USize make_size, DN_ZMem z_mem);
|
|
DN_API void *DN_CArray2_AddArray (void *data, DN_USize *size, DN_USize max, DN_USize data_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add);
|
|
DN_API bool DN_CArray2_Resize (void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max);
|
|
DN_API bool DN_CArray2_Grow (void **data, DN_USize *size, DN_USize *max, DN_USize data_size, DN_Pool *pool, DN_USize new_max);
|
|
DN_API bool DN_CArray2_GrowIfNeededFromPool (void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool);
|
|
DN_API void *DN_CSLList_Detach (void **link, void **next);
|
|
|
|
DN_API bool DN_Ring_HasSpace (DN_Ring const *ring, DN_U64 size);
|
|
DN_API bool DN_Ring_HasData (DN_Ring const *ring, DN_U64 size);
|
|
DN_API void DN_Ring_Write (DN_Ring *ring, void const *src, DN_U64 src_size);
|
|
#define DN_Ring_WriteStruct(ring, item) DN_Ring_Write((ring), (item), sizeof(*(item)))
|
|
DN_API void DN_Ring_Read (DN_Ring *ring, void *dest, DN_U64 dest_size);
|
|
#define DN_Ring_ReadStruct(ring, dest) DN_Ring_Read((ring), (dest), sizeof(*(dest)))
|
|
|
|
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_ZMem z_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_ZMem z_mem);
|
|
template <typename T> DN_SArray<T> DN_SArray_InitSlice (DN_Arena *arena, DN_Slice<T> slice, DN_USize size, DN_ZMem z_mem);
|
|
template <typename T, size_t N> DN_SArray<T> DN_SArray_InitCArray (DN_Arena *arena, T const (&array)[N], DN_USize size, DN_ZMem);
|
|
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_ZMem z_mem);
|
|
template <typename T> T * DN_SArray_Make (DN_SArray<T> *array, DN_ZMem z_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_ZMem z_mem);
|
|
template <typename T, DN_USize N> T * DN_FArray_Make (DN_FArray<T, N> *array, DN_ZMem z_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_ZMem z_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_ZMem z_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)
|
|
// DN: Single header generator commented out this header => #include "Base/dn_base_leak.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
|
|
enum DN_LeakAllocFlag
|
|
{
|
|
DN_LeakAllocFlag_Freed = 1 << 0,
|
|
DN_LeakAllocFlag_LeakPermitted = 1 << 1,
|
|
};
|
|
|
|
struct DN_LeakAlloc
|
|
{
|
|
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_LeakAllocFlag`
|
|
};
|
|
|
|
// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get
|
|
// expensive. Enforce that there is no unexpected padding.
|
|
DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively
|
|
|
|
struct DN_LeakTracker
|
|
{
|
|
DN_DSMap<DN_LeakAlloc> alloc_table;
|
|
DN_TicketMutex alloc_table_mutex;
|
|
DN_Arena alloc_table_arena;
|
|
DN_U64 alloc_table_bytes_allocated_for_stack_traces;
|
|
};
|
|
|
|
DN_API void DN_LeakTrackAlloc_ (DN_LeakTracker *leak, void *ptr, DN_USize size, bool alloc_can_leak);
|
|
DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr);
|
|
DN_API void DN_LeakDump_ (DN_LeakTracker *leak);
|
|
|
|
#if defined(DN_LEAK_TRACKING)
|
|
#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) DN_LeakTrackAlloc_(leak, ptr, size, alloc_can_leak)
|
|
#define DN_LeakTrackDealloc(leak, ptr) DN_LeakTrackDealloc_(leak, ptr)
|
|
#define DN_LeakDump(leak) DN_LeakDump_(leak)
|
|
#else
|
|
#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
|
|
#define DN_LeakTrackDealloc(leak, ptr) do { (void)ptr; } while (0)
|
|
#define DN_LeakDump(leak) do { } while (0)
|
|
#endif
|
|
|
|
|
|
#endif // !defined(DN_BASE_INC_H)
|
|
// DN: Single header generator commented out this header => #include "dn_os_inc.h"
|
|
#if !defined(DN_OS_INC_H)
|
|
#define DN_OS_INC_H
|
|
|
|
#if defined(DN_PLATFORM_WIN32)
|
|
// DN: Single header generator commented out this header => #include "OS/dn_os_windows.h"
|
|
#if !defined(DN_OS_WINDOWS_H)
|
|
#define DN_OS_WINDOWS_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
|
#pragma comment(lib, "bcrypt")
|
|
#pragma comment(lib, "winhttp")
|
|
#pragma comment(lib, "dbghelp")
|
|
#pragma comment(lib, "comdlg32")
|
|
#pragma comment(lib, "pathcch")
|
|
#pragma comment(lib, "Shell32") // ShellExecuteW
|
|
#pragma comment(lib, "shlwapi")
|
|
#endif
|
|
|
|
#if defined(DN_NO_WINDOWS_H_REPLACEMENT_HEADER) || defined(_INC_WINDOWS)
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <Windows.h> // LONG
|
|
#include <bcrypt.h> // DN_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc
|
|
#include <shellapi.h> // DN_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc
|
|
#include <shlwapi.h> // PathRelativePathTO
|
|
#include <pathcch.h> // PathCchCanonicalizeEx
|
|
#include <winhttp.h> // WinHttp*
|
|
#include <psapi.h> // PROCESS_MEMORY_COUNTERS_EX2
|
|
#include <commdlg.h> // OPENFILENAMEW
|
|
#include <DbgHelp.h>
|
|
#else
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
|
|
|
|
// NOTE: basetsd.h /////////////////////////////////////////////////////////////////////////////
|
|
typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
|
|
typedef unsigned __int64 UINT_PTR, *PUINT_PTR;
|
|
typedef ULONG_PTR SIZE_T, *PSIZE_T;
|
|
typedef __int64 LONG_PTR, *PLONG_PTR;
|
|
typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
|
|
typedef unsigned __int64 ULONG64, *PULONG64;
|
|
typedef unsigned __int64 DWORD64, *PDWORD64;
|
|
|
|
// NOTE: shared/minwindef.h ////////////////////////////////////////////////////////////////////
|
|
struct HINSTANCE__ {
|
|
int unused;
|
|
};
|
|
typedef struct HINSTANCE__ *HINSTANCE;
|
|
|
|
typedef unsigned long DWORD;
|
|
typedef int BOOL;
|
|
typedef int INT;
|
|
typedef unsigned long ULONG;
|
|
typedef unsigned int UINT;
|
|
typedef unsigned short WORD;
|
|
typedef unsigned char BYTE;
|
|
typedef unsigned char UCHAR;
|
|
typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */
|
|
typedef void * HANDLE;
|
|
typedef HANDLE HLOCAL;
|
|
|
|
typedef unsigned __int64 WPARAM;
|
|
typedef LONG_PTR LPARAM;
|
|
typedef LONG_PTR LRESULT;
|
|
|
|
#define MAX_PATH 260
|
|
|
|
typedef struct _FILETIME {
|
|
DWORD dwLowDateTime;
|
|
DWORD dwHighDateTime;
|
|
} FILETIME, *PFILETIME, *LPFILETIME;
|
|
|
|
// NOTE: shared/winerror.h /////////////////////////////////////////////////////////////////////
|
|
// NOTE: GetModuleFileNameW
|
|
#define ERROR_INSUFFICIENT_BUFFER 122L // dderror
|
|
|
|
// NOTE: um/winnls.h ///////////////////////////////////////////////////////////////////////////
|
|
// NOTE: MultiByteToWideChar
|
|
#define CP_UTF8 65001 // UTF-8 translation
|
|
|
|
// NOTE: um/winnt.h ////////////////////////////////////////////////////////////////////////////
|
|
typedef void VOID;
|
|
typedef __int64 LONGLONG;
|
|
typedef unsigned __int64 ULONGLONG;
|
|
typedef void * HANDLE;
|
|
typedef char CHAR;
|
|
typedef short SHORT;
|
|
typedef long LONG;
|
|
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
|
|
typedef CHAR * NPSTR, *LPSTR, *PSTR;
|
|
typedef WCHAR * NWPSTR, *LPWSTR, *PWSTR;
|
|
typedef long HRESULT;
|
|
|
|
// NOTE: VirtualAlloc: Allocation Type
|
|
#define MEM_RESERVE 0x00002000
|
|
#define MEM_COMMIT 0x00001000
|
|
#define MEM_DECOMMIT 0x00004000
|
|
#define MEM_RELEASE 0x00008000
|
|
|
|
// NOTE: VirtualAlloc: Page Permissions
|
|
#define PAGE_NOACCESS 0x01
|
|
#define PAGE_READONLY 0x02
|
|
#define PAGE_READWRITE 0x04
|
|
#define PAGE_GUARD 0x100
|
|
|
|
// NOTE: HeapAlloc
|
|
#define HEAP_ZERO_MEMORY 0x00000008
|
|
#define HEAP_NO_SERIALIZE 0x00000001
|
|
#define HEAP_GROWABLE 0x00000002
|
|
#define HEAP_GENERATE_EXCEPTIONS 0x00000004
|
|
#define HEAP_ZERO_MEMORY 0x00000008
|
|
#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010
|
|
#define HEAP_TAIL_CHECKING_ENABLED 0x00000020
|
|
#define HEAP_FREE_CHECKING_ENABLED 0x00000040
|
|
#define HEAP_DISABLE_COALESCE_ON_FREE 0x00000080
|
|
#define HEAP_CREATE_ALIGN_16 0x00010000
|
|
#define HEAP_CREATE_ENABLE_TRACING 0x00020000
|
|
#define HEAP_CREATE_ENABLE_EXECUTE 0x00040000
|
|
#define HEAP_MAXIMUM_TAG 0x0FFF
|
|
#define HEAP_PSEUDO_TAG_FLAG 0x8000
|
|
#define HEAP_TAG_SHIFT 18
|
|
#define HEAP_CREATE_SEGMENT_HEAP 0x00000100
|
|
#define HEAP_CREATE_HARDENED 0x00000200
|
|
|
|
// NOTE: FormatMessageA
|
|
#define MAKELANGID(p, s) ((((WORD )(s)) << 10) | (WORD )(p))
|
|
#define LANG_NEUTRAL 0x00
|
|
#define SUBLANG_DEFAULT 0x01 // user default
|
|
|
|
// NOTE: CreateFile
|
|
#define GENERIC_READ (0x80000000L)
|
|
#define GENERIC_WRITE (0x40000000L)
|
|
#define GENERIC_EXECUTE (0x20000000L)
|
|
#define GENERIC_ALL (0x10000000L)
|
|
|
|
#define FILE_APPEND_DATA (0x0004) // file
|
|
|
|
// NOTE: CreateFile/FindFirstFile
|
|
#define FILE_SHARE_READ 0x00000001
|
|
#define FILE_SHARE_WRITE 0x00000002
|
|
#define FILE_SHARE_DELETE 0x00000004
|
|
|
|
#define FILE_ATTRIBUTE_READONLY 0x00000001
|
|
#define FILE_ATTRIBUTE_HIDDEN 0x00000002
|
|
#define FILE_ATTRIBUTE_SYSTEM 0x00000004
|
|
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
|
|
#define FILE_ATTRIBUTE_NORMAL 0x00000080
|
|
|
|
// NOTE: STACKFRAME64
|
|
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
|
|
|
|
// NOTE: WaitForSingleObject
|
|
#define WAIT_TIMEOUT 258L // dderror
|
|
#define STATUS_WAIT_0 ((DWORD )0x00000000L)
|
|
#define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L)
|
|
|
|
#define S_OK ((HRESULT)0L)
|
|
#define S_FALSE ((HRESULT)1L)
|
|
|
|
typedef union _ULARGE_INTEGER {
|
|
struct {
|
|
DWORD LowPart;
|
|
DWORD HighPart;
|
|
} DUMMYSTRUCTNAME;
|
|
struct {
|
|
DWORD LowPart;
|
|
DWORD HighPart;
|
|
} u;
|
|
ULONGLONG QuadPart;
|
|
} ULARGE_INTEGER;
|
|
|
|
typedef union _LARGE_INTEGER {
|
|
struct {
|
|
DWORD LowPart;
|
|
LONG HighPart;
|
|
} DUMMYSTRUCTNAME;
|
|
struct {
|
|
DWORD LowPart;
|
|
LONG HighPart;
|
|
} u;
|
|
LONGLONG QuadPart;
|
|
} LARGE_INTEGER;
|
|
|
|
typedef struct __declspec(align(16)) _M128A {
|
|
ULONGLONG Low;
|
|
LONGLONG High;
|
|
} M128A, *PM128A;
|
|
|
|
typedef struct __declspec(align(16)) _XSAVE_FORMAT {
|
|
WORD ControlWord;
|
|
WORD StatusWord;
|
|
BYTE TagWord;
|
|
BYTE Reserved1;
|
|
WORD ErrorOpcode;
|
|
DWORD ErrorOffset;
|
|
WORD ErrorSelector;
|
|
WORD Reserved2;
|
|
DWORD DataOffset;
|
|
WORD DataSelector;
|
|
WORD Reserved3;
|
|
DWORD MxCsr;
|
|
DWORD MxCsr_Mask;
|
|
M128A FloatRegisters[8];
|
|
#if defined(_WIN64)
|
|
M128A XmmRegisters[16];
|
|
BYTE Reserved4[96];
|
|
#else
|
|
M128A XmmRegisters[8];
|
|
BYTE Reserved4[224];
|
|
#endif
|
|
} XSAVE_FORMAT, *PXSAVE_FORMAT;
|
|
typedef XSAVE_FORMAT XMM_SAVE_AREA32, *PXMM_SAVE_AREA32;
|
|
|
|
typedef struct __declspec(align(16)) _CONTEXT {
|
|
DWORD64 P1Home;
|
|
DWORD64 P2Home;
|
|
DWORD64 P3Home;
|
|
DWORD64 P4Home;
|
|
DWORD64 P5Home;
|
|
DWORD64 P6Home;
|
|
DWORD ContextFlags;
|
|
DWORD MxCsr;
|
|
WORD SegCs;
|
|
WORD SegDs;
|
|
WORD SegEs;
|
|
WORD SegFs;
|
|
WORD SegGs;
|
|
WORD SegSs;
|
|
DWORD EFlags;
|
|
DWORD64 Dr0;
|
|
DWORD64 Dr1;
|
|
DWORD64 Dr2;
|
|
DWORD64 Dr3;
|
|
DWORD64 Dr6;
|
|
DWORD64 Dr7;
|
|
DWORD64 Rax;
|
|
DWORD64 Rcx;
|
|
DWORD64 Rdx;
|
|
DWORD64 Rbx;
|
|
DWORD64 Rsp;
|
|
DWORD64 Rbp;
|
|
DWORD64 Rsi;
|
|
DWORD64 Rdi;
|
|
DWORD64 R8;
|
|
DWORD64 R9;
|
|
DWORD64 R10;
|
|
DWORD64 R11;
|
|
DWORD64 R12;
|
|
DWORD64 R13;
|
|
DWORD64 R14;
|
|
DWORD64 R15;
|
|
DWORD64 Rip;
|
|
|
|
union {
|
|
XMM_SAVE_AREA32 FltSave;
|
|
|
|
struct {
|
|
M128A Header[2];
|
|
M128A Legacy[8];
|
|
M128A Xmm0;
|
|
M128A Xmm1;
|
|
M128A Xmm2;
|
|
M128A Xmm3;
|
|
M128A Xmm4;
|
|
M128A Xmm5;
|
|
M128A Xmm6;
|
|
M128A Xmm7;
|
|
M128A Xmm8;
|
|
M128A Xmm9;
|
|
M128A Xmm10;
|
|
M128A Xmm11;
|
|
M128A Xmm12;
|
|
M128A Xmm13;
|
|
M128A Xmm14;
|
|
M128A Xmm15;
|
|
} DUMMYSTRUCTNAME;
|
|
} DUMMYUNIONNAME;
|
|
|
|
M128A VectorRegister[26];
|
|
DWORD64 VectorControl;
|
|
DWORD64 DebugControl;
|
|
DWORD64 LastBranchToRip;
|
|
DWORD64 LastBranchFromRip;
|
|
DWORD64 LastExceptionToRip;
|
|
DWORD64 LastExceptionFromRip;
|
|
} CONTEXT;
|
|
|
|
typedef struct _LIST_ENTRY {
|
|
struct _LIST_ENTRY *Flink;
|
|
struct _LIST_ENTRY *Blink;
|
|
} LIST_ENTRY, *PLIST_ENTRY, PRLIST_ENTRY;
|
|
|
|
typedef struct _RTL_CRITICAL_SECTION_DEBUG {
|
|
WORD Type;
|
|
WORD CreatorBackTraceIndex;
|
|
struct _RTL_CRITICAL_SECTION *CriticalSection;
|
|
LIST_ENTRY ProcessLocksList;
|
|
DWORD EntryCount;
|
|
DWORD ContentionCount;
|
|
DWORD Flags;
|
|
WORD CreatorBackTraceIndexHigh;
|
|
WORD Identifier;
|
|
} RTL_CRITICAL_SECTION_DEBUG, *PRTL_CRITICAL_SECTION_DEBUG, RTL_RESOURCE_DEBUG, *PRTL_RESOURCE_DEBUG;
|
|
|
|
typedef struct _RTL_CONDITION_VARIABLE {
|
|
VOID *Ptr;
|
|
} RTL_CONDITION_VARIABLE, *PRTL_CONDITION_VARIABLE;
|
|
|
|
#pragma pack(push, 8)
|
|
typedef struct _RTL_CRITICAL_SECTION {
|
|
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
|
|
|
|
//
|
|
// The following three fields control entering and exiting the critical
|
|
// section for the resource
|
|
//
|
|
|
|
LONG LockCount;
|
|
LONG RecursionCount;
|
|
HANDLE OwningThread; // from the thread's ClientId->UniqueThread
|
|
HANDLE LockSemaphore;
|
|
ULONG_PTR SpinCount; // force size on 64-bit systems when packed
|
|
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
|
|
#pragma pack(pop)
|
|
|
|
typedef struct _MODLOAD_DATA {
|
|
DWORD ssize; // size of this struct
|
|
DWORD ssig; // signature identifying the passed data
|
|
VOID *data; // pointer to passed data
|
|
DWORD size; // size of passed data
|
|
DWORD flags; // options
|
|
} MODLOAD_DATA, *PMODLOAD_DATA;
|
|
|
|
#define SLMFLAG_VIRTUAL 0x1
|
|
#define SLMFLAG_ALT_INDEX 0x2
|
|
#define SLMFLAG_NO_SYMBOLS 0x4
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) VOID __stdcall RtlCaptureContext(CONTEXT *ContextRecord);
|
|
__declspec(dllimport) HANDLE __stdcall GetCurrentProcess(void);
|
|
__declspec(dllimport) HANDLE __stdcall GetCurrentThread(void);
|
|
__declspec(dllimport) DWORD __stdcall SymSetOptions(DWORD SymOptions);
|
|
__declspec(dllimport) BOOL __stdcall SymInitialize(HANDLE hProcess, const CHAR* UserSearchPath, BOOL fInvadeProcess);
|
|
__declspec(dllimport) DWORD64 __stdcall SymLoadModuleEx(HANDLE hProcess, HANDLE hFile, CHAR const *ImageName, CHAR const *ModuleName, DWORD64 BaseOfDll, DWORD DllSize, MODLOAD_DATA *Data, DWORD Flags);
|
|
__declspec(dllimport) BOOL __stdcall SymUnloadModule64(HANDLE hProcess, DWORD64 BaseOfDll);
|
|
}
|
|
|
|
// NOTE: um/heapapi.h ////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) HANDLE __stdcall HeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize);
|
|
__declspec(dllimport) BOOL __stdcall HeapDestroy(HANDLE hHeap);
|
|
__declspec(dllimport) VOID * __stdcall HeapAlloc(HANDLE hHeap, DWORD dwFlags,SIZE_T dwBytes);
|
|
__declspec(dllimport) VOID * __stdcall HeapReAlloc(HANDLE hHeap, DWORD dwFlags, VOID *lpMem, SIZE_T dwBytes);
|
|
__declspec(dllimport) BOOL __stdcall HeapFree(HANDLE hHeap, DWORD dwFlags, VOID *lpMem);
|
|
__declspec(dllimport) SIZE_T __stdcall HeapSize(HANDLE hHeap, DWORD dwFlags, VOID const *lpMem);
|
|
__declspec(dllimport) HANDLE __stdcall GetProcessHeap(VOID);
|
|
__declspec(dllimport) SIZE_T __stdcall HeapCompact(HANDLE hHeap, DWORD dwFlags);
|
|
}
|
|
|
|
// NOTE: shared/windef.h ////////////////////////////////////////////////////////////////////
|
|
typedef struct tagPOINT
|
|
{
|
|
LONG x;
|
|
LONG y;
|
|
} POINT, *PPOINT, *NPPOINT, *LPPOINT;
|
|
|
|
// NOTE: handleapi.h ///////////////////////////////////////////////////////////////////////////
|
|
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall CloseHandle(HANDLE hObject);
|
|
}
|
|
|
|
// NOTE: consoleapi.h ///////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall WriteConsoleA(HANDLE hConsoleOutput, const VOID* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD *lpNumberOfCharsWritten, VOID *lpReserved);
|
|
__declspec(dllimport) BOOL __stdcall AllocConsole(VOID);
|
|
__declspec(dllimport) BOOL __stdcall FreeConsole(VOID);
|
|
__declspec(dllimport) BOOL __stdcall AttachConsole(DWORD dwProcessId);
|
|
__declspec(dllimport) BOOL __stdcall GetConsoleMode(HANDLE hConsoleHandle, DWORD *lpMode);
|
|
}
|
|
|
|
// NOTE: um/minwinbase.h ///////////////////////////////////////////////////////////////////////
|
|
// NOTE: FindFirstFile
|
|
#define FIND_FIRST_EX_CASE_SENSITIVE 0x00000001
|
|
#define FIND_FIRST_EX_LARGE_FETCH 0x00000002
|
|
|
|
// NOTE: WaitFor..
|
|
#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
|
|
#define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 )
|
|
#define WAIT_ABANDONED ((STATUS_ABANDONED_WAIT_0 ) + 0 )
|
|
#define WAIT_ABANDONED_0 ((STATUS_ABANDONED_WAIT_0 ) + 0 )
|
|
|
|
// NOTE: CreateProcessW
|
|
#define CREATE_UNICODE_ENVIRONMENT 0x00000400
|
|
#define CREATE_NO_WINDOW 0x08000000
|
|
|
|
typedef enum _GET_FILEEX_INFO_LEVELS {
|
|
GetFileExInfoStandard,
|
|
GetFileExMaxInfoLevel
|
|
} GET_FILEEX_INFO_LEVELS;
|
|
|
|
typedef struct _SECURITY_ATTRIBUTES {
|
|
DWORD nLength;
|
|
VOID *lpSecurityDescriptor;
|
|
BOOL bInheritHandle;
|
|
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
|
|
|
|
typedef enum _FINDEX_INFO_LEVELS {
|
|
FindExInfoStandard,
|
|
FindExInfoBasic,
|
|
FindExInfoMaxInfoLevel
|
|
} FINDEX_INFO_LEVELS;
|
|
|
|
typedef enum _FINDEX_SEARCH_OPS {
|
|
FindExSearchNameMatch,
|
|
FindExSearchLimitToDirectories,
|
|
FindExSearchLimitToDevices,
|
|
FindExSearchMaxSearchOp
|
|
} FINDEX_SEARCH_OPS;
|
|
|
|
typedef struct _WIN32_FIND_DATAW {
|
|
DWORD dwFileAttributes;
|
|
FILETIME ftCreationTime;
|
|
FILETIME ftLastAccessTime;
|
|
FILETIME ftLastWriteTime;
|
|
DWORD nFileSizeHigh;
|
|
DWORD nFileSizeLow;
|
|
DWORD dwReserved0;
|
|
DWORD dwReserved1;
|
|
WCHAR cFileName[ MAX_PATH ];
|
|
WCHAR cAlternateFileName[ 14 ];
|
|
#ifdef _MAC
|
|
DWORD dwFileType;
|
|
DWORD dwCreatorType;
|
|
WORD wFinderFlags;
|
|
#endif
|
|
} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;
|
|
|
|
typedef struct _SYSTEMTIME {
|
|
WORD wYear;
|
|
WORD wMonth;
|
|
WORD wDayOfWeek;
|
|
WORD wDay;
|
|
WORD wHour;
|
|
WORD wMinute;
|
|
WORD wSecond;
|
|
WORD wMilliseconds;
|
|
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
|
|
|
|
typedef struct _OVERLAPPED {
|
|
ULONG_PTR Internal;
|
|
ULONG_PTR InternalHigh;
|
|
union {
|
|
struct {
|
|
DWORD Offset;
|
|
DWORD OffsetHigh;
|
|
} DUMMYSTRUCTNAME;
|
|
VOID *Pointer;
|
|
} DUMMYUNIONNAME;
|
|
|
|
HANDLE hEvent;
|
|
} OVERLAPPED, *LPOVERLAPPED;
|
|
|
|
typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;
|
|
|
|
#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
|
|
#define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 )
|
|
|
|
#define INFINITE 0xFFFFFFFF // Wait/Synchronisation: Infinite timeout
|
|
|
|
#define STD_INPUT_HANDLE ((DWORD)-10)
|
|
#define STD_OUTPUT_HANDLE ((DWORD)-11)
|
|
#define STD_ERROR_HANDLE ((DWORD)-12)
|
|
|
|
#define HANDLE_FLAG_INHERIT 0x00000001
|
|
#define HANDLE_FLAG_PROTECT_FROM_CLOSE 0x00000002
|
|
|
|
// NOTE: MoveFile
|
|
#define MOVEFILE_REPLACE_EXISTING 0x00000001
|
|
#define MOVEFILE_COPY_ALLOWED 0x00000002
|
|
|
|
// NOTE: FormatMessageA
|
|
#define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100
|
|
#define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200
|
|
#define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
|
|
#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
|
|
|
|
// NOTE: CreateProcessW
|
|
#define STARTF_USESTDHANDLES 0x00000100
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall MoveFileExW (const WCHAR *lpExistingFileName, const WCHAR *lpNewFileName, DWORD dwFlags);
|
|
__declspec(dllimport) BOOL __stdcall CopyFileW (const WCHAR *lpExistingFileName, const WCHAR *lpNewFileName, BOOL bFailIfExists);
|
|
__declspec(dllimport) HANDLE __stdcall CreateSemaphoreA(SECURITY_ATTRIBUTES *lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, const CHAR *lpName);
|
|
__declspec(dllimport) DWORD __stdcall FormatMessageW (DWORD dwFlags, VOID const *lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPWSTR lpBuffer, DWORD nSize, va_list *Arguments);
|
|
__declspec(dllimport) HLOCAL __stdcall LocalFree (HLOCAL hMem);
|
|
}
|
|
|
|
// NOTE: um/stringapiset.h /////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, const CHAR *lpMultiByteStr, int cbMultiByte, WCHAR *lpWideCharStr, int cchWideChar);
|
|
__declspec(dllimport) int __stdcall WideCharToMultiByte(UINT CodePage, DWORD dwFlags, const WCHAR *lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, const CHAR *lpDefaultChar, BOOL *lpUsedDefaultChar);
|
|
}
|
|
|
|
// NOTE: um/fileapi.h //////////////////////////////////////////////////////////////////////////
|
|
#define INVALID_FILE_SIZE ((DWORD)0xFFFFFFFF)
|
|
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
|
|
|
|
// NOTE: CreateFile
|
|
#define CREATE_NEW 1
|
|
#define CREATE_ALWAYS 2
|
|
#define OPEN_EXISTING 3
|
|
#define OPEN_ALWAYS 4
|
|
#define TRUNCATE_EXISTING 5
|
|
|
|
typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
|
|
DWORD dwFileAttributes;
|
|
FILETIME ftCreationTime;
|
|
FILETIME ftLastAccessTime;
|
|
FILETIME ftLastWriteTime;
|
|
DWORD nFileSizeHigh;
|
|
DWORD nFileSizeLow;
|
|
} WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall FlushFileBuffers (HANDLE hFile);
|
|
__declspec(dllimport) BOOL __stdcall CreateDirectoryW (const WCHAR *lpPathName, SECURITY_ATTRIBUTES *lpSecurityAttributes);
|
|
__declspec(dllimport) BOOL __stdcall RemoveDirectoryW (const WCHAR *lpPathName);
|
|
__declspec(dllimport) BOOL __stdcall FindNextFileW (HANDLE hFindFile, WIN32_FIND_DATAW *lpFindFileData);
|
|
__declspec(dllimport) BOOL __stdcall FindClose (HANDLE hFindFile);
|
|
|
|
__declspec(dllimport) HANDLE __stdcall FindFirstFileExW (const WCHAR *lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, VOID *lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, VOID *lpSearchFilter, DWORD dwAdditionalFlags);
|
|
__declspec(dllimport) BOOL __stdcall GetFileAttributesExW(const WCHAR *lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, VOID *lpFileInformation);
|
|
__declspec(dllimport) BOOL __stdcall GetFileSizeEx (HANDLE hFile, LARGE_INTEGER *lpFileSize);
|
|
__declspec(dllimport) BOOL __stdcall DeleteFileW (const WCHAR *lpFileName);
|
|
__declspec(dllimport) HANDLE __stdcall CreateFileW (const WCHAR *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
|
|
__declspec(dllimport) BOOL __stdcall ReadFile (HANDLE hFile, VOID *lpBuffer, DWORD nNumberOfBytesToRead, DWORD *lpNumberOfBytesRead, OVERLAPPED *lpOverlapped);
|
|
__declspec(dllimport) BOOL __stdcall WriteFile (HANDLE hFile, const VOID *lpBuffer, DWORD nNumberOfBytesToWrite, DWORD *lpNumberOfBytesWritten, OVERLAPPED *lpOverlapped);
|
|
__declspec(dllimport) BOOL __stdcall GetDiskFreeSpaceExW (WCHAR const *lpDirectoryName, ULARGE_INTEGER *lpFreeBytesAvailableToCaller, ULARGE_INTEGER *lpTotalNumberOfBytes, ULARGE_INTEGER *lpTotalNumberOfFreeBytes);
|
|
}
|
|
|
|
// NOTE: um/processenv.h ///////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) DWORD __stdcall GetCurrentDirectoryW (DWORD nBufferLength, WCHAR *lpBuffer);
|
|
__declspec(dllimport) HANDLE __stdcall GetStdHandle (DWORD nStdHandle);
|
|
__declspec(dllimport) WCHAR* __stdcall GetEnvironmentStringsW ();
|
|
__declspec(dllimport) BOOL __stdcall FreeEnvironmentStringsW(WCHAR *penv);
|
|
__declspec(dllimport) DWORD __stdcall GetEnvironmentVariableW(WCHAR const *lpName, WCHAR *lpBuffer, DWORD nSize);
|
|
__declspec(dllimport) BOOL __stdcall SetEnvironmentVariableW(WCHAR const *lpName, WCHAR const *lpValue);
|
|
}
|
|
|
|
// NOTE: um/psapi.h ////////////////////////////////////////////////////////////////////////////
|
|
typedef struct _PROCESS_MEMORY_COUNTERS {
|
|
DWORD cb;
|
|
DWORD PageFaultCount;
|
|
SIZE_T PeakWorkingSetSize;
|
|
SIZE_T WorkingSetSize;
|
|
SIZE_T QuotaPeakPagedPoolUsage;
|
|
SIZE_T QuotaPagedPoolUsage;
|
|
SIZE_T QuotaPeakNonPagedPoolUsage;
|
|
SIZE_T QuotaNonPagedPoolUsage;
|
|
SIZE_T PagefileUsage;
|
|
SIZE_T PeakPagefileUsage;
|
|
} PROCESS_MEMORY_COUNTERS;
|
|
typedef PROCESS_MEMORY_COUNTERS *PPROCESS_MEMORY_COUNTERS;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall GetProcessMemoryInfo(HANDLE Process, PPROCESS_MEMORY_COUNTERS ppsmemCounters, DWORD cb);
|
|
}
|
|
|
|
// NOTE: um/sysinfoapi.h ///////////////////////////////////////////////////////////////////////
|
|
typedef struct _SYSTEM_INFO {
|
|
union {
|
|
DWORD dwOemId; // Obsolete field...do not use
|
|
struct {
|
|
WORD wProcessorArchitecture;
|
|
WORD wReserved;
|
|
} DUMMYSTRUCTNAME;
|
|
} DUMMYUNIONNAME;
|
|
DWORD dwPageSize;
|
|
VOID *lpMinimumApplicationAddress;
|
|
VOID *lpMaximumApplicationAddress;
|
|
DWORD_PTR dwActiveProcessorMask;
|
|
DWORD dwNumberOfProcessors;
|
|
DWORD dwProcessorType;
|
|
DWORD dwAllocationGranularity;
|
|
WORD wProcessorLevel;
|
|
WORD wProcessorRevision;
|
|
} SYSTEM_INFO, *LPSYSTEM_INFO;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) VOID __stdcall GetSystemInfo(SYSTEM_INFO *lpSystemInfo);
|
|
__declspec(dllimport) VOID __stdcall GetSystemTime(SYSTEMTIME *lpSystemTime);
|
|
__declspec(dllimport) VOID __stdcall GetSystemTimeAsFileTime(FILETIME *lpSystemTimeAsFileTime);
|
|
__declspec(dllimport) VOID __stdcall GetLocalTime(SYSTEMTIME *lpSystemTime);
|
|
}
|
|
|
|
// NOTE: um/timezoneapi.h //////////////////////////////////////////////////////////////////////
|
|
typedef struct _TIME_ZONE_INFORMATION {
|
|
LONG Bias;
|
|
WCHAR StandardName[32];
|
|
SYSTEMTIME StandardDate;
|
|
LONG StandardBias;
|
|
WCHAR DaylightName[32];
|
|
SYSTEMTIME DaylightDate;
|
|
LONG DaylightBias;
|
|
} TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION, *LPTIME_ZONE_INFORMATION;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall FileTimeToSystemTime (const FILETIME* lpFileTime, SYSTEMTIME *lpSystemTime);
|
|
__declspec(dllimport) BOOL __stdcall SystemTimeToFileTime (const SYSTEMTIME* lpSystemTime, FILETIME *lpFileTime);
|
|
__declspec(dllimport) BOOL __stdcall TzSpecificLocalTimeToSystemTime(const TIME_ZONE_INFORMATION* lpTimeZoneInformation, const SYSTEMTIME* lpLocalTime, const LPSYSTEMTIME lpUniversalTime);
|
|
}
|
|
|
|
// NOTE: shared/windef.h ///////////////////////////////////////////////////////////////////////
|
|
typedef struct tagRECT {
|
|
LONG left;
|
|
LONG top;
|
|
LONG right;
|
|
LONG bottom;
|
|
} RECT;
|
|
|
|
struct HWND__ {
|
|
int unused;
|
|
};
|
|
typedef struct HWND__ *HWND;
|
|
|
|
struct DPI_AWARENESS_CONTEXT__ {
|
|
int unused;
|
|
};
|
|
typedef struct DPI_AWARENESS_CONTEXT__ *DPI_AWARENESS_CONTEXT;
|
|
|
|
typedef enum DPI_AWARENESS {
|
|
DPI_AWARENESS_INVALID = -1,
|
|
DPI_AWARENESS_UNAWARE = 0,
|
|
DPI_AWARENESS_SYSTEM_AWARE = 1,
|
|
DPI_AWARENESS_PER_MONITOR_AWARE = 2
|
|
} DPI_AWARENESS;
|
|
|
|
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)
|
|
|
|
// NOTE: um/winuser.h //////////////////////////////////////////////////////////////////////////
|
|
typedef struct tagWINDOWPLACEMENT {
|
|
UINT length;
|
|
UINT flags;
|
|
UINT showCmd;
|
|
POINT ptMinPosition;
|
|
POINT ptMaxPosition;
|
|
RECT rcNormalPosition;
|
|
#ifdef _MAC
|
|
RECT rcDevice;
|
|
#endif
|
|
} WINDOWPLACEMENT;
|
|
typedef WINDOWPLACEMENT *PWINDOWPLACEMENT, *LPWINDOWPLACEMENT;
|
|
|
|
#define SW_HIDE 0
|
|
#define SW_NORMAL 1
|
|
#define SW_MAXIMIZE 3
|
|
#define SW_SHOWNOACTIVATE 4
|
|
#define SW_SHOW 5
|
|
#define SW_FORCEMINIMIZE 11
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall GetWindowRect (HWND hWnd, RECT *lpRect);
|
|
__declspec(dllimport) BOOL __stdcall SetWindowPos (HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags);
|
|
__declspec(dllimport) UINT __stdcall GetWindowModuleFileNameA(HWND hwnd, LPSTR pszFileName, UINT cchFileNameMax);
|
|
__declspec(dllimport) BOOL __stdcall ShowWindow (HWND hWnd, int nCmdShow);
|
|
__declspec(dllimport) BOOL __stdcall GetWindowPlacement (HWND hWnd, WINDOWPLACEMENT *lpwndpl);
|
|
|
|
}
|
|
|
|
// NOTE: um/wininet.h //////////////////////////////////////////////////////////////////////////
|
|
typedef WORD INTERNET_PORT;
|
|
typedef VOID *HINTERNET;
|
|
|
|
// NOTE: um/winhttp.h //////////////////////////////////////////////////////////////////////////
|
|
#define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0
|
|
#define WINHTTP_ACCESS_TYPE_NO_PROXY 1
|
|
#define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3
|
|
#define WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY 4
|
|
|
|
#define INTERNET_DEFAULT_PORT 0 // use the protocol-specific default
|
|
#define INTERNET_DEFAULT_HTTP_PORT 80 // " " HTTP "
|
|
#define INTERNET_DEFAULT_HTTPS_PORT 443 // " " HTTPS "
|
|
|
|
// NOTE: WinHttpOpen
|
|
#define WINHTTP_FLAG_ASYNC 0x10000000 // this session is asynchronous (where supported)
|
|
#define WINHTTP_FLAG_SECURE_DEFAULTS 0x30000000 // note that this flag also forces async
|
|
|
|
// NOTE: WinHttpOpenRequest
|
|
#define WINHTTP_FLAG_SECURE 0x00800000 // use SSL if applicable (HTTPS)
|
|
#define WINHTTP_FLAG_ESCAPE_PERCENT 0x00000004 // if escaping enabled, escape percent as well
|
|
#define WINHTTP_FLAG_NULL_CODEPAGE 0x00000008 // assume all symbols are ASCII, use fast convertion
|
|
#define WINHTTP_FLAG_ESCAPE_DISABLE 0x00000040 // disable escaping
|
|
#define WINHTTP_FLAG_ESCAPE_DISABLE_QUERY 0x00000080 // if escaping enabled escape path part, but do not escape query
|
|
#define WINHTTP_FLAG_BYPASS_PROXY_CACHE 0x00000100 // add "pragma: no-cache" request header
|
|
#define WINHTTP_FLAG_REFRESH WINHTTP_FLAG_BYPASS_PROXY_CACHE
|
|
#define WINHTTP_FLAG_AUTOMATIC_CHUNKING 0x00000200 // Send request without content-length header or chunked TE
|
|
|
|
#define WINHTTP_NO_PROXY_NAME NULL
|
|
#define WINHTTP_NO_PROXY_BYPASS NULL
|
|
|
|
//
|
|
// WINHTTP_QUERY_FLAG_NUMBER - if this bit is set in the dwInfoLevel parameter of
|
|
// HttpQueryHeader(), then the value of the header will be converted to a number
|
|
// before being returned to the caller, if applicable
|
|
//
|
|
#define WINHTTP_QUERY_FLAG_NUMBER 0x20000000
|
|
|
|
#define WINHTTP_QUERY_MIME_VERSION 0
|
|
#define WINHTTP_QUERY_CONTENT_TYPE 1
|
|
#define WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING 2
|
|
#define WINHTTP_QUERY_CONTENT_ID 3
|
|
#define WINHTTP_QUERY_CONTENT_DESCRIPTION 4
|
|
#define WINHTTP_QUERY_CONTENT_LENGTH 5
|
|
#define WINHTTP_QUERY_CONTENT_LANGUAGE 6
|
|
#define WINHTTP_QUERY_ALLOW 7
|
|
#define WINHTTP_QUERY_PUBLIC 8
|
|
#define WINHTTP_QUERY_DATE 9
|
|
#define WINHTTP_QUERY_EXPIRES 10
|
|
#define WINHTTP_QUERY_LAST_MODIFIED 11
|
|
#define WINHTTP_QUERY_MESSAGE_ID 12
|
|
#define WINHTTP_QUERY_URI 13
|
|
#define WINHTTP_QUERY_DERIVED_FROM 14
|
|
#define WINHTTP_QUERY_COST 15
|
|
#define WINHTTP_QUERY_LINK 16
|
|
#define WINHTTP_QUERY_PRAGMA 17
|
|
#define WINHTTP_QUERY_VERSION 18 // special: part of status line
|
|
#define WINHTTP_QUERY_STATUS_CODE 19 // special: part of status line
|
|
#define WINHTTP_QUERY_STATUS_TEXT 20 // special: part of status line
|
|
#define WINHTTP_QUERY_RAW_HEADERS 21 // special: all headers as ASCIIZ
|
|
#define WINHTTP_QUERY_RAW_HEADERS_CRLF 22 // special: all headers
|
|
#define WINHTTP_QUERY_CONNECTION 23
|
|
#define WINHTTP_QUERY_ACCEPT 24
|
|
#define WINHTTP_QUERY_ACCEPT_CHARSET 25
|
|
#define WINHTTP_QUERY_ACCEPT_ENCODING 26
|
|
#define WINHTTP_QUERY_ACCEPT_LANGUAGE 27
|
|
#define WINHTTP_QUERY_AUTHORIZATION 28
|
|
#define WINHTTP_QUERY_CONTENT_ENCODING 29
|
|
#define WINHTTP_QUERY_FORWARDED 30
|
|
#define WINHTTP_QUERY_FROM 31
|
|
#define WINHTTP_QUERY_IF_MODIFIED_SINCE 32
|
|
#define WINHTTP_QUERY_LOCATION 33
|
|
#define WINHTTP_QUERY_ORIG_URI 34
|
|
#define WINHTTP_QUERY_REFERER 35
|
|
#define WINHTTP_QUERY_RETRY_AFTER 36
|
|
#define WINHTTP_QUERY_SERVER 37
|
|
#define WINHTTP_QUERY_TITLE 38
|
|
#define WINHTTP_QUERY_USER_AGENT 39
|
|
#define WINHTTP_QUERY_WWW_AUTHENTICATE 40
|
|
#define WINHTTP_QUERY_PROXY_AUTHENTICATE 41
|
|
#define WINHTTP_QUERY_ACCEPT_RANGES 42
|
|
#define WINHTTP_QUERY_SET_COOKIE 43
|
|
#define WINHTTP_QUERY_COOKIE 44
|
|
#define WINHTTP_QUERY_REQUEST_METHOD 45 // special: GET/POST etc.
|
|
#define WINHTTP_QUERY_REFRESH 46
|
|
#define WINHTTP_QUERY_CONTENT_DISPOSITION 47
|
|
|
|
// NOTE: WinHttpQueryHeaders prettifiers for optional parameters.
|
|
#define WINHTTP_HEADER_NAME_BY_INDEX NULL
|
|
#define WINHTTP_NO_OUTPUT_BUFFER NULL
|
|
#define WINHTTP_NO_HEADER_INDEX NULL
|
|
|
|
// NOTE: Http Response Status Codes
|
|
#define HTTP_STATUS_CONTINUE 100 // OK to continue with request
|
|
#define HTTP_STATUS_SWITCH_PROTOCOLS 101 // server has switched protocols in upgrade header
|
|
|
|
#define HTTP_STATUS_OK 200 // request completed
|
|
#define HTTP_STATUS_CREATED 201 // object created, reason = new URI
|
|
#define HTTP_STATUS_ACCEPTED 202 // async completion (TBS)
|
|
#define HTTP_STATUS_PARTIAL 203 // partial completion
|
|
#define HTTP_STATUS_NO_CONTENT 204 // no info to return
|
|
#define HTTP_STATUS_RESET_CONTENT 205 // request completed, but clear form
|
|
#define HTTP_STATUS_PARTIAL_CONTENT 206 // partial GET fulfilled
|
|
#define HTTP_STATUS_WEBDAV_MULTI_STATUS 207 // WebDAV Multi-Status
|
|
|
|
#define HTTP_STATUS_AMBIGUOUS 300 // server couldn't decide what to return
|
|
#define HTTP_STATUS_MOVED 301 // object permanently moved
|
|
#define HTTP_STATUS_REDIRECT 302 // object temporarily moved
|
|
#define HTTP_STATUS_REDIRECT_METHOD 303 // redirection w/ new access method
|
|
#define HTTP_STATUS_NOT_MODIFIED 304 // if-modified-since was not modified
|
|
#define HTTP_STATUS_USE_PROXY 305 // redirection to proxy, location header specifies proxy to use
|
|
#define HTTP_STATUS_REDIRECT_KEEP_VERB 307 // HTTP/1.1: keep same verb
|
|
#define HTTP_STATUS_PERMANENT_REDIRECT 308 // Object permanently moved keep verb
|
|
|
|
#define HTTP_STATUS_BAD_REQUEST 400 // invalid syntax
|
|
#define HTTP_STATUS_DENIED 401 // access denied
|
|
#define HTTP_STATUS_PAYMENT_REQ 402 // payment required
|
|
#define HTTP_STATUS_FORBIDDEN 403 // request forbidden
|
|
#define HTTP_STATUS_NOT_FOUND 404 // object not found
|
|
#define HTTP_STATUS_BAD_METHOD 405 // method is not allowed
|
|
#define HTTP_STATUS_NONE_ACCEPTABLE 406 // no response acceptable to client found
|
|
#define HTTP_STATUS_PROXY_AUTH_REQ 407 // proxy authentication required
|
|
#define HTTP_STATUS_REQUEST_TIMEOUT 408 // server timed out waiting for request
|
|
#define HTTP_STATUS_CONFLICT 409 // user should resubmit with more info
|
|
#define HTTP_STATUS_GONE 410 // the resource is no longer available
|
|
#define HTTP_STATUS_LENGTH_REQUIRED 411 // the server refused to accept request w/o a length
|
|
#define HTTP_STATUS_PRECOND_FAILED 412 // precondition given in request failed
|
|
#define HTTP_STATUS_REQUEST_TOO_LARGE 413 // request entity was too large
|
|
#define HTTP_STATUS_URI_TOO_LONG 414 // request URI too long
|
|
#define HTTP_STATUS_UNSUPPORTED_MEDIA 415 // unsupported media type
|
|
#define HTTP_STATUS_RETRY_WITH 449 // retry after doing the appropriate action.
|
|
|
|
#define HTTP_STATUS_SERVER_ERROR 500 // internal server error
|
|
#define HTTP_STATUS_NOT_SUPPORTED 501 // required not supported
|
|
#define HTTP_STATUS_BAD_GATEWAY 502 // error response received from gateway
|
|
#define HTTP_STATUS_SERVICE_UNAVAIL 503 // temporarily overloaded
|
|
#define HTTP_STATUS_GATEWAY_TIMEOUT 504 // timed out waiting for gateway
|
|
#define HTTP_STATUS_VERSION_NOT_SUP 505 // HTTP version not supported
|
|
|
|
#define HTTP_STATUS_FIRST HTTP_STATUS_CONTINUE
|
|
#define HTTP_STATUS_LAST HTTP_STATUS_VERSION_NOT_SUP
|
|
|
|
#define WINHTTP_CALLBACK_STATUS_RESOLVING_NAME 0x00000001
|
|
#define WINHTTP_CALLBACK_STATUS_NAME_RESOLVED 0x00000002
|
|
#define WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER 0x00000004
|
|
#define WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER 0x00000008
|
|
#define WINHTTP_CALLBACK_STATUS_SENDING_REQUEST 0x00000010
|
|
#define WINHTTP_CALLBACK_STATUS_REQUEST_SENT 0x00000020
|
|
#define WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE 0x00000040
|
|
#define WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED 0x00000080
|
|
#define WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION 0x00000100
|
|
#define WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED 0x00000200
|
|
#define WINHTTP_CALLBACK_STATUS_HANDLE_CREATED 0x00000400
|
|
#define WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING 0x00000800
|
|
#define WINHTTP_CALLBACK_STATUS_DETECTING_PROXY 0x00001000
|
|
#define WINHTTP_CALLBACK_STATUS_REDIRECT 0x00004000
|
|
#define WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE 0x00008000
|
|
#define WINHTTP_CALLBACK_STATUS_SECURE_FAILURE 0x00010000
|
|
#define WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE 0x00020000
|
|
#define WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE 0x00040000
|
|
#define WINHTTP_CALLBACK_STATUS_READ_COMPLETE 0x00080000
|
|
#define WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE 0x00100000
|
|
#define WINHTTP_CALLBACK_STATUS_REQUEST_ERROR 0x00200000
|
|
#define WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE 0x00400000
|
|
|
|
#define WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE 0x01000000
|
|
#define WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE 0x02000000
|
|
#define WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE 0x04000000
|
|
#define WINHTTP_CALLBACK_STATUS_SETTINGS_WRITE_COMPLETE 0x10000000
|
|
#define WINHTTP_CALLBACK_STATUS_SETTINGS_READ_COMPLETE 0x20000000
|
|
|
|
#define WINHTTP_CALLBACK_FLAG_RESOLVE_NAME (WINHTTP_CALLBACK_STATUS_RESOLVING_NAME | WINHTTP_CALLBACK_STATUS_NAME_RESOLVED)
|
|
#define WINHTTP_CALLBACK_FLAG_CONNECT_TO_SERVER (WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER | WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER)
|
|
#define WINHTTP_CALLBACK_FLAG_SEND_REQUEST (WINHTTP_CALLBACK_STATUS_SENDING_REQUEST | WINHTTP_CALLBACK_STATUS_REQUEST_SENT)
|
|
#define WINHTTP_CALLBACK_FLAG_RECEIVE_RESPONSE (WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE | WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED)
|
|
#define WINHTTP_CALLBACK_FLAG_CLOSE_CONNECTION (WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION | WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED)
|
|
#define WINHTTP_CALLBACK_FLAG_HANDLES (WINHTTP_CALLBACK_STATUS_HANDLE_CREATED | WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING)
|
|
#define WINHTTP_CALLBACK_FLAG_DETECTING_PROXY WINHTTP_CALLBACK_STATUS_DETECTING_PROXY
|
|
#define WINHTTP_CALLBACK_FLAG_REDIRECT WINHTTP_CALLBACK_STATUS_REDIRECT
|
|
#define WINHTTP_CALLBACK_FLAG_INTERMEDIATE_RESPONSE WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE
|
|
#define WINHTTP_CALLBACK_FLAG_SECURE_FAILURE WINHTTP_CALLBACK_STATUS_SECURE_FAILURE
|
|
#define WINHTTP_CALLBACK_FLAG_SENDREQUEST_COMPLETE WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE
|
|
#define WINHTTP_CALLBACK_FLAG_HEADERS_AVAILABLE WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE
|
|
#define WINHTTP_CALLBACK_FLAG_DATA_AVAILABLE WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE
|
|
#define WINHTTP_CALLBACK_FLAG_READ_COMPLETE WINHTTP_CALLBACK_STATUS_READ_COMPLETE
|
|
#define WINHTTP_CALLBACK_FLAG_WRITE_COMPLETE WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE
|
|
#define WINHTTP_CALLBACK_FLAG_REQUEST_ERROR WINHTTP_CALLBACK_STATUS_REQUEST_ERROR
|
|
|
|
#define WINHTTP_CALLBACK_FLAG_GETPROXYFORURL_COMPLETE WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE
|
|
|
|
#define WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS (WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE \
|
|
| WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE \
|
|
| WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE \
|
|
| WINHTTP_CALLBACK_STATUS_READ_COMPLETE \
|
|
| WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE \
|
|
| WINHTTP_CALLBACK_STATUS_REQUEST_ERROR \
|
|
| WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE)
|
|
|
|
#define WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS 0xffffffff
|
|
#define WINHTTP_INVALID_STATUS_CALLBACK ((WINHTTP_STATUS_CALLBACK)(-1L))
|
|
|
|
typedef struct _WINHTTP_EXTENDED_HEADER
|
|
{
|
|
union
|
|
{
|
|
CHAR const *pwszName;
|
|
WCHAR const *pszName;
|
|
};
|
|
union
|
|
{
|
|
WCHAR const *pwszValue;
|
|
CHAR const *pszValue;
|
|
};
|
|
} WINHTTP_EXTENDED_HEADER, *PWINHTTP_EXTENDED_HEADER;
|
|
|
|
typedef struct _WINHTTP_ASYNC_RESULT
|
|
{
|
|
DWORD *dwResult; // indicates which async API has encountered an error
|
|
DWORD dwError; // the error code if the API failed
|
|
} WINHTTP_ASYNC_RESULT, *LPWINHTTP_ASYNC_RESULT, *PWINHTTP_ASYNC_RESULT;
|
|
|
|
typedef
|
|
VOID
|
|
(*WINHTTP_STATUS_CALLBACK)(
|
|
HINTERNET hInternet,
|
|
DWORD *dwContext,
|
|
DWORD dwInternetStatus,
|
|
VOID *lpvStatusInformation,
|
|
DWORD dwStatusInformationLength
|
|
);
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) HINTERNET __stdcall WinHttpOpen(WCHAR const *pszAgentW, DWORD dwAccessType, WCHAR const *pszProxyW, WCHAR const *pszProxyBypassW, DWORD dwFlags);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpCloseHandle(HINTERNET hInternet);
|
|
__declspec(dllimport) HINTERNET __stdcall WinHttpConnect(HINTERNET hSession, WCHAR const *pswzServerName, INTERNET_PORT nServerPort, DWORD dwReserved);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpReadData(HINTERNET hRequest, VOID *lpBuffer, DWORD dwNumberOfBytesToRead, DWORD *lpdwNumberOfBytesRead);
|
|
__declspec(dllimport) HINTERNET __stdcall WinHttpOpenRequest(HINTERNET hConnect, WCHAR const *pwszVerb, WCHAR const *pwszObjectName, WCHAR const *pwszVersion, WCHAR const *pwszReferrer, WCHAR const *ppwszAcceptTypes, DWORD dwFlags);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpSendRequest(HINTERNET hRequest, WCHAR const *lpszHeaders, DWORD dwHeadersLength, VOID *lpOptional, DWORD dwOptionalLength, DWORD dwTotalLength, DWORD_PTR dwContext);
|
|
__declspec(dllimport) DWORD __stdcall WinHttpAddRequestHeadersEx(HINTERNET hRequest, DWORD dwModifiers, ULONGLONG ullFlags, ULONGLONG ullExtra, DWORD cHeaders, WINHTTP_EXTENDED_HEADER *pHeaders);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpSetCredentials(HINTERNET hRequest, // HINTERNET handle returned by WinHttpOpenRequest.
|
|
DWORD AuthTargets, // Only WINHTTP_AUTH_TARGET_SERVER and WINHTTP_AUTH_TARGET_PROXY are supported in this version and they are mutually exclusive
|
|
DWORD AuthScheme, // must be one of the supported Auth Schemes returned from WinHttpQueryAuthSchemes()
|
|
WCHAR * pwszUserName, // 1) NULL if default creds is to be used, in which case pszPassword will be ignored
|
|
WCHAR * pwszPassword, // 1) "" == Blank Password; 2)Parameter ignored if pszUserName is NULL; 3) Invalid to pass in NULL if pszUserName is not NULL
|
|
VOID * pAuthParams);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpQueryHeaders(HINTERNET hRequest, DWORD dwInfoLevel, WCHAR const *pwszName, VOID *lpBuffer, DWORD *lpdwBufferLength, DWORD *lpdwIndex);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpReceiveResponse(HINTERNET hRequest, VOID *lpReserved);
|
|
__declspec(dllimport) WINHTTP_STATUS_CALLBACK __stdcall WinHttpSetStatusCallback(HINTERNET hInternet, WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags, DWORD_PTR dwReserved);
|
|
}
|
|
|
|
// NOTE: um/DbgHelp.h //////////////////////////////////////////////////////////////////////////
|
|
#define SYMOPT_CASE_INSENSITIVE 0x00000001
|
|
#define SYMOPT_UNDNAME 0x00000002
|
|
#define SYMOPT_DEFERRED_LOADS 0x00000004
|
|
#define SYMOPT_NO_CPP 0x00000008
|
|
#define SYMOPT_LOAD_LINES 0x00000010
|
|
#define SYMOPT_OMAP_FIND_NEAREST 0x00000020
|
|
#define SYMOPT_LOAD_ANYTHING 0x00000040
|
|
#define SYMOPT_IGNORE_CVREC 0x00000080
|
|
#define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
|
|
#define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
|
|
#define SYMOPT_EXACT_SYMBOLS 0x00000400
|
|
#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
|
|
#define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
|
|
#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
|
|
#define SYMOPT_PUBLICS_ONLY 0x00004000
|
|
#define SYMOPT_NO_PUBLICS 0x00008000
|
|
#define SYMOPT_AUTO_PUBLICS 0x00010000
|
|
#define SYMOPT_NO_IMAGE_SEARCH 0x00020000
|
|
#define SYMOPT_SECURE 0x00040000
|
|
#define SYMOPT_NO_PROMPTS 0x00080000
|
|
#define SYMOPT_OVERWRITE 0x00100000
|
|
#define SYMOPT_IGNORE_IMAGEDIR 0x00200000
|
|
#define SYMOPT_FLAT_DIRECTORY 0x00400000
|
|
#define SYMOPT_FAVOR_COMPRESSED 0x00800000
|
|
#define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000
|
|
#define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000
|
|
#define SYMOPT_READONLY_CACHE 0x04000000
|
|
#define SYMOPT_SYMPATH_LAST 0x08000000
|
|
#define SYMOPT_DISABLE_FAST_SYMBOLS 0x10000000
|
|
#define SYMOPT_DISABLE_SYMSRV_TIMEOUT 0x20000000
|
|
#define SYMOPT_DISABLE_SRVSTAR_ON_STARTUP 0x40000000
|
|
#define SYMOPT_DEBUG 0x80000000
|
|
|
|
#define MAX_SYM_NAME 2000
|
|
|
|
typedef enum {
|
|
AddrMode1616,
|
|
AddrMode1632,
|
|
AddrModeReal,
|
|
AddrModeFlat
|
|
} ADDRESS_MODE;
|
|
|
|
typedef struct _tagADDRESS64 {
|
|
DWORD64 Offset;
|
|
WORD Segment;
|
|
ADDRESS_MODE Mode;
|
|
} ADDRESS64, *LPADDRESS64;
|
|
|
|
|
|
typedef struct _KDHELP64 {
|
|
DWORD64 Thread;
|
|
DWORD ThCallbackStack;
|
|
DWORD ThCallbackBStore;
|
|
DWORD NextCallback;
|
|
DWORD FramePointer;
|
|
DWORD64 KiCallUserMode;
|
|
DWORD64 KeUserCallbackDispatcher;
|
|
DWORD64 SystemRangeStart;
|
|
DWORD64 KiUserExceptionDispatcher;
|
|
DWORD64 StackBase;
|
|
DWORD64 StackLimit;
|
|
DWORD BuildVersion;
|
|
DWORD RetpolineStubFunctionTableSize;
|
|
DWORD64 RetpolineStubFunctionTable;
|
|
DWORD RetpolineStubOffset;
|
|
DWORD RetpolineStubSize;
|
|
DWORD64 Reserved0[2];
|
|
} KDHELP64, *PKDHELP64;
|
|
|
|
typedef struct _tagSTACKFRAME64 {
|
|
ADDRESS64 AddrPC; // program counter
|
|
ADDRESS64 AddrReturn; // return address
|
|
ADDRESS64 AddrFrame; // frame pointer
|
|
ADDRESS64 AddrStack; // stack pointer
|
|
ADDRESS64 AddrBStore; // backing store pointer
|
|
VOID *FuncTableEntry; // pointer to pdata/fpo or NULL
|
|
DWORD64 Params[4]; // possible arguments to the function
|
|
BOOL Far; // WOW far call
|
|
BOOL Virtual; // is this a virtual frame?
|
|
DWORD64 Reserved[3];
|
|
KDHELP64 KdHelp;
|
|
} STACKFRAME64;
|
|
|
|
typedef struct _IMAGEHLP_LINEW64 {
|
|
DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
|
|
VOID *Key; // internal
|
|
DWORD LineNumber; // line number in file
|
|
WCHAR *FileName; // full filename
|
|
DWORD64 Address; // first instruction of line
|
|
} IMAGEHLP_LINEW64;
|
|
|
|
typedef struct _SYMBOL_INFOW {
|
|
ULONG SizeOfStruct;
|
|
ULONG TypeIndex; // Type Index of symbol
|
|
ULONG64 Reserved[2];
|
|
ULONG Index;
|
|
ULONG Size;
|
|
ULONG64 ModBase; // Base Address of module comtaining this symbol
|
|
ULONG Flags;
|
|
ULONG64 Value; // Value of symbol, ValuePresent should be 1
|
|
ULONG64 Address; // Address of symbol including base address of module
|
|
ULONG Register; // register holding value or pointer to value
|
|
ULONG Scope; // scope of the symbol
|
|
ULONG Tag; // pdb classification
|
|
ULONG NameLen; // Actual length of name
|
|
ULONG MaxNameLen;
|
|
WCHAR Name[1]; // Name of symbol
|
|
} SYMBOL_INFOW;
|
|
|
|
typedef BOOL (__stdcall READ_PROCESS_MEMORY_ROUTINE64)(HANDLE hProcess, DWORD64 qwBaseAddress, VOID *lpBuffer, DWORD nSize, DWORD *lpNumberOfBytesRead);
|
|
typedef VOID * (__stdcall FUNCTION_TABLE_ACCESS_ROUTINE64)(HANDLE ahProcess, DWORD64 AddrBase);
|
|
typedef DWORD64(__stdcall GET_MODULE_BASE_ROUTINE64)(HANDLE hProcess, DWORD64 Address);
|
|
typedef DWORD64(__stdcall TRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess, HANDLE hThread, ADDRESS64 *lpaddr);
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall StackWalk64 (DWORD MachineType, HANDLE hProcess, HANDLE hThread, STACKFRAME64 *StackFrame, VOID *ContextRecord, READ_PROCESS_MEMORY_ROUTINE64 *ReadMemoryRoutine, FUNCTION_TABLE_ACCESS_ROUTINE64 *FunctionTableAccessRoutine, GET_MODULE_BASE_ROUTINE64 *GetModuleBaseRoutine, TRANSLATE_ADDRESS_ROUTINE64 *TranslateAddress);
|
|
__declspec(dllimport) BOOL __stdcall SymFromAddrW (HANDLE hProcess, DWORD64 Address, DWORD64 *Displacement, SYMBOL_INFOW *Symbol);
|
|
__declspec(dllimport) VOID * __stdcall SymFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase);
|
|
__declspec(dllimport) BOOL __stdcall SymGetLineFromAddrW64 (HANDLE hProcess, DWORD64 dwAddr, DWORD *pdwDisplacement, IMAGEHLP_LINEW64 *Line);
|
|
__declspec(dllimport) DWORD64 __stdcall SymGetModuleBase64 (HANDLE hProcess, DWORD64 qwAddr);
|
|
__declspec(dllimport) BOOL __stdcall SymRefreshModuleList (HANDLE hProcess);
|
|
};
|
|
|
|
// NOTE: um/errhandlingapi.h ///////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) DWORD __stdcall GetLastError(VOID);
|
|
}
|
|
|
|
// NOTE: um/libloaderapi.h /////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) HMODULE __stdcall LoadLibraryA (const CHAR *lpLibFileName);
|
|
__declspec(dllimport) BOOL __stdcall FreeLibrary (HMODULE hLibModule);
|
|
__declspec(dllimport) void * __stdcall GetProcAddress (HMODULE hModule, const CHAR *lpProcName);
|
|
__declspec(dllimport) HMODULE __stdcall GetModuleHandleA (const CHAR *lpModuleName);
|
|
__declspec(dllimport) DWORD __stdcall GetModuleFileNameW(HMODULE hModule, WCHAR *lpFilename, DWORD nSize);
|
|
}
|
|
|
|
// NOTE: um/synchapi.h /////////////////////////////////////////////////////////////////////////
|
|
typedef RTL_CONDITION_VARIABLE CONDITION_VARIABLE, *PCONDITION_VARIABLE;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) VOID __stdcall InitializeConditionVariable (CONDITION_VARIABLE *ConditionVariable);
|
|
__declspec(dllimport) VOID __stdcall WakeConditionVariable (CONDITION_VARIABLE *ConditionVariable);
|
|
__declspec(dllimport) VOID __stdcall WakeAllConditionVariable (CONDITION_VARIABLE *ConditionVariable);
|
|
__declspec(dllimport) BOOL __stdcall SleepConditionVariableCS (CONDITION_VARIABLE *ConditionVariable, CRITICAL_SECTION *CriticalSection, DWORD dwMilliseconds);
|
|
|
|
__declspec(dllimport) VOID __stdcall InitializeCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
|
__declspec(dllimport) VOID __stdcall EnterCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
|
__declspec(dllimport) VOID __stdcall LeaveCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
|
__declspec(dllimport) BOOL __stdcall InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount);
|
|
__declspec(dllimport) BOOL __stdcall InitializeCriticalSectionEx (CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount, DWORD Flags);
|
|
__declspec(dllimport) DWORD __stdcall SetCriticalSectionSpinCount (CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount);
|
|
__declspec(dllimport) BOOL __stdcall TryEnterCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
|
__declspec(dllimport) VOID __stdcall DeleteCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
|
|
|
__declspec(dllimport) DWORD __stdcall WaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds);
|
|
__declspec(dllimport) BOOL __stdcall ReleaseSemaphore (HANDLE hSemaphore, LONG lReleaseCount, LONG *lpPreviousCount);
|
|
__declspec(dllimport) VOID __stdcall Sleep (DWORD dwMilliseconds);
|
|
}
|
|
|
|
// NOTE: um/profileapi.h ///////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall QueryPerformanceCounter (LARGE_INTEGER* lpPerformanceCount);
|
|
__declspec(dllimport) BOOL __stdcall QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency);
|
|
}
|
|
|
|
// NOTE: um/processthreadsapi.h ////////////////////////////////////////////////////////////////
|
|
typedef struct _PROCESS_INFORMATION {
|
|
HANDLE hProcess;
|
|
HANDLE hThread;
|
|
DWORD dwProcessId;
|
|
DWORD dwThreadId;
|
|
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
|
|
|
|
typedef struct _STARTUPINFOW {
|
|
DWORD cb;
|
|
WCHAR *lpReserved;
|
|
WCHAR *lpDesktop;
|
|
WCHAR *lpTitle;
|
|
DWORD dwX;
|
|
DWORD dwY;
|
|
DWORD dwXSize;
|
|
DWORD dwYSize;
|
|
DWORD dwXCountChars;
|
|
DWORD dwYCountChars;
|
|
DWORD dwFillAttribute;
|
|
DWORD dwFlags;
|
|
WORD wShowWindow;
|
|
WORD cbReserved2;
|
|
BYTE *lpReserved2;
|
|
HANDLE hStdInput;
|
|
HANDLE hStdOutput;
|
|
HANDLE hStdError;
|
|
} STARTUPINFOW, *LPSTARTUPINFOW;
|
|
|
|
typedef DWORD (__stdcall *PTHREAD_START_ROUTINE)(
|
|
VOID *lpThreadParameter
|
|
);
|
|
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall CreateProcessW (WCHAR const *lpApplicationName, WCHAR *lpCommandLine, SECURITY_ATTRIBUTES *lpProcessAttributes, SECURITY_ATTRIBUTES *lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, VOID *lpEnvironment, WCHAR const *lpCurrentDirectory, STARTUPINFOW *lpStartupInfo, PROCESS_INFORMATION *lpProcessInformation);
|
|
__declspec(dllimport) HANDLE __stdcall CreateThread (SECURITY_ATTRIBUTES *lpThreadAttributes, SIZE_T dwStackSize, PTHREAD_START_ROUTINE lpStartAddress, VOID *lpParameter, DWORD dwCreationFlags, DWORD *lpThreadId);
|
|
__declspec(dllimport) DWORD __stdcall GetCurrentThreadId(VOID);
|
|
__declspec(dllimport) BOOL __stdcall GetExitCodeProcess(HANDLE hProcess, DWORD *lpExitCode);
|
|
__declspec(dllimport) void __stdcall ExitProcess (UINT uExitCode);
|
|
}
|
|
|
|
// NOTE: um/memoryapi.h ////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) VOID * __stdcall VirtualAlloc (VOID *lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
|
__declspec(dllimport) BOOL __stdcall VirtualProtect(VOID *lpAddress, SIZE_T dwSize, DWORD flNewProtect, DWORD *lpflOldProtect);
|
|
__declspec(dllimport) BOOL __stdcall VirtualFree (VOID *lpAddress, SIZE_T dwSize, DWORD dwFreeType);
|
|
}
|
|
|
|
// NOTE: shared/bcrypt.h ///////////////////////////////////////////////////////////////////////
|
|
typedef VOID *BCRYPT_ALG_HANDLE;
|
|
typedef LONG NTSTATUS;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) NTSTATUS __stdcall BCryptOpenAlgorithmProvider(BCRYPT_ALG_HANDLE *phAlgorithm, const WCHAR *pszAlgId, const WCHAR *pszImplementation, ULONG dwFlags);
|
|
__declspec(dllimport) NTSTATUS __stdcall BCryptGenRandom (BCRYPT_ALG_HANDLE hAlgorithm, UCHAR *pbBuffer, ULONG cbBuffer, ULONG dwFlags);
|
|
}
|
|
|
|
// NOTE: um/shellapi.h /////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) HINSTANCE __stdcall ShellExecuteA(HWND hwnd, CHAR const *lpOperation, CHAR const *lpFile, CHAR const *lpParameters, CHAR const *lpDirectory, INT nShowCmd);
|
|
__declspec(dllimport) HINSTANCE __stdcall ShellExecuteW(HWND hwnd, WCHAR const *lpOperation, WCHAR const *lpFile, WCHAR const *lpParameters, WCHAR const *lpDirectory, INT nShowCmd);
|
|
}
|
|
|
|
// NOTE: um/debugapi.h /////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall IsDebuggerPresent();
|
|
}
|
|
|
|
// NOTE: um/namedpipeapi.h /////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall CreatePipe (HANDLE *hReadPipe, HANDLE *hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize);
|
|
__declspec(dllimport) BOOL __stdcall PeekNamedPipe(HANDLE hNamedPipe, VOID *lpBuffer, DWORD nBufferSize, DWORD *lpBytesRead, DWORD *lpTotalBytesAvail, DWORD *lpBytesLeftThisMessage);
|
|
}
|
|
|
|
// NOTE: um/handleapi.h ////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
|
|
}
|
|
|
|
// NOTE: um/commdlg.h //////////////////////////////////////////////////////////////////////////
|
|
typedef UINT_PTR (__stdcall *LPOFNHOOKPROC)(HWND, UINT, WPARAM, LPARAM);
|
|
typedef struct tagOFNW {
|
|
DWORD lStructSize;
|
|
HWND hwndOwner;
|
|
HINSTANCE hInstance;
|
|
WCHAR const * lpstrFilter;
|
|
LPWSTR lpstrCustomFilter;
|
|
DWORD nMaxCustFilter;
|
|
DWORD nFilterIndex;
|
|
LPWSTR lpstrFile;
|
|
DWORD nMaxFile;
|
|
LPWSTR lpstrFileTitle;
|
|
DWORD nMaxFileTitle;
|
|
WCHAR const * lpstrInitialDir;
|
|
WCHAR const * lpstrTitle;
|
|
DWORD Flags;
|
|
WORD nFileOffset;
|
|
WORD nFileExtension;
|
|
WCHAR const * lpstrDefExt;
|
|
LPARAM lCustData;
|
|
LPOFNHOOKPROC lpfnHook;
|
|
WCHAR const * lpTemplateName;
|
|
#ifdef _MAC
|
|
LPEDITMENU lpEditInfo;
|
|
LPCSTR lpstrPrompt;
|
|
#endif
|
|
#if (_WIN32_WINNT >= 0x0500)
|
|
void * pvReserved;
|
|
DWORD dwReserved;
|
|
DWORD FlagsEx;
|
|
#endif // (_WIN32_WINNT >= 0x0500)
|
|
} OPENFILENAMEW, *LPOPENFILENAMEW;
|
|
|
|
|
|
#define OFN_READONLY 0x00000001
|
|
#define OFN_OVERWRITEPROMPT 0x00000002
|
|
#define OFN_HIDEREADONLY 0x00000004
|
|
#define OFN_NOCHANGEDIR 0x00000008
|
|
#define OFN_SHOWHELP 0x00000010
|
|
#define OFN_ENABLEHOOK 0x00000020
|
|
#define OFN_ENABLETEMPLATE 0x00000040
|
|
#define OFN_ENABLETEMPLATEHANDLE 0x00000080
|
|
#define OFN_NOVALIDATE 0x00000100
|
|
#define OFN_ALLOWMULTISELECT 0x00000200
|
|
#define OFN_EXTENSIONDIFFERENT 0x00000400
|
|
#define OFN_PATHMUSTEXIST 0x00000800
|
|
#define OFN_FILEMUSTEXIST 0x00001000
|
|
#define OFN_CREATEPROMPT 0x00002000
|
|
#define OFN_SHAREAWARE 0x00004000
|
|
#define OFN_NOREADONLYRETURN 0x00008000
|
|
#define OFN_NOTESTFILECREATE 0x00010000
|
|
#define OFN_NONETWORKBUTTON 0x00020000
|
|
#define OFN_NOLONGNAMES 0x00040000 // force no long names for 4.x modules
|
|
#if(WINVER >= 0x0400)
|
|
#define OFN_EXPLORER 0x00080000 // new look commdlg
|
|
#define OFN_NODEREFERENCELINKS 0x00100000
|
|
#define OFN_LONGNAMES 0x00200000 // force long names for 3.x modules
|
|
// OFN_ENABLEINCLUDENOTIFY and OFN_ENABLESIZING require
|
|
// Windows 2000 or higher to have any effect.
|
|
#define OFN_ENABLEINCLUDENOTIFY 0x00400000 // send include message to callback
|
|
#define OFN_ENABLESIZING 0x00800000
|
|
#endif /* WINVER >= 0x0400 */
|
|
#if (_WIN32_WINNT >= 0x0500)
|
|
#define OFN_DONTADDTORECENT 0x02000000
|
|
#define OFN_FORCESHOWHIDDEN 0x10000000 // Show All files including System and hidden files
|
|
#endif // (_WIN32_WINNT >= 0x0500)
|
|
|
|
//FlagsEx Values
|
|
#if (_WIN32_WINNT >= 0x0500)
|
|
#define OFN_EX_NOPLACESBAR 0x00000001
|
|
#endif // (_WIN32_WINNT >= 0x0500)
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall GetSaveFileNameW(LPOPENFILENAMEW);
|
|
__declspec(dllimport) BOOL __stdcall GetOpenFileNameW(LPOPENFILENAMEW);
|
|
}
|
|
|
|
// NOTE: um/shlwapi.h //////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall PathRelativePathToW(WCHAR *pszPath, WCHAR const *pszFrom, DWORD dwAttrFrom, WCHAR const *pszTo, DWORD dwAttrTo);
|
|
__declspec(dllimport) BOOL __stdcall PathIsRelativeW(WCHAR *pszPath);
|
|
}
|
|
|
|
// NOTE: um/pathcch.h //////////////////////////////////////////////////////////////////////////
|
|
typedef enum PATHCCH_OPTIONS
|
|
{
|
|
PATHCCH_NONE = 0x0,
|
|
|
|
// This option allows applications to gain access to long paths. It has two
|
|
// different behaviors. For process configured to enable long paths it will allow
|
|
// the returned path to be longer than the max path limit that is normally imposed.
|
|
// For process that are not this option will convert long paths into the extended
|
|
// length DOS device form (with \\?\ prefix) when the path is longer than the limit.
|
|
// This form is not length limited by the Win32 file system API on all versions of Windows.
|
|
// This second behavior is the same behavior for OSes that don't have the long path feature.
|
|
// This can not be specified with PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH.
|
|
PATHCCH_ALLOW_LONG_PATHS = 0x01,
|
|
|
|
// Can only be used when PATHCCH_ALLOW_LONG_PATHS is specified. This
|
|
// Forces the API to treat the caller as long path enabled, independent of the
|
|
// process's long name enabled state. Cannot be used with PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS.
|
|
PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS = 0x02,
|
|
|
|
// Can only be used when PATHCCH_ALLOW_LONG_PATHS is specified. This
|
|
// Forces the API to treat the caller as long path disabled, independent of the
|
|
// process's long name enabled state. Cannot be used with PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS.
|
|
PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS = 0x04,
|
|
|
|
// Disable the normalization of path segments that includes removing trailing dots and spaces.
|
|
// This enables access to paths that win32 path normalization will block.
|
|
PATHCCH_DO_NOT_NORMALIZE_SEGMENTS = 0x08,
|
|
|
|
// Convert the input path into the extended length DOS device path form (with the \\?\ prefix)
|
|
// if not already in that form. This enables access to paths that are otherwise not addressable
|
|
// due to Win32 normalization rules (that can strip trailing dots and spaces) and path
|
|
// length limitations. This option implies the same behavior of PATHCCH_DO_NOT_NORMALIZE_SEGMENTS.
|
|
// This can not be specified with PATHCCH_ALLOW_LONG_PATHS.
|
|
PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH = 0x10,
|
|
|
|
// When combining or normalizing a path ensure there is a trailing backslash.
|
|
PATHCCH_ENSURE_TRAILING_SLASH = 0x020,
|
|
|
|
// Convert forward slashes to back slashes and collapse multiple slashes.
|
|
// This is needed to to support sub-path or identity comparisons.
|
|
PATHCCH_CANONICALIZE_SLASHES = 0x040,
|
|
} PATHCCH_OPTIONS;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) HRESULT __stdcall PathCchCanonicalizeEx(PWSTR pszPathOut, size_t cchPathOut, WCHAR const *pszPathIn, ULONG dwFlags);
|
|
};
|
|
|
|
// NOTE: um/errhandlingapi.h ///////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) VOID __stdcall RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, const ULONG_PTR* lpArguments);
|
|
};
|
|
|
|
// NOTE: include/excpt.h ///////////////////////////////////////////////////////////////////
|
|
#define EXCEPTION_EXECUTE_HANDLER 1
|
|
#define EXCEPTION_CONTINUE_SEARCH 0
|
|
#define EXCEPTION_CONTINUE_EXECUTION (-1)
|
|
|
|
DN_MSVC_WARNING_POP
|
|
#endif // !defined(_INC_WINDOWS)
|
|
#endif // !defined(DN_OS_WINDOWS_H)
|
|
// DN: Single header generator commented out this header => #include "OS/dn_os_w32.h"
|
|
#if !defined(DN_OS_WIN32_H)
|
|
#define DN_OS_WIN32_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "dn_os_windows.h"
|
|
#if !defined(DN_OS_WINDOWS_H)
|
|
#define DN_OS_WINDOWS_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
|
|
#pragma comment(lib, "bcrypt")
|
|
#pragma comment(lib, "winhttp")
|
|
#pragma comment(lib, "dbghelp")
|
|
#pragma comment(lib, "comdlg32")
|
|
#pragma comment(lib, "pathcch")
|
|
#pragma comment(lib, "Shell32") // ShellExecuteW
|
|
#pragma comment(lib, "shlwapi")
|
|
#endif
|
|
|
|
#if defined(DN_NO_WINDOWS_H_REPLACEMENT_HEADER) || defined(_INC_WINDOWS)
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <Windows.h> // LONG
|
|
#include <bcrypt.h> // DN_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc
|
|
#include <shellapi.h> // DN_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc
|
|
#include <shlwapi.h> // PathRelativePathTO
|
|
#include <pathcch.h> // PathCchCanonicalizeEx
|
|
#include <winhttp.h> // WinHttp*
|
|
#include <psapi.h> // PROCESS_MEMORY_COUNTERS_EX2
|
|
#include <commdlg.h> // OPENFILENAMEW
|
|
#include <DbgHelp.h>
|
|
#else
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
|
|
|
|
// NOTE: basetsd.h /////////////////////////////////////////////////////////////////////////////
|
|
typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
|
|
typedef unsigned __int64 UINT_PTR, *PUINT_PTR;
|
|
typedef ULONG_PTR SIZE_T, *PSIZE_T;
|
|
typedef __int64 LONG_PTR, *PLONG_PTR;
|
|
typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
|
|
typedef unsigned __int64 ULONG64, *PULONG64;
|
|
typedef unsigned __int64 DWORD64, *PDWORD64;
|
|
|
|
// NOTE: shared/minwindef.h ////////////////////////////////////////////////////////////////////
|
|
struct HINSTANCE__ {
|
|
int unused;
|
|
};
|
|
typedef struct HINSTANCE__ *HINSTANCE;
|
|
|
|
typedef unsigned long DWORD;
|
|
typedef int BOOL;
|
|
typedef int INT;
|
|
typedef unsigned long ULONG;
|
|
typedef unsigned int UINT;
|
|
typedef unsigned short WORD;
|
|
typedef unsigned char BYTE;
|
|
typedef unsigned char UCHAR;
|
|
typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */
|
|
typedef void * HANDLE;
|
|
typedef HANDLE HLOCAL;
|
|
|
|
typedef unsigned __int64 WPARAM;
|
|
typedef LONG_PTR LPARAM;
|
|
typedef LONG_PTR LRESULT;
|
|
|
|
#define MAX_PATH 260
|
|
|
|
typedef struct _FILETIME {
|
|
DWORD dwLowDateTime;
|
|
DWORD dwHighDateTime;
|
|
} FILETIME, *PFILETIME, *LPFILETIME;
|
|
|
|
// NOTE: shared/winerror.h /////////////////////////////////////////////////////////////////////
|
|
// NOTE: GetModuleFileNameW
|
|
#define ERROR_INSUFFICIENT_BUFFER 122L // dderror
|
|
|
|
// NOTE: um/winnls.h ///////////////////////////////////////////////////////////////////////////
|
|
// NOTE: MultiByteToWideChar
|
|
#define CP_UTF8 65001 // UTF-8 translation
|
|
|
|
// NOTE: um/winnt.h ////////////////////////////////////////////////////////////////////////////
|
|
typedef void VOID;
|
|
typedef __int64 LONGLONG;
|
|
typedef unsigned __int64 ULONGLONG;
|
|
typedef void * HANDLE;
|
|
typedef char CHAR;
|
|
typedef short SHORT;
|
|
typedef long LONG;
|
|
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
|
|
typedef CHAR * NPSTR, *LPSTR, *PSTR;
|
|
typedef WCHAR * NWPSTR, *LPWSTR, *PWSTR;
|
|
typedef long HRESULT;
|
|
|
|
// NOTE: VirtualAlloc: Allocation Type
|
|
#define MEM_RESERVE 0x00002000
|
|
#define MEM_COMMIT 0x00001000
|
|
#define MEM_DECOMMIT 0x00004000
|
|
#define MEM_RELEASE 0x00008000
|
|
|
|
// NOTE: VirtualAlloc: Page Permissions
|
|
#define PAGE_NOACCESS 0x01
|
|
#define PAGE_READONLY 0x02
|
|
#define PAGE_READWRITE 0x04
|
|
#define PAGE_GUARD 0x100
|
|
|
|
// NOTE: HeapAlloc
|
|
#define HEAP_ZERO_MEMORY 0x00000008
|
|
#define HEAP_NO_SERIALIZE 0x00000001
|
|
#define HEAP_GROWABLE 0x00000002
|
|
#define HEAP_GENERATE_EXCEPTIONS 0x00000004
|
|
#define HEAP_ZERO_MEMORY 0x00000008
|
|
#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010
|
|
#define HEAP_TAIL_CHECKING_ENABLED 0x00000020
|
|
#define HEAP_FREE_CHECKING_ENABLED 0x00000040
|
|
#define HEAP_DISABLE_COALESCE_ON_FREE 0x00000080
|
|
#define HEAP_CREATE_ALIGN_16 0x00010000
|
|
#define HEAP_CREATE_ENABLE_TRACING 0x00020000
|
|
#define HEAP_CREATE_ENABLE_EXECUTE 0x00040000
|
|
#define HEAP_MAXIMUM_TAG 0x0FFF
|
|
#define HEAP_PSEUDO_TAG_FLAG 0x8000
|
|
#define HEAP_TAG_SHIFT 18
|
|
#define HEAP_CREATE_SEGMENT_HEAP 0x00000100
|
|
#define HEAP_CREATE_HARDENED 0x00000200
|
|
|
|
// NOTE: FormatMessageA
|
|
#define MAKELANGID(p, s) ((((WORD )(s)) << 10) | (WORD )(p))
|
|
#define LANG_NEUTRAL 0x00
|
|
#define SUBLANG_DEFAULT 0x01 // user default
|
|
|
|
// NOTE: CreateFile
|
|
#define GENERIC_READ (0x80000000L)
|
|
#define GENERIC_WRITE (0x40000000L)
|
|
#define GENERIC_EXECUTE (0x20000000L)
|
|
#define GENERIC_ALL (0x10000000L)
|
|
|
|
#define FILE_APPEND_DATA (0x0004) // file
|
|
|
|
// NOTE: CreateFile/FindFirstFile
|
|
#define FILE_SHARE_READ 0x00000001
|
|
#define FILE_SHARE_WRITE 0x00000002
|
|
#define FILE_SHARE_DELETE 0x00000004
|
|
|
|
#define FILE_ATTRIBUTE_READONLY 0x00000001
|
|
#define FILE_ATTRIBUTE_HIDDEN 0x00000002
|
|
#define FILE_ATTRIBUTE_SYSTEM 0x00000004
|
|
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
|
|
#define FILE_ATTRIBUTE_NORMAL 0x00000080
|
|
|
|
// NOTE: STACKFRAME64
|
|
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
|
|
|
|
// NOTE: WaitForSingleObject
|
|
#define WAIT_TIMEOUT 258L // dderror
|
|
#define STATUS_WAIT_0 ((DWORD )0x00000000L)
|
|
#define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L)
|
|
|
|
#define S_OK ((HRESULT)0L)
|
|
#define S_FALSE ((HRESULT)1L)
|
|
|
|
typedef union _ULARGE_INTEGER {
|
|
struct {
|
|
DWORD LowPart;
|
|
DWORD HighPart;
|
|
} DUMMYSTRUCTNAME;
|
|
struct {
|
|
DWORD LowPart;
|
|
DWORD HighPart;
|
|
} u;
|
|
ULONGLONG QuadPart;
|
|
} ULARGE_INTEGER;
|
|
|
|
typedef union _LARGE_INTEGER {
|
|
struct {
|
|
DWORD LowPart;
|
|
LONG HighPart;
|
|
} DUMMYSTRUCTNAME;
|
|
struct {
|
|
DWORD LowPart;
|
|
LONG HighPart;
|
|
} u;
|
|
LONGLONG QuadPart;
|
|
} LARGE_INTEGER;
|
|
|
|
typedef struct __declspec(align(16)) _M128A {
|
|
ULONGLONG Low;
|
|
LONGLONG High;
|
|
} M128A, *PM128A;
|
|
|
|
typedef struct __declspec(align(16)) _XSAVE_FORMAT {
|
|
WORD ControlWord;
|
|
WORD StatusWord;
|
|
BYTE TagWord;
|
|
BYTE Reserved1;
|
|
WORD ErrorOpcode;
|
|
DWORD ErrorOffset;
|
|
WORD ErrorSelector;
|
|
WORD Reserved2;
|
|
DWORD DataOffset;
|
|
WORD DataSelector;
|
|
WORD Reserved3;
|
|
DWORD MxCsr;
|
|
DWORD MxCsr_Mask;
|
|
M128A FloatRegisters[8];
|
|
#if defined(_WIN64)
|
|
M128A XmmRegisters[16];
|
|
BYTE Reserved4[96];
|
|
#else
|
|
M128A XmmRegisters[8];
|
|
BYTE Reserved4[224];
|
|
#endif
|
|
} XSAVE_FORMAT, *PXSAVE_FORMAT;
|
|
typedef XSAVE_FORMAT XMM_SAVE_AREA32, *PXMM_SAVE_AREA32;
|
|
|
|
typedef struct __declspec(align(16)) _CONTEXT {
|
|
DWORD64 P1Home;
|
|
DWORD64 P2Home;
|
|
DWORD64 P3Home;
|
|
DWORD64 P4Home;
|
|
DWORD64 P5Home;
|
|
DWORD64 P6Home;
|
|
DWORD ContextFlags;
|
|
DWORD MxCsr;
|
|
WORD SegCs;
|
|
WORD SegDs;
|
|
WORD SegEs;
|
|
WORD SegFs;
|
|
WORD SegGs;
|
|
WORD SegSs;
|
|
DWORD EFlags;
|
|
DWORD64 Dr0;
|
|
DWORD64 Dr1;
|
|
DWORD64 Dr2;
|
|
DWORD64 Dr3;
|
|
DWORD64 Dr6;
|
|
DWORD64 Dr7;
|
|
DWORD64 Rax;
|
|
DWORD64 Rcx;
|
|
DWORD64 Rdx;
|
|
DWORD64 Rbx;
|
|
DWORD64 Rsp;
|
|
DWORD64 Rbp;
|
|
DWORD64 Rsi;
|
|
DWORD64 Rdi;
|
|
DWORD64 R8;
|
|
DWORD64 R9;
|
|
DWORD64 R10;
|
|
DWORD64 R11;
|
|
DWORD64 R12;
|
|
DWORD64 R13;
|
|
DWORD64 R14;
|
|
DWORD64 R15;
|
|
DWORD64 Rip;
|
|
|
|
union {
|
|
XMM_SAVE_AREA32 FltSave;
|
|
|
|
struct {
|
|
M128A Header[2];
|
|
M128A Legacy[8];
|
|
M128A Xmm0;
|
|
M128A Xmm1;
|
|
M128A Xmm2;
|
|
M128A Xmm3;
|
|
M128A Xmm4;
|
|
M128A Xmm5;
|
|
M128A Xmm6;
|
|
M128A Xmm7;
|
|
M128A Xmm8;
|
|
M128A Xmm9;
|
|
M128A Xmm10;
|
|
M128A Xmm11;
|
|
M128A Xmm12;
|
|
M128A Xmm13;
|
|
M128A Xmm14;
|
|
M128A Xmm15;
|
|
} DUMMYSTRUCTNAME;
|
|
} DUMMYUNIONNAME;
|
|
|
|
M128A VectorRegister[26];
|
|
DWORD64 VectorControl;
|
|
DWORD64 DebugControl;
|
|
DWORD64 LastBranchToRip;
|
|
DWORD64 LastBranchFromRip;
|
|
DWORD64 LastExceptionToRip;
|
|
DWORD64 LastExceptionFromRip;
|
|
} CONTEXT;
|
|
|
|
typedef struct _LIST_ENTRY {
|
|
struct _LIST_ENTRY *Flink;
|
|
struct _LIST_ENTRY *Blink;
|
|
} LIST_ENTRY, *PLIST_ENTRY, PRLIST_ENTRY;
|
|
|
|
typedef struct _RTL_CRITICAL_SECTION_DEBUG {
|
|
WORD Type;
|
|
WORD CreatorBackTraceIndex;
|
|
struct _RTL_CRITICAL_SECTION *CriticalSection;
|
|
LIST_ENTRY ProcessLocksList;
|
|
DWORD EntryCount;
|
|
DWORD ContentionCount;
|
|
DWORD Flags;
|
|
WORD CreatorBackTraceIndexHigh;
|
|
WORD Identifier;
|
|
} RTL_CRITICAL_SECTION_DEBUG, *PRTL_CRITICAL_SECTION_DEBUG, RTL_RESOURCE_DEBUG, *PRTL_RESOURCE_DEBUG;
|
|
|
|
typedef struct _RTL_CONDITION_VARIABLE {
|
|
VOID *Ptr;
|
|
} RTL_CONDITION_VARIABLE, *PRTL_CONDITION_VARIABLE;
|
|
|
|
#pragma pack(push, 8)
|
|
typedef struct _RTL_CRITICAL_SECTION {
|
|
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
|
|
|
|
//
|
|
// The following three fields control entering and exiting the critical
|
|
// section for the resource
|
|
//
|
|
|
|
LONG LockCount;
|
|
LONG RecursionCount;
|
|
HANDLE OwningThread; // from the thread's ClientId->UniqueThread
|
|
HANDLE LockSemaphore;
|
|
ULONG_PTR SpinCount; // force size on 64-bit systems when packed
|
|
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
|
|
#pragma pack(pop)
|
|
|
|
typedef struct _MODLOAD_DATA {
|
|
DWORD ssize; // size of this struct
|
|
DWORD ssig; // signature identifying the passed data
|
|
VOID *data; // pointer to passed data
|
|
DWORD size; // size of passed data
|
|
DWORD flags; // options
|
|
} MODLOAD_DATA, *PMODLOAD_DATA;
|
|
|
|
#define SLMFLAG_VIRTUAL 0x1
|
|
#define SLMFLAG_ALT_INDEX 0x2
|
|
#define SLMFLAG_NO_SYMBOLS 0x4
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) VOID __stdcall RtlCaptureContext(CONTEXT *ContextRecord);
|
|
__declspec(dllimport) HANDLE __stdcall GetCurrentProcess(void);
|
|
__declspec(dllimport) HANDLE __stdcall GetCurrentThread(void);
|
|
__declspec(dllimport) DWORD __stdcall SymSetOptions(DWORD SymOptions);
|
|
__declspec(dllimport) BOOL __stdcall SymInitialize(HANDLE hProcess, const CHAR* UserSearchPath, BOOL fInvadeProcess);
|
|
__declspec(dllimport) DWORD64 __stdcall SymLoadModuleEx(HANDLE hProcess, HANDLE hFile, CHAR const *ImageName, CHAR const *ModuleName, DWORD64 BaseOfDll, DWORD DllSize, MODLOAD_DATA *Data, DWORD Flags);
|
|
__declspec(dllimport) BOOL __stdcall SymUnloadModule64(HANDLE hProcess, DWORD64 BaseOfDll);
|
|
}
|
|
|
|
// NOTE: um/heapapi.h ////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) HANDLE __stdcall HeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize);
|
|
__declspec(dllimport) BOOL __stdcall HeapDestroy(HANDLE hHeap);
|
|
__declspec(dllimport) VOID * __stdcall HeapAlloc(HANDLE hHeap, DWORD dwFlags,SIZE_T dwBytes);
|
|
__declspec(dllimport) VOID * __stdcall HeapReAlloc(HANDLE hHeap, DWORD dwFlags, VOID *lpMem, SIZE_T dwBytes);
|
|
__declspec(dllimport) BOOL __stdcall HeapFree(HANDLE hHeap, DWORD dwFlags, VOID *lpMem);
|
|
__declspec(dllimport) SIZE_T __stdcall HeapSize(HANDLE hHeap, DWORD dwFlags, VOID const *lpMem);
|
|
__declspec(dllimport) HANDLE __stdcall GetProcessHeap(VOID);
|
|
__declspec(dllimport) SIZE_T __stdcall HeapCompact(HANDLE hHeap, DWORD dwFlags);
|
|
}
|
|
|
|
// NOTE: shared/windef.h ////////////////////////////////////////////////////////////////////
|
|
typedef struct tagPOINT
|
|
{
|
|
LONG x;
|
|
LONG y;
|
|
} POINT, *PPOINT, *NPPOINT, *LPPOINT;
|
|
|
|
// NOTE: handleapi.h ///////////////////////////////////////////////////////////////////////////
|
|
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall CloseHandle(HANDLE hObject);
|
|
}
|
|
|
|
// NOTE: consoleapi.h ///////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall WriteConsoleA(HANDLE hConsoleOutput, const VOID* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD *lpNumberOfCharsWritten, VOID *lpReserved);
|
|
__declspec(dllimport) BOOL __stdcall AllocConsole(VOID);
|
|
__declspec(dllimport) BOOL __stdcall FreeConsole(VOID);
|
|
__declspec(dllimport) BOOL __stdcall AttachConsole(DWORD dwProcessId);
|
|
__declspec(dllimport) BOOL __stdcall GetConsoleMode(HANDLE hConsoleHandle, DWORD *lpMode);
|
|
}
|
|
|
|
// NOTE: um/minwinbase.h ///////////////////////////////////////////////////////////////////////
|
|
// NOTE: FindFirstFile
|
|
#define FIND_FIRST_EX_CASE_SENSITIVE 0x00000001
|
|
#define FIND_FIRST_EX_LARGE_FETCH 0x00000002
|
|
|
|
// NOTE: WaitFor..
|
|
#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
|
|
#define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 )
|
|
#define WAIT_ABANDONED ((STATUS_ABANDONED_WAIT_0 ) + 0 )
|
|
#define WAIT_ABANDONED_0 ((STATUS_ABANDONED_WAIT_0 ) + 0 )
|
|
|
|
// NOTE: CreateProcessW
|
|
#define CREATE_UNICODE_ENVIRONMENT 0x00000400
|
|
#define CREATE_NO_WINDOW 0x08000000
|
|
|
|
typedef enum _GET_FILEEX_INFO_LEVELS {
|
|
GetFileExInfoStandard,
|
|
GetFileExMaxInfoLevel
|
|
} GET_FILEEX_INFO_LEVELS;
|
|
|
|
typedef struct _SECURITY_ATTRIBUTES {
|
|
DWORD nLength;
|
|
VOID *lpSecurityDescriptor;
|
|
BOOL bInheritHandle;
|
|
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
|
|
|
|
typedef enum _FINDEX_INFO_LEVELS {
|
|
FindExInfoStandard,
|
|
FindExInfoBasic,
|
|
FindExInfoMaxInfoLevel
|
|
} FINDEX_INFO_LEVELS;
|
|
|
|
typedef enum _FINDEX_SEARCH_OPS {
|
|
FindExSearchNameMatch,
|
|
FindExSearchLimitToDirectories,
|
|
FindExSearchLimitToDevices,
|
|
FindExSearchMaxSearchOp
|
|
} FINDEX_SEARCH_OPS;
|
|
|
|
typedef struct _WIN32_FIND_DATAW {
|
|
DWORD dwFileAttributes;
|
|
FILETIME ftCreationTime;
|
|
FILETIME ftLastAccessTime;
|
|
FILETIME ftLastWriteTime;
|
|
DWORD nFileSizeHigh;
|
|
DWORD nFileSizeLow;
|
|
DWORD dwReserved0;
|
|
DWORD dwReserved1;
|
|
WCHAR cFileName[ MAX_PATH ];
|
|
WCHAR cAlternateFileName[ 14 ];
|
|
#ifdef _MAC
|
|
DWORD dwFileType;
|
|
DWORD dwCreatorType;
|
|
WORD wFinderFlags;
|
|
#endif
|
|
} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;
|
|
|
|
typedef struct _SYSTEMTIME {
|
|
WORD wYear;
|
|
WORD wMonth;
|
|
WORD wDayOfWeek;
|
|
WORD wDay;
|
|
WORD wHour;
|
|
WORD wMinute;
|
|
WORD wSecond;
|
|
WORD wMilliseconds;
|
|
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
|
|
|
|
typedef struct _OVERLAPPED {
|
|
ULONG_PTR Internal;
|
|
ULONG_PTR InternalHigh;
|
|
union {
|
|
struct {
|
|
DWORD Offset;
|
|
DWORD OffsetHigh;
|
|
} DUMMYSTRUCTNAME;
|
|
VOID *Pointer;
|
|
} DUMMYUNIONNAME;
|
|
|
|
HANDLE hEvent;
|
|
} OVERLAPPED, *LPOVERLAPPED;
|
|
|
|
typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;
|
|
|
|
#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
|
|
#define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 )
|
|
|
|
#define INFINITE 0xFFFFFFFF // Wait/Synchronisation: Infinite timeout
|
|
|
|
#define STD_INPUT_HANDLE ((DWORD)-10)
|
|
#define STD_OUTPUT_HANDLE ((DWORD)-11)
|
|
#define STD_ERROR_HANDLE ((DWORD)-12)
|
|
|
|
#define HANDLE_FLAG_INHERIT 0x00000001
|
|
#define HANDLE_FLAG_PROTECT_FROM_CLOSE 0x00000002
|
|
|
|
// NOTE: MoveFile
|
|
#define MOVEFILE_REPLACE_EXISTING 0x00000001
|
|
#define MOVEFILE_COPY_ALLOWED 0x00000002
|
|
|
|
// NOTE: FormatMessageA
|
|
#define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100
|
|
#define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200
|
|
#define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
|
|
#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
|
|
|
|
// NOTE: CreateProcessW
|
|
#define STARTF_USESTDHANDLES 0x00000100
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall MoveFileExW (const WCHAR *lpExistingFileName, const WCHAR *lpNewFileName, DWORD dwFlags);
|
|
__declspec(dllimport) BOOL __stdcall CopyFileW (const WCHAR *lpExistingFileName, const WCHAR *lpNewFileName, BOOL bFailIfExists);
|
|
__declspec(dllimport) HANDLE __stdcall CreateSemaphoreA(SECURITY_ATTRIBUTES *lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, const CHAR *lpName);
|
|
__declspec(dllimport) DWORD __stdcall FormatMessageW (DWORD dwFlags, VOID const *lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPWSTR lpBuffer, DWORD nSize, va_list *Arguments);
|
|
__declspec(dllimport) HLOCAL __stdcall LocalFree (HLOCAL hMem);
|
|
}
|
|
|
|
// NOTE: um/stringapiset.h /////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, const CHAR *lpMultiByteStr, int cbMultiByte, WCHAR *lpWideCharStr, int cchWideChar);
|
|
__declspec(dllimport) int __stdcall WideCharToMultiByte(UINT CodePage, DWORD dwFlags, const WCHAR *lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, const CHAR *lpDefaultChar, BOOL *lpUsedDefaultChar);
|
|
}
|
|
|
|
// NOTE: um/fileapi.h //////////////////////////////////////////////////////////////////////////
|
|
#define INVALID_FILE_SIZE ((DWORD)0xFFFFFFFF)
|
|
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
|
|
|
|
// NOTE: CreateFile
|
|
#define CREATE_NEW 1
|
|
#define CREATE_ALWAYS 2
|
|
#define OPEN_EXISTING 3
|
|
#define OPEN_ALWAYS 4
|
|
#define TRUNCATE_EXISTING 5
|
|
|
|
typedef struct _WIN32_FILE_ATTRIBUTE_DATA {
|
|
DWORD dwFileAttributes;
|
|
FILETIME ftCreationTime;
|
|
FILETIME ftLastAccessTime;
|
|
FILETIME ftLastWriteTime;
|
|
DWORD nFileSizeHigh;
|
|
DWORD nFileSizeLow;
|
|
} WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall FlushFileBuffers (HANDLE hFile);
|
|
__declspec(dllimport) BOOL __stdcall CreateDirectoryW (const WCHAR *lpPathName, SECURITY_ATTRIBUTES *lpSecurityAttributes);
|
|
__declspec(dllimport) BOOL __stdcall RemoveDirectoryW (const WCHAR *lpPathName);
|
|
__declspec(dllimport) BOOL __stdcall FindNextFileW (HANDLE hFindFile, WIN32_FIND_DATAW *lpFindFileData);
|
|
__declspec(dllimport) BOOL __stdcall FindClose (HANDLE hFindFile);
|
|
|
|
__declspec(dllimport) HANDLE __stdcall FindFirstFileExW (const WCHAR *lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, VOID *lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, VOID *lpSearchFilter, DWORD dwAdditionalFlags);
|
|
__declspec(dllimport) BOOL __stdcall GetFileAttributesExW(const WCHAR *lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, VOID *lpFileInformation);
|
|
__declspec(dllimport) BOOL __stdcall GetFileSizeEx (HANDLE hFile, LARGE_INTEGER *lpFileSize);
|
|
__declspec(dllimport) BOOL __stdcall DeleteFileW (const WCHAR *lpFileName);
|
|
__declspec(dllimport) HANDLE __stdcall CreateFileW (const WCHAR *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
|
|
__declspec(dllimport) BOOL __stdcall ReadFile (HANDLE hFile, VOID *lpBuffer, DWORD nNumberOfBytesToRead, DWORD *lpNumberOfBytesRead, OVERLAPPED *lpOverlapped);
|
|
__declspec(dllimport) BOOL __stdcall WriteFile (HANDLE hFile, const VOID *lpBuffer, DWORD nNumberOfBytesToWrite, DWORD *lpNumberOfBytesWritten, OVERLAPPED *lpOverlapped);
|
|
__declspec(dllimport) BOOL __stdcall GetDiskFreeSpaceExW (WCHAR const *lpDirectoryName, ULARGE_INTEGER *lpFreeBytesAvailableToCaller, ULARGE_INTEGER *lpTotalNumberOfBytes, ULARGE_INTEGER *lpTotalNumberOfFreeBytes);
|
|
}
|
|
|
|
// NOTE: um/processenv.h ///////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) DWORD __stdcall GetCurrentDirectoryW (DWORD nBufferLength, WCHAR *lpBuffer);
|
|
__declspec(dllimport) HANDLE __stdcall GetStdHandle (DWORD nStdHandle);
|
|
__declspec(dllimport) WCHAR* __stdcall GetEnvironmentStringsW ();
|
|
__declspec(dllimport) BOOL __stdcall FreeEnvironmentStringsW(WCHAR *penv);
|
|
__declspec(dllimport) DWORD __stdcall GetEnvironmentVariableW(WCHAR const *lpName, WCHAR *lpBuffer, DWORD nSize);
|
|
__declspec(dllimport) BOOL __stdcall SetEnvironmentVariableW(WCHAR const *lpName, WCHAR const *lpValue);
|
|
}
|
|
|
|
// NOTE: um/psapi.h ////////////////////////////////////////////////////////////////////////////
|
|
typedef struct _PROCESS_MEMORY_COUNTERS {
|
|
DWORD cb;
|
|
DWORD PageFaultCount;
|
|
SIZE_T PeakWorkingSetSize;
|
|
SIZE_T WorkingSetSize;
|
|
SIZE_T QuotaPeakPagedPoolUsage;
|
|
SIZE_T QuotaPagedPoolUsage;
|
|
SIZE_T QuotaPeakNonPagedPoolUsage;
|
|
SIZE_T QuotaNonPagedPoolUsage;
|
|
SIZE_T PagefileUsage;
|
|
SIZE_T PeakPagefileUsage;
|
|
} PROCESS_MEMORY_COUNTERS;
|
|
typedef PROCESS_MEMORY_COUNTERS *PPROCESS_MEMORY_COUNTERS;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall GetProcessMemoryInfo(HANDLE Process, PPROCESS_MEMORY_COUNTERS ppsmemCounters, DWORD cb);
|
|
}
|
|
|
|
// NOTE: um/sysinfoapi.h ///////////////////////////////////////////////////////////////////////
|
|
typedef struct _SYSTEM_INFO {
|
|
union {
|
|
DWORD dwOemId; // Obsolete field...do not use
|
|
struct {
|
|
WORD wProcessorArchitecture;
|
|
WORD wReserved;
|
|
} DUMMYSTRUCTNAME;
|
|
} DUMMYUNIONNAME;
|
|
DWORD dwPageSize;
|
|
VOID *lpMinimumApplicationAddress;
|
|
VOID *lpMaximumApplicationAddress;
|
|
DWORD_PTR dwActiveProcessorMask;
|
|
DWORD dwNumberOfProcessors;
|
|
DWORD dwProcessorType;
|
|
DWORD dwAllocationGranularity;
|
|
WORD wProcessorLevel;
|
|
WORD wProcessorRevision;
|
|
} SYSTEM_INFO, *LPSYSTEM_INFO;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) VOID __stdcall GetSystemInfo(SYSTEM_INFO *lpSystemInfo);
|
|
__declspec(dllimport) VOID __stdcall GetSystemTime(SYSTEMTIME *lpSystemTime);
|
|
__declspec(dllimport) VOID __stdcall GetSystemTimeAsFileTime(FILETIME *lpSystemTimeAsFileTime);
|
|
__declspec(dllimport) VOID __stdcall GetLocalTime(SYSTEMTIME *lpSystemTime);
|
|
}
|
|
|
|
// NOTE: um/timezoneapi.h //////////////////////////////////////////////////////////////////////
|
|
typedef struct _TIME_ZONE_INFORMATION {
|
|
LONG Bias;
|
|
WCHAR StandardName[32];
|
|
SYSTEMTIME StandardDate;
|
|
LONG StandardBias;
|
|
WCHAR DaylightName[32];
|
|
SYSTEMTIME DaylightDate;
|
|
LONG DaylightBias;
|
|
} TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION, *LPTIME_ZONE_INFORMATION;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall FileTimeToSystemTime (const FILETIME* lpFileTime, SYSTEMTIME *lpSystemTime);
|
|
__declspec(dllimport) BOOL __stdcall SystemTimeToFileTime (const SYSTEMTIME* lpSystemTime, FILETIME *lpFileTime);
|
|
__declspec(dllimport) BOOL __stdcall TzSpecificLocalTimeToSystemTime(const TIME_ZONE_INFORMATION* lpTimeZoneInformation, const SYSTEMTIME* lpLocalTime, const LPSYSTEMTIME lpUniversalTime);
|
|
}
|
|
|
|
// NOTE: shared/windef.h ///////////////////////////////////////////////////////////////////////
|
|
typedef struct tagRECT {
|
|
LONG left;
|
|
LONG top;
|
|
LONG right;
|
|
LONG bottom;
|
|
} RECT;
|
|
|
|
struct HWND__ {
|
|
int unused;
|
|
};
|
|
typedef struct HWND__ *HWND;
|
|
|
|
struct DPI_AWARENESS_CONTEXT__ {
|
|
int unused;
|
|
};
|
|
typedef struct DPI_AWARENESS_CONTEXT__ *DPI_AWARENESS_CONTEXT;
|
|
|
|
typedef enum DPI_AWARENESS {
|
|
DPI_AWARENESS_INVALID = -1,
|
|
DPI_AWARENESS_UNAWARE = 0,
|
|
DPI_AWARENESS_SYSTEM_AWARE = 1,
|
|
DPI_AWARENESS_PER_MONITOR_AWARE = 2
|
|
} DPI_AWARENESS;
|
|
|
|
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)
|
|
|
|
// NOTE: um/winuser.h //////////////////////////////////////////////////////////////////////////
|
|
typedef struct tagWINDOWPLACEMENT {
|
|
UINT length;
|
|
UINT flags;
|
|
UINT showCmd;
|
|
POINT ptMinPosition;
|
|
POINT ptMaxPosition;
|
|
RECT rcNormalPosition;
|
|
#ifdef _MAC
|
|
RECT rcDevice;
|
|
#endif
|
|
} WINDOWPLACEMENT;
|
|
typedef WINDOWPLACEMENT *PWINDOWPLACEMENT, *LPWINDOWPLACEMENT;
|
|
|
|
#define SW_HIDE 0
|
|
#define SW_NORMAL 1
|
|
#define SW_MAXIMIZE 3
|
|
#define SW_SHOWNOACTIVATE 4
|
|
#define SW_SHOW 5
|
|
#define SW_FORCEMINIMIZE 11
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall GetWindowRect (HWND hWnd, RECT *lpRect);
|
|
__declspec(dllimport) BOOL __stdcall SetWindowPos (HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags);
|
|
__declspec(dllimport) UINT __stdcall GetWindowModuleFileNameA(HWND hwnd, LPSTR pszFileName, UINT cchFileNameMax);
|
|
__declspec(dllimport) BOOL __stdcall ShowWindow (HWND hWnd, int nCmdShow);
|
|
__declspec(dllimport) BOOL __stdcall GetWindowPlacement (HWND hWnd, WINDOWPLACEMENT *lpwndpl);
|
|
|
|
}
|
|
|
|
// NOTE: um/wininet.h //////////////////////////////////////////////////////////////////////////
|
|
typedef WORD INTERNET_PORT;
|
|
typedef VOID *HINTERNET;
|
|
|
|
// NOTE: um/winhttp.h //////////////////////////////////////////////////////////////////////////
|
|
#define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0
|
|
#define WINHTTP_ACCESS_TYPE_NO_PROXY 1
|
|
#define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3
|
|
#define WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY 4
|
|
|
|
#define INTERNET_DEFAULT_PORT 0 // use the protocol-specific default
|
|
#define INTERNET_DEFAULT_HTTP_PORT 80 // " " HTTP "
|
|
#define INTERNET_DEFAULT_HTTPS_PORT 443 // " " HTTPS "
|
|
|
|
// NOTE: WinHttpOpen
|
|
#define WINHTTP_FLAG_ASYNC 0x10000000 // this session is asynchronous (where supported)
|
|
#define WINHTTP_FLAG_SECURE_DEFAULTS 0x30000000 // note that this flag also forces async
|
|
|
|
// NOTE: WinHttpOpenRequest
|
|
#define WINHTTP_FLAG_SECURE 0x00800000 // use SSL if applicable (HTTPS)
|
|
#define WINHTTP_FLAG_ESCAPE_PERCENT 0x00000004 // if escaping enabled, escape percent as well
|
|
#define WINHTTP_FLAG_NULL_CODEPAGE 0x00000008 // assume all symbols are ASCII, use fast convertion
|
|
#define WINHTTP_FLAG_ESCAPE_DISABLE 0x00000040 // disable escaping
|
|
#define WINHTTP_FLAG_ESCAPE_DISABLE_QUERY 0x00000080 // if escaping enabled escape path part, but do not escape query
|
|
#define WINHTTP_FLAG_BYPASS_PROXY_CACHE 0x00000100 // add "pragma: no-cache" request header
|
|
#define WINHTTP_FLAG_REFRESH WINHTTP_FLAG_BYPASS_PROXY_CACHE
|
|
#define WINHTTP_FLAG_AUTOMATIC_CHUNKING 0x00000200 // Send request without content-length header or chunked TE
|
|
|
|
#define WINHTTP_NO_PROXY_NAME NULL
|
|
#define WINHTTP_NO_PROXY_BYPASS NULL
|
|
|
|
//
|
|
// WINHTTP_QUERY_FLAG_NUMBER - if this bit is set in the dwInfoLevel parameter of
|
|
// HttpQueryHeader(), then the value of the header will be converted to a number
|
|
// before being returned to the caller, if applicable
|
|
//
|
|
#define WINHTTP_QUERY_FLAG_NUMBER 0x20000000
|
|
|
|
#define WINHTTP_QUERY_MIME_VERSION 0
|
|
#define WINHTTP_QUERY_CONTENT_TYPE 1
|
|
#define WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING 2
|
|
#define WINHTTP_QUERY_CONTENT_ID 3
|
|
#define WINHTTP_QUERY_CONTENT_DESCRIPTION 4
|
|
#define WINHTTP_QUERY_CONTENT_LENGTH 5
|
|
#define WINHTTP_QUERY_CONTENT_LANGUAGE 6
|
|
#define WINHTTP_QUERY_ALLOW 7
|
|
#define WINHTTP_QUERY_PUBLIC 8
|
|
#define WINHTTP_QUERY_DATE 9
|
|
#define WINHTTP_QUERY_EXPIRES 10
|
|
#define WINHTTP_QUERY_LAST_MODIFIED 11
|
|
#define WINHTTP_QUERY_MESSAGE_ID 12
|
|
#define WINHTTP_QUERY_URI 13
|
|
#define WINHTTP_QUERY_DERIVED_FROM 14
|
|
#define WINHTTP_QUERY_COST 15
|
|
#define WINHTTP_QUERY_LINK 16
|
|
#define WINHTTP_QUERY_PRAGMA 17
|
|
#define WINHTTP_QUERY_VERSION 18 // special: part of status line
|
|
#define WINHTTP_QUERY_STATUS_CODE 19 // special: part of status line
|
|
#define WINHTTP_QUERY_STATUS_TEXT 20 // special: part of status line
|
|
#define WINHTTP_QUERY_RAW_HEADERS 21 // special: all headers as ASCIIZ
|
|
#define WINHTTP_QUERY_RAW_HEADERS_CRLF 22 // special: all headers
|
|
#define WINHTTP_QUERY_CONNECTION 23
|
|
#define WINHTTP_QUERY_ACCEPT 24
|
|
#define WINHTTP_QUERY_ACCEPT_CHARSET 25
|
|
#define WINHTTP_QUERY_ACCEPT_ENCODING 26
|
|
#define WINHTTP_QUERY_ACCEPT_LANGUAGE 27
|
|
#define WINHTTP_QUERY_AUTHORIZATION 28
|
|
#define WINHTTP_QUERY_CONTENT_ENCODING 29
|
|
#define WINHTTP_QUERY_FORWARDED 30
|
|
#define WINHTTP_QUERY_FROM 31
|
|
#define WINHTTP_QUERY_IF_MODIFIED_SINCE 32
|
|
#define WINHTTP_QUERY_LOCATION 33
|
|
#define WINHTTP_QUERY_ORIG_URI 34
|
|
#define WINHTTP_QUERY_REFERER 35
|
|
#define WINHTTP_QUERY_RETRY_AFTER 36
|
|
#define WINHTTP_QUERY_SERVER 37
|
|
#define WINHTTP_QUERY_TITLE 38
|
|
#define WINHTTP_QUERY_USER_AGENT 39
|
|
#define WINHTTP_QUERY_WWW_AUTHENTICATE 40
|
|
#define WINHTTP_QUERY_PROXY_AUTHENTICATE 41
|
|
#define WINHTTP_QUERY_ACCEPT_RANGES 42
|
|
#define WINHTTP_QUERY_SET_COOKIE 43
|
|
#define WINHTTP_QUERY_COOKIE 44
|
|
#define WINHTTP_QUERY_REQUEST_METHOD 45 // special: GET/POST etc.
|
|
#define WINHTTP_QUERY_REFRESH 46
|
|
#define WINHTTP_QUERY_CONTENT_DISPOSITION 47
|
|
|
|
// NOTE: WinHttpQueryHeaders prettifiers for optional parameters.
|
|
#define WINHTTP_HEADER_NAME_BY_INDEX NULL
|
|
#define WINHTTP_NO_OUTPUT_BUFFER NULL
|
|
#define WINHTTP_NO_HEADER_INDEX NULL
|
|
|
|
// NOTE: Http Response Status Codes
|
|
#define HTTP_STATUS_CONTINUE 100 // OK to continue with request
|
|
#define HTTP_STATUS_SWITCH_PROTOCOLS 101 // server has switched protocols in upgrade header
|
|
|
|
#define HTTP_STATUS_OK 200 // request completed
|
|
#define HTTP_STATUS_CREATED 201 // object created, reason = new URI
|
|
#define HTTP_STATUS_ACCEPTED 202 // async completion (TBS)
|
|
#define HTTP_STATUS_PARTIAL 203 // partial completion
|
|
#define HTTP_STATUS_NO_CONTENT 204 // no info to return
|
|
#define HTTP_STATUS_RESET_CONTENT 205 // request completed, but clear form
|
|
#define HTTP_STATUS_PARTIAL_CONTENT 206 // partial GET fulfilled
|
|
#define HTTP_STATUS_WEBDAV_MULTI_STATUS 207 // WebDAV Multi-Status
|
|
|
|
#define HTTP_STATUS_AMBIGUOUS 300 // server couldn't decide what to return
|
|
#define HTTP_STATUS_MOVED 301 // object permanently moved
|
|
#define HTTP_STATUS_REDIRECT 302 // object temporarily moved
|
|
#define HTTP_STATUS_REDIRECT_METHOD 303 // redirection w/ new access method
|
|
#define HTTP_STATUS_NOT_MODIFIED 304 // if-modified-since was not modified
|
|
#define HTTP_STATUS_USE_PROXY 305 // redirection to proxy, location header specifies proxy to use
|
|
#define HTTP_STATUS_REDIRECT_KEEP_VERB 307 // HTTP/1.1: keep same verb
|
|
#define HTTP_STATUS_PERMANENT_REDIRECT 308 // Object permanently moved keep verb
|
|
|
|
#define HTTP_STATUS_BAD_REQUEST 400 // invalid syntax
|
|
#define HTTP_STATUS_DENIED 401 // access denied
|
|
#define HTTP_STATUS_PAYMENT_REQ 402 // payment required
|
|
#define HTTP_STATUS_FORBIDDEN 403 // request forbidden
|
|
#define HTTP_STATUS_NOT_FOUND 404 // object not found
|
|
#define HTTP_STATUS_BAD_METHOD 405 // method is not allowed
|
|
#define HTTP_STATUS_NONE_ACCEPTABLE 406 // no response acceptable to client found
|
|
#define HTTP_STATUS_PROXY_AUTH_REQ 407 // proxy authentication required
|
|
#define HTTP_STATUS_REQUEST_TIMEOUT 408 // server timed out waiting for request
|
|
#define HTTP_STATUS_CONFLICT 409 // user should resubmit with more info
|
|
#define HTTP_STATUS_GONE 410 // the resource is no longer available
|
|
#define HTTP_STATUS_LENGTH_REQUIRED 411 // the server refused to accept request w/o a length
|
|
#define HTTP_STATUS_PRECOND_FAILED 412 // precondition given in request failed
|
|
#define HTTP_STATUS_REQUEST_TOO_LARGE 413 // request entity was too large
|
|
#define HTTP_STATUS_URI_TOO_LONG 414 // request URI too long
|
|
#define HTTP_STATUS_UNSUPPORTED_MEDIA 415 // unsupported media type
|
|
#define HTTP_STATUS_RETRY_WITH 449 // retry after doing the appropriate action.
|
|
|
|
#define HTTP_STATUS_SERVER_ERROR 500 // internal server error
|
|
#define HTTP_STATUS_NOT_SUPPORTED 501 // required not supported
|
|
#define HTTP_STATUS_BAD_GATEWAY 502 // error response received from gateway
|
|
#define HTTP_STATUS_SERVICE_UNAVAIL 503 // temporarily overloaded
|
|
#define HTTP_STATUS_GATEWAY_TIMEOUT 504 // timed out waiting for gateway
|
|
#define HTTP_STATUS_VERSION_NOT_SUP 505 // HTTP version not supported
|
|
|
|
#define HTTP_STATUS_FIRST HTTP_STATUS_CONTINUE
|
|
#define HTTP_STATUS_LAST HTTP_STATUS_VERSION_NOT_SUP
|
|
|
|
#define WINHTTP_CALLBACK_STATUS_RESOLVING_NAME 0x00000001
|
|
#define WINHTTP_CALLBACK_STATUS_NAME_RESOLVED 0x00000002
|
|
#define WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER 0x00000004
|
|
#define WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER 0x00000008
|
|
#define WINHTTP_CALLBACK_STATUS_SENDING_REQUEST 0x00000010
|
|
#define WINHTTP_CALLBACK_STATUS_REQUEST_SENT 0x00000020
|
|
#define WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE 0x00000040
|
|
#define WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED 0x00000080
|
|
#define WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION 0x00000100
|
|
#define WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED 0x00000200
|
|
#define WINHTTP_CALLBACK_STATUS_HANDLE_CREATED 0x00000400
|
|
#define WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING 0x00000800
|
|
#define WINHTTP_CALLBACK_STATUS_DETECTING_PROXY 0x00001000
|
|
#define WINHTTP_CALLBACK_STATUS_REDIRECT 0x00004000
|
|
#define WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE 0x00008000
|
|
#define WINHTTP_CALLBACK_STATUS_SECURE_FAILURE 0x00010000
|
|
#define WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE 0x00020000
|
|
#define WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE 0x00040000
|
|
#define WINHTTP_CALLBACK_STATUS_READ_COMPLETE 0x00080000
|
|
#define WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE 0x00100000
|
|
#define WINHTTP_CALLBACK_STATUS_REQUEST_ERROR 0x00200000
|
|
#define WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE 0x00400000
|
|
|
|
#define WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE 0x01000000
|
|
#define WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE 0x02000000
|
|
#define WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE 0x04000000
|
|
#define WINHTTP_CALLBACK_STATUS_SETTINGS_WRITE_COMPLETE 0x10000000
|
|
#define WINHTTP_CALLBACK_STATUS_SETTINGS_READ_COMPLETE 0x20000000
|
|
|
|
#define WINHTTP_CALLBACK_FLAG_RESOLVE_NAME (WINHTTP_CALLBACK_STATUS_RESOLVING_NAME | WINHTTP_CALLBACK_STATUS_NAME_RESOLVED)
|
|
#define WINHTTP_CALLBACK_FLAG_CONNECT_TO_SERVER (WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER | WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER)
|
|
#define WINHTTP_CALLBACK_FLAG_SEND_REQUEST (WINHTTP_CALLBACK_STATUS_SENDING_REQUEST | WINHTTP_CALLBACK_STATUS_REQUEST_SENT)
|
|
#define WINHTTP_CALLBACK_FLAG_RECEIVE_RESPONSE (WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE | WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED)
|
|
#define WINHTTP_CALLBACK_FLAG_CLOSE_CONNECTION (WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION | WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED)
|
|
#define WINHTTP_CALLBACK_FLAG_HANDLES (WINHTTP_CALLBACK_STATUS_HANDLE_CREATED | WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING)
|
|
#define WINHTTP_CALLBACK_FLAG_DETECTING_PROXY WINHTTP_CALLBACK_STATUS_DETECTING_PROXY
|
|
#define WINHTTP_CALLBACK_FLAG_REDIRECT WINHTTP_CALLBACK_STATUS_REDIRECT
|
|
#define WINHTTP_CALLBACK_FLAG_INTERMEDIATE_RESPONSE WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE
|
|
#define WINHTTP_CALLBACK_FLAG_SECURE_FAILURE WINHTTP_CALLBACK_STATUS_SECURE_FAILURE
|
|
#define WINHTTP_CALLBACK_FLAG_SENDREQUEST_COMPLETE WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE
|
|
#define WINHTTP_CALLBACK_FLAG_HEADERS_AVAILABLE WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE
|
|
#define WINHTTP_CALLBACK_FLAG_DATA_AVAILABLE WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE
|
|
#define WINHTTP_CALLBACK_FLAG_READ_COMPLETE WINHTTP_CALLBACK_STATUS_READ_COMPLETE
|
|
#define WINHTTP_CALLBACK_FLAG_WRITE_COMPLETE WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE
|
|
#define WINHTTP_CALLBACK_FLAG_REQUEST_ERROR WINHTTP_CALLBACK_STATUS_REQUEST_ERROR
|
|
|
|
#define WINHTTP_CALLBACK_FLAG_GETPROXYFORURL_COMPLETE WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE
|
|
|
|
#define WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS (WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE \
|
|
| WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE \
|
|
| WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE \
|
|
| WINHTTP_CALLBACK_STATUS_READ_COMPLETE \
|
|
| WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE \
|
|
| WINHTTP_CALLBACK_STATUS_REQUEST_ERROR \
|
|
| WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE)
|
|
|
|
#define WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS 0xffffffff
|
|
#define WINHTTP_INVALID_STATUS_CALLBACK ((WINHTTP_STATUS_CALLBACK)(-1L))
|
|
|
|
typedef struct _WINHTTP_EXTENDED_HEADER
|
|
{
|
|
union
|
|
{
|
|
CHAR const *pwszName;
|
|
WCHAR const *pszName;
|
|
};
|
|
union
|
|
{
|
|
WCHAR const *pwszValue;
|
|
CHAR const *pszValue;
|
|
};
|
|
} WINHTTP_EXTENDED_HEADER, *PWINHTTP_EXTENDED_HEADER;
|
|
|
|
typedef struct _WINHTTP_ASYNC_RESULT
|
|
{
|
|
DWORD *dwResult; // indicates which async API has encountered an error
|
|
DWORD dwError; // the error code if the API failed
|
|
} WINHTTP_ASYNC_RESULT, *LPWINHTTP_ASYNC_RESULT, *PWINHTTP_ASYNC_RESULT;
|
|
|
|
typedef
|
|
VOID
|
|
(*WINHTTP_STATUS_CALLBACK)(
|
|
HINTERNET hInternet,
|
|
DWORD *dwContext,
|
|
DWORD dwInternetStatus,
|
|
VOID *lpvStatusInformation,
|
|
DWORD dwStatusInformationLength
|
|
);
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) HINTERNET __stdcall WinHttpOpen(WCHAR const *pszAgentW, DWORD dwAccessType, WCHAR const *pszProxyW, WCHAR const *pszProxyBypassW, DWORD dwFlags);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpCloseHandle(HINTERNET hInternet);
|
|
__declspec(dllimport) HINTERNET __stdcall WinHttpConnect(HINTERNET hSession, WCHAR const *pswzServerName, INTERNET_PORT nServerPort, DWORD dwReserved);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpReadData(HINTERNET hRequest, VOID *lpBuffer, DWORD dwNumberOfBytesToRead, DWORD *lpdwNumberOfBytesRead);
|
|
__declspec(dllimport) HINTERNET __stdcall WinHttpOpenRequest(HINTERNET hConnect, WCHAR const *pwszVerb, WCHAR const *pwszObjectName, WCHAR const *pwszVersion, WCHAR const *pwszReferrer, WCHAR const *ppwszAcceptTypes, DWORD dwFlags);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpSendRequest(HINTERNET hRequest, WCHAR const *lpszHeaders, DWORD dwHeadersLength, VOID *lpOptional, DWORD dwOptionalLength, DWORD dwTotalLength, DWORD_PTR dwContext);
|
|
__declspec(dllimport) DWORD __stdcall WinHttpAddRequestHeadersEx(HINTERNET hRequest, DWORD dwModifiers, ULONGLONG ullFlags, ULONGLONG ullExtra, DWORD cHeaders, WINHTTP_EXTENDED_HEADER *pHeaders);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpSetCredentials(HINTERNET hRequest, // HINTERNET handle returned by WinHttpOpenRequest.
|
|
DWORD AuthTargets, // Only WINHTTP_AUTH_TARGET_SERVER and WINHTTP_AUTH_TARGET_PROXY are supported in this version and they are mutually exclusive
|
|
DWORD AuthScheme, // must be one of the supported Auth Schemes returned from WinHttpQueryAuthSchemes()
|
|
WCHAR * pwszUserName, // 1) NULL if default creds is to be used, in which case pszPassword will be ignored
|
|
WCHAR * pwszPassword, // 1) "" == Blank Password; 2)Parameter ignored if pszUserName is NULL; 3) Invalid to pass in NULL if pszUserName is not NULL
|
|
VOID * pAuthParams);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpQueryHeaders(HINTERNET hRequest, DWORD dwInfoLevel, WCHAR const *pwszName, VOID *lpBuffer, DWORD *lpdwBufferLength, DWORD *lpdwIndex);
|
|
__declspec(dllimport) BOOL __stdcall WinHttpReceiveResponse(HINTERNET hRequest, VOID *lpReserved);
|
|
__declspec(dllimport) WINHTTP_STATUS_CALLBACK __stdcall WinHttpSetStatusCallback(HINTERNET hInternet, WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags, DWORD_PTR dwReserved);
|
|
}
|
|
|
|
// NOTE: um/DbgHelp.h //////////////////////////////////////////////////////////////////////////
|
|
#define SYMOPT_CASE_INSENSITIVE 0x00000001
|
|
#define SYMOPT_UNDNAME 0x00000002
|
|
#define SYMOPT_DEFERRED_LOADS 0x00000004
|
|
#define SYMOPT_NO_CPP 0x00000008
|
|
#define SYMOPT_LOAD_LINES 0x00000010
|
|
#define SYMOPT_OMAP_FIND_NEAREST 0x00000020
|
|
#define SYMOPT_LOAD_ANYTHING 0x00000040
|
|
#define SYMOPT_IGNORE_CVREC 0x00000080
|
|
#define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
|
|
#define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
|
|
#define SYMOPT_EXACT_SYMBOLS 0x00000400
|
|
#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
|
|
#define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
|
|
#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
|
|
#define SYMOPT_PUBLICS_ONLY 0x00004000
|
|
#define SYMOPT_NO_PUBLICS 0x00008000
|
|
#define SYMOPT_AUTO_PUBLICS 0x00010000
|
|
#define SYMOPT_NO_IMAGE_SEARCH 0x00020000
|
|
#define SYMOPT_SECURE 0x00040000
|
|
#define SYMOPT_NO_PROMPTS 0x00080000
|
|
#define SYMOPT_OVERWRITE 0x00100000
|
|
#define SYMOPT_IGNORE_IMAGEDIR 0x00200000
|
|
#define SYMOPT_FLAT_DIRECTORY 0x00400000
|
|
#define SYMOPT_FAVOR_COMPRESSED 0x00800000
|
|
#define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000
|
|
#define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000
|
|
#define SYMOPT_READONLY_CACHE 0x04000000
|
|
#define SYMOPT_SYMPATH_LAST 0x08000000
|
|
#define SYMOPT_DISABLE_FAST_SYMBOLS 0x10000000
|
|
#define SYMOPT_DISABLE_SYMSRV_TIMEOUT 0x20000000
|
|
#define SYMOPT_DISABLE_SRVSTAR_ON_STARTUP 0x40000000
|
|
#define SYMOPT_DEBUG 0x80000000
|
|
|
|
#define MAX_SYM_NAME 2000
|
|
|
|
typedef enum {
|
|
AddrMode1616,
|
|
AddrMode1632,
|
|
AddrModeReal,
|
|
AddrModeFlat
|
|
} ADDRESS_MODE;
|
|
|
|
typedef struct _tagADDRESS64 {
|
|
DWORD64 Offset;
|
|
WORD Segment;
|
|
ADDRESS_MODE Mode;
|
|
} ADDRESS64, *LPADDRESS64;
|
|
|
|
|
|
typedef struct _KDHELP64 {
|
|
DWORD64 Thread;
|
|
DWORD ThCallbackStack;
|
|
DWORD ThCallbackBStore;
|
|
DWORD NextCallback;
|
|
DWORD FramePointer;
|
|
DWORD64 KiCallUserMode;
|
|
DWORD64 KeUserCallbackDispatcher;
|
|
DWORD64 SystemRangeStart;
|
|
DWORD64 KiUserExceptionDispatcher;
|
|
DWORD64 StackBase;
|
|
DWORD64 StackLimit;
|
|
DWORD BuildVersion;
|
|
DWORD RetpolineStubFunctionTableSize;
|
|
DWORD64 RetpolineStubFunctionTable;
|
|
DWORD RetpolineStubOffset;
|
|
DWORD RetpolineStubSize;
|
|
DWORD64 Reserved0[2];
|
|
} KDHELP64, *PKDHELP64;
|
|
|
|
typedef struct _tagSTACKFRAME64 {
|
|
ADDRESS64 AddrPC; // program counter
|
|
ADDRESS64 AddrReturn; // return address
|
|
ADDRESS64 AddrFrame; // frame pointer
|
|
ADDRESS64 AddrStack; // stack pointer
|
|
ADDRESS64 AddrBStore; // backing store pointer
|
|
VOID *FuncTableEntry; // pointer to pdata/fpo or NULL
|
|
DWORD64 Params[4]; // possible arguments to the function
|
|
BOOL Far; // WOW far call
|
|
BOOL Virtual; // is this a virtual frame?
|
|
DWORD64 Reserved[3];
|
|
KDHELP64 KdHelp;
|
|
} STACKFRAME64;
|
|
|
|
typedef struct _IMAGEHLP_LINEW64 {
|
|
DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
|
|
VOID *Key; // internal
|
|
DWORD LineNumber; // line number in file
|
|
WCHAR *FileName; // full filename
|
|
DWORD64 Address; // first instruction of line
|
|
} IMAGEHLP_LINEW64;
|
|
|
|
typedef struct _SYMBOL_INFOW {
|
|
ULONG SizeOfStruct;
|
|
ULONG TypeIndex; // Type Index of symbol
|
|
ULONG64 Reserved[2];
|
|
ULONG Index;
|
|
ULONG Size;
|
|
ULONG64 ModBase; // Base Address of module comtaining this symbol
|
|
ULONG Flags;
|
|
ULONG64 Value; // Value of symbol, ValuePresent should be 1
|
|
ULONG64 Address; // Address of symbol including base address of module
|
|
ULONG Register; // register holding value or pointer to value
|
|
ULONG Scope; // scope of the symbol
|
|
ULONG Tag; // pdb classification
|
|
ULONG NameLen; // Actual length of name
|
|
ULONG MaxNameLen;
|
|
WCHAR Name[1]; // Name of symbol
|
|
} SYMBOL_INFOW;
|
|
|
|
typedef BOOL (__stdcall READ_PROCESS_MEMORY_ROUTINE64)(HANDLE hProcess, DWORD64 qwBaseAddress, VOID *lpBuffer, DWORD nSize, DWORD *lpNumberOfBytesRead);
|
|
typedef VOID * (__stdcall FUNCTION_TABLE_ACCESS_ROUTINE64)(HANDLE ahProcess, DWORD64 AddrBase);
|
|
typedef DWORD64(__stdcall GET_MODULE_BASE_ROUTINE64)(HANDLE hProcess, DWORD64 Address);
|
|
typedef DWORD64(__stdcall TRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess, HANDLE hThread, ADDRESS64 *lpaddr);
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall StackWalk64 (DWORD MachineType, HANDLE hProcess, HANDLE hThread, STACKFRAME64 *StackFrame, VOID *ContextRecord, READ_PROCESS_MEMORY_ROUTINE64 *ReadMemoryRoutine, FUNCTION_TABLE_ACCESS_ROUTINE64 *FunctionTableAccessRoutine, GET_MODULE_BASE_ROUTINE64 *GetModuleBaseRoutine, TRANSLATE_ADDRESS_ROUTINE64 *TranslateAddress);
|
|
__declspec(dllimport) BOOL __stdcall SymFromAddrW (HANDLE hProcess, DWORD64 Address, DWORD64 *Displacement, SYMBOL_INFOW *Symbol);
|
|
__declspec(dllimport) VOID * __stdcall SymFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase);
|
|
__declspec(dllimport) BOOL __stdcall SymGetLineFromAddrW64 (HANDLE hProcess, DWORD64 dwAddr, DWORD *pdwDisplacement, IMAGEHLP_LINEW64 *Line);
|
|
__declspec(dllimport) DWORD64 __stdcall SymGetModuleBase64 (HANDLE hProcess, DWORD64 qwAddr);
|
|
__declspec(dllimport) BOOL __stdcall SymRefreshModuleList (HANDLE hProcess);
|
|
};
|
|
|
|
// NOTE: um/errhandlingapi.h ///////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) DWORD __stdcall GetLastError(VOID);
|
|
}
|
|
|
|
// NOTE: um/libloaderapi.h /////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) HMODULE __stdcall LoadLibraryA (const CHAR *lpLibFileName);
|
|
__declspec(dllimport) BOOL __stdcall FreeLibrary (HMODULE hLibModule);
|
|
__declspec(dllimport) void * __stdcall GetProcAddress (HMODULE hModule, const CHAR *lpProcName);
|
|
__declspec(dllimport) HMODULE __stdcall GetModuleHandleA (const CHAR *lpModuleName);
|
|
__declspec(dllimport) DWORD __stdcall GetModuleFileNameW(HMODULE hModule, WCHAR *lpFilename, DWORD nSize);
|
|
}
|
|
|
|
// NOTE: um/synchapi.h /////////////////////////////////////////////////////////////////////////
|
|
typedef RTL_CONDITION_VARIABLE CONDITION_VARIABLE, *PCONDITION_VARIABLE;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) VOID __stdcall InitializeConditionVariable (CONDITION_VARIABLE *ConditionVariable);
|
|
__declspec(dllimport) VOID __stdcall WakeConditionVariable (CONDITION_VARIABLE *ConditionVariable);
|
|
__declspec(dllimport) VOID __stdcall WakeAllConditionVariable (CONDITION_VARIABLE *ConditionVariable);
|
|
__declspec(dllimport) BOOL __stdcall SleepConditionVariableCS (CONDITION_VARIABLE *ConditionVariable, CRITICAL_SECTION *CriticalSection, DWORD dwMilliseconds);
|
|
|
|
__declspec(dllimport) VOID __stdcall InitializeCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
|
__declspec(dllimport) VOID __stdcall EnterCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
|
__declspec(dllimport) VOID __stdcall LeaveCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
|
__declspec(dllimport) BOOL __stdcall InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount);
|
|
__declspec(dllimport) BOOL __stdcall InitializeCriticalSectionEx (CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount, DWORD Flags);
|
|
__declspec(dllimport) DWORD __stdcall SetCriticalSectionSpinCount (CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount);
|
|
__declspec(dllimport) BOOL __stdcall TryEnterCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
|
__declspec(dllimport) VOID __stdcall DeleteCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
|
|
|
__declspec(dllimport) DWORD __stdcall WaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds);
|
|
__declspec(dllimport) BOOL __stdcall ReleaseSemaphore (HANDLE hSemaphore, LONG lReleaseCount, LONG *lpPreviousCount);
|
|
__declspec(dllimport) VOID __stdcall Sleep (DWORD dwMilliseconds);
|
|
}
|
|
|
|
// NOTE: um/profileapi.h ///////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall QueryPerformanceCounter (LARGE_INTEGER* lpPerformanceCount);
|
|
__declspec(dllimport) BOOL __stdcall QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency);
|
|
}
|
|
|
|
// NOTE: um/processthreadsapi.h ////////////////////////////////////////////////////////////////
|
|
typedef struct _PROCESS_INFORMATION {
|
|
HANDLE hProcess;
|
|
HANDLE hThread;
|
|
DWORD dwProcessId;
|
|
DWORD dwThreadId;
|
|
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
|
|
|
|
typedef struct _STARTUPINFOW {
|
|
DWORD cb;
|
|
WCHAR *lpReserved;
|
|
WCHAR *lpDesktop;
|
|
WCHAR *lpTitle;
|
|
DWORD dwX;
|
|
DWORD dwY;
|
|
DWORD dwXSize;
|
|
DWORD dwYSize;
|
|
DWORD dwXCountChars;
|
|
DWORD dwYCountChars;
|
|
DWORD dwFillAttribute;
|
|
DWORD dwFlags;
|
|
WORD wShowWindow;
|
|
WORD cbReserved2;
|
|
BYTE *lpReserved2;
|
|
HANDLE hStdInput;
|
|
HANDLE hStdOutput;
|
|
HANDLE hStdError;
|
|
} STARTUPINFOW, *LPSTARTUPINFOW;
|
|
|
|
typedef DWORD (__stdcall *PTHREAD_START_ROUTINE)(
|
|
VOID *lpThreadParameter
|
|
);
|
|
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall CreateProcessW (WCHAR const *lpApplicationName, WCHAR *lpCommandLine, SECURITY_ATTRIBUTES *lpProcessAttributes, SECURITY_ATTRIBUTES *lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, VOID *lpEnvironment, WCHAR const *lpCurrentDirectory, STARTUPINFOW *lpStartupInfo, PROCESS_INFORMATION *lpProcessInformation);
|
|
__declspec(dllimport) HANDLE __stdcall CreateThread (SECURITY_ATTRIBUTES *lpThreadAttributes, SIZE_T dwStackSize, PTHREAD_START_ROUTINE lpStartAddress, VOID *lpParameter, DWORD dwCreationFlags, DWORD *lpThreadId);
|
|
__declspec(dllimport) DWORD __stdcall GetCurrentThreadId(VOID);
|
|
__declspec(dllimport) BOOL __stdcall GetExitCodeProcess(HANDLE hProcess, DWORD *lpExitCode);
|
|
__declspec(dllimport) void __stdcall ExitProcess (UINT uExitCode);
|
|
}
|
|
|
|
// NOTE: um/memoryapi.h ////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) VOID * __stdcall VirtualAlloc (VOID *lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
|
__declspec(dllimport) BOOL __stdcall VirtualProtect(VOID *lpAddress, SIZE_T dwSize, DWORD flNewProtect, DWORD *lpflOldProtect);
|
|
__declspec(dllimport) BOOL __stdcall VirtualFree (VOID *lpAddress, SIZE_T dwSize, DWORD dwFreeType);
|
|
}
|
|
|
|
// NOTE: shared/bcrypt.h ///////////////////////////////////////////////////////////////////////
|
|
typedef VOID *BCRYPT_ALG_HANDLE;
|
|
typedef LONG NTSTATUS;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) NTSTATUS __stdcall BCryptOpenAlgorithmProvider(BCRYPT_ALG_HANDLE *phAlgorithm, const WCHAR *pszAlgId, const WCHAR *pszImplementation, ULONG dwFlags);
|
|
__declspec(dllimport) NTSTATUS __stdcall BCryptGenRandom (BCRYPT_ALG_HANDLE hAlgorithm, UCHAR *pbBuffer, ULONG cbBuffer, ULONG dwFlags);
|
|
}
|
|
|
|
// NOTE: um/shellapi.h /////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) HINSTANCE __stdcall ShellExecuteA(HWND hwnd, CHAR const *lpOperation, CHAR const *lpFile, CHAR const *lpParameters, CHAR const *lpDirectory, INT nShowCmd);
|
|
__declspec(dllimport) HINSTANCE __stdcall ShellExecuteW(HWND hwnd, WCHAR const *lpOperation, WCHAR const *lpFile, WCHAR const *lpParameters, WCHAR const *lpDirectory, INT nShowCmd);
|
|
}
|
|
|
|
// NOTE: um/debugapi.h /////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall IsDebuggerPresent();
|
|
}
|
|
|
|
// NOTE: um/namedpipeapi.h /////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall CreatePipe (HANDLE *hReadPipe, HANDLE *hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize);
|
|
__declspec(dllimport) BOOL __stdcall PeekNamedPipe(HANDLE hNamedPipe, VOID *lpBuffer, DWORD nBufferSize, DWORD *lpBytesRead, DWORD *lpTotalBytesAvail, DWORD *lpBytesLeftThisMessage);
|
|
}
|
|
|
|
// NOTE: um/handleapi.h ////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
|
|
}
|
|
|
|
// NOTE: um/commdlg.h //////////////////////////////////////////////////////////////////////////
|
|
typedef UINT_PTR (__stdcall *LPOFNHOOKPROC)(HWND, UINT, WPARAM, LPARAM);
|
|
typedef struct tagOFNW {
|
|
DWORD lStructSize;
|
|
HWND hwndOwner;
|
|
HINSTANCE hInstance;
|
|
WCHAR const * lpstrFilter;
|
|
LPWSTR lpstrCustomFilter;
|
|
DWORD nMaxCustFilter;
|
|
DWORD nFilterIndex;
|
|
LPWSTR lpstrFile;
|
|
DWORD nMaxFile;
|
|
LPWSTR lpstrFileTitle;
|
|
DWORD nMaxFileTitle;
|
|
WCHAR const * lpstrInitialDir;
|
|
WCHAR const * lpstrTitle;
|
|
DWORD Flags;
|
|
WORD nFileOffset;
|
|
WORD nFileExtension;
|
|
WCHAR const * lpstrDefExt;
|
|
LPARAM lCustData;
|
|
LPOFNHOOKPROC lpfnHook;
|
|
WCHAR const * lpTemplateName;
|
|
#ifdef _MAC
|
|
LPEDITMENU lpEditInfo;
|
|
LPCSTR lpstrPrompt;
|
|
#endif
|
|
#if (_WIN32_WINNT >= 0x0500)
|
|
void * pvReserved;
|
|
DWORD dwReserved;
|
|
DWORD FlagsEx;
|
|
#endif // (_WIN32_WINNT >= 0x0500)
|
|
} OPENFILENAMEW, *LPOPENFILENAMEW;
|
|
|
|
|
|
#define OFN_READONLY 0x00000001
|
|
#define OFN_OVERWRITEPROMPT 0x00000002
|
|
#define OFN_HIDEREADONLY 0x00000004
|
|
#define OFN_NOCHANGEDIR 0x00000008
|
|
#define OFN_SHOWHELP 0x00000010
|
|
#define OFN_ENABLEHOOK 0x00000020
|
|
#define OFN_ENABLETEMPLATE 0x00000040
|
|
#define OFN_ENABLETEMPLATEHANDLE 0x00000080
|
|
#define OFN_NOVALIDATE 0x00000100
|
|
#define OFN_ALLOWMULTISELECT 0x00000200
|
|
#define OFN_EXTENSIONDIFFERENT 0x00000400
|
|
#define OFN_PATHMUSTEXIST 0x00000800
|
|
#define OFN_FILEMUSTEXIST 0x00001000
|
|
#define OFN_CREATEPROMPT 0x00002000
|
|
#define OFN_SHAREAWARE 0x00004000
|
|
#define OFN_NOREADONLYRETURN 0x00008000
|
|
#define OFN_NOTESTFILECREATE 0x00010000
|
|
#define OFN_NONETWORKBUTTON 0x00020000
|
|
#define OFN_NOLONGNAMES 0x00040000 // force no long names for 4.x modules
|
|
#if(WINVER >= 0x0400)
|
|
#define OFN_EXPLORER 0x00080000 // new look commdlg
|
|
#define OFN_NODEREFERENCELINKS 0x00100000
|
|
#define OFN_LONGNAMES 0x00200000 // force long names for 3.x modules
|
|
// OFN_ENABLEINCLUDENOTIFY and OFN_ENABLESIZING require
|
|
// Windows 2000 or higher to have any effect.
|
|
#define OFN_ENABLEINCLUDENOTIFY 0x00400000 // send include message to callback
|
|
#define OFN_ENABLESIZING 0x00800000
|
|
#endif /* WINVER >= 0x0400 */
|
|
#if (_WIN32_WINNT >= 0x0500)
|
|
#define OFN_DONTADDTORECENT 0x02000000
|
|
#define OFN_FORCESHOWHIDDEN 0x10000000 // Show All files including System and hidden files
|
|
#endif // (_WIN32_WINNT >= 0x0500)
|
|
|
|
//FlagsEx Values
|
|
#if (_WIN32_WINNT >= 0x0500)
|
|
#define OFN_EX_NOPLACESBAR 0x00000001
|
|
#endif // (_WIN32_WINNT >= 0x0500)
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall GetSaveFileNameW(LPOPENFILENAMEW);
|
|
__declspec(dllimport) BOOL __stdcall GetOpenFileNameW(LPOPENFILENAMEW);
|
|
}
|
|
|
|
// NOTE: um/shlwapi.h //////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) BOOL __stdcall PathRelativePathToW(WCHAR *pszPath, WCHAR const *pszFrom, DWORD dwAttrFrom, WCHAR const *pszTo, DWORD dwAttrTo);
|
|
__declspec(dllimport) BOOL __stdcall PathIsRelativeW(WCHAR *pszPath);
|
|
}
|
|
|
|
// NOTE: um/pathcch.h //////////////////////////////////////////////////////////////////////////
|
|
typedef enum PATHCCH_OPTIONS
|
|
{
|
|
PATHCCH_NONE = 0x0,
|
|
|
|
// This option allows applications to gain access to long paths. It has two
|
|
// different behaviors. For process configured to enable long paths it will allow
|
|
// the returned path to be longer than the max path limit that is normally imposed.
|
|
// For process that are not this option will convert long paths into the extended
|
|
// length DOS device form (with \\?\ prefix) when the path is longer than the limit.
|
|
// This form is not length limited by the Win32 file system API on all versions of Windows.
|
|
// This second behavior is the same behavior for OSes that don't have the long path feature.
|
|
// This can not be specified with PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH.
|
|
PATHCCH_ALLOW_LONG_PATHS = 0x01,
|
|
|
|
// Can only be used when PATHCCH_ALLOW_LONG_PATHS is specified. This
|
|
// Forces the API to treat the caller as long path enabled, independent of the
|
|
// process's long name enabled state. Cannot be used with PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS.
|
|
PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS = 0x02,
|
|
|
|
// Can only be used when PATHCCH_ALLOW_LONG_PATHS is specified. This
|
|
// Forces the API to treat the caller as long path disabled, independent of the
|
|
// process's long name enabled state. Cannot be used with PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS.
|
|
PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS = 0x04,
|
|
|
|
// Disable the normalization of path segments that includes removing trailing dots and spaces.
|
|
// This enables access to paths that win32 path normalization will block.
|
|
PATHCCH_DO_NOT_NORMALIZE_SEGMENTS = 0x08,
|
|
|
|
// Convert the input path into the extended length DOS device path form (with the \\?\ prefix)
|
|
// if not already in that form. This enables access to paths that are otherwise not addressable
|
|
// due to Win32 normalization rules (that can strip trailing dots and spaces) and path
|
|
// length limitations. This option implies the same behavior of PATHCCH_DO_NOT_NORMALIZE_SEGMENTS.
|
|
// This can not be specified with PATHCCH_ALLOW_LONG_PATHS.
|
|
PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH = 0x10,
|
|
|
|
// When combining or normalizing a path ensure there is a trailing backslash.
|
|
PATHCCH_ENSURE_TRAILING_SLASH = 0x020,
|
|
|
|
// Convert forward slashes to back slashes and collapse multiple slashes.
|
|
// This is needed to to support sub-path or identity comparisons.
|
|
PATHCCH_CANONICALIZE_SLASHES = 0x040,
|
|
} PATHCCH_OPTIONS;
|
|
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) HRESULT __stdcall PathCchCanonicalizeEx(PWSTR pszPathOut, size_t cchPathOut, WCHAR const *pszPathIn, ULONG dwFlags);
|
|
};
|
|
|
|
// NOTE: um/errhandlingapi.h ///////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
{
|
|
__declspec(dllimport) VOID __stdcall RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, const ULONG_PTR* lpArguments);
|
|
};
|
|
|
|
// NOTE: include/excpt.h ///////////////////////////////////////////////////////////////////
|
|
#define EXCEPTION_EXECUTE_HANDLER 1
|
|
#define EXCEPTION_CONTINUE_SEARCH 0
|
|
#define EXCEPTION_CONTINUE_EXECUTION (-1)
|
|
|
|
DN_MSVC_WARNING_POP
|
|
#endif // !defined(_INC_WINDOWS)
|
|
#endif // !defined(DN_OS_WINDOWS_H)
|
|
#endif
|
|
|
|
struct DN_W32Error
|
|
{
|
|
unsigned long code;
|
|
DN_Str8 msg;
|
|
};
|
|
|
|
struct DN_W32FolderIteratorW
|
|
{
|
|
void *handle;
|
|
DN_Str16 file_name;
|
|
wchar_t file_name_buf[512];
|
|
};
|
|
|
|
enum DN_W32SyncPrimitiveType
|
|
{
|
|
DN_OSW32SyncPrimitiveType_Semaphore,
|
|
DN_OSW32SyncPrimitiveType_Mutex,
|
|
DN_OSW32SyncPrimitiveType_ConditionVariable,
|
|
};
|
|
|
|
struct DN_W32SyncPrimitive
|
|
{
|
|
union
|
|
{
|
|
void *sem;
|
|
CRITICAL_SECTION mutex;
|
|
CONDITION_VARIABLE cv;
|
|
};
|
|
|
|
DN_W32SyncPrimitive *next;
|
|
};
|
|
|
|
typedef HRESULT DN_W32SetThreadDescriptionFunc(HANDLE hThread, PWSTR const lpThreadDescription);
|
|
struct DN_W32Core
|
|
{
|
|
DN_W32SetThreadDescriptionFunc *set_thread_description;
|
|
LARGE_INTEGER qpc_frequency;
|
|
void *bcrypt_rng_handle;
|
|
bool bcrypt_init_success;
|
|
bool sym_initialised;
|
|
|
|
CRITICAL_SECTION sync_primitive_free_list_mutex;
|
|
DN_W32SyncPrimitive *sync_primitive_free_list;
|
|
};
|
|
|
|
DN_API void DN_W32_ThreadSetName (DN_Str8 name);
|
|
|
|
DN_API DN_Str16 DN_W32_ErrorCodeToMsg16Alloc(DN_U32 error_code);
|
|
DN_API DN_W32Error DN_W32_ErrorCodeToMsg (DN_Arena *arena, DN_U32 error_code);
|
|
DN_API DN_W32Error DN_W32_ErrorCodeToMsgAlloc (DN_U32 error_code);
|
|
DN_API DN_W32Error DN_W32_LastError (DN_Arena *arena);
|
|
DN_API DN_W32Error DN_W32_LastErrorAlloc ();
|
|
DN_API void DN_W32_MakeProcessDPIAware ();
|
|
|
|
// NOTE: Windows Str8 <-> Str16 ////////////////////////////////////////////////////////////////////
|
|
DN_API DN_Str16 DN_W32_Str8ToStr16 (DN_Arena *arena, DN_Str8 src);
|
|
DN_API int DN_W32_Str8ToStr16Buffer (DN_Str16 src, char *dest, int dest_size);
|
|
DN_API DN_Str8 DN_W32_Str16ToStr8 (DN_Arena *arena, DN_Str16 src);
|
|
DN_API int DN_W32_Str16ToStr8Buffer (DN_Str16 src, char *dest, int dest_size);
|
|
DN_API DN_Str8 DN_W32_Str16ToStr8FromHeap(DN_Str16 src);
|
|
|
|
// NOTE: Path navigation ///////////////////////////////////////////////////////////////////////////
|
|
DN_API DN_Str16 DN_W32_EXEPathW (DN_Arena *arena);
|
|
DN_API DN_Str16 DN_W32_EXEDirW (DN_Arena *arena);
|
|
DN_API DN_Str8 DN_W32_WorkingDir (DN_Arena *arena, DN_Str8 suffix);
|
|
DN_API DN_Str16 DN_W32_WorkingDirW (DN_Arena *arena, DN_Str16 suffix);
|
|
DN_API bool DN_W32_DirWIterate (DN_Str16 path, DN_W32FolderIteratorW *it);
|
|
#endif // !defined(DN_OS_WIN32)
|
|
#elif defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_EMSCRIPTEN)
|
|
// DN: Single header generator commented out this header => #include "OS/dn_os_posix.h"
|
|
#if !defined(DN_OS_POSIX_H)
|
|
#define DN_OS_POSIX_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
#include <pthread.h>
|
|
#include <semaphore.h>
|
|
|
|
/*
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// $$$$$$\ $$$$$$\ $$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\
|
|
// $$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ \_$$ _|$$ | $$ |
|
|
// $$ / $$ |$$ / \__| $$ | $$ |$$ / $$ |$$ / \__| $$ | \$$\ $$ |
|
|
// $$ | $$ |\$$$$$$\ $$$$$$$ |$$ | $$ |\$$$$$$\ $$ | \$$$$ /
|
|
// $$ | $$ | \____$$\ $$ ____/ $$ | $$ | \____$$\ $$ | $$ $$<
|
|
// $$ | $$ |$$\ $$ | $$ | $$ | $$ |$$\ $$ | $$ | $$ /\$$\
|
|
// $$$$$$ |\$$$$$$ | $$ | $$$$$$ |\$$$$$$ |$$$$$$\ $$ / $$ |
|
|
// \______/ \______/ \__| \______/ \______/ \______|\__| \__|
|
|
//
|
|
// dn_os_posix.h
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
*/
|
|
|
|
struct DN_POSIXProcSelfStatus
|
|
{
|
|
char name[64];
|
|
DN_U8 name_size;
|
|
DN_U32 pid;
|
|
DN_U64 vm_peak;
|
|
DN_U32 vm_size;
|
|
};
|
|
|
|
// NOTE: The POSIX implementation disallows copies of synchronisation objects in
|
|
// general hence we have to dynamically allocate these primitives to maintain a
|
|
// consistent address.
|
|
//
|
|
//
|
|
// Source: The Open Group Base Specifications Issue 7, 2018 edition
|
|
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_09
|
|
//
|
|
// 2.9.9 Synchronization Object Copies and Alternative Mappings
|
|
//
|
|
// For barriers, condition variables, mutexes, and read-write locks, [TSH]
|
|
// [Option Start] if the process-shared attribute is set to
|
|
// PTHREAD_PROCESS_PRIVATE, [Option End] only the synchronization object at the
|
|
// address used to initialize it can be used for performing synchronization. The
|
|
// effect of referring to another mapping of the same object when locking,
|
|
// unlocking, or destroying the object is undefined. [...] The effect of
|
|
// referring to a copy of the object when locking, unlocking, or destroying it
|
|
// is undefined.
|
|
|
|
enum DN_POSIXSyncPrimitiveType
|
|
{
|
|
DN_OSPOSIXSyncPrimitiveType_Semaphore,
|
|
DN_OSPOSIXSyncPrimitiveType_Mutex,
|
|
DN_OSPOSIXSyncPrimitiveType_ConditionVariable,
|
|
};
|
|
|
|
struct DN_POSIXSyncPrimitive
|
|
{
|
|
union
|
|
{
|
|
sem_t sem;
|
|
pthread_mutex_t mutex;
|
|
pthread_cond_t cv;
|
|
};
|
|
DN_POSIXSyncPrimitive *next;
|
|
};
|
|
|
|
struct DN_POSIXCore
|
|
{
|
|
DN_POSIXSyncPrimitive *sync_primitive_free_list;
|
|
pthread_mutex_t sync_primitive_free_list_mutex;
|
|
bool clock_monotonic_raw;
|
|
};
|
|
|
|
DN_API void DN_Posix_Init(DN_POSIXCore *posix);
|
|
DN_API void DN_Posix_ThreadSetName(DN_Str8 name);
|
|
DN_API DN_POSIXProcSelfStatus DN_Posix_ProcSelfStatus();
|
|
#endif // !defined(DN_OS_POSIX_H)
|
|
#else
|
|
#error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
|
|
#endif
|
|
|
|
// DN: Single header generator commented out this header => #include "OS/dn_os_tls.h"
|
|
#if !defined(DN_OS_TLS_H)
|
|
#define DN_OS_TLS_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_os_inc.h"
|
|
#endif
|
|
|
|
// 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;
|
|
};
|
|
|
|
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)
|
|
|
|
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)
|
|
// DN: Single header generator commented out this header => #include "OS/dn_os.h"
|
|
#if !defined(DN_OS_H)
|
|
#define DN_OS_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_os_inc.h"
|
|
#endif
|
|
|
|
#include <new> // operator new
|
|
|
|
#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
|
|
|
|
extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
|
|
|
|
// 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_Str8Lit(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;
|
|
};
|
|
|
|
// NOTE: DN_OSSemaphore ////////////////////////////////////////////////////////////////////////////
|
|
DN_U32 const DN_OS_SEMAPHORE_INFINITE_TIMEOUT = UINT32_MAX;
|
|
|
|
struct DN_OSSemaphore
|
|
{
|
|
DN_U64 handle;
|
|
};
|
|
|
|
enum DN_OSSemaphoreWaitResult
|
|
{
|
|
DN_OSSemaphoreWaitResult_Failed,
|
|
DN_OSSemaphoreWaitResult_Success,
|
|
DN_OSSemaphoreWaitResult_Timeout,
|
|
};
|
|
|
|
struct DN_OSMutex
|
|
{
|
|
DN_U64 handle;
|
|
};
|
|
|
|
struct DN_OSConditionVariable
|
|
{
|
|
DN_U64 handle;
|
|
};
|
|
|
|
// NOTE: DN_OSThread
|
|
typedef DN_I32(DN_OSThreadFunc)(struct DN_OSThread *);
|
|
|
|
struct DN_OSThread
|
|
{
|
|
DN_Str8x64 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;
|
|
};
|
|
|
|
// 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 w32_request_session;
|
|
HINTERNET w32_request_connection;
|
|
HINTERNET w32_request_handle;
|
|
#endif
|
|
};
|
|
|
|
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 logical_processor_count;
|
|
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
|
|
|
|
DN_Arena arena;
|
|
void *platform_context;
|
|
};
|
|
|
|
struct DN_OSDiskSpace
|
|
{
|
|
bool success;
|
|
DN_U64 avail;
|
|
DN_U64 size;
|
|
};
|
|
|
|
DN_API void DN_OS_EmitLogsWithOSPrintFunctions (DN_OSCore *os);
|
|
DN_API void DN_OS_DumpThreadContextArenaStat (DN_Str8 file_path);
|
|
|
|
DN_API DN_Str8 DN_OS_BytesFromHexPtrArenaFrame (void const *hex, DN_USize hex_count);
|
|
DN_API DN_Str8 DN_OS_BytesFromHexStr8ArenaFrame (DN_Str8 hex);
|
|
DN_API DN_Str8 DN_OS_HexFromBytesPtrArenaFrame (void const *bytes, DN_USize bytes_count);
|
|
DN_API DN_Str8 DN_OS_HexFromBytesPtrArenaTLS (void const *bytes, DN_USize bytes_count);
|
|
|
|
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);
|
|
|
|
DN_API void * DN_OS_MemAlloc (DN_USize size, DN_ZMem z_mem);
|
|
DN_API void DN_OS_MemDealloc (void *ptr);
|
|
|
|
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 ();
|
|
#define DN_OS_DateUnixTimeUs() (DN_OS_DateUnixTimeNs() / 1000)
|
|
#define DN_OS_DateUnixTimeMs() (DN_OS_DateUnixTimeNs() / (1000 * 1000))
|
|
#define DN_OS_DateUnixTimeS() (DN_OS_DateUnixTimeNs() / (1000 * 1000 * 1000))
|
|
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);
|
|
|
|
DN_API void DN_OS_GenBytesSecure (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);
|
|
|
|
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);
|
|
|
|
DN_API bool DN_OS_FileCopy (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_OSErrSink *err);
|
|
DN_API bool DN_OS_FileMove (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_OSErrSink *err);
|
|
|
|
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);
|
|
|
|
DN_API DN_Str8 DN_OS_FileReadAll (DN_Allocator alloc_type, void *allocator, DN_Str8 path, DN_OSErrSink *err);
|
|
DN_API DN_Str8 DN_OS_FileReadAllArena (DN_Arena *arena, DN_Str8 path, DN_OSErrSink *err);
|
|
DN_API DN_Str8 DN_OS_FileReadAllPool (DN_Pool *pool, DN_Str8 path, DN_OSErrSink *err);
|
|
DN_API DN_Str8 DN_OS_FileReadAllTLS (DN_Str8 path, DN_OSErrSink *err);
|
|
DN_API bool DN_OS_FileWriteAll (DN_Str8 path, DN_Str8 buffer, DN_OSErrSink *err);
|
|
DN_API bool DN_OS_FileWriteAllFV (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
|
|
DN_API bool DN_OS_FileWriteAllF (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API bool DN_OS_FileWriteAllSafe (DN_Str8 path, DN_Str8 buffer, DN_OSErrSink *err);
|
|
DN_API bool DN_OS_FileWriteAllSafeFV (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
|
|
DN_API bool DN_OS_FileWriteAllSafeF (DN_Str8 path, DN_OSErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
|
|
|
|
DN_API DN_OSPathInfo DN_OS_PathInfo (DN_Str8 path);
|
|
DN_API bool DN_OS_PathIsOlderThan (DN_Str8 file, DN_Str8 check_against);
|
|
DN_API bool DN_OS_PathDelete (DN_Str8 path);
|
|
DN_API bool DN_OS_PathIsFile (DN_Str8 path);
|
|
DN_API bool DN_OS_PathIsDir (DN_Str8 path);
|
|
DN_API bool DN_OS_PathMakeDir (DN_Str8 path);
|
|
DN_API bool DN_OS_PathIterateDir (DN_Str8 path, DN_OSDirIterator *it);
|
|
|
|
DN_API bool DN_OS_PathAddRef (DN_Arena *arena, DN_OSPath *fs_path, DN_Str8 path);
|
|
DN_API bool DN_OS_PathAddRefTLS (DN_OSPath *fs_path, DN_Str8 path);
|
|
DN_API bool DN_OS_PathAddRefFrame (DN_OSPath *fs_path, DN_Str8 path);
|
|
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_Str8Lit("/"))
|
|
#define DN_OS_PathBuildBackSlash(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_Str8Lit("\\"))
|
|
#define DN_OS_PathBuild(allocator, fs_path) DN_OS_PathBuildWithSeparator(allocator, fs_path, DN_OSPathSeparatorString)
|
|
|
|
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())
|
|
|
|
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);
|
|
|
|
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_MutexScope(mutex) DN_DeferLoop(DN_OS_MutexLock(mutex), DN_OS_MutexUnlock(mutex))
|
|
|
|
DN_API DN_OSConditionVariable DN_OS_ConditionVariableInit ();
|
|
DN_API void DN_OS_ConditionVariableDeinit (DN_OSConditionVariable *cv);
|
|
DN_API bool DN_OS_ConditionVariableWait (DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 sleep_ms);
|
|
DN_API bool DN_OS_ConditionVariableWaitUntil (DN_OSConditionVariable *cv, DN_OSMutex *mutex, DN_U64 end_ts_ms);
|
|
DN_API void DN_OS_ConditionVariableSignal (DN_OSConditionVariable *cv);
|
|
DN_API void DN_OS_ConditionVariableBroadcast (DN_OSConditionVariable *cv);
|
|
|
|
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);
|
|
|
|
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)
|
|
// DN: Single header generator commented out this header => #include "OS/dn_os_allocator.h"
|
|
#if !defined(DN_OS_ALLOCATOR_H)
|
|
#define DN_OS_ALLOCATOR_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
DN_API DN_Arena DN_ArenaFromHeap(DN_U64 size, DN_ArenaFlags flags);
|
|
DN_API DN_Arena DN_ArenaFromVMem(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags);
|
|
|
|
#endif // !defined(DN_OS_ALLOCATOR_H)
|
|
// DN: Single header generator commented out this header => #include "OS/dn_os_containers.h"
|
|
#if !defined(DN_OS_CONTAINERS_H)
|
|
#define DN_OS_CONTAINERS_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
// 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_ZMem z_mem);
|
|
template <typename T> T * DN_VArray_Make (DN_VArray<T> *array, DN_ZMem z_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_ZMem z_mem);
|
|
#endif // !defined(DN_OS_CONTAINERS_H)
|
|
// DN: Single header generator commented out this header => #include "OS/dn_os_print.h"
|
|
#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)
|
|
// DN: Single header generator commented out this header => #include "OS/dn_os_string.h"
|
|
#if !defined(DN_OS_STRING_H)
|
|
#define DN_OS_STRING_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_os_inc.h"
|
|
#endif
|
|
|
|
// NOTE: DN_Str8
|
|
|
|
DN_API DN_Str8 DN_Str8FromFmtVArenaFrame (DN_FMT_ATTRIB char const *fmt, va_list args);
|
|
DN_API DN_Str8 DN_Str8FromFmtArenaFrame (DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API DN_Str8 DN_Str8FromArenaFrame (DN_USize size, DN_ZMem z_mem);
|
|
DN_API DN_Str8 DN_Str8FromHeapF (DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API DN_Str8 DN_Str8FromHeap (DN_USize size, DN_ZMem z_mem);
|
|
DN_API DN_Str8 DN_Str8FromTLSFV (DN_FMT_ATTRIB char const *fmt, va_list args);
|
|
DN_API DN_Str8 DN_Str8FromTLSF (DN_FMT_ATTRIB char const *fmt, ...);
|
|
DN_API DN_Str8 DN_Str8FromTLS (DN_USize size, DN_ZMem z_mem);
|
|
DN_API DN_Str8 DN_Str8FromStr8Frame (DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8FromStr8TLS (DN_Str8 string);
|
|
|
|
DN_API DN_Str8SplitResult DN_Str8SplitFromFrame (DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
|
|
DN_API DN_Str8SplitResult DN_Str8SplitFromTLS (DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
|
|
|
|
DN_API DN_Str8 DN_Str8SegmentFromFrame (DN_Str8 src, DN_USize segment_size, char segment_char);
|
|
DN_API DN_Str8 DN_Str8SegmentFromTLS (DN_Str8 src, DN_USize segment_size, char segment_char);
|
|
|
|
DN_API DN_Str8 DN_Str8ReverseSegmentFromFrame (DN_Str8 src, DN_USize segment_size, char segment_char);
|
|
DN_API DN_Str8 DN_Str8ReverseSegmentFromTLS (DN_Str8 src, DN_USize segment_size, char segment_char);
|
|
|
|
DN_API DN_Str8 DN_Str8AppendFFromFrame (DN_Str8 string, char const *fmt, ...);
|
|
DN_API DN_Str8 DN_Str8AppendFFromTLS (DN_Str8 string, char const *fmt, ...);
|
|
|
|
DN_API DN_Str8 DN_Str8FillFFromFrame (DN_Str8 string, char const *fmt, ...);
|
|
DN_API DN_Str8 DN_Str8FillFFromTLS (DN_Str8 string, char const *fmt, ...);
|
|
|
|
DN_API DN_Str8TruncateResult DN_Str8TruncateMiddleArenaFrame (DN_Str8 str8, uint32_t side_size, DN_Str8 truncator);
|
|
DN_API DN_Str8TruncateResult DN_Str8TruncateMiddleArenaTLS (DN_Str8 str8, uint32_t side_size, DN_Str8 truncator);
|
|
|
|
DN_API DN_Str8 DN_Str8PadNewLines (DN_Arena *arena, DN_Str8 src, DN_Str8 pad);
|
|
DN_API DN_Str8 DN_Str8PadNewLinesFromFrame (DN_Str8 src, DN_Str8 pad);
|
|
DN_API DN_Str8 DN_Str8PadNewLinesFromTLS (DN_Str8 src, DN_Str8 pad);
|
|
|
|
DN_API DN_Str8 DN_Str8UpperFromFrame (DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8UpperFromTLS (DN_Str8 string);
|
|
|
|
DN_API DN_Str8 DN_Str8LowerFromFrame (DN_Str8 string);
|
|
DN_API DN_Str8 DN_Str8LowerFromTLS (DN_Str8 string);
|
|
|
|
DN_API DN_Str8 DN_Str8Replace (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_Str8ReplaceInsensitive (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena);
|
|
|
|
// NOTE: DN_Str8Builder ////////////////////////////////////////////////////////////////////////////
|
|
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromArena () { return DN_Str8BuilderFromArena(DN_OS_TLSGet()->frame_arena); }
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromTLS () { return DN_Str8BuilderFromArena(DN_OS_TLSTopArena()); }
|
|
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrRefFrame (DN_Str8 const *strings, DN_USize size) { return DN_Str8BuilderFromStr8PtrRef(DN_OS_TLSGet()->frame_arena, strings, size); }
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrRefTLS (DN_Str8 const *strings, DN_USize size) { return DN_Str8BuilderFromStr8PtrRef(DN_OS_TLSTopArena(), strings, size); }
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrCopyFrame (DN_Str8 const *strings, DN_USize size) { return DN_Str8BuilderFromStr8PtrCopy(DN_OS_TLSGet()->frame_arena, strings, size); }
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromStr8PtrCopyTLS (DN_Str8 const *strings, DN_USize size) { return DN_Str8BuilderFromStr8PtrCopy(DN_OS_TLSTopArena(), strings, size); }
|
|
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromBuilderFrame (DN_Str8Builder const *builder) { return DN_Str8BuilderFromBuilder(DN_OS_TLSGet()->frame_arena, builder); }
|
|
DN_API DN_Str8Builder DN_Str8BuilderFromBuilderTLS (DN_Str8Builder const *builder) { return DN_Str8BuilderFromBuilder(DN_OS_TLSTopArena(), builder); }
|
|
|
|
DN_API DN_Str8 DN_Str8BuilderBuildFromFrame (DN_Str8Builder const *builder) { return DN_Str8BuilderBuild(builder, DN_OS_TLSGet()->frame_arena); }
|
|
DN_API DN_Str8 DN_Str8BuilderBuildFromHeap (DN_Str8Builder const *builder, DN_Arena *arena);
|
|
DN_API DN_Str8 DN_Str8BuilderBuildFromTLS (DN_Str8Builder const *builder) { return DN_Str8BuilderBuild(builder, DN_OS_TLSTopArena()); }
|
|
|
|
DN_API DN_Str8 DN_Str8BuilderBuildDelimitedFromFrame(DN_Str8Builder const *builder, DN_Str8 delimiter) { return DN_Str8BuilderBuildDelimited(builder, delimiter, DN_OS_TLSGet()->frame_arena); }
|
|
DN_API DN_Str8 DN_Str8BuilderBuildDelimitedFromTLS (DN_Str8Builder const *builder, DN_Str8 delimiter) { return DN_Str8BuilderBuildDelimited(builder, delimiter, DN_OS_TLSTopArena()); }
|
|
|
|
DN_API DN_Slice<DN_Str8> DN_Str8BuilderBuildSliceFromFrame (DN_Str8Builder const *builder) { return DN_Str8BuilderBuildSlice(builder, DN_OS_TLSGet()->frame_arena); }
|
|
DN_API DN_Slice<DN_Str8> DN_Str8BuilderBuildSliceFromTLS (DN_Str8Builder const *builder) { return DN_Str8BuilderBuildSlice(builder, DN_OS_TLSTopArena()); }
|
|
|
|
#endif // !defined(DN_OS_STRING_H)
|
|
|
|
#endif // DN_OS_INC_H
|
|
#endif
|
|
|
|
struct DN_InitArgs
|
|
{
|
|
DN_U64 os_tls_reserve;
|
|
DN_U64 os_tls_commit;
|
|
DN_U64 os_tls_err_sink_reserve;
|
|
DN_U64 os_tls_err_sink_commit;
|
|
};
|
|
|
|
typedef DN_USize DN_InitFlags;
|
|
enum DN_InitFlags_
|
|
{
|
|
DN_InitFlags_Nil = 0,
|
|
DN_InitFlags_OS = 1 << 0,
|
|
DN_InitFlags_OSLeakTracker = 1 << 1,
|
|
DN_InitFlags_LogLibFeatures = 1 << 2,
|
|
DN_InitFlags_LogCPUFeatures = 1 << 3,
|
|
DN_InitFlags_LogAllFeatures = DN_InitFlags_LogLibFeatures | DN_InitFlags_LogCPUFeatures,
|
|
};
|
|
|
|
struct DN_Core
|
|
{
|
|
DN_InitFlags init_flags;
|
|
DN_USize mem_allocs_frame;
|
|
DN_LeakTracker leak;
|
|
#if defined(DN_OS_H)
|
|
DN_OSCore os;
|
|
#endif
|
|
};
|
|
|
|
extern DN_Core *g_dn_;
|
|
|
|
DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args);
|
|
DN_API void DN_BeginFrame();
|
|
|
|
#endif // !defined(DN_INC_H)
|
|
#endif
|
|
|
|
DN_Core *g_dn_;
|
|
|
|
static void DN_InitOS_(DN_OSCore *os, DN_InitArgs *args)
|
|
{
|
|
#if defined(DN_OS_H) && defined(DN_OS_CPP)
|
|
// NOTE: OS
|
|
{
|
|
#if defined(DN_PLATFORM_WIN32)
|
|
SYSTEM_INFO system_info = {};
|
|
GetSystemInfo(&system_info);
|
|
|
|
os->logical_processor_count = system_info.dwNumberOfProcessors;
|
|
os->page_size = system_info.dwPageSize;
|
|
os->alloc_granularity = system_info.dwAllocationGranularity;
|
|
#else
|
|
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
os->logical_processor_count = 1;
|
|
#else
|
|
os->logical_processor_count = get_nprocs();
|
|
#endif
|
|
os->page_size = getpagesize();
|
|
os->alloc_granularity = os->page_size;
|
|
#endif
|
|
}
|
|
|
|
// NOTE: Setup logging
|
|
DN_OS_EmitLogsWithOSPrintFunctions(os);
|
|
|
|
{
|
|
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
os->arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_ArenaFlags_NoAllocTrack);
|
|
#else
|
|
os->arena = DN_ArenaFromVMem(DN_Megabytes(1), DN_Kilobytes(4), DN_ArenaFlags_NoAllocTrack);
|
|
#endif
|
|
|
|
#if defined(DN_PLATFORM_WIN32)
|
|
os->platform_context = DN_ArenaNew(&os->arena, DN_W32Core, DN_ZMem_Yes);
|
|
#elif defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_EMSCRIPTEN)
|
|
os->platform_context = DN_ArenaNew(&os->arena, DN_POSIXCore, DN_ZMem_Yes);
|
|
#endif
|
|
|
|
#if defined(DN_PLATFORM_WIN32)
|
|
DN_W32Core *w32 = DN_Cast(DN_W32Core *) os->platform_context;
|
|
InitializeCriticalSection(&w32->sync_primitive_free_list_mutex);
|
|
|
|
QueryPerformanceFrequency(&w32->qpc_frequency);
|
|
HMODULE module = LoadLibraryA("kernel32.dll");
|
|
if (module) {
|
|
w32->set_thread_description = DN_Cast(DN_W32SetThreadDescriptionFunc *) GetProcAddress(module, "SetThreadDescription");
|
|
FreeLibrary(module);
|
|
}
|
|
|
|
// NOTE: win32 bcrypt
|
|
wchar_t const BCRYPT_ALGORITHM[] = L"RNG";
|
|
long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider(&w32->bcrypt_rng_handle, BCRYPT_ALGORITHM, nullptr /*implementation*/, 0 /*flags*/);
|
|
if (w32->bcrypt_rng_handle && init_status == 0)
|
|
w32->bcrypt_init_success = true;
|
|
else
|
|
DN_LOG_ErrorF("Failed to initialise Windows secure random number generator, error: %d", init_status);
|
|
#else
|
|
DN_Posix_Init(DN_Cast(DN_POSIXCore *)os->platform_context);
|
|
#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->os_tls_commit;
|
|
tls_init_args.reserve = args->os_tls_reserve;
|
|
tls_init_args.err_sink_reserve = args->os_tls_err_sink_reserve;
|
|
tls_init_args.err_sink_commit = args->os_tls_err_sink_commit;
|
|
}
|
|
|
|
DN_OS_TLSInit(&os->tls, tls_init_args);
|
|
DN_OS_TLSSetCurrentThreadTLS(&os->tls);
|
|
os->cpu_report = DN_CPUGetReport();
|
|
|
|
#define DN_CPU_FEAT_XENTRY(label) g_dn_cpu_feature_decl[DN_CPUFeature_##label] = {DN_CPUFeature_##label, DN_Str8Lit(#label)};
|
|
DN_CPU_FEAT_XMACRO
|
|
#undef DN_CPU_FEAT_XENTRY
|
|
DN_Assert(g_dn_);
|
|
#endif // defined(DN_OS_H) && defined(DN_OS_CPP)
|
|
}
|
|
|
|
|
|
DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args)
|
|
{
|
|
g_dn_ = dn;
|
|
dn->init_flags = flags;
|
|
|
|
if (flags & DN_InitFlags_OS) {
|
|
#if defined(DN_OS_H) && defined(DN_OS_CPP)
|
|
DN_InitOS_(&dn->os, args);
|
|
if (flags & DN_InitFlags_OSLeakTracker) {
|
|
// NOTE: Setup the allocation table with allocation tracking turned off on
|
|
// the arena we're using to initialise the table.
|
|
dn->leak.alloc_table_arena = DN_ArenaFromVMem(DN_Megabytes(1), DN_Kilobytes(512), DN_ArenaFlags_NoAllocTrack | DN_ArenaFlags_AllocCanLeak);
|
|
dn->leak.alloc_table = DN_DSMap_Init<DN_LeakAlloc>(&dn->leak.alloc_table_arena, 4096, DN_DSMapFlags_Nil);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// NOTE: Print out init features
|
|
char buf[4096];
|
|
DN_USize buf_size = 0;
|
|
if (flags & DN_InitFlags_LogLibFeatures) {
|
|
DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), "DN initialised:\n");
|
|
#if defined(DN_OS_CPP)
|
|
DN_F32 page_size_kib = dn->os.page_size / 1024.0f;
|
|
DN_F32 alloc_granularity_kib = dn->os.alloc_granularity / 1024.0f;
|
|
DN_FmtAppendTruncate(buf,
|
|
&buf_size,
|
|
sizeof(buf),
|
|
DN_Str8Lit("..."),
|
|
" OS Page Size/Alloc Granularity: %.1f/%.1fKiB\n"
|
|
" Logical Processor Count: %u\n",
|
|
page_size_kib,
|
|
alloc_granularity_kib,
|
|
dn->os.logical_processor_count);
|
|
#endif
|
|
|
|
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
|
if (DN_ASAN_POISON) {
|
|
DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), " ASAN manual poisoning%s\n", DN_ASAN_VET_POISON ? " (+vet sanity checks)" : "");
|
|
DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), " ASAN poison guard size: %u\n", DN_ASAN_POISON_GUARD_SIZE);
|
|
}
|
|
#endif
|
|
|
|
#if defined(DN_LEAK_TRACKING)
|
|
DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), " Allocation leak tracing\n");
|
|
#endif
|
|
|
|
#if defined(DN_PLATFORM_EMSCRIPTEN) || defined(DN_PLATFORM_POSIX)
|
|
DN_POSIXCore *posix = DN_Cast(DN_POSIXCore *)g_dn_->os.platform_context;
|
|
DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), " Clock GetTime: %S\n", posix->clock_monotonic_raw ? DN_Str8Lit("CLOCK_MONOTONIC_RAW") : DN_Str8Lit("CLOCK_MONOTONIC"));
|
|
#endif
|
|
// TODO(doyle): Add stacktrace feature log
|
|
}
|
|
|
|
if (flags & DN_InitFlags_LogCPUFeatures) {
|
|
DN_CPUReport const *report = &dn->os.cpu_report;
|
|
DN_Str8 brand = DN_Str8TrimWhitespaceAround(DN_Str8FromPtr(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_Str8BuilderAppendF' Actual type: 'struct DN_Str8'.
|
|
DN_FmtAppendTruncate(buf, &buf_size, sizeof(buf), DN_Str8Lit("..."), " CPU '%S' from '%s' detected:\n", brand, report->vendor);
|
|
DN_MSVC_WARNING_POP
|
|
|
|
DN_USize longest_feature_name = 0;
|
|
for (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);
|
|
}
|
|
|
|
for (DN_ForIndexU(feature_index, DN_CPUFeature_Count)) {
|
|
DN_CPUFeatureDecl feature_decl = g_dn_cpu_feature_decl[feature_index];
|
|
bool has_feature = DN_CPUHasFeature(report, feature_decl.value);
|
|
DN_FmtAppendTruncate(buf,
|
|
&buf_size,
|
|
sizeof(buf),
|
|
DN_Str8Lit("..."),
|
|
" %.*s:%*s%s\n",
|
|
DN_Str8PrintFmt(feature_decl.label),
|
|
DN_Cast(int)(longest_feature_name - feature_decl.label.size),
|
|
"",
|
|
has_feature ? "available" : "not available");
|
|
}
|
|
}
|
|
|
|
if (buf_size)
|
|
DN_LOG_DebugF("%.*s", DN_Cast(int)buf_size, buf);
|
|
}
|
|
|
|
DN_API void DN_BeginFrame()
|
|
{
|
|
DN_AtomicSetValue64(&g_dn_->os.mem_allocs_frame, 0);
|
|
}
|
|
#define DN_MATH_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "dn_math.h"
|
|
#if !defined(DN_MATH_H)
|
|
#define DN_MATH_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
|
|
union DN_V2I32
|
|
{
|
|
struct { DN_I32 x, y; };
|
|
struct { DN_I32 w, h; };
|
|
DN_I32 data[2];
|
|
};
|
|
|
|
union DN_V2U16
|
|
{
|
|
struct { DN_U16 x, y; };
|
|
struct { DN_U16 w, h; };
|
|
DN_U16 data[2];
|
|
};
|
|
|
|
union DN_V2U32
|
|
{
|
|
struct { DN_U32 x, y; };
|
|
struct { DN_U32 w, h; };
|
|
DN_U32 data[2];
|
|
};
|
|
|
|
union DN_V2F32
|
|
{
|
|
struct { DN_F32 x, y; };
|
|
struct { DN_F32 w, h; };
|
|
DN_F32 data[2];
|
|
};
|
|
|
|
union DN_V3F32
|
|
{
|
|
struct { DN_F32 x, y, z; };
|
|
struct { DN_F32 r, g, b; };
|
|
DN_F32 data[3];
|
|
};
|
|
|
|
|
|
union DN_V4F32
|
|
{
|
|
struct { DN_F32 x, y, z, w; };
|
|
struct { DN_F32 r, g, b, a; };
|
|
#if !defined(DN_NO_V3)
|
|
DN_V3F32 rgb;
|
|
DN_V3F32 xyz;
|
|
#endif
|
|
DN_F32 data[4];
|
|
};
|
|
DN_MSVC_WARNING_POP
|
|
|
|
struct DN_M4
|
|
{
|
|
DN_F32 columns[4][4]; // Column major matrix
|
|
};
|
|
|
|
union DN_M2x3
|
|
{
|
|
DN_F32 e[6];
|
|
DN_F32 row[2][3];
|
|
};
|
|
|
|
struct DN_Rect
|
|
{
|
|
DN_V2F32 pos, size;
|
|
};
|
|
|
|
struct DN_RectMinMax
|
|
{
|
|
DN_V2F32 min, max;
|
|
};
|
|
|
|
enum DN_RectCutClip
|
|
{
|
|
DN_RectCutClip_No,
|
|
DN_RectCutClip_Yes,
|
|
};
|
|
|
|
enum DN_RectCutSide
|
|
{
|
|
DN_RectCutSide_Left,
|
|
DN_RectCutSide_Right,
|
|
DN_RectCutSide_Top,
|
|
DN_RectCutSide_Bottom,
|
|
};
|
|
|
|
struct DN_RectCut
|
|
{
|
|
DN_Rect* rect;
|
|
DN_RectCutSide side;
|
|
};
|
|
|
|
struct DN_RaycastLineIntersectV2Result
|
|
{
|
|
bool hit; // True if there was an intersection, false if the lines are parallel
|
|
DN_F32 t_a; // Distance along `dir_a` that the intersection occurred, e.g. `origin_a + (dir_a * t_a)`
|
|
DN_F32 t_b; // Distance along `dir_b` that the intersection occurred, e.g. `origin_b + (dir_b * t_b)`
|
|
};
|
|
|
|
#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_From1N(x) DN_Literal(DN_V2I32){{(int32_t)(x), (int32_t)(x)}}
|
|
#define DN_V2I32_From2N(x, y) DN_Literal(DN_V2I32){{(int32_t)(x), (int32_t)(y)}}
|
|
#define DN_V2I32_InitV2(xy) DN_Literal(DN_V2I32){{(int32_t)(xy).x, (int32_t)(xy).y}}
|
|
|
|
DN_API bool operator!= (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API bool operator== (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API bool operator>= (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API bool operator<= (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API bool operator< (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API bool operator> (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 operator- (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 operator- (DN_V2I32 lhs);
|
|
DN_API DN_V2I32 operator+ (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 operator* (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 operator* (DN_V2I32 lhs, DN_F32 rhs);
|
|
DN_API DN_V2I32 operator* (DN_V2I32 lhs, int32_t rhs);
|
|
DN_API DN_V2I32 operator/ (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 operator/ (DN_V2I32 lhs, DN_F32 rhs);
|
|
DN_API DN_V2I32 operator/ (DN_V2I32 lhs, int32_t rhs);
|
|
DN_API DN_V2I32 & operator*= (DN_V2I32& lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 & operator*= (DN_V2I32& lhs, DN_F32 rhs);
|
|
DN_API DN_V2I32 & operator*= (DN_V2I32& lhs, int32_t rhs);
|
|
DN_API DN_V2I32 & operator/= (DN_V2I32& lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 & operator/= (DN_V2I32& lhs, DN_F32 rhs);
|
|
DN_API DN_V2I32 & operator/= (DN_V2I32& lhs, int32_t rhs);
|
|
DN_API DN_V2I32 & operator-= (DN_V2I32& lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 & operator+= (DN_V2I32& lhs, DN_V2I32 rhs);
|
|
|
|
DN_API DN_V2I32 DN_V2I32_Min (DN_V2I32 a, DN_V2I32 b);
|
|
DN_API DN_V2I32 DN_V2I32_Max (DN_V2I32 a, DN_V2I32 b);
|
|
DN_API DN_V2I32 DN_V2I32_Abs (DN_V2I32 a);
|
|
|
|
#define DN_V2U16_Zero DN_Literal(DN_V2U16){{(uint16_t)(0), (uint16_t)(0)}}
|
|
#define DN_V2U16_One DN_Literal(DN_V2U16){{(uint16_t)(1), (uint16_t)(1)}}
|
|
#define DN_V2U16_From1N(x) DN_Literal(DN_V2U16){{(uint16_t)(x), (uint16_t)(x)}}
|
|
#define DN_V2U16_From2N(x, y) DN_Literal(DN_V2U16){{(uint16_t)(x), (uint16_t)(y)}}
|
|
|
|
DN_API bool operator!= (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API bool operator== (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API bool operator>= (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API bool operator<= (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API bool operator< (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API bool operator> (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 operator- (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 operator+ (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 operator* (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 operator* (DN_V2U16 lhs, DN_F32 rhs);
|
|
DN_API DN_V2U16 operator* (DN_V2U16 lhs, int32_t rhs);
|
|
DN_API DN_V2U16 operator/ (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 operator/ (DN_V2U16 lhs, DN_F32 rhs);
|
|
DN_API DN_V2U16 operator/ (DN_V2U16 lhs, int32_t rhs);
|
|
DN_API DN_V2U16 & operator*= (DN_V2U16& lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 & operator*= (DN_V2U16& lhs, DN_F32 rhs);
|
|
DN_API DN_V2U16 & operator*= (DN_V2U16& lhs, int32_t rhs);
|
|
DN_API DN_V2U16 & operator/= (DN_V2U16& lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 & operator/= (DN_V2U16& lhs, DN_F32 rhs);
|
|
DN_API DN_V2U16 & operator/= (DN_V2U16& lhs, int32_t rhs);
|
|
DN_API DN_V2U16 & operator-= (DN_V2U16& lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 & operator+= (DN_V2U16& lhs, DN_V2U16 rhs);
|
|
|
|
#define DN_V2F32_Zero DN_Literal(DN_V2F32){{(DN_F32)(0), (DN_F32)(0)}}
|
|
#define DN_V2F32_One DN_Literal(DN_V2F32){{(DN_F32)(1), (DN_F32)(1)}}
|
|
#define DN_V2F32_From1N(x) DN_Literal(DN_V2F32){{(DN_F32)(x), (DN_F32)(x)}}
|
|
#define DN_V2F32_From2N(x, y) DN_Literal(DN_V2F32){{(DN_F32)(x), (DN_F32)(y)}}
|
|
#define DN_V2F32_FromV2I32(xy) DN_Literal(DN_V2F32){{(DN_F32)(xy).x, (DN_F32)(xy).y}}
|
|
#define DN_V2F32_FromV2U32(xy) DN_Literal(DN_V2F32){{(DN_F32)(xy).x, (DN_F32)(xy).y}}
|
|
|
|
DN_API bool operator!= (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool operator== (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool operator>= (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool operator<= (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool operator< (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool operator> (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
|
|
DN_API DN_V2F32 operator- (DN_V2F32 lhs);
|
|
DN_API DN_V2F32 operator- (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 operator- (DN_V2F32 lhs, DN_V2I32 rhs);
|
|
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 operator+ (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 operator+ (DN_V2F32 lhs, DN_V2I32 rhs);
|
|
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 operator* (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 operator* (DN_V2F32 lhs, DN_V2I32 rhs);
|
|
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 operator/ (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 operator/ (DN_V2F32 lhs, DN_V2I32 rhs);
|
|
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 & operator*= (DN_V2F32& lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 & operator*= (DN_V2F32& lhs, DN_V2I32 rhs);
|
|
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 & operator/= (DN_V2F32& lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 & operator/= (DN_V2F32& lhs, DN_V2I32 rhs);
|
|
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 & operator-= (DN_V2F32& lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 & operator-= (DN_V2F32& lhs, DN_V2I32 rhs);
|
|
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 & operator+= (DN_V2F32& lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 & operator+= (DN_V2F32& lhs, DN_V2I32 rhs);
|
|
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_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_LengthSq2V2 (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool DN_V2F32_LengthSqIsWithin2V2 (DN_V2F32 lhs, DN_V2F32 rhs, DN_F32 within_amount_sq);
|
|
DN_API DN_F32 DN_V2F32_Length2V2 (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);
|
|
|
|
#define DN_V3F32_From1N(x) DN_Literal(DN_V3F32){{(DN_F32)(x), (DN_F32)(x), (DN_F32)(x)}}
|
|
#define DN_V3F32_From3N(x, y, z) DN_Literal(DN_V3F32){{(DN_F32)(x), (DN_F32)(y), (DN_F32)(z)}}
|
|
#define DN_V3F32_FromV2F32And1N(xy, z) DN_Literal(DN_V3F32){{(DN_F32)(xy.x), (DN_F32)(xy.y), (DN_F32)(z)}}
|
|
|
|
DN_API bool operator== (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API bool operator!= (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API bool operator>= (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API bool operator<= (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API bool operator< (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API bool operator> (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 operator- (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 operator- (DN_V3F32 lhs);
|
|
DN_API DN_V3F32 operator+ (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 operator* (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 operator* (DN_V3F32 lhs, DN_F32 rhs);
|
|
DN_API DN_V3F32 operator* (DN_V3F32 lhs, int32_t rhs);
|
|
DN_API DN_V3F32 operator/ (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 operator/ (DN_V3F32 lhs, DN_F32 rhs);
|
|
DN_API DN_V3F32 operator/ (DN_V3F32 lhs, int32_t rhs);
|
|
DN_API DN_V3F32 & operator*= (DN_V3F32 &lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 & operator*= (DN_V3F32 &lhs, DN_F32 rhs);
|
|
DN_API DN_V3F32 & operator*= (DN_V3F32 &lhs, int32_t rhs);
|
|
DN_API DN_V3F32 & operator/= (DN_V3F32 &lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 & operator/= (DN_V3F32 &lhs, DN_F32 rhs);
|
|
DN_API DN_V3F32 & operator/= (DN_V3F32 &lhs, int32_t rhs);
|
|
DN_API DN_V3F32 & operator-= (DN_V3F32 &lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 & operator+= (DN_V3F32 &lhs, DN_V3F32 rhs);
|
|
DN_API DN_F32 DN_V3F32_LengthSq (DN_V3F32 a);
|
|
DN_API DN_F32 DN_V3F32_Length (DN_V3F32 a);
|
|
DN_API DN_V3F32 DN_V3F32_Normalise (DN_V3F32 a);
|
|
|
|
DN_U32 const DN_V4_R_MASK_U32 = 0xFF000000;
|
|
DN_U32 const DN_V4_G_MASK_U32 = 0x00FF0000;
|
|
DN_U32 const DN_V4_B_MASK_U32 = 0x0000FF00;
|
|
DN_U32 const DN_V4_A_MASK_U32 = 0x000000FF;
|
|
|
|
#define DN_V4F32_From1N(x) DN_Literal(DN_V4F32){{(DN_F32)(x), (DN_F32)(x), (DN_F32)(x), (DN_F32)(x)}}
|
|
#define DN_V4F32_From4N(x, y, z, w) DN_Literal(DN_V4F32){{(DN_F32)(x), (DN_F32)(y), (DN_F32)(z), (DN_F32)(w)}}
|
|
#define DN_V4F32_FromV3And1N(xyz, w) DN_Literal(DN_V4F32){{xyz.x, xyz.y, xyz.z, w}}
|
|
#define DN_V4F32_FromRGBAU8(r, g, b, a) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, a / 255.f}}
|
|
#define DN_V4F32_FromRGBU8(r, g, b) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, 1.f}}
|
|
DN_API DN_V4F32 DN_V4F32_FromRGBU32(DN_U32 u32);
|
|
DN_API DN_V4F32 DN_V4F32_FromRGBAU32(DN_U32 u32);
|
|
#define DN_V4F32_FromV4Alpha(v4, alpha) DN_V4F32_FromV3And1N(v4.xyz, alpha)
|
|
DN_API bool operator== (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API bool operator!= (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API bool operator<= (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API bool operator< (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API bool operator> (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 operator- (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 operator- (DN_V4F32 lhs);
|
|
DN_API DN_V4F32 operator+ (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 operator* (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 operator* (DN_V4F32 lhs, DN_F32 rhs);
|
|
DN_API DN_V4F32 operator* (DN_V4F32 lhs, int32_t rhs);
|
|
DN_API DN_V4F32 operator/ (DN_V4F32 lhs, DN_F32 rhs);
|
|
DN_API DN_V4F32 & operator*= (DN_V4F32 &lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 & operator*= (DN_V4F32 &lhs, DN_F32 rhs);
|
|
DN_API DN_V4F32 & operator*= (DN_V4F32 &lhs, int32_t rhs);
|
|
DN_API DN_V4F32 & operator-= (DN_V4F32 &lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 & operator+= (DN_V4F32 &lhs, DN_V4F32 rhs);
|
|
DN_API DN_F32 DN_V4F32_Dot (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);
|
|
DN_API DN_M4 DN_M4_Scale (DN_V3F32 xyz);
|
|
DN_API DN_M4 DN_M4_TranslateF (DN_F32 x, DN_F32 y, DN_F32 z);
|
|
DN_API DN_M4 DN_M4_Translate (DN_V3F32 xyz);
|
|
DN_API DN_M4 DN_M4_Transpose (DN_M4 mat);
|
|
DN_API DN_M4 DN_M4_Rotate (DN_V3F32 axis, DN_F32 radians);
|
|
DN_API DN_M4 DN_M4_Orthographic (DN_F32 left, DN_F32 right, DN_F32 bottom, DN_F32 top, DN_F32 z_near, DN_F32 z_far);
|
|
DN_API DN_M4 DN_M4_Perspective (DN_F32 fov /*radians*/, DN_F32 aspect, DN_F32 z_near, DN_F32 z_far);
|
|
DN_API DN_M4 DN_M4_Add (DN_M4 lhs, DN_M4 rhs);
|
|
DN_API DN_M4 DN_M4_Sub (DN_M4 lhs, DN_M4 rhs);
|
|
DN_API DN_M4 DN_M4_Mul (DN_M4 lhs, DN_M4 rhs);
|
|
DN_API DN_M4 DN_M4_Div (DN_M4 lhs, DN_M4 rhs);
|
|
DN_API DN_M4 DN_M4_AddF (DN_M4 lhs, DN_F32 rhs);
|
|
DN_API DN_M4 DN_M4_SubF (DN_M4 lhs, DN_F32 rhs);
|
|
DN_API DN_M4 DN_M4_MulF (DN_M4 lhs, DN_F32 rhs);
|
|
DN_API DN_M4 DN_M4_DivF (DN_M4 lhs, DN_F32 rhs);
|
|
DN_API DN_Str8x256 DN_M4_ColumnMajorString (DN_M4 mat);
|
|
|
|
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 ();
|
|
DN_API DN_M2x3 DN_M2x3_Translate (DN_V2F32 offset);
|
|
DN_API DN_M2x3 DN_M2x3_Scale (DN_V2F32 scale);
|
|
DN_API DN_M2x3 DN_M2x3_Rotate (DN_F32 radians);
|
|
DN_API DN_M2x3 DN_M2x3_Mul (DN_M2x3 m1, DN_M2x3 m2);
|
|
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);
|
|
|
|
#define DN_Rect_From2V2(pos, size) DN_Literal(DN_Rect){(pos), (size)}
|
|
#define DN_Rect_From4N(x, y, w, h) DN_Literal(DN_Rect){DN_Literal(DN_V2F32){{x, y}}, DN_Literal(DN_V2F32){{w, h}}}
|
|
|
|
DN_API bool operator== (const DN_Rect& lhs, const DN_Rect& rhs);
|
|
DN_API DN_V2F32 DN_Rect_Center (DN_Rect rect);
|
|
DN_API bool DN_Rect_ContainsPoint (DN_Rect rect, DN_V2F32 p);
|
|
DN_API bool DN_Rect_ContainsRect (DN_Rect a, DN_Rect b);
|
|
DN_API DN_Rect DN_Rect_Expand (DN_Rect a, DN_F32 amount);
|
|
DN_API DN_Rect DN_Rect_ExpandV2 (DN_Rect a, DN_V2F32 amount);
|
|
DN_API bool DN_Rect_Intersects (DN_Rect a, DN_Rect b);
|
|
DN_API DN_Rect DN_Rect_Intersection (DN_Rect a, DN_Rect b);
|
|
DN_API DN_Rect DN_Rect_Union (DN_Rect a, DN_Rect b);
|
|
DN_API DN_RectMinMax DN_Rect_MinMax (DN_Rect a);
|
|
DN_API DN_F32 DN_Rect_Area (DN_Rect a);
|
|
DN_API DN_V2F32 DN_Rect_InterpolatedPoint (DN_Rect rect, DN_V2F32 t01);
|
|
DN_API DN_V2F32 DN_Rect_TopLeft (DN_Rect rect);
|
|
DN_API DN_V2F32 DN_Rect_TopRight (DN_Rect rect);
|
|
DN_API DN_V2F32 DN_Rect_BottomLeft (DN_Rect rect);
|
|
DN_API DN_V2F32 DN_Rect_BottomRight (DN_Rect rect);
|
|
|
|
DN_API DN_Rect DN_Rect_CutLeftClip (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);
|
|
DN_API DN_Rect DN_Rect_CutRightClip (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);
|
|
DN_API DN_Rect DN_Rect_CutTopClip (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);
|
|
DN_API DN_Rect DN_Rect_CutBottomClip (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);
|
|
|
|
#define DN_Rect_CutLeft(rect, amount) DN_Rect_CutLeftClip(rect, amount, DN_RectCutClip_Yes)
|
|
#define DN_Rect_CutRight(rect, amount) DN_Rect_CutRightClip(rect, amount, DN_RectCutClip_Yes)
|
|
#define DN_Rect_CutTop(rect, amount) DN_Rect_CutTopClip(rect, amount, DN_RectCutClip_Yes)
|
|
#define DN_Rect_CutBottom(rect, amount) DN_Rect_CutBottomClip(rect, amount, DN_RectCutClip_Yes)
|
|
|
|
#define DN_Rect_CutLeftNoClip(rect, amount) DN_Rect_CutLeftClip(rect, amount, DN_RectCutClip_No)
|
|
#define DN_Rect_CutRightNoClip(rect, amount) DN_Rect_CutRightClip(rect, amount, DN_RectCutClip_No)
|
|
#define DN_Rect_CutTopNoClip(rect, amount) DN_Rect_CutTopClip(rect, amount, DN_RectCutClip_No)
|
|
#define DN_Rect_CutBottomNoClip(rect, amount) DN_Rect_CutBottomClip(rect, amount, DN_RectCutClip_No)
|
|
|
|
DN_API DN_Rect DN_RectCut_Cut (DN_RectCut rect_cut, DN_V2F32 size, DN_RectCutClip clip);
|
|
#define DN_RectCut_Init(rect, side) DN_Literal(DN_RectCut){rect, side}
|
|
#define DN_RectCut_Left(rect) DN_Literal(DN_RectCut){rect, DN_RectCutSide_Left}
|
|
#define DN_RectCut_Right(rect) DN_Literal(DN_RectCut){rect, DN_RectCutSide_Right}
|
|
#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}
|
|
|
|
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)
|
|
#endif
|
|
|
|
DN_API bool operator==(DN_V2I32 lhs, DN_V2I32 rhs)
|
|
{
|
|
bool result = (lhs.x == rhs.x) && (lhs.y == rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator!=(DN_V2I32 lhs, DN_V2I32 rhs)
|
|
{
|
|
bool result = !(lhs == rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator>=(DN_V2I32 lhs, DN_V2I32 rhs)
|
|
{
|
|
bool result = (lhs.x >= rhs.x) && (lhs.y >= rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator<=(DN_V2I32 lhs, DN_V2I32 rhs)
|
|
{
|
|
bool result = (lhs.x <= rhs.x) && (lhs.y <= rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator<(DN_V2I32 lhs, DN_V2I32 rhs)
|
|
{
|
|
bool result = (lhs.x < rhs.x) && (lhs.y < rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator>(DN_V2I32 lhs, DN_V2I32 rhs)
|
|
{
|
|
bool result = (lhs.x > rhs.x) && (lhs.y > rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 operator-(DN_V2I32 lhs, DN_V2I32 rhs)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(lhs.x - rhs.x, lhs.y - rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 operator-(DN_V2I32 lhs)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(-lhs.x, -lhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 operator+(DN_V2I32 lhs, DN_V2I32 rhs)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(lhs.x + rhs.x, lhs.y + rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 operator*(DN_V2I32 lhs, DN_V2I32 rhs)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(lhs.x * rhs.x, lhs.y * rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 operator*(DN_V2I32 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(lhs.x * rhs, lhs.y * rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 operator*(DN_V2I32 lhs, int32_t rhs)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(lhs.x * rhs, lhs.y * rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 operator/(DN_V2I32 lhs, DN_V2I32 rhs)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(lhs.x / rhs.x, lhs.y / rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 operator/(DN_V2I32 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(lhs.x / rhs, lhs.y / rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 operator/(DN_V2I32 lhs, int32_t rhs)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(lhs.x / rhs, lhs.y / rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 &operator*=(DN_V2I32 &lhs, DN_V2I32 rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2I32 &operator*=(DN_V2I32 &lhs, DN_F32 rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2I32 &operator*=(DN_V2I32 &lhs, int32_t rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2I32 &operator/=(DN_V2I32 &lhs, DN_V2I32 rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2I32 &operator/=(DN_V2I32 &lhs, DN_F32 rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2I32 &operator/=(DN_V2I32 &lhs, int32_t rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2I32 &operator-=(DN_V2I32 &lhs, DN_V2I32 rhs)
|
|
{
|
|
lhs = lhs - rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2I32 &operator+=(DN_V2I32 &lhs, DN_V2I32 rhs)
|
|
{
|
|
lhs = lhs + rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2I32 DN_V2I32_Min(DN_V2I32 a, DN_V2I32 b)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(DN_Min(a.x, b.x), DN_Min(a.y, b.y));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 DN_V2I32_Max(DN_V2I32 a, DN_V2I32 b)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(DN_Max(a.x, b.x), DN_Max(a.y, b.y));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2I32 DN_V2I32_Abs(DN_V2I32 a)
|
|
{
|
|
DN_V2I32 result = DN_V2I32_From2N(DN_Abs(a.x), DN_Abs(a.y));
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator!=(DN_V2U16 lhs, DN_V2U16 rhs)
|
|
{
|
|
bool result = !(lhs == rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator==(DN_V2U16 lhs, DN_V2U16 rhs)
|
|
{
|
|
bool result = (lhs.x == rhs.x) && (lhs.y == rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator>=(DN_V2U16 lhs, DN_V2U16 rhs)
|
|
{
|
|
bool result = (lhs.x >= rhs.x) && (lhs.y >= rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator<=(DN_V2U16 lhs, DN_V2U16 rhs)
|
|
{
|
|
bool result = (lhs.x <= rhs.x) && (lhs.y <= rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator<(DN_V2U16 lhs, DN_V2U16 rhs)
|
|
{
|
|
bool result = (lhs.x < rhs.x) && (lhs.y < rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator>(DN_V2U16 lhs, DN_V2U16 rhs)
|
|
{
|
|
bool result = (lhs.x > rhs.x) && (lhs.y > rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2U16 operator-(DN_V2U16 lhs, DN_V2U16 rhs)
|
|
{
|
|
DN_V2U16 result = DN_V2U16_From2N(lhs.x - rhs.x, lhs.y - rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2U16 operator+(DN_V2U16 lhs, DN_V2U16 rhs)
|
|
{
|
|
DN_V2U16 result = DN_V2U16_From2N(lhs.x + rhs.x, lhs.y + rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2U16 operator*(DN_V2U16 lhs, DN_V2U16 rhs)
|
|
{
|
|
DN_V2U16 result = DN_V2U16_From2N(lhs.x * rhs.x, lhs.y * rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2U16 operator*(DN_V2U16 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V2U16 result = DN_V2U16_From2N(lhs.x * rhs, lhs.y * rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2U16 operator*(DN_V2U16 lhs, int32_t rhs)
|
|
{
|
|
DN_V2U16 result = DN_V2U16_From2N(lhs.x * rhs, lhs.y * rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2U16 operator/(DN_V2U16 lhs, DN_V2U16 rhs)
|
|
{
|
|
DN_V2U16 result = DN_V2U16_From2N(lhs.x / rhs.x, lhs.y / rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2U16 operator/(DN_V2U16 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V2U16 result = DN_V2U16_From2N(lhs.x / rhs, lhs.y / rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2U16 operator/(DN_V2U16 lhs, int32_t rhs)
|
|
{
|
|
DN_V2U16 result = DN_V2U16_From2N(lhs.x / rhs, lhs.y / rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2U16 &operator*=(DN_V2U16 &lhs, DN_V2U16 rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2U16 &operator*=(DN_V2U16 &lhs, DN_F32 rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2U16 &operator*=(DN_V2U16 &lhs, int32_t rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2U16 &operator/=(DN_V2U16 &lhs, DN_V2U16 rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2U16 &operator/=(DN_V2U16 &lhs, DN_F32 rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2U16 &operator/=(DN_V2U16 &lhs, int32_t rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2U16 &operator-=(DN_V2U16 &lhs, DN_V2U16 rhs)
|
|
{
|
|
lhs = lhs - rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2U16 &operator+=(DN_V2U16 &lhs, DN_V2U16 rhs)
|
|
{
|
|
lhs = lhs + rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API bool operator!=(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
bool result = !(lhs == rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator==(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
bool result = (lhs.x == rhs.x) && (lhs.y == rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator>=(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
bool result = (lhs.x >= rhs.x) && (lhs.y >= rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator<=(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
bool result = (lhs.x <= rhs.x) && (lhs.y <= rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator<(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
bool result = (lhs.x < rhs.x) && (lhs.y < rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator>(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
bool result = (lhs.x > rhs.x) && (lhs.y > rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator-(DN_V2F32 lhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(-lhs.x, -lhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator-(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x - rhs.x, lhs.y - rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator-(DN_V2F32 lhs, DN_V2I32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x - rhs.x, lhs.y - rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator-(DN_V2F32 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x - rhs, lhs.y - rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator-(DN_V2F32 lhs, int32_t rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x - rhs, lhs.y - rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator+(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x + rhs.x, lhs.y + rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator+(DN_V2F32 lhs, DN_V2I32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x + rhs.x, lhs.y + rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator+(DN_V2F32 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x + rhs, lhs.y + rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator+(DN_V2F32 lhs, int32_t rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x + rhs, lhs.y + rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator*(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x * rhs.x, lhs.y * rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator*(DN_V2F32 lhs, DN_V2I32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x * rhs.x, lhs.y * rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator*(DN_V2F32 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x * rhs, lhs.y * rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator*(DN_V2F32 lhs, int32_t rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x * rhs, lhs.y * rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator/(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x / rhs.x, lhs.y / rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator/(DN_V2F32 lhs, DN_V2I32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x / rhs.x, lhs.y / rhs.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator/(DN_V2F32 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x / rhs, lhs.y / rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 operator/(DN_V2F32 lhs, int32_t rhs)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(lhs.x / rhs, lhs.y / rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator*=(DN_V2F32 &lhs, DN_V2F32 rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator*=(DN_V2F32 &lhs, DN_V2I32 rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator*=(DN_V2F32 &lhs, DN_F32 rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator*=(DN_V2F32 &lhs, int32_t rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator/=(DN_V2F32 &lhs, DN_V2F32 rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator/=(DN_V2F32 &lhs, DN_V2I32 rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator/=(DN_V2F32 &lhs, DN_F32 rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator/=(DN_V2F32 &lhs, int32_t rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator-=(DN_V2F32 &lhs, DN_V2F32 rhs)
|
|
{
|
|
lhs = lhs - rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator-=(DN_V2F32 &lhs, DN_V2I32 rhs)
|
|
{
|
|
lhs = lhs - rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator-=(DN_V2F32 &lhs, DN_F32 rhs)
|
|
{
|
|
lhs = lhs - rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator-=(DN_V2F32 &lhs, int32_t rhs)
|
|
{
|
|
lhs = lhs - rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator+=(DN_V2F32 &lhs, DN_V2F32 rhs)
|
|
{
|
|
lhs = lhs + rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator+=(DN_V2F32 &lhs, DN_V2I32 rhs)
|
|
{
|
|
lhs = lhs + rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator+=(DN_V2F32 &lhs, DN_F32 rhs)
|
|
{
|
|
lhs = lhs + rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 &operator+=(DN_V2F32 &lhs, int32_t rhs)
|
|
{
|
|
lhs = lhs + rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_V2F32_Min(DN_V2F32 a, DN_V2F32 b)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(DN_Min(a.x, b.x), DN_Min(a.y, b.y));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_V2F32_Max(DN_V2F32 a, DN_V2F32 b)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(DN_Max(a.x, b.x), DN_Max(a.y, b.y));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_V2F32_Abs(DN_V2F32 a)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(DN_Abs(a.x), DN_Abs(a.y));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_F32 DN_V2F32_Dot(DN_V2F32 a, DN_V2F32 b)
|
|
{
|
|
// NOTE: Scalar projection of B onto A /////////////////////////////////////////////////////////
|
|
//
|
|
// Scalar projection calculates the signed distance between `b` and `a`
|
|
// where `a` is a unit vector then, the dot product calculates the projection
|
|
// of `b` onto the infinite line that the direction of `a` represents. This
|
|
// calculation is the signed distance.
|
|
//
|
|
// signed_distance = dot_product(a, b) = (a.x * b.x) + (a.y * b.y)
|
|
//
|
|
// Y
|
|
// ^ b
|
|
// | /|
|
|
// | / |
|
|
// | / |
|
|
// | / | Projection
|
|
// | / |
|
|
// |/ V
|
|
// +--->--------> X
|
|
// . a .
|
|
// . .
|
|
// |------| <- Calculated signed distance
|
|
//
|
|
// The signed-ness of the result indicates the relationship:
|
|
//
|
|
// Distance <0 means `b` is behind `a`
|
|
// Distance >0 means `b` is in-front of `a`
|
|
// Distance ==0 means `b` is perpendicular to `a`
|
|
//
|
|
// If `a` is not normalized then the signed-ness of the result still holds
|
|
// however result no longer represents the actual distance between the
|
|
// 2 objects. One of the vectors must be normalised (e.g. turned into a unit
|
|
// vector).
|
|
//
|
|
// NOTE: DN_V projection /////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DN_V projection calculates the exact X,Y coordinates of where `b` meets
|
|
// `a` when it was projected. This is calculated by multipying the
|
|
// 'scalar projection' result by the unit vector of `a`
|
|
//
|
|
// vector_projection = a * signed_distance = a * dot_product(a, b)
|
|
|
|
DN_F32 result = (a.x * b.x) + (a.y * b.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_F32 DN_V2F32_LengthSq2V2(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
// NOTE: Pythagoras's theorem (a^2 + b^2 = c^2) without the square root
|
|
DN_F32 a = rhs.x - lhs.x;
|
|
DN_F32 b = rhs.y - lhs.y;
|
|
DN_F32 c_squared = DN_Squared(a) + DN_Squared(b);
|
|
DN_F32 result = c_squared;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_V2F32_LengthSqIsWithin2V2(DN_V2F32 lhs, DN_V2F32 rhs, DN_F32 within_amount_sq)
|
|
{
|
|
DN_F32 dist = DN_V2F32_LengthSq2V2(lhs, rhs);
|
|
bool result = dist <= within_amount_sq;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_F32 DN_V2F32_Length2V2(DN_V2F32 lhs, DN_V2F32 rhs)
|
|
{
|
|
DN_F32 result_squared = DN_V2F32_LengthSq2V2(lhs, rhs);
|
|
DN_F32 result = DN_SqrtF32(result_squared);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_F32 DN_V2F32_LengthSq(DN_V2F32 lhs)
|
|
{
|
|
// NOTE: Pythagoras's theorem without the square root
|
|
DN_F32 c_squared = DN_Squared(lhs.x) + DN_Squared(lhs.y);
|
|
DN_F32 result = c_squared;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_F32 DN_V2F32_Length(DN_V2F32 lhs)
|
|
{
|
|
DN_F32 c_squared = DN_V2F32_LengthSq(lhs);
|
|
DN_F32 result = DN_SqrtF32(c_squared);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_V2F32_Normalise(DN_V2F32 a)
|
|
{
|
|
DN_F32 length = DN_V2F32_Length(a);
|
|
DN_V2F32 result = a / length;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_V2F32_Perpendicular(DN_V2F32 a)
|
|
{
|
|
// NOTE: Matrix form of a 2D vector can be defined as
|
|
//
|
|
// x' = x cos(t) - y sin(t)
|
|
// y' = x sin(t) + y cos(t)
|
|
//
|
|
// Calculate a line perpendicular to a vector means rotating the vector by
|
|
// 90 degrees
|
|
//
|
|
// x' = x cos(90) - y sin(90)
|
|
// y' = x sin(90) + y cos(90)
|
|
//
|
|
// Where `cos(90) = 0` and `sin(90) = 1` then,
|
|
//
|
|
// x' = -y
|
|
// y' = +x
|
|
|
|
DN_V2F32 result = DN_V2F32_From2N(-a.y, a.x);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_V2F32_Reflect(DN_V2F32 in, DN_V2F32 surface)
|
|
{
|
|
DN_V2F32 normal = DN_V2F32_Perpendicular(surface);
|
|
DN_V2F32 normal_norm = DN_V2F32_Normalise(normal);
|
|
DN_F32 signed_dist = DN_V2F32_Dot(in, normal_norm);
|
|
DN_V2F32 result = DN_V2F32_From2N(in.x, in.y + (-signed_dist * 2.f));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_F32 DN_V2F32_Area(DN_V2F32 a)
|
|
{
|
|
DN_F32 result = a.w * a.h;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator!=(DN_V3F32 lhs, DN_V3F32 rhs)
|
|
{
|
|
bool result = !(lhs == rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator==(DN_V3F32 lhs, DN_V3F32 rhs)
|
|
{
|
|
bool result = (lhs.x == rhs.x) && (lhs.y == rhs.y) && (lhs.z == rhs.z);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator>=(DN_V3F32 lhs, DN_V3F32 rhs)
|
|
{
|
|
bool result = (lhs.x >= rhs.x) && (lhs.y >= rhs.y) && (lhs.z >= rhs.z);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator<=(DN_V3F32 lhs, DN_V3F32 rhs)
|
|
{
|
|
bool result = (lhs.x <= rhs.x) && (lhs.y <= rhs.y) && (lhs.z <= rhs.z);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator<(DN_V3F32 lhs, DN_V3F32 rhs)
|
|
{
|
|
bool result = (lhs.x < rhs.x) && (lhs.y < rhs.y) && (lhs.z < rhs.z);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator>(DN_V3F32 lhs, DN_V3F32 rhs)
|
|
{
|
|
bool result = (lhs.x > rhs.x) && (lhs.y > rhs.y) && (lhs.z > rhs.z);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V3F32 operator-(DN_V3F32 lhs, DN_V3F32 rhs)
|
|
{
|
|
DN_V3F32 result = DN_V3F32_From3N(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V3F32 operator-(DN_V3F32 lhs)
|
|
{
|
|
DN_V3F32 result = DN_V3F32_From3N(-lhs.x, -lhs.y, -lhs.z);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V3F32 operator+(DN_V3F32 lhs, DN_V3F32 rhs)
|
|
{
|
|
DN_V3F32 result = DN_V3F32_From3N(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V3F32 operator*(DN_V3F32 lhs, DN_V3F32 rhs)
|
|
{
|
|
DN_V3F32 result = DN_V3F32_From3N(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V3F32 operator*(DN_V3F32 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V3F32 result = DN_V3F32_From3N(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V3F32 operator*(DN_V3F32 lhs, int32_t rhs)
|
|
{
|
|
DN_V3F32 result = DN_V3F32_From3N(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V3F32 operator/(DN_V3F32 lhs, DN_V3F32 rhs)
|
|
{
|
|
DN_V3F32 result = DN_V3F32_From3N(lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V3F32 operator/(DN_V3F32 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V3F32 result = DN_V3F32_From3N(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V3F32 operator/(DN_V3F32 lhs, int32_t rhs)
|
|
{
|
|
DN_V3F32 result = DN_V3F32_From3N(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V3F32 &operator*=(DN_V3F32 &lhs, DN_V3F32 rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V3F32 &operator*=(DN_V3F32 &lhs, DN_F32 rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V3F32 &operator*=(DN_V3F32 &lhs, int32_t rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V3F32 &operator/=(DN_V3F32 &lhs, DN_V3F32 rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V3F32 &operator/=(DN_V3F32 &lhs, DN_F32 rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V3F32 &operator/=(DN_V3F32 &lhs, int32_t rhs)
|
|
{
|
|
lhs = lhs / rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V3F32 &operator-=(DN_V3F32 &lhs, DN_V3F32 rhs)
|
|
{
|
|
lhs = lhs - rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V3F32 &operator+=(DN_V3F32 &lhs, DN_V3F32 rhs)
|
|
{
|
|
lhs = lhs + rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_F32 DN_V3_LengthSq(DN_V3F32 a)
|
|
{
|
|
DN_F32 result = DN_Squared(a.x) + DN_Squared(a.y) + DN_Squared(a.z);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_F32 DN_V3_Length(DN_V3F32 a)
|
|
{
|
|
DN_F32 length_sq = DN_Squared(a.x) + DN_Squared(a.y) + DN_Squared(a.z);
|
|
DN_F32 result = DN_SqrtF32(length_sq);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V3F32 DN_V3_Normalise(DN_V3F32 a)
|
|
{
|
|
DN_F32 length = DN_V3_Length(a);
|
|
DN_V3F32 result = a / length;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V4F32 DN_V4F32_FromRGBU32(DN_U32 u32)
|
|
{
|
|
DN_U8 r = (DN_U8)((u32 & DN_V4_R_MASK_U32) >> 24);
|
|
DN_U8 g = (DN_U8)((u32 & DN_V4_G_MASK_U32) >> 16);
|
|
DN_U8 b = (DN_U8)((u32 & DN_V4_B_MASK_U32) >> 8);
|
|
DN_V4F32 result = DN_V4F32_FromRGBU8(r, g, b);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V4F32 DN_V4F32_FromRGBAU32(DN_U32 u32)
|
|
{
|
|
DN_U8 r = (DN_U8)((u32 & DN_V4_R_MASK_U32) >> 24);
|
|
DN_U8 g = (DN_U8)((u32 & DN_V4_G_MASK_U32) >> 16);
|
|
DN_U8 b = (DN_U8)((u32 & DN_V4_B_MASK_U32) >> 8);
|
|
DN_U8 a = (DN_U8)((u32 & DN_V4_A_MASK_U32) >> 0);
|
|
DN_V4F32 result = DN_V4F32_FromRGBAU8(r, g, b, a);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator==(DN_V4F32 lhs, DN_V4F32 rhs)
|
|
{
|
|
bool result = (lhs.x == rhs.x) && (lhs.y == rhs.y) && (lhs.z == rhs.z) && (lhs.w == rhs.w);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator!=(DN_V4F32 lhs, DN_V4F32 rhs)
|
|
{
|
|
bool result = !(lhs == rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator>=(DN_V4F32 lhs, DN_V4F32 rhs)
|
|
{
|
|
bool result = (lhs.x >= rhs.x) && (lhs.y >= rhs.y) && (lhs.z >= rhs.z) && (lhs.w >= rhs.w);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator<=(DN_V4F32 lhs, DN_V4F32 rhs)
|
|
{
|
|
bool result = (lhs.x <= rhs.x) && (lhs.y <= rhs.y) && (lhs.z <= rhs.z) && (lhs.w <= rhs.w);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator<(DN_V4F32 lhs, DN_V4F32 rhs)
|
|
{
|
|
bool result = (lhs.x < rhs.x) && (lhs.y < rhs.y) && (lhs.z < rhs.z) && (lhs.w < rhs.w);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator>(DN_V4F32 lhs, DN_V4F32 rhs)
|
|
{
|
|
bool result = (lhs.x > rhs.x) && (lhs.y > rhs.y) && (lhs.z > rhs.z) && (lhs.w > rhs.w);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V4F32 operator-(DN_V4F32 lhs, DN_V4F32 rhs)
|
|
{
|
|
DN_V4F32 result = DN_V4F32_From4N(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V4F32 operator-(DN_V4F32 lhs)
|
|
{
|
|
DN_V4F32 result = DN_V4F32_From4N(-lhs.x, -lhs.y, -lhs.z, -lhs.w);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V4F32 operator+(DN_V4F32 lhs, DN_V4F32 rhs)
|
|
{
|
|
DN_V4F32 result = DN_V4F32_From4N(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V4F32 operator*(DN_V4F32 lhs, DN_V4F32 rhs)
|
|
{
|
|
DN_V4F32 result = DN_V4F32_From4N(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V4F32 operator*(DN_V4F32 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V4F32 result = DN_V4F32_From4N(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V4F32 operator*(DN_V4F32 lhs, int32_t rhs)
|
|
{
|
|
DN_V4F32 result = DN_V4F32_From4N(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V4F32 operator/(DN_V4F32 lhs, DN_F32 rhs)
|
|
{
|
|
DN_V4F32 result = DN_V4F32_From4N(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs, lhs.w / rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V4F32 &operator*=(DN_V4F32 &lhs, DN_V4F32 rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V4F32 &operator*=(DN_V4F32 &lhs, DN_F32 rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V4F32 &operator*=(DN_V4F32 &lhs, int32_t rhs)
|
|
{
|
|
lhs = lhs * rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V4F32 &operator-=(DN_V4F32 &lhs, DN_V4F32 rhs)
|
|
{
|
|
lhs = lhs - rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_V4F32 &operator+=(DN_V4F32 &lhs, DN_V4F32 rhs)
|
|
{
|
|
lhs = lhs + rhs;
|
|
return lhs;
|
|
}
|
|
|
|
DN_API DN_F32 DN_V4F32Dot(DN_V4F32 a, DN_V4F32 b)
|
|
{
|
|
DN_F32 result = (a.x * b.x) + (a.y * b.y) + (a.z * b.z) + (a.w * b.w);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_Identity()
|
|
{
|
|
DN_M4 result =
|
|
{
|
|
{
|
|
{1, 0, 0, 0},
|
|
{0, 1, 0, 0},
|
|
{0, 0, 1, 0},
|
|
{0, 0, 0, 1},
|
|
}
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_ScaleF(DN_F32 x, DN_F32 y, DN_F32 z)
|
|
{
|
|
DN_M4 result =
|
|
{
|
|
{
|
|
{x, 0, 0, 0},
|
|
{0, y, 0, 0},
|
|
{0, 0, z, 0},
|
|
{0, 0, 0, 1},
|
|
}
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_Scale(DN_V3F32 xyz)
|
|
{
|
|
DN_M4 result =
|
|
{
|
|
{
|
|
{xyz.x, 0, 0, 0},
|
|
{0, xyz.y, 0, 0},
|
|
{0, 0, xyz.z, 0},
|
|
{0, 0, 0, 1},
|
|
}
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_TranslateF(DN_F32 x, DN_F32 y, DN_F32 z)
|
|
{
|
|
DN_M4 result =
|
|
{
|
|
{
|
|
{1, 0, 0, 0},
|
|
{0, 1, 0, 0},
|
|
{0, 0, 1, 0},
|
|
{x, y, z, 1},
|
|
}
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_Translate(DN_V3F32 xyz)
|
|
{
|
|
DN_M4 result =
|
|
{
|
|
{
|
|
{1, 0, 0, 0},
|
|
{0, 1, 0, 0},
|
|
{0, 0, 1, 0},
|
|
{xyz.x, xyz.y, xyz.z, 1},
|
|
}
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_Transpose(DN_M4 mat)
|
|
{
|
|
DN_M4 result = {};
|
|
for (int col = 0; col < 4; col++)
|
|
for (int row = 0; row < 4; row++)
|
|
result.columns[col][row] = mat.columns[row][col];
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_Rotate(DN_V3F32 axis01, DN_F32 radians)
|
|
{
|
|
DN_AssertF(DN_Abs(DN_V3_Length(axis01) - 1.f) <= 0.01f,
|
|
"Rotation axis must be normalised, length = %f",
|
|
DN_V3_Length(axis01));
|
|
|
|
DN_F32 sin = DN_SinF32(radians);
|
|
DN_F32 cos = DN_CosF32(radians);
|
|
DN_F32 one_minus_cos = 1.f - cos;
|
|
|
|
DN_F32 x = axis01.x;
|
|
DN_F32 y = axis01.y;
|
|
DN_F32 z = axis01.z;
|
|
DN_F32 x2 = DN_Squared(x);
|
|
DN_F32 y2 = DN_Squared(y);
|
|
DN_F32 z2 = DN_Squared(z);
|
|
|
|
DN_M4 result =
|
|
{
|
|
{
|
|
{cos + x2 * one_minus_cos, y * x * one_minus_cos + z * sin, z * x * one_minus_cos - y * sin, 0}, // Col 1
|
|
{x * y * one_minus_cos - z * sin, cos + y2 * one_minus_cos, z * y * one_minus_cos + x * sin, 0}, // Col 2
|
|
{x * z * one_minus_cos + y * sin, y * z * one_minus_cos - x * sin, cos + z2 * one_minus_cos, 0}, // Col 3
|
|
{0, 0, 0, 1}, // Col 4
|
|
}
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_Orthographic(DN_F32 left, DN_F32 right, DN_F32 bottom, DN_F32 top, DN_F32 z_near, DN_F32 z_far)
|
|
{
|
|
// NOTE: Here is the matrix in column major for readability. Below it's
|
|
// transposed due to how you have to declare column major matrices in C/C++.
|
|
//
|
|
// m = [2/r-l, 0, 0, -1*(r+l)/(r-l)]
|
|
// [0, 2/t-b, 0, 1*(t+b)/(t-b)]
|
|
// [0, 0, -2/f-n, -1*(f+n)/(f-n)]
|
|
// [0, 0, 0, 1 ]
|
|
|
|
DN_M4 result =
|
|
{
|
|
{
|
|
{2.f / (right - left), 0.f, 0.f, 0.f},
|
|
{0.f, 2.f / (top - bottom), 0.f, 0.f},
|
|
{0.f, 0.f, -2.f / (z_far - z_near), 0.f},
|
|
{(-1.f * (right + left)) / (right - left), (-1.f * (top + bottom)) / (top - bottom), (-1.f * (z_far + z_near)) / (z_far - z_near), 1.f},
|
|
}
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_Perspective(DN_F32 fov /*radians*/, DN_F32 aspect, DN_F32 z_near, DN_F32 z_far)
|
|
{
|
|
DN_F32 tan_fov = DN_TanF32(fov / 2.f);
|
|
DN_M4 result =
|
|
{
|
|
{
|
|
{1.f / (aspect * tan_fov), 0.f, 0.f, 0.f},
|
|
{0, 1.f / tan_fov, 0.f, 0.f},
|
|
{0.f, 0.f, (z_near + z_far) / (z_near - z_far), -1.f},
|
|
{0.f, 0.f, (2.f * z_near * z_far) / (z_near - z_far), 0.f},
|
|
}
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_Add(DN_M4 lhs, DN_M4 rhs)
|
|
{
|
|
DN_M4 result;
|
|
for (int col = 0; col < 4; col++)
|
|
for (int it = 0; it < 4; it++)
|
|
result.columns[col][it] = lhs.columns[col][it] + rhs.columns[col][it];
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_Sub(DN_M4 lhs, DN_M4 rhs)
|
|
{
|
|
DN_M4 result;
|
|
for (int col = 0; col < 4; col++)
|
|
for (int it = 0; it < 4; it++)
|
|
result.columns[col][it] = lhs.columns[col][it] - rhs.columns[col][it];
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_Mul(DN_M4 lhs, DN_M4 rhs)
|
|
{
|
|
DN_M4 result;
|
|
for (int col = 0; col < 4; col++) {
|
|
for (int row = 0; row < 4; row++) {
|
|
DN_F32 sum = 0;
|
|
for (int f32_it = 0; f32_it < 4; f32_it++)
|
|
sum += lhs.columns[f32_it][row] * rhs.columns[col][f32_it];
|
|
|
|
result.columns[col][row] = sum;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_Div(DN_M4 lhs, DN_M4 rhs)
|
|
{
|
|
DN_M4 result;
|
|
for (int col = 0; col < 4; col++)
|
|
for (int it = 0; it < 4; it++)
|
|
result.columns[col][it] = lhs.columns[col][it] / rhs.columns[col][it];
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_AddF(DN_M4 lhs, DN_F32 rhs)
|
|
{
|
|
DN_M4 result;
|
|
for (int col = 0; col < 4; col++)
|
|
for (int it = 0; it < 4; it++)
|
|
result.columns[col][it] = lhs.columns[col][it] + rhs;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_SubF(DN_M4 lhs, DN_F32 rhs)
|
|
{
|
|
DN_M4 result;
|
|
for (int col = 0; col < 4; col++)
|
|
for (int it = 0; it < 4; it++)
|
|
result.columns[col][it] = lhs.columns[col][it] - rhs;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_MulF(DN_M4 lhs, DN_F32 rhs)
|
|
{
|
|
DN_M4 result;
|
|
for (int col = 0; col < 4; col++)
|
|
for (int it = 0; it < 4; it++)
|
|
result.columns[col][it] = lhs.columns[col][it] * rhs;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M4 DN_M4_DivF(DN_M4 lhs, DN_F32 rhs)
|
|
{
|
|
DN_M4 result;
|
|
for (int col = 0; col < 4; col++)
|
|
for (int it = 0; it < 4; it++)
|
|
result.columns[col][it] = lhs.columns[col][it] / rhs;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Str8x256 DN_M4_ColumnMajorString(DN_M4 mat)
|
|
{
|
|
DN_Str8x256 result = {};
|
|
for (int row = 0; row < 4; row++) {
|
|
for (int it = 0; it < 4; it++) {
|
|
if (it == 0)
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), "|");
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), "%.5f", mat.columns[it][row]);
|
|
if (it != 3)
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), ", ");
|
|
else
|
|
DN_FmtAppend(result.data, &result.size, sizeof(result.data), "|\n");
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator==(DN_M2x3 const &lhs, DN_M2x3 const &rhs)
|
|
{
|
|
bool result = DN_Memcmp(lhs.e, rhs.e, sizeof(lhs.e[0]) * DN_ArrayCountU(lhs.e)) == 0;
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator!=(DN_M2x3 const &lhs, DN_M2x3 const &rhs)
|
|
{
|
|
bool result = !(lhs == rhs);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M2x3 DN_M2x3_Identity()
|
|
{
|
|
DN_M2x3 result = {
|
|
{
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
1,
|
|
0,
|
|
}
|
|
};
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M2x3 DN_M2x3_Translate(DN_V2F32 offset)
|
|
{
|
|
DN_M2x3 result = {
|
|
{
|
|
1,
|
|
0,
|
|
offset.x,
|
|
0,
|
|
1,
|
|
offset.y,
|
|
}
|
|
};
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M2x3 DN_M2x3_Scale(DN_V2F32 scale)
|
|
{
|
|
DN_M2x3 result = {
|
|
{
|
|
scale.x,
|
|
0,
|
|
0,
|
|
0,
|
|
scale.y,
|
|
0,
|
|
}
|
|
};
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M2x3 DN_M2x3_Rotate(DN_F32 radians)
|
|
{
|
|
DN_M2x3 result = {
|
|
{
|
|
DN_CosF32(radians),
|
|
DN_SinF32(radians),
|
|
0,
|
|
-DN_SinF32(radians),
|
|
DN_CosF32(radians),
|
|
0,
|
|
}
|
|
};
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_M2x3 DN_M2x3_Mul(DN_M2x3 m1, DN_M2x3 m2)
|
|
{
|
|
// NOTE: Ordinarily you can't multiply M2x3 with M2x3 because column count
|
|
// (3) != row count (2). We pretend we have two 3x3 matrices with the last
|
|
// row set to [0 0 1] and perform a 3x3 matrix multiply.
|
|
//
|
|
// | (0)a (1)b (2)c | | (0)g (1)h (2)i |
|
|
// | (3)d (4)e (5)f | x | (3)j (4)k (5)l |
|
|
// | (6)0 (7)0 (8)1 | | (6)0 (7)0 (8)1 |
|
|
|
|
DN_M2x3 result = {
|
|
{
|
|
m1.e[0] * m2.e[0] + m1.e[1] * m2.e[3], // a*g + b*j + c*0[omitted],
|
|
m1.e[0] * m2.e[1] + m1.e[1] * m2.e[4], // a*h + b*k + c*0[omitted],
|
|
m1.e[0] * m2.e[2] + m1.e[1] * m2.e[5] + m1.e[2], // a*i + b*l + c*1,
|
|
|
|
m1.e[3] * m2.e[0] + m1.e[4] * m2.e[3], // d*g + e*j + f*0[omitted],
|
|
m1.e[3] * m2.e[1] + m1.e[4] * m2.e[4], // d*h + e*k + f*0[omitted],
|
|
m1.e[3] * m2.e[2] + m1.e[4] * m2.e[5] + m1.e[5], // d*i + e*l + f*1,
|
|
}
|
|
};
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_M2x3_Mul2F32(DN_M2x3 m1, DN_F32 x, DN_F32 y)
|
|
{
|
|
// NOTE: Ordinarily you can't multiply M2x3 with V2 because column count (3)
|
|
// != row count (2). We pretend we have a V3 with `z` set to `1`.
|
|
//
|
|
// | (0)a (1)b (2)c | | x |
|
|
// | (3)d (4)e (5)f | x | y |
|
|
// | 1 |
|
|
|
|
DN_V2F32 result = {
|
|
{
|
|
m1.e[0] * x + m1.e[1] * y + m1.e[2], // a*x + b*y + c*1
|
|
m1.e[3] * x + m1.e[4] * y + m1.e[5], // d*x + e*y + f*1
|
|
}
|
|
};
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_M2x3_MulV2(DN_M2x3 m1, DN_V2F32 v2)
|
|
{
|
|
DN_V2F32 result = DN_M2x3_Mul2F32(m1, v2.x, v2.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool operator==(const DN_Rect &lhs, const DN_Rect &rhs)
|
|
{
|
|
bool result = (lhs.pos == rhs.pos) && (lhs.size == rhs.size);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_Rect_Center(DN_Rect rect)
|
|
{
|
|
DN_V2F32 result = rect.pos + (rect.size * .5f);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Rect_ContainsPoint(DN_Rect rect, DN_V2F32 p)
|
|
{
|
|
DN_V2F32 min = rect.pos;
|
|
DN_V2F32 max = rect.pos + rect.size;
|
|
bool result = (p.x >= min.x && p.x <= max.x && p.y >= min.y && p.y <= max.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Rect_ContainsRect(DN_Rect a, DN_Rect b)
|
|
{
|
|
DN_V2F32 a_min = a.pos;
|
|
DN_V2F32 a_max = a.pos + a.size;
|
|
DN_V2F32 b_min = b.pos;
|
|
DN_V2F32 b_max = b.pos + b.size;
|
|
bool result = (b_min >= a_min && b_max <= a_max);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Rect DN_Rect_Expand(DN_Rect a, DN_F32 amount)
|
|
{
|
|
DN_Rect result = a;
|
|
result.pos -= amount;
|
|
result.size += (amount * 2.f);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Rect DN_Rect_ExpandV2(DN_Rect a, DN_V2F32 amount)
|
|
{
|
|
DN_Rect result = a;
|
|
result.pos -= amount;
|
|
result.size += (amount * 2.f);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_Rect_Intersects(DN_Rect a, DN_Rect b)
|
|
{
|
|
DN_V2F32 a_min = a.pos;
|
|
DN_V2F32 a_max = a.pos + a.size;
|
|
DN_V2F32 b_min = b.pos;
|
|
DN_V2F32 b_max = b.pos + b.size;
|
|
bool has_size = a.size.x && a.size.y && b.size.x && b.size.y;
|
|
bool result = false;
|
|
if (has_size)
|
|
result = (a_min.x <= b_max.x && a_max.x >= b_min.x) &&
|
|
(a_min.y <= b_max.y && a_max.y >= b_min.y);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Rect DN_Rect_Intersection(DN_Rect a, DN_Rect b)
|
|
{
|
|
DN_Rect result = DN_Rect_From2V2(a.pos, DN_V2F32_From1N(0));
|
|
if (DN_Rect_Intersects(a, b)) {
|
|
DN_V2F32 a_min = a.pos;
|
|
DN_V2F32 a_max = a.pos + a.size;
|
|
DN_V2F32 b_min = b.pos;
|
|
DN_V2F32 b_max = b.pos + b.size;
|
|
|
|
DN_V2F32 min = {};
|
|
DN_V2F32 max = {};
|
|
min.x = DN_Max(a_min.x, b_min.x);
|
|
min.y = DN_Max(a_min.y, b_min.y);
|
|
max.x = DN_Min(a_max.x, b_max.x);
|
|
max.y = DN_Min(a_max.y, b_max.y);
|
|
result = DN_Rect_From2V2(min, max - min);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Rect DN_Rect_Union(DN_Rect a, DN_Rect b)
|
|
{
|
|
DN_V2F32 a_min = a.pos;
|
|
DN_V2F32 a_max = a.pos + a.size;
|
|
DN_V2F32 b_min = b.pos;
|
|
DN_V2F32 b_max = b.pos + b.size;
|
|
|
|
DN_V2F32 min, max;
|
|
min.x = DN_Min(a_min.x, b_min.x);
|
|
min.y = DN_Min(a_min.y, b_min.y);
|
|
max.x = DN_Max(a_max.x, b_max.x);
|
|
max.y = DN_Max(a_max.y, b_max.y);
|
|
DN_Rect result = DN_Rect_From2V2(min, max - min);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_RectMinMax DN_Rect_MinMax(DN_Rect a)
|
|
{
|
|
DN_RectMinMax result = {};
|
|
result.min = a.pos;
|
|
result.max = a.pos + a.size;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_F32 DN_Rect_Area(DN_Rect a)
|
|
{
|
|
DN_F32 result = a.size.w * a.size.h;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Rect DN_Rect_CutLeftClip(DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip)
|
|
{
|
|
DN_F32 min_x = rect->pos.x;
|
|
DN_F32 max_x = rect->pos.x + rect->size.w;
|
|
DN_F32 result_max_x = min_x + amount;
|
|
if (clip)
|
|
result_max_x = DN_Min(result_max_x, max_x);
|
|
DN_Rect result = DN_Rect_From4N(min_x, rect->pos.y, result_max_x - min_x, rect->size.h);
|
|
rect->pos.x = result_max_x;
|
|
rect->size.w = max_x - result_max_x;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Rect DN_Rect_CutRightClip(DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip)
|
|
{
|
|
DN_F32 min_x = rect->pos.x;
|
|
DN_F32 max_x = rect->pos.x + rect->size.w;
|
|
DN_F32 result_min_x = max_x - amount;
|
|
if (clip)
|
|
result_min_x = DN_Max(result_min_x, 0);
|
|
DN_Rect result = DN_Rect_From4N(result_min_x, rect->pos.y, max_x - result_min_x, rect->size.h);
|
|
rect->size.w = result_min_x - min_x;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Rect DN_Rect_CutTopClip(DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip)
|
|
{
|
|
DN_F32 min_y = rect->pos.y;
|
|
DN_F32 max_y = rect->pos.y + rect->size.h;
|
|
DN_F32 result_max_y = min_y + amount;
|
|
if (clip)
|
|
result_max_y = DN_Min(result_max_y, max_y);
|
|
DN_Rect result = DN_Rect_From4N(rect->pos.x, min_y, rect->size.w, result_max_y - min_y);
|
|
rect->pos.y = result_max_y;
|
|
rect->size.h = max_y - result_max_y;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Rect DN_Rect_CutBottomClip(DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip)
|
|
{
|
|
DN_F32 min_y = rect->pos.y;
|
|
DN_F32 max_y = rect->pos.y + rect->size.h;
|
|
DN_F32 result_min_y = max_y - amount;
|
|
if (clip)
|
|
result_min_y = DN_Max(result_min_y, 0);
|
|
DN_Rect result = DN_Rect_From4N(rect->pos.x, result_min_y, rect->size.w, max_y - result_min_y);
|
|
rect->size.h = result_min_y - min_y;
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_Rect DN_RectCut_Cut(DN_RectCut rect_cut, DN_V2F32 size, DN_RectCutClip clip)
|
|
{
|
|
DN_Rect result = {};
|
|
if (rect_cut.rect) {
|
|
switch (rect_cut.side) {
|
|
case DN_RectCutSide_Left: result = DN_Rect_CutLeftClip(rect_cut.rect, size.w, clip); break;
|
|
case DN_RectCutSide_Right: result = DN_Rect_CutRightClip(rect_cut.rect, size.w, clip); break;
|
|
case DN_RectCutSide_Top: result = DN_Rect_CutTopClip(rect_cut.rect, size.h, clip); break;
|
|
case DN_RectCutSide_Bottom: result = DN_Rect_CutBottomClip(rect_cut.rect, size.h, clip); break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_Rect_InterpolatedPoint(DN_Rect rect, DN_V2F32 t01)
|
|
{
|
|
DN_V2F32 result = DN_V2F32_From2N(rect.pos.w + (rect.size.w * t01.x),
|
|
rect.pos.h + (rect.size.h * t01.y));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_Rect_TopLeft(DN_Rect rect)
|
|
{
|
|
DN_V2F32 result = DN_Rect_InterpolatedPoint(rect, DN_V2F32_From2N(0, 0));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_Rect_TopRight(DN_Rect rect)
|
|
{
|
|
DN_V2F32 result = DN_Rect_InterpolatedPoint(rect, DN_V2F32_From2N(1, 0));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_Rect_BottomLeft(DN_Rect rect)
|
|
{
|
|
DN_V2F32 result = DN_Rect_InterpolatedPoint(rect, DN_V2F32_From2N(0, 1));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_Rect_BottomRight(DN_Rect rect)
|
|
{
|
|
DN_V2F32 result = DN_Rect_InterpolatedPoint(rect, DN_V2F32_From2N(1, 1));
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_RaycastLineIntersectV2Result DN_Raycast_LineIntersectV2(DN_V2F32 origin_a, DN_V2F32 dir_a, DN_V2F32 origin_b, DN_V2F32 dir_b)
|
|
{
|
|
// NOTE: Parametric equation of a line
|
|
//
|
|
// p = o + (t*d)
|
|
//
|
|
// - o is the starting 2d point
|
|
// - d is the direction of the line
|
|
// - t is a scalar that scales along the direction of the point
|
|
//
|
|
// To determine if a ray intersections a ray, we want to solve
|
|
//
|
|
// (o_a + (t_a * d_a)) = (o_b + (t_b * d_b))
|
|
//
|
|
// Where '_a' and '_b' represent the 1st and 2nd point's origin, direction
|
|
// and 't' components respectively. This is 2 equations with 2 unknowns
|
|
// (`t_a` and `t_b`) which we can solve for by expressing the equation in
|
|
// terms of `t_a` and `t_b`.
|
|
//
|
|
// Working that math out produces the formula below for 't'.
|
|
|
|
DN_RaycastLineIntersectV2Result result = {};
|
|
DN_F32 denominator = ((dir_b.y * dir_a.x) - (dir_b.x * dir_a.y));
|
|
if (denominator != 0.0f) {
|
|
result.t_a = (((origin_a.y - origin_b.y) * dir_b.x) + ((origin_b.x - origin_a.x) * dir_b.y)) / denominator;
|
|
result.t_b = (((origin_a.y - origin_b.y) * dir_a.x) + ((origin_b.x - origin_a.x) * dir_a.y)) / denominator;
|
|
result.hit = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_V2F32 DN_Lerp_V2F32(DN_V2F32 a, DN_F32 t, DN_V2F32 b)
|
|
{
|
|
DN_V2F32 result = {};
|
|
result.x = a.x + ((b.x - a.x) * t);
|
|
result.y = a.y + ((b.y - a.y) * t);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_F32 DN_Lerp_F32(DN_F32 a, DN_F32 t, DN_F32 b)
|
|
{
|
|
DN_F32 result = a + ((b - a) * t);
|
|
return result;
|
|
}
|
|
#define DN_ASYNC_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_os_inc.h"
|
|
// DN: Single header generator commented out this header => #include "dn_async.h"
|
|
#if !defined(DN_ASYNC_H)
|
|
#define DN_ASYNC_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "../dn_os_inc.h"
|
|
#endif
|
|
|
|
enum DN_ASYNCPriority
|
|
{
|
|
DN_ASYNCPriority_Low,
|
|
DN_ASYNCPriority_High,
|
|
DN_ASYNCPriority_Count,
|
|
};
|
|
|
|
struct DN_ASYNCCore
|
|
{
|
|
DN_OSMutex ring_mutex;
|
|
DN_OSConditionVariable ring_write_cv;
|
|
DN_OSSemaphore worker_sem;
|
|
DN_Ring ring;
|
|
DN_OSThread *threads;
|
|
DN_U32 thread_count;
|
|
DN_U32 busy_threads;
|
|
DN_U32 join_threads;
|
|
};
|
|
|
|
struct DN_ASYNCWorkArgs
|
|
{
|
|
DN_OSThread *thread;
|
|
void *input;
|
|
};
|
|
|
|
typedef void(DN_ASYNCWorkFunc)(DN_ASYNCWorkArgs work_args);
|
|
|
|
struct DN_ASYNCWork
|
|
{
|
|
DN_ASYNCWorkFunc *func;
|
|
void *input;
|
|
void *output;
|
|
};
|
|
|
|
struct DN_ASYNCTask
|
|
{
|
|
bool queued;
|
|
DN_ASYNCWork work;
|
|
DN_OSSemaphore completion_sem;
|
|
};
|
|
|
|
DN_API void DN_ASYNC_Init (DN_ASYNCCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size);
|
|
DN_API void DN_ASYNC_Deinit (DN_ASYNCCore *async);
|
|
DN_API bool DN_ASYNC_QueueWork(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms);
|
|
DN_API DN_ASYNCTask DN_ASYNC_QueueTask(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms);
|
|
DN_API void DN_ASYNC_WaitTask (DN_OSSemaphore *sem, DN_U32 timeout_ms);
|
|
|
|
#endif // DN_ASYNC_H
|
|
#endif
|
|
|
|
static DN_I32 DN_ASYNC_ThreadEntryPoint_(DN_OSThread *thread)
|
|
{
|
|
DN_OS_ThreadSetName(DN_Str8FromPtr(thread->name.data, thread->name.size));
|
|
DN_ASYNCCore *async = DN_Cast(DN_ASYNCCore *) thread->user_context;
|
|
DN_Ring *ring = &async->ring;
|
|
for (;;) {
|
|
DN_OS_SemaphoreWait(&async->worker_sem, UINT32_MAX);
|
|
if (async->join_threads)
|
|
break;
|
|
|
|
DN_ASYNCTask task = {};
|
|
for (DN_OS_MutexScope(&async->ring_mutex)) {
|
|
if (DN_Ring_HasData(ring, sizeof(task)))
|
|
DN_Ring_Read(ring, &task, sizeof(task));
|
|
}
|
|
|
|
if (task.work.func) {
|
|
DN_OS_ConditionVariableBroadcast(&async->ring_write_cv); // Resume any blocked ring write(s)
|
|
|
|
DN_ASYNCWorkArgs args = {};
|
|
args.input = task.work.input;
|
|
args.thread = thread;
|
|
|
|
DN_AtomicAddU32(&async->busy_threads, 1);
|
|
task.work.func(args);
|
|
DN_AtomicSubU32(&async->busy_threads, 1);
|
|
|
|
if (task.completion_sem.handle != 0)
|
|
DN_OS_SemaphoreIncrement(&task.completion_sem, 1);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DN_API void DN_ASYNC_Init(DN_ASYNCCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size)
|
|
{
|
|
DN_Assert(async);
|
|
async->ring.size = base_size;
|
|
async->ring.base = base;
|
|
async->ring_mutex = DN_OS_MutexInit();
|
|
async->ring_write_cv = DN_OS_ConditionVariableInit();
|
|
async->worker_sem = DN_OS_SemaphoreInit(0);
|
|
async->thread_count = threads_size;
|
|
async->threads = threads;
|
|
for (DN_ForIndexU(index, async->thread_count)) {
|
|
DN_OSThread *thread = async->threads + index;
|
|
DN_FmtAppend(thread->name.data, &thread->name.size, DN_ArrayCountU(thread->name.data), "ASYNC W%zu", index);
|
|
DN_OS_ThreadInit(thread, DN_ASYNC_ThreadEntryPoint_, async);
|
|
}
|
|
}
|
|
|
|
DN_API void DN_ASYNC_Deinit(DN_ASYNCCore *async)
|
|
{
|
|
DN_Assert(async);
|
|
DN_AtomicSetValue32(&async->join_threads, true);
|
|
DN_OS_SemaphoreIncrement(&async->worker_sem, async->thread_count);
|
|
for (DN_ForItSize(it, DN_OSThread, async->threads, async->thread_count))
|
|
DN_OS_ThreadDeinit(it.data);
|
|
}
|
|
|
|
static bool DN_ASYNC_QueueTask_(DN_ASYNCCore *async, DN_ASYNCTask const *task, DN_U64 wait_time_ms) {
|
|
DN_U64 end_time_ms = DN_OS_DateUnixTimeMs() + wait_time_ms;
|
|
bool result = false;
|
|
for (DN_OS_MutexScope(&async->ring_mutex)) {
|
|
for (;;) {
|
|
if (DN_Ring_HasSpace(&async->ring, sizeof(*task))) {
|
|
DN_Ring_WriteStruct(&async->ring, task);
|
|
result = true;
|
|
break;
|
|
}
|
|
DN_OS_ConditionVariableWaitUntil(&async->ring_write_cv, &async->ring_mutex, end_time_ms);
|
|
if (DN_OS_DateUnixTimeMs() >= end_time_ms)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result)
|
|
DN_OS_SemaphoreIncrement(&async->worker_sem, 1); // Flag that a job is available
|
|
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_ASYNC_QueueWork(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms)
|
|
{
|
|
DN_ASYNCTask task = {};
|
|
task.work.func = func;
|
|
task.work.input = input;
|
|
bool result = DN_ASYNC_QueueTask_(async, &task, wait_time_ms);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_ASYNCTask DN_ASYNC_QueueTask(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms)
|
|
{
|
|
DN_ASYNCTask result = {};
|
|
result.work.func = func;
|
|
result.work.input = input;
|
|
result.completion_sem = DN_OS_SemaphoreInit(0);
|
|
result.queued = DN_ASYNC_QueueTask_(async, &result, wait_time_ms);
|
|
if (!result.queued)
|
|
DN_OS_SemaphoreDeinit(&result.completion_sem);
|
|
return result;
|
|
}
|
|
|
|
DN_API bool DN_ASYNC_WaitTask(DN_ASYNCTask *task, DN_U32 timeout_ms)
|
|
{
|
|
bool result = true;
|
|
if (!task->queued)
|
|
return result;
|
|
|
|
DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&task->completion_sem, timeout_ms);
|
|
result = wait == DN_OSSemaphoreWaitResult_Success;
|
|
if (result)
|
|
DN_OS_SemaphoreDeinit(&task->completion_sem);
|
|
return result;
|
|
}
|
|
|
|
#define DN_BIN_PACK_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "dn_bin_pack.h"
|
|
#if !defined(DN_BIN_PACK_H)
|
|
#define DN_BIN_PACK_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
#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_Str8FromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string);
|
|
DN_API void DN_BinPack_Str8FromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string);
|
|
DN_API void DN_BinPack_BytesFromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size);
|
|
DN_API void DN_BinPack_BytesFromPool (DN_BinPack *pack, DN_Pool *pool, 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)
|
|
#endif
|
|
|
|
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_Str8BuilderAppendBytesCopy(&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_Str8FromArena(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_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size);
|
|
} else {
|
|
DN_Str8 src = DN_Str8Slice(pack->read, pack->read_index, string->size);
|
|
*string = DN_Str8FromStr8Arena(arena, src);
|
|
pack->read_index += src.size;
|
|
}
|
|
}
|
|
|
|
DN_API void DN_BinPack_Str8FromPool(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_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size);
|
|
} else {
|
|
DN_Str8 src = DN_Str8Slice(pack->read, pack->read_index, string->size);
|
|
*string = DN_Str8FromStr8Pool(pool, src);
|
|
pack->read_index += src.size;
|
|
}
|
|
}
|
|
|
|
DN_API void DN_BinPack_BytesFromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size)
|
|
{
|
|
DN_Str8 string = DN_Str8FromPtr(*ptr, *size);
|
|
DN_BinPack_Str8FromArena(pack, arena, mode, &string);
|
|
*ptr = string.data;
|
|
*size = string.size;
|
|
}
|
|
|
|
DN_API void DN_BinPack_BytesFromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size)
|
|
{
|
|
DN_Str8 string = DN_Str8FromPtr(*ptr, *size);
|
|
DN_BinPack_Str8FromPool(pack, pool, 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_Str8BuilderAppendBytesCopy(&pack->writer, ptr, size);
|
|
} else {
|
|
DN_Str8 src = DN_Str8Slice(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_Str8BuilderBuild(&pack->writer, arena);
|
|
return result;
|
|
}
|
|
#define DN_CSV_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "dn_csv.h"
|
|
#if !defined(DN_CSV_H)
|
|
#define DN_CSV_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
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)
|
|
#endif
|
|
|
|
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) && tokeniser->string.size) {
|
|
// 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 (tokeniser->string.size == 0) {
|
|
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) || fields[result].size == 0)
|
|
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_U64FromResult to_u64 = DN_U64FromStr8(csv_value, 0);
|
|
DN_Assert(to_u64.success);
|
|
*value = to_u64.value;
|
|
} else {
|
|
DN_Str8BuilderAppendF(&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_I64FromResult to_i64 = DN_I64FromStr8(csv_value, 0);
|
|
DN_Assert(to_i64.success);
|
|
*value = to_i64.value;
|
|
} else {
|
|
DN_Str8BuilderAppendF(&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_Str8FromStr8Arena(arena, csv_value);
|
|
} else {
|
|
DN_Str8BuilderAppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_Str8PrintFmt(*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_Str8BuilderAppendF(&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_Str8BuilderAppendRef(&pack->write_builder, DN_Str8Lit("\n"));
|
|
}
|
|
return result;
|
|
}
|
|
DN_MSVC_WARNING_POP
|
|
#define DN_HASH_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "dn_hash.h"
|
|
#if !defined(DN_HASH_H)
|
|
#define DN_HASH_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
/*
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// $$\ $$\ $$$$$$\ $$$$$$\ $$\ $$\
|
|
// $$ | $$ |$$ __$$\ $$ __$$\ $$ | $$ |
|
|
// $$ | $$ |$$ / $$ |$$ / \__|$$ | $$ |
|
|
// $$$$$$$$ |$$$$$$$$ |\$$$$$$\ $$$$$$$$ |
|
|
// $$ __$$ |$$ __$$ | \____$$\ $$ __$$ |
|
|
// $$ | $$ |$$ | $$ |$$\ $$ |$$ | $$ |
|
|
// $$ | $$ |$$ | $$ |\$$$$$$ |$$ | $$ |
|
|
// \__| \__|\__| \__| \______/ \__| \__|
|
|
//
|
|
// dn_hash.h -- Hashing functions
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
*/
|
|
|
|
// NOTE: DN_FNV1A //////////////////////////////////////////////////////////////////////////////////
|
|
#if !defined(DN_FNV1A32_SEED)
|
|
#define DN_FNV1A32_SEED 2166136261U
|
|
#endif
|
|
|
|
#if !defined(DN_FNV1A64_SEED)
|
|
#define DN_FNV1A64_SEED 14695981039346656037ULL
|
|
#endif
|
|
|
|
// NOTE: DN_MurmurHash3 ////////////////////////////////////////////////////////////////////////////
|
|
struct DN_MurmurHash3 { uint64_t e[2]; };
|
|
|
|
// 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: 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)
|
|
#endif
|
|
|
|
/*
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// $$\ $$\ $$$$$$\ $$$$$$\ $$\ $$\
|
|
// $$ | $$ |$$ __$$\ $$ __$$\ $$ | $$ |
|
|
// $$ | $$ |$$ / $$ |$$ / \__|$$ | $$ |
|
|
// $$$$$$$$ |$$$$$$$$ |\$$$$$$\ $$$$$$$$ |
|
|
// $$ __$$ |$$ __$$ | \____$$\ $$ __$$ |
|
|
// $$ | $$ |$$ | $$ |$$\ $$ |$$ | $$ |
|
|
// $$ | $$ |$$ | $$ |\$$$$$$ |$$ | $$ |
|
|
// \__| \__|\__| \__| \______/ \__| \__|
|
|
//
|
|
// dn_hash.cpp
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
*/
|
|
|
|
// NOTE: DN_FNV1A
|
|
// Default values recommended by: http://isthe.com/chongo/tech/comp/fnv/
|
|
DN_API DN_U32 DN_FNV1A32_Iterate(void const *bytes, DN_USize size, DN_U32 hash)
|
|
{
|
|
auto buffer = DN_Cast(DN_U8 const *)bytes;
|
|
for (DN_USize i = 0; i < size; i++)
|
|
hash = (buffer[i] ^ hash) * 16777619 /*FNV Prime*/;
|
|
return hash;
|
|
}
|
|
|
|
DN_API DN_U32 DN_FNV1A32_Hash(void const *bytes, DN_USize size)
|
|
{
|
|
DN_U32 result = DN_FNV1A32_Iterate(bytes, size, DN_FNV1A32_SEED);
|
|
return result;
|
|
}
|
|
|
|
DN_API DN_U64 DN_FNV1A64_Iterate(void const *bytes, DN_USize size, DN_U64 hash)
|
|
{
|
|
auto buffer = DN_Cast(DN_U8 const *)bytes;
|
|
for (DN_USize i = 0; i < size; i++)
|
|
hash = (buffer[i] ^ hash) * 1099511628211 /*FNV Prime*/;
|
|
return hash;
|
|
}
|
|
|
|
DN_API DN_U64 DN_FNV1A64_Hash(void const *bytes, DN_USize size)
|
|
{
|
|
DN_U64 result = DN_FNV1A64_Iterate(bytes, size, DN_FNV1A64_SEED);
|
|
return result;
|
|
}
|
|
|
|
// 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)
|
|
#else
|
|
#define DN_MMH3_ROTL32(x, y) ((x) << (y)) | ((x) >> (32 - (y)))
|
|
#define DN_MMH3_ROTL64(x, y) ((x) << (y)) | ((x) >> (64 - (y)))
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Block read - if your platform needs to do endian-swapping or can only
|
|
// handle aligned reads, do the conversion here
|
|
|
|
DN_FORCE_INLINE DN_U32 DN_MurmurHash3_GetBlock32(DN_U32 const *p, int i)
|
|
{
|
|
return p[i];
|
|
}
|
|
|
|
DN_FORCE_INLINE DN_U64 DN_MurmurHash3_GetBlock64(DN_U64 const *p, int i)
|
|
{
|
|
return p[i];
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Finalization mix - force all bits of a hash block to avalanche
|
|
|
|
DN_FORCE_INLINE DN_U32 DN_MurmurHash3_FMix32(DN_U32 h)
|
|
{
|
|
h ^= h >> 16;
|
|
h *= 0x85ebca6b;
|
|
h ^= h >> 13;
|
|
h *= 0xc2b2ae35;
|
|
h ^= h >> 16;
|
|
return h;
|
|
}
|
|
|
|
DN_FORCE_INLINE DN_U64 DN_MurmurHash3_FMix64(DN_U64 k)
|
|
{
|
|
k ^= k >> 33;
|
|
k *= 0xff51afd7ed558ccd;
|
|
k ^= k >> 33;
|
|
k *= 0xc4ceb9fe1a85ec53;
|
|
k ^= k >> 33;
|
|
return k;
|
|
}
|
|
|
|
DN_API DN_U32 DN_MurmurHash3_x86U32(void const *key, int len, DN_U32 seed)
|
|
{
|
|
const DN_U8 *data = (const DN_U8 *)key;
|
|
const int nblocks = len / 4;
|
|
|
|
DN_U32 h1 = seed;
|
|
|
|
const DN_U32 c1 = 0xcc9e2d51;
|
|
const DN_U32 c2 = 0x1b873593;
|
|
|
|
//----------
|
|
// body
|
|
|
|
const DN_U32 *blocks = (const DN_U32 *)(data + nblocks * 4);
|
|
|
|
for (int i = -nblocks; i; i++)
|
|
{
|
|
DN_U32 k1 = DN_MurmurHash3_GetBlock32(blocks, i);
|
|
|
|
k1 *= c1;
|
|
k1 = DN_MMH3_ROTL32(k1, 15);
|
|
k1 *= c2;
|
|
|
|
h1 ^= k1;
|
|
h1 = DN_MMH3_ROTL32(h1, 13);
|
|
h1 = h1 * 5 + 0xe6546b64;
|
|
}
|
|
|
|
//----------
|
|
// tail
|
|
|
|
const DN_U8 *tail = (const DN_U8 *)(data + nblocks * 4);
|
|
|
|
DN_U32 k1 = 0;
|
|
|
|
switch (len & 3)
|
|
{
|
|
case 3:
|
|
k1 ^= tail[2] << 16;
|
|
case 2:
|
|
k1 ^= tail[1] << 8;
|
|
case 1:
|
|
k1 ^= tail[0];
|
|
k1 *= c1;
|
|
k1 = DN_MMH3_ROTL32(k1, 15);
|
|
k1 *= c2;
|
|
h1 ^= k1;
|
|
};
|
|
|
|
//----------
|
|
// finalization
|
|
|
|
h1 ^= len;
|
|
|
|
h1 = DN_MurmurHash3_FMix32(h1);
|
|
|
|
return h1;
|
|
}
|
|
|
|
DN_API DN_MurmurHash3 DN_MurmurHash3_x64U128(void const *key, int len, DN_U32 seed)
|
|
{
|
|
const DN_U8 *data = (const DN_U8 *)key;
|
|
const int nblocks = len / 16;
|
|
|
|
DN_U64 h1 = seed;
|
|
DN_U64 h2 = seed;
|
|
|
|
const DN_U64 c1 = 0x87c37b91114253d5;
|
|
const DN_U64 c2 = 0x4cf5ad432745937f;
|
|
|
|
//----------
|
|
// body
|
|
|
|
const DN_U64 *blocks = (const DN_U64 *)(data);
|
|
|
|
for (int i = 0; i < nblocks; i++)
|
|
{
|
|
DN_U64 k1 = DN_MurmurHash3_GetBlock64(blocks, i * 2 + 0);
|
|
DN_U64 k2 = DN_MurmurHash3_GetBlock64(blocks, i * 2 + 1);
|
|
|
|
k1 *= c1;
|
|
k1 = DN_MMH3_ROTL64(k1, 31);
|
|
k1 *= c2;
|
|
h1 ^= k1;
|
|
|
|
h1 = DN_MMH3_ROTL64(h1, 27);
|
|
h1 += h2;
|
|
h1 = h1 * 5 + 0x52dce729;
|
|
|
|
k2 *= c2;
|
|
k2 = DN_MMH3_ROTL64(k2, 33);
|
|
k2 *= c1;
|
|
h2 ^= k2;
|
|
|
|
h2 = DN_MMH3_ROTL64(h2, 31);
|
|
h2 += h1;
|
|
h2 = h2 * 5 + 0x38495ab5;
|
|
}
|
|
|
|
//----------
|
|
// tail
|
|
|
|
const DN_U8 *tail = (const DN_U8 *)(data + nblocks * 16);
|
|
|
|
DN_U64 k1 = 0;
|
|
DN_U64 k2 = 0;
|
|
|
|
switch (len & 15)
|
|
{
|
|
case 15:
|
|
k2 ^= ((DN_U64)tail[14]) << 48;
|
|
case 14:
|
|
k2 ^= ((DN_U64)tail[13]) << 40;
|
|
case 13:
|
|
k2 ^= ((DN_U64)tail[12]) << 32;
|
|
case 12:
|
|
k2 ^= ((DN_U64)tail[11]) << 24;
|
|
case 11:
|
|
k2 ^= ((DN_U64)tail[10]) << 16;
|
|
case 10:
|
|
k2 ^= ((DN_U64)tail[9]) << 8;
|
|
case 9:
|
|
k2 ^= ((DN_U64)tail[8]) << 0;
|
|
k2 *= c2;
|
|
k2 = DN_MMH3_ROTL64(k2, 33);
|
|
k2 *= c1;
|
|
h2 ^= k2;
|
|
|
|
case 8:
|
|
k1 ^= ((DN_U64)tail[7]) << 56;
|
|
case 7:
|
|
k1 ^= ((DN_U64)tail[6]) << 48;
|
|
case 6:
|
|
k1 ^= ((DN_U64)tail[5]) << 40;
|
|
case 5:
|
|
k1 ^= ((DN_U64)tail[4]) << 32;
|
|
case 4:
|
|
k1 ^= ((DN_U64)tail[3]) << 24;
|
|
case 3:
|
|
k1 ^= ((DN_U64)tail[2]) << 16;
|
|
case 2:
|
|
k1 ^= ((DN_U64)tail[1]) << 8;
|
|
case 1:
|
|
k1 ^= ((DN_U64)tail[0]) << 0;
|
|
k1 *= c1;
|
|
k1 = DN_MMH3_ROTL64(k1, 31);
|
|
k1 *= c2;
|
|
h1 ^= k1;
|
|
};
|
|
|
|
//----------
|
|
// finalization
|
|
|
|
h1 ^= len;
|
|
h2 ^= len;
|
|
|
|
h1 += h2;
|
|
h2 += h1;
|
|
|
|
h1 = DN_MurmurHash3_FMix64(h1);
|
|
h2 = DN_MurmurHash3_FMix64(h2);
|
|
|
|
h1 += h2;
|
|
h2 += h1;
|
|
|
|
DN_MurmurHash3 result = {};
|
|
result.e[0] = h1;
|
|
result.e[1] = h2;
|
|
return result;
|
|
}
|
|
#define DN_HELPERS_CPP
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "dn_helpers.h"
|
|
#if !defined(DN_HELPERS_H)
|
|
#define DN_HELPERS_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
// DN: Single header generator commented out this header => #include "dn_math.h"
|
|
#if !defined(DN_MATH_H)
|
|
#define DN_MATH_H
|
|
|
|
#if defined(_CLANGD)
|
|
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
|
|
#endif
|
|
|
|
DN_MSVC_WARNING_PUSH
|
|
DN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
|
|
union DN_V2I32
|
|
{
|
|
struct { DN_I32 x, y; };
|
|
struct { DN_I32 w, h; };
|
|
DN_I32 data[2];
|
|
};
|
|
|
|
union DN_V2U16
|
|
{
|
|
struct { DN_U16 x, y; };
|
|
struct { DN_U16 w, h; };
|
|
DN_U16 data[2];
|
|
};
|
|
|
|
union DN_V2U32
|
|
{
|
|
struct { DN_U32 x, y; };
|
|
struct { DN_U32 w, h; };
|
|
DN_U32 data[2];
|
|
};
|
|
|
|
union DN_V2F32
|
|
{
|
|
struct { DN_F32 x, y; };
|
|
struct { DN_F32 w, h; };
|
|
DN_F32 data[2];
|
|
};
|
|
|
|
union DN_V3F32
|
|
{
|
|
struct { DN_F32 x, y, z; };
|
|
struct { DN_F32 r, g, b; };
|
|
DN_F32 data[3];
|
|
};
|
|
|
|
|
|
union DN_V4F32
|
|
{
|
|
struct { DN_F32 x, y, z, w; };
|
|
struct { DN_F32 r, g, b, a; };
|
|
#if !defined(DN_NO_V3)
|
|
DN_V3F32 rgb;
|
|
DN_V3F32 xyz;
|
|
#endif
|
|
DN_F32 data[4];
|
|
};
|
|
DN_MSVC_WARNING_POP
|
|
|
|
struct DN_M4
|
|
{
|
|
DN_F32 columns[4][4]; // Column major matrix
|
|
};
|
|
|
|
union DN_M2x3
|
|
{
|
|
DN_F32 e[6];
|
|
DN_F32 row[2][3];
|
|
};
|
|
|
|
struct DN_Rect
|
|
{
|
|
DN_V2F32 pos, size;
|
|
};
|
|
|
|
struct DN_RectMinMax
|
|
{
|
|
DN_V2F32 min, max;
|
|
};
|
|
|
|
enum DN_RectCutClip
|
|
{
|
|
DN_RectCutClip_No,
|
|
DN_RectCutClip_Yes,
|
|
};
|
|
|
|
enum DN_RectCutSide
|
|
{
|
|
DN_RectCutSide_Left,
|
|
DN_RectCutSide_Right,
|
|
DN_RectCutSide_Top,
|
|
DN_RectCutSide_Bottom,
|
|
};
|
|
|
|
struct DN_RectCut
|
|
{
|
|
DN_Rect* rect;
|
|
DN_RectCutSide side;
|
|
};
|
|
|
|
struct DN_RaycastLineIntersectV2Result
|
|
{
|
|
bool hit; // True if there was an intersection, false if the lines are parallel
|
|
DN_F32 t_a; // Distance along `dir_a` that the intersection occurred, e.g. `origin_a + (dir_a * t_a)`
|
|
DN_F32 t_b; // Distance along `dir_b` that the intersection occurred, e.g. `origin_b + (dir_b * t_b)`
|
|
};
|
|
|
|
#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_From1N(x) DN_Literal(DN_V2I32){{(int32_t)(x), (int32_t)(x)}}
|
|
#define DN_V2I32_From2N(x, y) DN_Literal(DN_V2I32){{(int32_t)(x), (int32_t)(y)}}
|
|
#define DN_V2I32_InitV2(xy) DN_Literal(DN_V2I32){{(int32_t)(xy).x, (int32_t)(xy).y}}
|
|
|
|
DN_API bool operator!= (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API bool operator== (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API bool operator>= (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API bool operator<= (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API bool operator< (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API bool operator> (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 operator- (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 operator- (DN_V2I32 lhs);
|
|
DN_API DN_V2I32 operator+ (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 operator* (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 operator* (DN_V2I32 lhs, DN_F32 rhs);
|
|
DN_API DN_V2I32 operator* (DN_V2I32 lhs, int32_t rhs);
|
|
DN_API DN_V2I32 operator/ (DN_V2I32 lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 operator/ (DN_V2I32 lhs, DN_F32 rhs);
|
|
DN_API DN_V2I32 operator/ (DN_V2I32 lhs, int32_t rhs);
|
|
DN_API DN_V2I32 & operator*= (DN_V2I32& lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 & operator*= (DN_V2I32& lhs, DN_F32 rhs);
|
|
DN_API DN_V2I32 & operator*= (DN_V2I32& lhs, int32_t rhs);
|
|
DN_API DN_V2I32 & operator/= (DN_V2I32& lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 & operator/= (DN_V2I32& lhs, DN_F32 rhs);
|
|
DN_API DN_V2I32 & operator/= (DN_V2I32& lhs, int32_t rhs);
|
|
DN_API DN_V2I32 & operator-= (DN_V2I32& lhs, DN_V2I32 rhs);
|
|
DN_API DN_V2I32 & operator+= (DN_V2I32& lhs, DN_V2I32 rhs);
|
|
|
|
DN_API DN_V2I32 DN_V2I32_Min (DN_V2I32 a, DN_V2I32 b);
|
|
DN_API DN_V2I32 DN_V2I32_Max (DN_V2I32 a, DN_V2I32 b);
|
|
DN_API DN_V2I32 DN_V2I32_Abs (DN_V2I32 a);
|
|
|
|
#define DN_V2U16_Zero DN_Literal(DN_V2U16){{(uint16_t)(0), (uint16_t)(0)}}
|
|
#define DN_V2U16_One DN_Literal(DN_V2U16){{(uint16_t)(1), (uint16_t)(1)}}
|
|
#define DN_V2U16_From1N(x) DN_Literal(DN_V2U16){{(uint16_t)(x), (uint16_t)(x)}}
|
|
#define DN_V2U16_From2N(x, y) DN_Literal(DN_V2U16){{(uint16_t)(x), (uint16_t)(y)}}
|
|
|
|
DN_API bool operator!= (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API bool operator== (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API bool operator>= (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API bool operator<= (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API bool operator< (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API bool operator> (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 operator- (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 operator+ (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 operator* (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 operator* (DN_V2U16 lhs, DN_F32 rhs);
|
|
DN_API DN_V2U16 operator* (DN_V2U16 lhs, int32_t rhs);
|
|
DN_API DN_V2U16 operator/ (DN_V2U16 lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 operator/ (DN_V2U16 lhs, DN_F32 rhs);
|
|
DN_API DN_V2U16 operator/ (DN_V2U16 lhs, int32_t rhs);
|
|
DN_API DN_V2U16 & operator*= (DN_V2U16& lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 & operator*= (DN_V2U16& lhs, DN_F32 rhs);
|
|
DN_API DN_V2U16 & operator*= (DN_V2U16& lhs, int32_t rhs);
|
|
DN_API DN_V2U16 & operator/= (DN_V2U16& lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 & operator/= (DN_V2U16& lhs, DN_F32 rhs);
|
|
DN_API DN_V2U16 & operator/= (DN_V2U16& lhs, int32_t rhs);
|
|
DN_API DN_V2U16 & operator-= (DN_V2U16& lhs, DN_V2U16 rhs);
|
|
DN_API DN_V2U16 & operator+= (DN_V2U16& lhs, DN_V2U16 rhs);
|
|
|
|
#define DN_V2F32_Zero DN_Literal(DN_V2F32){{(DN_F32)(0), (DN_F32)(0)}}
|
|
#define DN_V2F32_One DN_Literal(DN_V2F32){{(DN_F32)(1), (DN_F32)(1)}}
|
|
#define DN_V2F32_From1N(x) DN_Literal(DN_V2F32){{(DN_F32)(x), (DN_F32)(x)}}
|
|
#define DN_V2F32_From2N(x, y) DN_Literal(DN_V2F32){{(DN_F32)(x), (DN_F32)(y)}}
|
|
#define DN_V2F32_FromV2I32(xy) DN_Literal(DN_V2F32){{(DN_F32)(xy).x, (DN_F32)(xy).y}}
|
|
#define DN_V2F32_FromV2U32(xy) DN_Literal(DN_V2F32){{(DN_F32)(xy).x, (DN_F32)(xy).y}}
|
|
|
|
DN_API bool operator!= (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool operator== (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool operator>= (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool operator<= (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool operator< (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool operator> (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
|
|
DN_API DN_V2F32 operator- (DN_V2F32 lhs);
|
|
DN_API DN_V2F32 operator- (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 operator- (DN_V2F32 lhs, DN_V2I32 rhs);
|
|
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 operator+ (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 operator+ (DN_V2F32 lhs, DN_V2I32 rhs);
|
|
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 operator* (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 operator* (DN_V2F32 lhs, DN_V2I32 rhs);
|
|
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 operator/ (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 operator/ (DN_V2F32 lhs, DN_V2I32 rhs);
|
|
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 & operator*= (DN_V2F32& lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 & operator*= (DN_V2F32& lhs, DN_V2I32 rhs);
|
|
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 & operator/= (DN_V2F32& lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 & operator/= (DN_V2F32& lhs, DN_V2I32 rhs);
|
|
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 & operator-= (DN_V2F32& lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 & operator-= (DN_V2F32& lhs, DN_V2I32 rhs);
|
|
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 & operator+= (DN_V2F32& lhs, DN_V2F32 rhs);
|
|
DN_API DN_V2F32 & operator+= (DN_V2F32& lhs, DN_V2I32 rhs);
|
|
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_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_LengthSq2V2 (DN_V2F32 lhs, DN_V2F32 rhs);
|
|
DN_API bool DN_V2F32_LengthSqIsWithin2V2 (DN_V2F32 lhs, DN_V2F32 rhs, DN_F32 within_amount_sq);
|
|
DN_API DN_F32 DN_V2F32_Length2V2 (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);
|
|
|
|
#define DN_V3F32_From1N(x) DN_Literal(DN_V3F32){{(DN_F32)(x), (DN_F32)(x), (DN_F32)(x)}}
|
|
#define DN_V3F32_From3N(x, y, z) DN_Literal(DN_V3F32){{(DN_F32)(x), (DN_F32)(y), (DN_F32)(z)}}
|
|
#define DN_V3F32_FromV2F32And1N(xy, z) DN_Literal(DN_V3F32){{(DN_F32)(xy.x), (DN_F32)(xy.y), (DN_F32)(z)}}
|
|
|
|
DN_API bool operator== (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API bool operator!= (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API bool operator>= (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API bool operator<= (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API bool operator< (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API bool operator> (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 operator- (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 operator- (DN_V3F32 lhs);
|
|
DN_API DN_V3F32 operator+ (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 operator* (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 operator* (DN_V3F32 lhs, DN_F32 rhs);
|
|
DN_API DN_V3F32 operator* (DN_V3F32 lhs, int32_t rhs);
|
|
DN_API DN_V3F32 operator/ (DN_V3F32 lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 operator/ (DN_V3F32 lhs, DN_F32 rhs);
|
|
DN_API DN_V3F32 operator/ (DN_V3F32 lhs, int32_t rhs);
|
|
DN_API DN_V3F32 & operator*= (DN_V3F32 &lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 & operator*= (DN_V3F32 &lhs, DN_F32 rhs);
|
|
DN_API DN_V3F32 & operator*= (DN_V3F32 &lhs, int32_t rhs);
|
|
DN_API DN_V3F32 & operator/= (DN_V3F32 &lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 & operator/= (DN_V3F32 &lhs, DN_F32 rhs);
|
|
DN_API DN_V3F32 & operator/= (DN_V3F32 &lhs, int32_t rhs);
|
|
DN_API DN_V3F32 & operator-= (DN_V3F32 &lhs, DN_V3F32 rhs);
|
|
DN_API DN_V3F32 & operator+= (DN_V3F32 &lhs, DN_V3F32 rhs);
|
|
DN_API DN_F32 DN_V3F32_LengthSq (DN_V3F32 a);
|
|
DN_API DN_F32 DN_V3F32_Length (DN_V3F32 a);
|
|
DN_API DN_V3F32 DN_V3F32_Normalise (DN_V3F32 a);
|
|
|
|
DN_U32 const DN_V4_R_MASK_U32 = 0xFF000000;
|
|
DN_U32 const DN_V4_G_MASK_U32 = 0x00FF0000;
|
|
DN_U32 const DN_V4_B_MASK_U32 = 0x0000FF00;
|
|
DN_U32 const DN_V4_A_MASK_U32 = 0x000000FF;
|
|
|
|
#define DN_V4F32_From1N(x) DN_Literal(DN_V4F32){{(DN_F32)(x), (DN_F32)(x), (DN_F32)(x), (DN_F32)(x)}}
|
|
#define DN_V4F32_From4N(x, y, z, w) DN_Literal(DN_V4F32){{(DN_F32)(x), (DN_F32)(y), (DN_F32)(z), (DN_F32)(w)}}
|
|
#define DN_V4F32_FromV3And1N(xyz, w) DN_Literal(DN_V4F32){{xyz.x, xyz.y, xyz.z, w}}
|
|
#define DN_V4F32_FromRGBAU8(r, g, b, a) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, a / 255.f}}
|
|
#define DN_V4F32_FromRGBU8(r, g, b) DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, 1.f}}
|
|
DN_API DN_V4F32 DN_V4F32_FromRGBU32(DN_U32 u32);
|
|
DN_API DN_V4F32 DN_V4F32_FromRGBAU32(DN_U32 u32);
|
|
#define DN_V4F32_FromV4Alpha(v4, alpha) DN_V4F32_FromV3And1N(v4.xyz, alpha)
|
|
DN_API bool operator== (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API bool operator!= (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API bool operator<= (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API bool operator< (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API bool operator> (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 operator- (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 operator- (DN_V4F32 lhs);
|
|
DN_API DN_V4F32 operator+ (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 operator* (DN_V4F32 lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 operator* (DN_V4F32 lhs, DN_F32 rhs);
|
|
DN_API DN_V4F32 operator* (DN_V4F32 lhs, int32_t rhs);
|
|
DN_API DN_V4F32 operator/ (DN_V4F32 lhs, DN_F32 rhs);
|
|
DN_API DN_V4F32 & operator*= (DN_V4F32 &lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 & operator*= (DN_V4F32 &lhs, DN_F32 rhs);
|
|
DN_API DN_V4F32 & operator*= (DN_V4F32 &lhs, int32_t rhs);
|
|
DN_API DN_V4F32 & operator-= (DN_V4F32 &lhs, DN_V4F32 rhs);
|
|
DN_API DN_V4F32 & operator+= (DN_V4F32 &lhs, DN_V4F32 rhs);
|
|
DN_API DN_F32 DN_V4F32_Dot (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);
|
|
DN_API DN_M4 DN_M4_Scale (DN_V3F32 xyz);
|
|
DN_API DN_M4 DN_M4_TranslateF (DN_F32 x, DN_F32 y, DN_F32 z);
|
|
DN_API DN_M4 DN_M4_Translate (DN_V3F32 xyz);
|
|
DN_API DN_M4 DN_M4_Transpose (DN_M4 mat);
|
|
DN_API DN_M4 DN_M4_Rotate (DN_V3F32 axis, DN_F32 radians);
|
|
DN_API DN_M4 DN_M4_Orthographic (DN_F32 left, DN_F32 right, DN_F32 bottom, DN_F32 top, DN_F32 z_near, DN_F32 z_far);
|
|
DN_API DN_M4 DN_M4_Perspective (DN_F32 fov /*radians*/, DN_F32 aspect, DN_F32 z_near, DN_F32 z_far);
|
|
DN_API DN_M4 DN_M4_Add (DN_M4 lhs, DN_M4 rhs);
|
|
DN_API DN_M4 DN_M4_Sub (DN_M4 lhs, DN_M4 rhs);
|
|
DN_API DN_M4 DN_M4_Mul (DN_M4 lhs, DN_M4 rhs);
|
|
DN_API DN_M4 DN_M4_Div (DN_M4 lhs, DN_M4 rhs);
|
|
DN_API DN_M4 DN_M4_AddF (DN_M4 lhs, DN_F32 rhs);
|
|
DN_API DN_M4 DN_M4_SubF (DN_M4 lhs, DN_F32 rhs);
|
|
DN_API DN_M4 DN_M4_MulF (DN_M4 lhs, DN_F32 rhs);
|
|
DN_API DN_M4 DN_M4_DivF (DN_M4 lhs, DN_F32 rhs);
|
|
DN_API DN_Str8x256 DN_M4_ColumnMajorString (DN_M4 mat);
|
|
|
|
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 ();
|
|
DN_API DN_M2x3 DN_M2x3_Translate (DN_V2F32 offset);
|
|
DN_API DN_M2x3 DN_M2x3_Scale (DN_V2F32 scale);
|
|
DN_API DN_M2x3 DN_M2x3_Rotate (DN_F32 radians);
|
|
DN_API DN_M2x3 DN_M2x3_Mul (DN_M2x3 m1, DN_M2x3 m2);
|
|
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);
|
|
|
|
#define DN_Rect_From2V2(pos, size) DN_Literal(DN_Rect){(pos), (size)}
|
|
#define DN_Rect_From4N(x, y, w, h) DN_Literal(DN_Rect){DN_Literal(DN_V2F32){{x, y}}, DN_Literal(DN_V2F32){{w, h}}}
|
|
|
|
DN_API bool operator== (const DN_Rect& lhs, const DN_Rect& rhs);
|
|
DN_API DN_V2F32 DN_Rect_Center (DN_Rect rect);
|
|
DN_API bool DN_Rect_ContainsPoint (DN_Rect rect, DN_V2F32 p);
|
|
DN_API bool DN_Rect_ContainsRect (DN_Rect a, DN_Rect b);
|
|
DN_API DN_Rect DN_Rect_Expand (DN_Rect a, DN_F32 amount);
|
|
DN_API DN_Rect DN_Rect_ExpandV2 (DN_Rect a, DN_V2F32 amount);
|
|
DN_API bool DN_Rect_Intersects (DN_Rect a, DN_Rect b);
|
|
DN_API DN_Rect DN_Rect_Intersection (DN_Rect a, DN_Rect b);
|
|
DN_API DN_Rect DN_Rect_Union (DN_Rect a, DN_Rect b);
|
|
DN_API DN_RectMinMax DN_Rect_MinMax (DN_Rect a);
|
|
DN_API DN_F32 DN_Rect_Area (DN_Rect a);
|
|
DN_API DN_V2F32 DN_Rect_InterpolatedPoint (DN_Rect rect, DN_V2F32 t01);
|
|
DN_API DN_V2F32 DN_Rect_TopLeft (DN_Rect rect);
|
|
DN_API DN_V2F32 DN_Rect_TopRight (DN_Rect rect);
|
|
DN_API DN_V2F32 DN_Rect_BottomLeft (DN_Rect rect);
|
|
DN_API DN_V2F32 DN_Rect_BottomRight (DN_Rect rect);
|
|
|
|
DN_API DN_Rect DN_Rect_CutLeftClip (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);
|
|
DN_API DN_Rect DN_Rect_CutRightClip (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);
|
|
DN_API DN_Rect DN_Rect_CutTopClip (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);
|
|
DN_API DN_Rect DN_Rect_CutBottomClip (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);
|
|
|
|
#define DN_Rect_CutLeft(rect, amount) DN_Rect_CutLeftClip(rect, amount, DN_RectCutClip_Yes)
|
|
#define DN_Rect_CutRight(rect, amount) DN_Rect_CutRightClip(rect, amount, DN_RectCutClip_Yes)
|
|
#define DN_Rect_CutTop(rect, amount) DN_Rect_CutTopClip(rect, amount, DN_RectCutClip_Yes)
|
|
#define DN_Rect_CutBottom(rect, amount) DN_Rect_CutBottomClip(rect, amount, DN_RectCutClip_Yes)
|
|
|
|
#define DN_Rect_CutLeftNoClip(rect, amount) DN_Rect_CutLeftClip(rect, amount, DN_RectCutClip_No)
|
|
#define DN_Rect_CutRightNoClip(rect, amount) DN_Rect_CutRightClip(rect, amount, DN_RectCutClip_No)
|
|
#define DN_Rect_CutTopNoClip(rect, amount) DN_Rect_CutTopClip(rect, amount, DN_RectCutClip_No)
|
|
#define DN_Rect_CutBottomNoClip(rect, amount) DN_Rect_CutBottomClip(rect, amount, DN_RectCutClip_No)
|
|
|
|
DN_API DN_Rect DN_RectCut_Cut (DN_RectCut rect_cut, DN_V2F32 size, DN_RectCutClip clip);
|
|
#define DN_RectCut_Init(rect, side) DN_Literal(DN_RectCut){rect, side}
|
|
#define DN_RectCut_Left(rect) DN_Literal(DN_RectCut){rect, DN_RectCutSide_Left}
|
|
#define DN_RectCut_Right(rect) DN_Literal(DN_RectCut){rect, DN_RectCutSide_Right}
|
|
#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}
|
|
|
|
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)
|
|
#endif
|
|
|
|
#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: 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_DeferLoop(DN_JSONBuilder_ObjectBegin(builder), \
|
|
DN_JSONBuilder_ObjectEnd(builder))
|
|
|
|
#define DN_JSONBuilder_ObjectNamed(builder, name) \
|
|
DN_DeferLoop(DN_JSONBuilder_ObjectBeginNamed(builder, name), \
|
|
DN_JSONBuilder_ObjectEnd(builder))
|
|
|
|
#define DN_JSONBuilder_Array(builder) \
|
|
DN_DeferLoop(DN_JSONBuilder_ArrayBegin(builder), \
|
|
DN_JSONBuilder_ArrayEnd(builder))
|
|
|
|
#define DN_JSONBuilder_ArrayNamed(builder, name) \
|
|
DN_DeferLoop(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_Str8Lit(""))
|
|
#define DN_JSONBuilder_ArrayBegin(builder) DN_JSONBuilder_ArrayBeginNamed(builder, DN_Str8Lit(""))
|
|
#define DN_JSONBuilder_Str8(builder, value) DN_JSONBuilder_Str8Named(builder, DN_Str8Lit(""), value)
|
|
#define DN_JSONBuilder_Literal(builder, value) DN_JSONBuilder_LiteralNamed(builder, DN_Str8Lit(""), value)
|
|
#define DN_JSONBuilder_U64(builder, value) DN_JSONBuilder_U64Named(builder, DN_Str8Lit(""), value)
|
|
#define DN_JSONBuilder_I64(builder, value) DN_JSONBuilder_I64Named(builder, DN_Str8Lit(""), value)
|
|
#define DN_JSONBuilder_F64(builder, value) DN_JSONBuilder_F64Named(builder, DN_Str8Lit(""), value)
|
|
#define DN_JSONBuilder_Bool(builder, value) DN_JSONBuilder_BoolNamed(builder, DN_Str8Lit(""), 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, void *user_context);
|
|
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_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)
|
|
#endif
|
|
|
|
/*
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
|
// $$ | $$ |$$ _____|$$ | $$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
|
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ |$$ / \__|
|
|
// $$$$$$$$ |$$$$$\ $$ | $$$$$$$ |$$$$$\ $$$$$$$ |\$$$$$$\
|
|
// $$ __$$ |$$ __| $$ | $$ ____/ $$ __| $$ __$$< \____$$\
|
|
// $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |$$\ $$ |
|
|
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$ | $$$$$$$$\ $$ | $$ |\$$$$$$ |
|
|
// \__| \__|\________|\________|\__| \________|\__| \__| \______/
|
|
//
|
|
// 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_Str8BuilderBuild(&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_Str8BuilderAppendF(&builder->string_builder,
|
|
"%.*s%*c\"%.*s\": %.*s",
|
|
prefix_size,
|
|
prefix,
|
|
spaces,
|
|
' ',
|
|
DN_Str8PrintFmt(key),
|
|
DN_Str8PrintFmt(value));
|
|
else if (spaces == 0)
|
|
DN_Str8BuilderAppendF(&builder->string_builder, "%.*s%.*s", prefix_size, prefix, DN_Str8PrintFmt(value));
|
|
else
|
|
DN_Str8BuilderAppendF(&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DN_Str8PrintFmt(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_Str8FromFmtVArena(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_Str8Lit("{"));
|
|
}
|
|
|
|
DN_API void DN_JSONBuilder_ObjectEnd(DN_JSONBuilder *builder)
|
|
{
|
|
DN_JSONBuilder_KeyValue(builder, DN_Str8Lit(""), DN_Str8Lit("}"));
|
|
}
|
|
|
|
DN_API void DN_JSONBuilder_ArrayBeginNamed(DN_JSONBuilder *builder, DN_Str8 name)
|
|
{
|
|
DN_JSONBuilder_KeyValue(builder, name, DN_Str8Lit("["));
|
|
}
|
|
|
|
DN_API void DN_JSONBuilder_ArrayEnd(DN_JSONBuilder *builder)
|
|
{
|
|
DN_JSONBuilder_KeyValue(builder, DN_Str8Lit(""), DN_Str8Lit("]"));
|
|
}
|
|
|
|
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_Str8Lit("true") : DN_Str8Lit("false");
|
|
DN_JSONBuilder_KeyValueF(builder, key, "%.*s", value_string.size, value_string.data);
|
|
}
|
|
#endif // !defined(DN_NO_JSON_BUILDER)
|