// Generated by the DN single header generator 2025-11-30 21:32:49

#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_Str8x16 DN_Str8x16FromFmt(DN_FMT_ATTRIB char const *fmt, ...)
{
  va_list args;
  va_start(args, fmt);
  DN_Str8x16 result = {};
  DN_FmtVAppend(result.data, &result.size, sizeof(result.data), fmt, args);
  va_end(args);
  return result;
}

DN_API DN_Str8x16 DN_Str8x16FromFmtV(DN_FMT_ATTRIB char const *fmt, va_list args)
{
  DN_Str8x16 result = {};
  DN_FmtVAppend(result.data, &result.size, sizeof(result.data), fmt, 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;
  }

  DN_Str8 unit_suffix = {};
  if (units & DN_AgeUnit_Year) {
    unit_suffix             = DN_Str8Lit("y");
    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%zu%.*s", result.size ? " " : "", value_usize, DN_Str8PrintFmt(unit_suffix));
  }

  if (units & DN_AgeUnit_Week) {
    unit_suffix             = DN_Str8Lit("w");
    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%zu%.*s", result.size ? " " : "", value_usize, DN_Str8PrintFmt(unit_suffix));
  }

  if (units & DN_AgeUnit_Day) {
    unit_suffix             = DN_Str8Lit("d");
    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%zu%.*s", result.size ? " " : "", value_usize, DN_Str8PrintFmt(unit_suffix));
  }

  if (units & DN_AgeUnit_Hr) {
    unit_suffix             = DN_Str8Lit("h");
    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%zu%.*s", result.size ? " " : "", value_usize, DN_Str8PrintFmt(unit_suffix));
  }

  if (units & DN_AgeUnit_Min) {
    unit_suffix             = DN_Str8Lit("m");
    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%zu%.*s", result.size ? " " : "", value_usize, DN_Str8PrintFmt(unit_suffix));
  }

  if (units & DN_AgeUnit_Sec) {
    unit_suffix = DN_Str8Lit("s");
    if (units & DN_AgeUnit_FractionalSec) {
      DN_F64 remainder_s = remainder_ms / 1000.0;
      DN_FmtAppend(result.data, &result.size, sizeof(result.data), "%s%.3f%.*s", result.size ? " " : "", remainder_s, DN_Str8PrintFmt(unit_suffix));
      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%zu%.*s", result.size ? " " : "", value_usize, DN_Str8PrintFmt(unit_suffix));
    }
  }

  if (units & DN_AgeUnit_Ms) {
    unit_suffix = DN_Str8Lit("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%zu%.*s", result.size ? " " : "", value_usize, DN_Str8PrintFmt(unit_suffix));
  }

  if (result.size == 0)
    DN_FmtAppend(result.data, &result.size, sizeof(result.data), "0%.*s", DN_Str8PrintFmt(unit_suffix));
  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 int DN_IsLeapYear(int year)
{
  if (year % 4 != 0)
    return 0;
  if (year % 100 != 0)
    return 1;
  return (year % 400 == 0);
}

DN_API bool DN_DateIsValid(DN_Date 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;
}

DN_API DN_Date DN_DateFromUnixTimeMs(DN_USize unix_ts_ms)
{
  DN_Date  result        = {};
  DN_USize ms            = unix_ts_ms % 1000;
  DN_USize total_seconds = unix_ts_ms / 1000;
  result.milliseconds    = (DN_U16)ms;

  DN_USize secs_in_day = total_seconds % 86400;
  DN_USize days        = total_seconds / 86400;

  result.hour    = (DN_U8)(secs_in_day / 3600);
  result.minutes = (DN_U8)((secs_in_day % 3600) / 60);
  result.seconds = (DN_U8)(secs_in_day % 60);

  DN_U16   days_in_month[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  DN_USize days_left         = days;
  DN_U16   year              = 1970;

  while (days_left >= (DN_IsLeapYear(year) ? 366 : 365)) {
    DN_USize days_in_year  = DN_IsLeapYear(year) ? 366 : 365;
    days_left             -= days_in_year;
    year++;
  }

  DN_U8 month = 1;
  for (;;) {
    DN_U16 day_count = days_in_month[month];
    if (month == 2 && DN_IsLeapYear(year))
      day_count = 29;
    if (days_left < day_count)
      break;
    days_left -= day_count;
    month++;
  }

  result.year  = year;
  result.month = month;
  result.day   = (DN_U8)days_left + 1;
  return result;
}

DN_API DN_U64 DN_UnixTimeMsFromDate(DN_Date date)
{
  DN_Assert(DN_DateIsValid(date));

  // Precomputed cumulative days before each month (non-leap year)
  const DN_U16 days_before_month[13] = {
      0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};

  DN_U16 y = date.year;
  DN_U8  m = date.month;
  DN_U8  d = date.day;

  DN_U32 days  = d - 1;                    // day of month starts at 0 internally
  days        += days_before_month[m - 1]; // Add days from previous months this year

  if (m > 2 && DN_IsLeapYear(y)) // Add February 29 if leap year and month > 2
    days += 1;

  // Add full years from 1970 to y-1
  for (DN_U16 year = 1970; year < y; ++year)
    days += DN_IsLeapYear(year) ? 366 : 365;

  // Convert to seconds
  DN_U64 seconds  = DN_Cast(DN_U64)days * 86400ULL;
  seconds        += DN_Cast(DN_U64)date.hour * 3600ULL;
  seconds        += DN_Cast(DN_U64)date.minutes * 60ULL;
  seconds        += DN_Cast(DN_U64)date.seconds;
  DN_U64 result   = seconds * 1000ULL + date.milliseconds;
  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 err_sink_reserve = args.err_sink_reserve ? args.err_sink_reserve : DN_Kilobytes(64);

  #if !defined(DN_PLATFORM_EMSCRIPTEN)
  DN_U64 commit           = args.commit           ? args.commit           : DN_Kilobytes(4);
  DN_U64 err_sink_commit  = args.err_sink_commit  ? args.err_sink_commit  : DN_Kilobytes(4);
  #endif

  // 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: {
        #if defined(DN_PLATFORM_EMSCRIPTEN)
        *arena = DN_ArenaFromHeap(reserve, DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_NoAllocTrack);
        #else
        *arena = DN_ArenaFromVMem(reserve, commit, DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_NoAllocTrack);
        #endif
      } break;

      case DN_OSTLSArena_ErrorSink: {
        #if defined(DN_PLATFORM_EMSCRIPTEN)
        *arena = DN_ArenaFromHeap(err_sink_reserve, DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_NoAllocTrack);
        #else
        *arena = DN_ArenaFromVMem(err_sink_reserve, err_sink_commit, DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_NoAllocTrack);
        #endif
      } 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_Date    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_Str8x32 DN_OS_DateLocalTimeStr8(DN_Date time, char date_separator, char hms_separator)
{
  DN_Str8x32 result = DN_Str8x32FromFmt("%hu%c%02hhu%c%02hhu %02hhu%c%02hhu%c%02hhu",
                                        time.year,
                                        date_separator,
                                        time.month,
                                        date_separator,
                                        time.day,
                                        time.hour,
                                        hms_separator,
                                        time.minutes,
                                        hms_separator,
                                        time.seconds);
  return result;
}

DN_API DN_Str8x32 DN_OS_DateLocalTimeStr8Now(char date_separator, char hms_separator)
{
  DN_Date    time   = DN_OS_DateLocalTimeNow();
  DN_Str8x32 result = DN_OS_DateLocalTimeStr8(time, date_separator, hms_separator);
  return result;
}

// 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)
{
  #if defined(DN_PLATFORM_EMSCRIPTEN)
  DN_InvalidCodePathF("Emscripten does not support virtual memory, you should use DN_OS_MemAlloc");
  #endif

  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)
{
  #if defined(DN_PLATFORM_EMSCRIPTEN)
  DN_InvalidCodePathF("Emscripten does not support virtual memory");
  #endif
  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)
{
  #if defined(DN_PLATFORM_EMSCRIPTEN)
  DN_InvalidCodePathF("Emscripten does not support virtual memory");
  #endif
  mprotect(ptr, size, PROT_NONE);
  madvise(ptr, size, MADV_FREE);
}

DN_API void DN_OS_MemRelease(void *ptr, DN_USize size)
{
  #if defined(DN_PLATFORM_EMSCRIPTEN)
  DN_InvalidCodePathF("Emscripten does not support virtual memory");
  #endif
  munmap(ptr, size);
}

DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags)
{
  #if defined(DN_PLATFORM_EMSCRIPTEN)
  DN_InvalidCodePathF("Emscripten does not support virtual memory");
  #endif
  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_Date DN_OS_DateLocalTimeNow()
{
  DN_Date         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.milliseconds = ts.tv_nsec / (1000 * 1000); // TODO: Verify that getting the milliseconds like this is correct

  result.day          = DN_Cast(DN_U8) time.tm_mday;
  result.month        = DN_Cast(DN_U8) time.tm_mon + 1;
  result.year         = 1900 + DN_Cast(DN_U16) time.tm_year;
  return result;
}

DN_API DN_U64 DN_OS_DateUnixTimeNs()
{
  struct timespec ts = {};
  clock_gettime(CLOCK_REALTIME, &ts);
  DN_U64 result = (ts.tv_sec * 1000 /*ms*/ * 1000 /*us*/ * 1000 /*ns*/) + ts.tv_nsec;
  return result;
}

DN_API DN_U64 DN_OS_DateUnixTimeSFromLocalDate(DN_Date date)
{
  struct tm tm_time = {0};
  tm_time.tm_year   = (int)date.year - 1900;
  tm_time.tm_mon    = (int)date.month - 1; // month is 1-12 in your struct
  tm_time.tm_mday   = (int)date.day;       // day of month 1-31
  tm_time.tm_hour   = (int)date.hour;
  tm_time.tm_min    = (int)date.minutes;
  tm_time.tm_sec    = (int)date.seconds;
  tm_time.tm_isdst  = -1; // tm_isdst = -1 lets mktime() determine whether DST is in effect
  time_t unix_time  = mktime(&tm_time);
  DN_U64 result     = DN_Cast(DN_U64) unix_time;
  return result;
}

DN_API DN_U64 DN_OS_DateLocalUnixTimeSFromUnixTimeS(DN_U64 unix_ts_s)
{
  struct tm tm_local;
  time_t    unix_ts = unix_ts_s;
  void     *ret     = localtime_r(&unix_ts, &tm_local);
  DN_Assert(ret);

  long   local_offset_seconds = tm_local.tm_gmtoff;
  DN_U64 result               = unix_ts_s;
  if (local_offset_seconds > 0)
    result += local_offset_seconds;
  else
    result -= local_offset_seconds;
  return result;
}

DN_API DN_Date DN_OS_DateUnixTimeSToDate(DN_U64 time)
{
  time_t        posix_time = DN_Cast(time_t) time;
  struct tm     posix_date = *gmtime(&posix_time);
  DN_Date       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_Date DN_OS_DateLocalTimeNow()
{
  SYSTEMTIME sys_time;
  GetLocalTime(&sys_time);

  DN_Date result      = {};
  result.hour         = DN_Cast(DN_U8) sys_time.wHour;
  result.milliseconds = DN_Cast(DN_U8) sys_time.wMilliseconds;
  result.minutes      = DN_Cast(DN_U8) sys_time.wMinute;
  result.seconds      = DN_Cast(DN_U8) sys_time.wSecond;
  result.day          = DN_Cast(DN_U8) sys_time.wDay;
  result.month        = DN_Cast(DN_U8) sys_time.wMonth;
  result.year         = DN_Cast(DN_U16) 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_Date 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;
  result.wMilliseconds = date.milliseconds;
  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_DateUnixTimeSFromLocalDate(DN_Date 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_DateLocalUnixTimeSFromUnixTimeS(DN_U64 unix_ts_s)
{
  DN_U64   unix_time    = DN_Cast(DN_U64) unix_ts_s * 10000000LL; // seconds -> 100ns units
  DN_U64   filetime_utc = unix_time + 116444736000000000LL;       // Unix epoch -> Windows epoch
  FILETIME ft_utc       = {DN_Cast(DWORD) filetime_utc, DN_Cast(DWORD)(filetime_utc >> 32)};
  FILETIME ft_local;
  bool     converted = FileTimeToLocalFileTime(&ft_utc, &ft_local);
  DN_Assert(converted);

  DN_U64 filetime_local = (DN_Cast(DN_U64) ft_local.dwHighDateTime << 32) | ft_local.dwLowDateTime;
  DN_U64 result         = (filetime_local - 116444736000000000LL) / 10000000LL;
  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_Str8x16  { char data[16];  DN_USize size; };
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_Date
{
  DN_U8  day;
  DN_U8  month;
  DN_U16 year;
  DN_U8  hour;
  DN_U8  minutes;
  DN_U8  seconds;
  DN_U16 milliseconds;
};

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_Str8x16               DN_Str8x16FromFmt           (DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8x16               DN_Str8x16FromFmtV          (DN_FMT_ATTRIB char const *fmt, va_list args);
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 int                      DN_IsLeapYear               (int year);
DN_API bool                     DN_DateIsValid              (DN_Date date);
DN_API DN_Date                  DN_DateFromUnixTimeMs       (DN_USize unix_ts_ms);
DN_API DN_U64                   DN_UnixTimeMsFromDate       (DN_Date date);

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) BOOL   __stdcall FileTimeToLocalFileTime(const FILETIME *lpFileTime, FILETIME *lpLocalFileTime);

  __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) BOOL   __stdcall FileTimeToLocalFileTime(const FILETIME *lpFileTime, FILETIME *lpLocalFileTime);

  __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];

struct DN_OSTimer /// Record time between two time-points using the OS's performance counter.
{
  DN_U64 start;
  DN_U64 end;
};

// 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,
};

// 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_Date                   DN_OS_DateLocalTimeNow                       ();
DN_API DN_Str8x32                DN_OS_DateLocalTimeStr8Now                   (char date_separator = '-', char hms_separator = ':');
DN_API DN_Str8x32                DN_OS_DateLocalTimeStr8                      (DN_Date 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_U64                    DN_OS_DateUnixTimeSFromLocalDate             (DN_Date date);
DN_API DN_U64                    DN_OS_DateLocalUnixTimeSFromUnixTimeS        (DN_U64 unix_ts_s);

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

#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 & 0x00FF0000) >> 16);
  DN_U8    g      = (DN_U8)((u32 & 0x0000FF00) >> 8);
  DN_U8    b      = (DN_U8)((u32 & 0x000000FF) >> 0);
  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 & 0xFF000000) >> 24);
  DN_U8    g      = (DN_U8)((u32 & 0x00FF0000) >> 16);
  DN_U8    b      = (DN_U8)((u32 & 0x0000FF00) >> 8);
  DN_U8    a      = (DN_U8)((u32 & 0x000000FF) >> 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 bool    DN_BinPackIsEndOfReadStream(DN_BinPack const *pack);
DN_API void    DN_BinPackUSize            (DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item);
DN_API void    DN_BinPackU64              (DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item);
DN_API void    DN_BinPackU32              (DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item);
DN_API void    DN_BinPackU16              (DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item);
DN_API void    DN_BinPackU8               (DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item);
DN_API void    DN_BinPackI64              (DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item);
DN_API void    DN_BinPackI32              (DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item);
DN_API void    DN_BinPackI16              (DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item);
DN_API void    DN_BinPackI8               (DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item);
DN_API void    DN_BinPackF64              (DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item);
DN_API void    DN_BinPackF32              (DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item);
#if defined                               (DN_MATH_H)
DN_API void    DN_BinPackV2               (DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item);
DN_API void    DN_BinPackV4               (DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item);
#endif
DN_API void    DN_BinPackBool             (DN_BinPack *pack, DN_BinPackMode mode, bool *item);
DN_API void    DN_BinPackStr8FromArena    (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string);
DN_API void    DN_BinPackStr8FromPool     (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string);
DN_API DN_Str8 DN_BinPackStr8FromBuffer   (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max);
DN_API void    DN_BinPackBytesFromArena   (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size);
DN_API void    DN_BinPackBytesFromPool    (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size);
DN_API void    DN_BinPackCArray           (DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size);
DN_API void    DN_BinPackCBuffer          (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max);
DN_API DN_Str8 DN_BinPackBuild            (DN_BinPack const *pack, DN_Arena *arena);

#endif // !defined(DN_BIN_PACK_H)
#endif

DN_API void DN_BinPackU64(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_BinPackVarInt_(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_BinPackU64(pack, mode, &value);

  if (mode == DN_BinPackMode_Deserialise) // Write U64 `value` into `item`
    DN_Memcpy(item, &value, size);
}

DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack)
{
  bool result = pack->read_index == pack->read.size;
  return result;
}

DN_API void DN_BinPackUSize(DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item)
{
  DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}

DN_API void DN_BinPackU32(DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item)
{
  DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}

DN_API void DN_BinPackU16(DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item)
{
  DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}

DN_API void DN_BinPackU8(DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item)
{
  DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}

DN_API void DN_BinPackI64(DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item)
{
  DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}

DN_API void DN_BinPackI32(DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item)
{
  DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}

DN_API void DN_BinPackI16(DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item)
{
  DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}

DN_API void DN_BinPackI8(DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item)
{
  DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}

DN_API void DN_BinPackF64(DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item)
{
  DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}

DN_API void DN_BinPackF32(DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item)
{
  DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}

#if defined(DN_MATH_H)
DN_API void DN_BinPackV2(DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item)
{
  DN_BinPackF32(pack, mode, &item->x);
  DN_BinPackF32(pack, mode, &item->y);
}

DN_API void DN_BinPackV4(DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item)
{
  DN_BinPackF32(pack, mode, &item->x);
  DN_BinPackF32(pack, mode, &item->y);
  DN_BinPackF32(pack, mode, &item->z);
  DN_BinPackF32(pack, mode, &item->w);
}
#endif

DN_API void DN_BinPackBool(DN_BinPack *pack, DN_BinPackMode mode, bool *item)
{
  DN_BinPackVarInt_(pack, mode, item, sizeof(*item));
}

DN_API void DN_BinPackStr8FromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string)
{
  DN_BinPackVarInt_(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_BinPackStr8FromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string)
{
  DN_BinPackVarInt_(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 DN_Str8 DN_BinPackStr8FromBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max)
{
  DN_BinPackCBuffer(pack, mode, ptr, size, max);
  DN_Str8 result = DN_Str8FromPtr(ptr, *size);
  return result;
}

DN_API void DN_BinPackBytesFromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size)
{
  DN_Str8 string = DN_Str8FromPtr(*ptr, *size);
  DN_BinPackStr8FromArena(pack, arena, mode, &string);
  *ptr  = string.data;
  *size = string.size;
}

DN_API void DN_BinPackBytesFromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size)
{
  DN_Str8 string = DN_Str8FromPtr(*ptr, *size);
  DN_BinPackStr8FromPool(pack, pool, mode, &string);
  *ptr  = string.data;
  *size = string.size;
}

DN_API void DN_BinPackCArray(DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size)
{
  DN_BinPackVarInt_(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 void DN_BinPackCBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max)
{
  if (mode == DN_BinPackMode_Serialise) {
    DN_BinPackUSize(pack, mode, size);
    DN_Str8BuilderAppendBytesCopy(&pack->writer, ptr, *size);
  } else {
    DN_U64 size_u64 = 0;
    DN_BinPackU64(pack, mode, &size_u64);
    DN_Assert(size_u64 < DN_USIZE_MAX);
    DN_Assert(size_u64 <= max);

    *size = DN_Min(size_u64, max);
    DN_Memcpy(ptr, pack->read.data + pack->read_index, *size);
    pack->read_index += size_u64;
  }
}

DN_API DN_Str8 DN_BinPackBuild(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);

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