#if !defined(DN_H)
#define DN_H

#if defined(_CLANGD)
  #define DN_WITH_OS 1
#endif

// NOTE: DN

// Getting Started
//   Copy the entire DN folder to your project and include `dn.h` alongside your headers and include
//   `dn.cpp` in one of your implementation files that has `dn.h` included in its scope.
//   There are #defines to selectively enable/disable features of the library.
//
//   For this example, we enable _every_ feature possible to show all the possibilities upfront.
//   All the #defines can be omitted and a default value will be set (in general everything defaults
//   to off/disabled or fairly conservative). For more information see the configuration section.
/*
       #define DN_WITH_OS 1                                // Enable OS features (e.g.: virtual mem, TLS, file system, threads)
       #define DN_WITH_STR8_AVX512F 1                      // Enable AVX512F accelerated string functions
       #define DN_WITH_NET 0                               // Enable NET interface for HTTP/WS requests functionality. Requires CURL or Emscrpten
       #define DN_WITH_NET_CURL 0                          // Enable NET interface implementation using CURL's       networking APIs (you must link libcurl    to this TU unit)
       #define DN_WITH_NET_EMSCRIPTEN 0                    // Enable NET interface implementation using Emscripten's networking APIs (you must link emscripten to this TU unit)
       #define DN_WITH_TESTS 1                             // Enable the unit tests that can be run with `DN_TestSuite(arena)`
       #define DN_ASAN_POISON 0                            // Poison uncommitted memory in an Arena with Address Sanitizer's poisoning API
       #define DN_SCRUB_UNINIT_MEM_BYTE 0xCD               // Temporary memory freed by an arena is scrubbed with this value to help catch UAFs
       #define DN_ARENA_TEMP_MEM_UAF_GUARD 1               // Enable Arenas to detect UAF when using temporary memory regions
       #define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 0 // Record stack traces when a UAF guard on an arena is created for better UAF crash diagnostics
       #define DN_PARANOIA_LEVEL 1                         // Enables various aforementioned debug functionality automatically, supported values [0..2]
       #include "dn.h"
       #include "dn.cpp"

       int main(int argc, char **argv)
       {
         DN_Core core = {};
         DN_Init(&core, DN_InitFlags_Nil, DN_TcInitArgsDefault());
         return 0;
       }
*/
//   `DN/Standalone` contains utilities that are self-contained and can be used without `dn.h` in
//   a similar manner, typically a single header and single implementation file.

// Configuration

//   OS layer
//     Enable the operating system layer which enables thread context, file system, threads, e.t.c
//     (default: 0)
//
//       #define DN_WITH_OS 1

//   Str8 AVX512F Layer
//     AVX512 string functions can be compiled and made available (default: 0)
//
//       #define DN_WITH_STR8_AVX512F 1

//   Unit Tests
//     Unit tests can be compiled and run using the `DN_TestSuite(...)` function (default: 0)
//
//       #define DN_WITH_TESTS 1

//   Networking layer
//     Enable the networking layer (pre-requisite that the OS layer is enabled) that allows sending
//     HTTP/Websocket requests either using CURL or Emscripten's networking APIs.
//
//       #define DN_WITH_NET            1
//       #define DN_WITH_NET_CURL       1
//       #define DN_WITH_NET_EMSCRIPTEN 1
//
//     For CURL, ensure that you the target application links to a libcurl and that the
//     #include <curl/curl.h> is visible before this translation unit.
//
//     For Emscripten ensure that this translation unit is compiled with `emcc` to have it enabled.

//   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 (default: nil)
//
//       #define DN_API <prefix to put on functions>
//
//     To replace all the functions prefixed with DN_API to be prefixed with 'static' ensuring that
//     the functions in the library do not export an entry into the linking table.
//     translation units.
//
//        #define DN_API static

//   Disabling the in-built <Windows.h> (if #define DN_WITH_OS 1)
//     If you are building DN for the Windows platform, <Windows.h> is a large legacy header that
//     applications have to include to use Windows APIs. By default this 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 mini header will conflict with
//     <Windows.h> if it needs to be included in your project. The mini header can be disabled by
//     defining:
//
//       #define 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
//     TODO: This is not really supported or dealt with properly. Re-review it
//     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.

//   ASAN Arena Poisoning
//     When compiled with address sanitizer (.e.g -fsanitize=address) you can optionally enable
//     memory region poisoning on the inbuilt arena's to catch in certain scenarios, use-after-free
//     (default: ON if PARANOIA level is >=1, OFF otherwise)
//
//       #define DN_ASAN_POISON 1
//
//     Since arenas manage their own block of memory it does not automatically benefit from ASAN's
//     memory markup that ASAN does and so it is implemented manually by using the ASAN user-level
//     poisoning APIs. Similarly, since the arena recycles its own memory rather than release back
//     to the OS, poisoning is not as effective for arenas but every little bit helps.

//   Scrub Uninitialised Memory
//     If this macro is defined, temp memory that is returned to an arena, or allocations freed by
//     a pool are scrubbed to this specified byte, in absence of this bytes returned to the
//     allocators are left as-is or memset to 0. For example to scrub bytes to 0xCD (MSVC's
//     pattern) (default: 0xCD if PARANOIA level is >=1, 0x0 otherwise)
//
//       #define DN_SCRUB_UNINIT_MEM_BYTE 0xCD
//
//     Due to the recycling of memory in arenas and pool, similarly to ASAN poisoning this reduces
//     the window in which a use-after-free can be detected using this guard, however every little
//     bit helps.

//   Arena temp memory use-after-free (UAF) tooling
//     UAF Guard
//       Set the following preprocessor value to 1 to enable UAF protection when using
//       scratch/temporary memory functionality (default: 1 if PARANOIA level >=1, 0 otherwise)
//
//         #define DN_ARENA_TEMP_MEM_UAF_GUARD 1
//
//       This enables arenas to markup itself with the active memory region and subsequent
//       allocations check if the allocation belongs to the same or different region. Different
//       regions cause a UAF violation.
//
//       More detailed diagnostics can be enabled by setting the flag DN_ArenaFlags_TempMemUAFGuard
//       on the affected arenas. Note that this incurs a performance penalty as each memory region
//       will store a stacktrace of its creation. It's recommended in development builds to always
//       run with temp-memory guarding, if a violation occurs, then enable tracing on the arena to
//       pinpoint the issue.
//
//       Enabling memory guard incurs additional memory requirements from the arena's backing
//       memory block and additional book-keeping fields on each arena and their temp memory
//       instances.
//
//     UAF Tracing
//       Set the following preprocessor value to 1 to enable tracing when the UAF guard triggers
//       (default: 1 if PARANOIA level >=2, 0 otherwise)
//
//         #define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 1
//
//       This opts in all arenas to tracing functionality by default globally. Arenas can opt out
//       by setting the flag DN_ArenaFlags_TempMemUAFTraceDisable on the arena. The disable flag
//       takes precedence over all other settings including the global preprocessor macro and the
//       enablement flag (DN_ArenaFlags_TempMemUAFTrace).
//
//       Tracing incurs an additional much heavier performance penalty than the UAF guard due to
//       the stacktrace that is stored per region to report to the user when a UAF guard violation
//       occurs.

//   Paranoia Level
//     Set the `DN_PARANOIA_LEVEL` to an integer value to enable various validation layers and
//     error checking mechanisms in the codebase and primitives exposed by the library
//     (default: PARANOID level 1 in debug builds, 0 otherwise)
//     paranoia level 0 in release builds and level 1 for debug.
//
//         #define DN_PARANOIA_LEVEL 1
//
//     Each level activates the following debug mechanisms. Note that any of the following #defines
//     enabled by a paranoia level can be overridden by defining the preprocessor definition before
//     the inclusion of this file.
//
//       Level 0
//         `DN_Assert` calls are compiled out
//
//         `DN_Verify` calls logs an error and continues
//
//         `DN_VerifyWarning` calls logs a warning and continues
//
//       Level 1
//         `DN_Assert` calls are compiled in
//
//         `DN_Verify` calls a debug trap rather than just logging and continuing
//
//         `DN_Verify` calls dump a stack trace when triggered
//
//         `DN_ASAN_POISON` is set. When an arena allocates memory unallocated bytes from the
//         memory owned by the arena are manually poisoned using ASAN. A fault will be triggered if
//         the memory is written to (UAF e.g. use-after-free). Address sanitizer must be enabled or
//         otherwise this is a no-op. This incurs a performance penalty on-top of the overhead of
//         running ASAN on your binary as recycling memory calls into ASAN to poison the region.
//
//         `DN_ARENA_TEMP_MEM_UAF_GUARD` is set. When an arena uses temporary memory it will record
//         the active temporary memory region and compare them when allocating to ensure that
//         memory is allocated in the active region otherwise a UAF fault is triggered. This has a
//         small runtime performance penalty.
//
//         `DN_SCRUB_UNINIT_MEM_BYTE` is set to `0xCD`. When memory is cleared in an arena or a
//         pool backed by an arena upon deallocation if the `DN_ZMem_Yes` flag is passed then the
//         bytes are scrubbed to this byte to make UAF more salient.
//
//       Level 2
//         `DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT` is set. When an arena uses temporary memory
//         regions that region's a stack trace of the call site is recorded. This is very expensive
//         but when a temporary memory region is used after it has been deallocated, a full stack
//         trace diagnostic is available of where the various regions where created and freed.
//

#if defined(_CLANGD)
  #define DN_WITH_OS 1
  #define DN_WITH_TESTS 1
  #define DN_STR8_AVX512F 1
  #define DN_PARANOIA_LEVEL 1
#endif

// NOTE: Compiler identification
// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER
#if defined(_MSC_VER)
  #if defined(__clang__)
    #define DN_COMPILER_CLANG_CL
    #define DN_COMPILER_CLANG
  #else
    #define DN_COMPILER_MSVC
  #endif
#elif defined(__clang__)
  #define DN_COMPILER_CLANG
#elif defined(__GNUC__)
  #define DN_COMPILER_GCC
#endif

// NOTE: __has_feature
// NOTE: 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
// NOTE: 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_ENABLE(...)  __pragma(warning(default :##__VA_ARGS__))
  #define DN_MSVC_WARNING_POP          __pragma(warning(pop))
#else
  #define DN_MSVC_WARNING_PUSH
  #define DN_MSVC_WARNING_ENABLE(...)
  #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_ARM64) &&      \
    !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 Prefix
#if !defined(DN_API)
  #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: Macros
#define DN_Stringify(x) #x
#define DN_TokenCombine2(x, y) x ## y
#define DN_TokenCombine(x, y) DN_TokenCombine2(x, y)

// NOTE: Error Checking/Validating
// Asserts are useful to verify invariants in the codebase, but there's sometimes the ambiguous
// question of what should be asserted, what happens when we should have triggered an assert
// in a release build (where they are canonically turned off), what alternative mechanisms should we
// use for error checking that should be visible to non-developers.
//
// The following is an excerpt from Tom Forsyth's assertion article which he references Chris
// Hargrove's guidelines on how asserts show be used. It is quite reasonable and we model our
// primitives after based on those concepts:
//
//   Logging, asserts and unit tests (https://tomforsyth1000.github.io/blog.wiki.html
//
//   Assert: Immediately fatal, and not ignorable. Fundamental assumption by an engineer has been
//   disproven and needs immediate handling. Requires discipline on the part of the engineer to not
//   add them in situations that are actually non-fatal (rule of thumb being that if a crash would
//   be almost certain to happen anyway due to the same condition, then you’re no worse off making
//   an assert).
//
//   Errors: Probably fatal soon, but not necessarily immediately. Basically a marker for “you are
//   now in a f*cked state, you might limp along a bit, but assume nothing”. Game continues, but an
//   ugly red number gets displayed onscreen for how many of these have been encountered (so when
//   people send you screenshots of bugs you can then point to the red error count and blame
//   accordingly). Savegames are disabled from this point so as not to make the error effectively
//   permanent; you should also deliberately violate a few other TCRs as soon as an error is
//   encountered in order to ensure that all parties up and down the publisher/developer chain are
//   aware of how bad things are. Errors are technically “ignorable” but everyone knows that it
//   might only buy you a little bit of borrowed time; these are only a small step away from the
//   immediately-blocking nature of an assert, but sometimes that small step can have a big impact
//   on productivity.
//
//   Warnings: Used for “you did something bad, but we caught it so it’s fine (the game state is
//   still okay), however it might not be fine in the future so if you want to save yourself some
//   headache you should fix this sooner rather than later”. Great for content problems. Also
//   displayed onscreen as a yellow number (near the red error number). You can keep these around
//   for a while and triage them when their utility is called into question.
//
//   Crumbs: The meta-category for a large number of “verbose” informational breadcrumb categories
//   that must be explicitly enabled so you don’t clutter everything up and obscure stuff that
//   matters. Note that the occurrance of certain Errors should automatically enable relevant
//   categories of crumbs so that more detailed information about the aforementioned f*cked state
//   will be provided during the limp-along timeframe.
//
// In the excerpt, their domain (games programming) prioritises continuity over immediate failure
// on warning and error as this allows non-developer clientele to continue using the application
// despite error laden states. This is useful in general as not all failures are critical to the
// use case that the end user is dealing with.
//
// We model `Errors` and `Warnings` as `DN_Verify` and `DN_VerifyWarning` respectively. The verify
// variants check the expression to test, log and a message and allow the developer to branch on the
// result and "recover" where appropriate. Verify checks are never compiled out. We have traditional
// `Asserts` as `DN_Assert` which can be compiled out.
//
// The article also defines what it calls a paranoia level. We `#define DN_PARANOIA_LEVEL <Integer>`
// to customise the validation layers of the codebase. See DN_PARANOIA_LEVEL in the customisation
// section for more information.
//
// In summary use each of the primitives in these situation:
//
//   `DN_Assert`: Fatal and immediately needs attention and can be compiled out
//
//   `DN_Verify`: Fatal or eventually fatal but not necessarily immediately, program is or will
//   degenerate into an incorrect state. Is always compiled in and is visible in non-debug
//   environments.
//
//   `DN_VerifyWarning`: Something bad happened, but we caught it and recovered from it. Program
//   state remains consistent. It is always compiled in and is visible in non-debug environments.

#if !defined(DN_PARANOIA_LEVEL)
  #if defined(NDEBUG)
    #define DN_PARANOIA_LEVEL 0
  #else
    #define DN_PARANOIA_LEVEL 1
  #endif
#endif

#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
  #include <sanitizer/asan_interface.h>
#endif

#define DN_ASAN_POISON_ALIGNMENT 8
#if !defined(DN_ASAN_VET_POISON)
  #define DN_ASAN_VET_POISON 0
#endif

#if !defined(DN_ASAN_POISON)
  #if DN_PARANOIA_LEVEL >= 1
    #define DN_ASAN_POISON 1
  #else
    #define DN_ASAN_POISON 0
  #endif
#endif

#if !defined(DN_ASAN_POISON_GUARD_SIZE)
  #define DN_ASAN_POISON_GUARD_SIZE 128
#endif

#if !defined(DN_ARENA_TEMP_MEM_UAF_GUARD)
  #if DN_PARANOIA_LEVEL >= 1
    #define DN_ARENA_TEMP_MEM_UAF_GUARD 1
  #else
    #define DN_ARENA_TEMP_MEM_UAF_GUARD 0
  #endif
#endif

#if !defined(DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT)
  #if DN_PARANOIA_LEVEL >= 2
    #define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 1
  #else
    #define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 0
  #endif
#endif

#if !defined(DN_SCRUB_UNINIT_MEM_BYTE)
  #if DN_PARANOIA_LEVEL >= 1
    #define DN_SCRUB_UNINIT_MEM_BYTE 0xCD
  #else
    #define DN_SCRUB_UNINIT_MEM_BYTE 0x00
  #endif
#endif

#define DN_AssertRaw(expr) do { if (!(expr)) DN_DebugBreak; } while (0)
#define DN_AssertAlwaysCallSiteF(expr, call_site, fmt, ...)                                                                                                   \
  do {                                                                                                                                                        \
    if (!(expr)) {                                                                                                                                            \
      DN_Str8         trace_    = DN_Str8FromStackTraceNowHeap(128 /*limit*/, 3 /*skip*/);                                                                    \
      DN_LogTypeParam log_type_ = DN_LogTypeParamFromType(DN_LogType_Error);                                                                                  \
      DN_LogPrintF(log_type_, call_site, DN_LogFlags_Nil, "Assertion triggered [" #expr "]. " fmt "\nTrace:\n%.*s", ## __VA_ARGS__, DN_Str8PrintFmt(trace_)); \
      DN_DebugBreak;                                                                                                                                          \
    }                                                                                                                                                         \
  } while (0)

#define DN_AssertAlwaysF(expr, fmt, ...)              DN_AssertAlwaysCallSiteF(expr, (DN_CallSiteNow), fmt, ##__VA_ARGS__)
#define DN_AssertAlways(expr)                         DN_AssertAlwaysF(expr, "")
#define DN_AssertInvalidCodePathF(fmt, ...)           DN_AssertAlwaysF(0, fmt, ##__VA_ARGS__)
#define DN_AssertInvalidCodePath                      DN_AssertInvalidCodePathF("Invalid code path")
#if DN_PARANOIA_LEVEL >= 1
#define DN_AssertCallSiteF(expr, call_site, fmt, ...) DN_AssertAlwaysCallSiteF(expr, call_site, fmt, ## __VA_ARGS__)
#define DN_AssertF(expr, fmt, ...)                    DN_AssertCallSiteF(expr, (DN_CallSiteNow), fmt, ## __VA_ARGS__)
#define DN_Assert(expr)                               DN_AssertAlways(expr)
#else
#define DN_AssertCallSiteF(expr, call_site, fmt, ...) (void)(expr); (void)call_site
#define DN_AssertF(expr, fmt, ...)                    (void)(expr)
#define DN_Assert(expr)                               (void)(expr)
#endif
#define DN_VerifyF(expr, fmt, ...)                    DN_VerifyArgsF(DN_VerifyType_Nil, expr, (DN_CallSiteNow), DN_Str8Lit(#expr), fmt, ##__VA_ARGS__)
#define DN_VerifyWarningF(expr, fmt, ...)             DN_VerifyArgsF(DN_VerifyType_Warning, expr, (DN_CallSiteNow), DN_Str8Lit(#expr), fmt, ##__VA_ARGS__)
#define DN_Verify(expr)                               DN_VerifyF(expr, 0)
#define DN_VerifyWarning(expr)                        DN_VerifyWarningF(expr, 0)

#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

#if defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
  #define DN_64_BIT
#else
  #define DN_32_BIT
#endif

#include <stdarg.h> // va_list
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#include <inttypes.h> // PRIu64...
#if !defined(__cplusplus)
  #include <stdbool.h>
#endif

#if !defined(DN_OS_WIN32)
#include <stdlib.h> // exit()
#endif

#define DN_ForIndexU(index, count)               DN_USize index = 0; index < count; index++
#define DN_ForIndexI(index, count)               DN_ISize index = 0; index < count; index++
#define DN_ForItSize(it, T, array, count)        struct { DN_USize index; T *data; } it = {0,           &(array)[0]};         it.index < (count);               it.index++, it.data = (array)         + it.index
#define DN_ForItSizeReverse(it, T, array, count) struct { DN_USize index; T *data; } it = {(count) - 1, &(array)[count - 1]}; it.index < (count);               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)->count;        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
  #if !defined(DN_PowF32)
    #define DN_PowF32(val, exp) powf(val, exp)
  #endif
#endif

// NOTE: Math
#define DN_PiF32 3.14159265359f

#define DN_DegreesToRadsF32(degrees) ((degrees) * (DN_PiF32 / 180.0f))
#define DN_RadsToDegreesF32(radians) ((radians) * (180.f * DN_PiF32))

#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_ArgShift(ptr, size) ((size) ? ((size)--, *(ptr)++) : 0)

#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: Helper macros to declare an array data structure for a given `Type`
#define DN_DArrayStructDecl(Type) \
  struct Type##Array             \
  {                              \
    Type*    data;               \
    DN_USize count;              \
    DN_USize max;                \
  }

#define DN_FixedArrayStructDecl(Type, capacity) \
  struct Type##x##capacity##Array \
  {                               \
    Type     data[capacity];      \
    DN_USize count;               \
    DN_USize max;                 \
  }

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

// 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(DN_64_BIT)
  #define DN_CountLeadingZerosUSize(value) DN_CountLeadingZerosU64(value)
#else
  #define DN_CountLeadingZerosUSize(value) DN_CountLeadingZerosU32(value)
#endif

typedef enum DN_VerifyType {
  DN_VerifyType_Nil,
  DN_VerifyType_Warning,
} DN_VerifyType;

typedef 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
} DN_ZMem;

typedef enum DN_AddType {
  DN_AddType_Prepend,
  DN_AddType_Append,
} DN_AddType;

typedef struct DN_Str8 DN_Str8;
struct DN_Str8
{
  char    *data; // The bytes of the string
  DN_USize count; // The number of bytes in the string
};

typedef struct DN_Str8Slice DN_Str8Slice;
struct DN_Str8Slice
{
  DN_Str8 *data;
  DN_USize count;
};

typedef struct DN_Str8x16 DN_Str8x16;
struct DN_Str8x16   { char data[16];  DN_USize count; };
typedef struct DN_Str8x32 DN_Str8x32;
struct DN_Str8x32   { char data[32];  DN_USize count; };
typedef struct DN_Str8x64 DN_Str8x64;
struct DN_Str8x64   { char data[64];  DN_USize count; };
typedef struct DN_Str8x128 DN_Str8x128;
struct DN_Str8x128  { char data[128]; DN_USize count; };
typedef struct DN_Str8x256 DN_Str8x256;
struct DN_Str8x256  { char data[256]; DN_USize count; };
typedef struct DN_Str8x512 DN_Str8x512;
struct DN_Str8x512  { char data[512]; DN_USize count; };
typedef struct DN_Str8x1024 DN_Str8x1024;
struct DN_Str8x1024 { char data[1024]; DN_USize count; };

typedef struct DN_Str16 DN_Str16;
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 count; // The number of characters in the string
};

typedef struct DN_Str16Slice DN_Str16Slice;
struct DN_Str16Slice
{
  DN_Str16 *data;
  DN_USize count;
};

typedef struct DN_CPURegisters DN_CPURegisters;
struct DN_CPURegisters
{
  int eax;
  int ebx;
  int ecx;
  int edx;
};

union DN_CPUIDResult
{
  DN_CPURegisters reg;
  int             values[4];
};

typedef struct DN_CPUIDArgs DN_CPUIDArgs;
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)

typedef enum DN_CPUFeature {
  #define DN_CPU_FEAT_XENTRY(label) DN_CPUFeature_##label,
  DN_CPU_FEAT_XMACRO
  #undef DN_CPU_FEAT_XENTRY
  DN_CPUFeature_Count,
} DN_CPUFeature;

typedef struct DN_CPUFeatureDecl DN_CPUFeatureDecl;
struct DN_CPUFeatureDecl
{
  DN_CPUFeature value;
  DN_Str8       label;
};

typedef struct DN_CPUFeatureQuery DN_CPUFeatureQuery;
struct DN_CPUFeatureQuery
{
  DN_CPUFeature feature;
  bool          available;
};

typedef struct DN_CPUReport DN_CPUReport;
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];
};

typedef struct DN_TicketMutex DN_TicketMutex;
struct DN_TicketMutex
{
  unsigned int volatile ticket;  // The next ticket to give out to the thread taking the mutex
  unsigned int volatile serving; // The ticket ID to block the mutex on until it is returned
};


typedef struct DN_Hex32 DN_Hex32;
struct DN_Hex32    { char data[32 + 1];  DN_USize count; };
typedef struct DN_Hex64 DN_Hex64;
struct DN_Hex64    { char data[64 + 1];  DN_USize count; };
typedef struct DN_Hex128 DN_Hex128;
struct DN_Hex128   { char data[128 + 1]; DN_USize count; };

typedef struct DN_HexU64 DN_HexU64;
struct DN_HexU64
{
  char  data[(sizeof(DN_U64) * 2) + 1 /*null-terminator*/];
  DN_U8 count;
};

typedef enum DN_HexFromU64Type {
  DN_HexFromU64Type_Nil,
  DN_HexFromU64Type_Uppercase,
} DN_HexFromU64Type;

typedef enum DN_TrimLeadingZero {
  DN_TrimLeadingZero_No,
  DN_TrimLeadingZero_Yes,
} DN_TrimLeadingZero;

typedef struct DN_U8x16 DN_U8x16;
struct DN_U8x16 { DN_U8 data[16]; };
typedef struct DN_U8x32 DN_U8x32;
struct DN_U8x32 { DN_U8 data[32]; };
typedef struct DN_U8x64 DN_U8x64;
struct DN_U8x64 { DN_U8 data[64]; };

DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
union DN_V2USize
{
  struct { DN_USize x,     y; };
  struct { DN_USize w,     h; };
  struct { DN_USize min,   max; };
  struct { DN_USize begin, end; };
  DN_USize data[2];
};

union DN_V2U64
{
  struct { DN_U64 x,     y; };
  struct { DN_U64 w,     h; };
  struct { DN_U64 min,   max; };
  struct { DN_U64 begin, end; };
  DN_U64 data[2];
};
DN_MSVC_WARNING_POP

typedef struct DN_CallSite DN_CallSite;
struct DN_CallSite
{
  DN_Str8 file;
  DN_Str8 function;
  DN_U32  line;
};

#define DN_CallSiteNow DN_Literal(DN_CallSite){DN_Str8Lit(__FILE__), DN_Str8Lit(__func__), __LINE__ }

#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

typedef struct DN_U64FromResult DN_U64FromResult;
struct DN_U64FromResult
{
  bool   success;
  DN_U64 value;
};

typedef struct DN_U32FromResult DN_U32FromResult;
struct DN_U32FromResult
{
  bool   success;
  DN_U32 value;
};

typedef struct DN_USizeFromResult DN_USizeFromResult;
struct DN_USizeFromResult
{
  bool     success;
  DN_USize value;
};

typedef struct DN_I64FromResult DN_I64FromResult;
struct DN_I64FromResult
{
  bool   success;
  DN_I64 value;
};

typedef struct DN_U8x32FromResult DN_U8x32FromResult;
struct DN_U8x32FromResult
{
  bool   success;
  DN_U8x32 value;
};

typedef struct DN_StackTraceFrame DN_StackTraceFrame;
struct DN_StackTraceFrame
{
  DN_U64  address;
  DN_U64  line_number;
  DN_Str8 file_name;
  DN_Str8 function_name;
};

typedef struct DN_StackTraceFrameSlice DN_StackTraceFrameSlice;
struct DN_StackTraceFrameSlice
{
  DN_StackTraceFrame *data;
  DN_USize            count;
};

typedef struct DN_StackTraceRawFrame DN_StackTraceRawFrame;
struct DN_StackTraceRawFrame
{
  void  *process;
  DN_U64 base_addr;
};

typedef struct DN_StackTrace DN_StackTrace;
struct DN_StackTrace
{
  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
};

typedef struct DN_StackTraceIterator DN_StackTraceIterator;
struct DN_StackTraceIterator
{
  DN_StackTraceRawFrame raw_frame;
  DN_U16                index;
};

typedef enum DN_MemCommit {
  DN_MemCommit_No,
  DN_MemCommit_Yes,
} DN_MemCommit;

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

typedef enum DN_HeapType {
  DN_HeapType_Nil,
  DN_HeapType_Basic,
  DN_HeapType_Virtual,
} DN_HeapType;

typedef void *(DN_HeapBasicAllocFunc)  (DN_USize count);
typedef void  (DN_HeapBasicDeallocFunc)(void *ptr);

typedef void *(DN_HeapVirtualReserveFunc)(DN_USize count, DN_MemCommit commit, DN_MemPage page_flags);
typedef bool  (DN_HeapVirtualCommitFunc)(void *ptr, DN_USize count, DN_U32 page_flags);
typedef void  (DN_HeapVirtualReleaseFunc)(void *ptr, DN_USize count);
typedef struct DN_Heap DN_Heap;
struct DN_Heap
{
  DN_HeapType                type;
  DN_USize                   bytes_alloc_total;
  DN_USize                   bytes_alloc;
  DN_USize                   bytes_freed;

  DN_HeapBasicAllocFunc*     basic_alloc;
  DN_HeapBasicDeallocFunc*   basic_dealloc;

  DN_U32                     virtual_page_size;
  DN_HeapVirtualReserveFunc  *virtual_reserve;
  DN_HeapVirtualCommitFunc   *virtual_commit;
  DN_HeapVirtualReleaseFunc  *virtual_release;
};

typedef struct DN_MemBlock DN_MemBlock;
struct DN_MemBlock
{
  DN_MemBlock* prev;
  DN_U64       used;
  DN_U64       commit;
  DN_U64       reserve;
  DN_U64       reserve_sum;
};

typedef struct DN_MemListInfo DN_MemListInfo;
struct DN_MemListInfo
{
  DN_U64 used;
  DN_U64 commit;
  DN_U64 reserve;
  DN_U64 blocks;
};

typedef struct DN_MemStats DN_MemStats;
struct DN_MemStats
{
  DN_MemListInfo info;
  DN_MemListInfo hwm;
};

typedef DN_U32 DN_MemFlags;
enum DN_MemFlags_
{
  DN_MemFlags_Nil             = 0,
  DN_MemFlags_NoGrow          = 1 << 0,
  DN_MemFlags_NoPoison        = 1 << 1,
  DN_MemFlags_NoAllocTrack    = 1 << 2,
  DN_MemFlags_AllocCanLeak    = 1 << 3,
  DN_MemFlags_SimAlloc        = 1 << 4,

  // NOTE: Records stack traces of temp memory regions on construction to provide more diagnostics
  // when UAF violation occurs in the use of a region (e.g. nested regions A and B, with A
  // allocating whilst B is active would result in A's memory being wiped at the end of B). Tracing
  // has a heavy performance penalty as each scratch/temp memory region triggers and stores the
  // stack trace.
  //
  // Ignored if UAF guard is disabled at the preprocessor level
  // (e.g.: #define DN_ARENA_TEMP_MEM_UAF_GUARD 0)
  DN_MemFlags_TempMemUAFTrace        = 1 << 5,

  // NOTE: Forcibly disables TempMemUAFTrace for the arena irrespective of global settings. Globally
  // UAF tracing can be enabled across all arenas via the preprocessor which turns the tracing
  // feature (e.g.: #define DN_ARENA_TEMP_MEM_UAF_TRACE_ON_BY_DEFAULT 1) into an opt-out situation
  // where arenas have to specify this flag, specifically to not be traced.
  //
  // If both TempMemUAFTrace, TempMemUAFTraceDisable and or the global preprocessor flag is set
  // disabling takes precedence, always if it is set.
  DN_MemFlags_TempMemUAFTraceDisable = 1 << 6,

  // NOTE: Internal flags. Do not use
  DN_MemFlags_UserBuffer = 1 << 7,
  DN_MemFlags_Heap       = 1 << 8,
};

typedef struct DN_MemList DN_MemList;
struct DN_MemList
{
  DN_MemBlock* curr;
  DN_MemFlags  flags;
  DN_Heap      heap;
  DN_MemStats  stats;
  DN_Str8      label;

  #if DN_ARENA_TEMP_MEM_UAF_GUARD
  DN_U32                 uaf_guard_next_id;
  DN_U32                 uaf_guard_active_id;
  struct DN_MemListTemp* uaf_guard_active_temp_mem;
  #endif
};

typedef struct DN_MemListTemp DN_MemListTemp;
struct DN_MemListTemp
{
  DN_MemList*   mem;
  DN_U64        used_sum;
  #if DN_ARENA_TEMP_MEM_UAF_GUARD
  DN_StackTrace trace;
  #endif
};

typedef enum DN_AllocatorType {
  DN_AllocatorType_MemList,
  DN_AllocatorType_Arena,
  DN_AllocatorType_Pool,
} DN_AllocatorType;

typedef struct DN_Allocator DN_Allocator;
struct DN_Allocator
{
  DN_AllocatorType type;
  void*            context;
};

typedef enum DN_ArenaReset {
  DN_ArenaReset_No,
  DN_ArenaReset_Yes,
} DN_ArenaReset;

typedef DN_U32 DN_ArenaFlags;
enum DN_ArenaFlags_
{
  DN_ArenaFlags_Nil = 0,
  DN_ArenaFlags_OwnsMemList = 1 << 0,
};

typedef struct DN_Arena DN_Arena;
struct DN_Arena
{
  DN_ArenaFlags   flags;
  DN_MemList*     mem;
  #if DN_ARENA_TEMP_MEM_UAF_GUARD
  DN_U32          uaf_guard_id;
  DN_MemListTemp* uaf_guard_temp_mem;
  DN_U32          uaf_guard_prev_id;
  DN_MemListTemp* uaf_guard_prev_temp_mem;
  bool            uaf_guard_is_being_checked;
  #else
  DN_MemListTemp  temp_mem;
  #endif
};

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

typedef struct DN_PoolSlot DN_PoolSlot;
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,
};

typedef struct DN_Pool DN_Pool;
struct DN_Pool
{
  DN_Arena    *arena;
  DN_PoolSlot *slots[DN_PoolSlotSize_Count];
  DN_U8        align;
};

typedef struct DN_Utf32FromResult DN_Utf32FromResult;
struct DN_Utf32FromResult
{
  bool     success;
  DN_Str8  remaining;
  DN_U32   codepoint;
  DN_USize byte_count; // Number of bytes from the stream that was decoded
};

typedef struct DN_Utf32FromIterator DN_Utf32FromUtf8Iterator;
struct DN_Utf32FromIterator
{
  bool     init;
  bool     success;
  DN_Str8  remaining;
  DN_USize bytes_decoded;
  DN_USize codepoint_index;
  DN_U32   codepoint;
};

enum DN_Utf32IterateDir
{
  DN_Utf32IterateDir_Forwards,
  DN_Utf32IterateDir_Reverse,
};

typedef DN_U32 DN_CodepointCountFlags;
enum DN_CodepointCountFlags_
{
  DN_CodepointCountFlags_Nil          = 0,
  DN_CodepointCountFlags_SkipAnsiCode = 1 << 0,
};

typedef struct DN_NibbleFromU8Result DN_NibbleFromU8Result;
struct DN_NibbleFromU8Result
{
  char nibble0;
  char nibble1;
};

enum DN_Str8EqCase
{
  DN_Str8EqCase_Sensitive,
  DN_Str8EqCase_Insensitive,
};

typedef DN_U32 DN_Str8IsFlags;
enum DN_Str8IsFlags_
{
  DN_Str8IsFlags_Digits    = 1 << 0,
  DN_Str8IsFlags_Hex       = 1 << 1, // String consists of [0x|0X] + <[digit|A-F|a-f]...>
  DN_Str8IsFlags_Lowercase = 1 << 2, // String is all lowercase characters (case-ness is only checked for ASCII, non-ASCII is permitted always)
  DN_Str8IsFlags_Uppercase = 1 << 3, // String is all uppercase characters (case-ness is only checked for ASCII, non-ASCII is permitted always)
};

typedef struct DN_Str8BSplitResult DN_Str8BSplitResult;
struct DN_Str8BSplitResult
{
  // If there are multiple strings passed to split against, this is the index into that array of
  // which the string was split on. If no array was passed this is always 0.
  DN_USize input_index;
  DN_Str8  lhs;
  DN_Str8  rhs;
};

typedef struct DN_Str8FindResult DN_Str8FindResult;
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
};

typedef DN_USize DN_Str8FindFlag;
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,
};

typedef DN_USize DN_Str8SplitFlags;
enum DN_Str8SplitFlags_
{
  DN_Str8SplitFlags_Nil                 = 0,
  DN_Str8SplitFlags_ExcludeEmptyStrings = 1 << 0,
  DN_Str8SplitFlags_HandleQuotedStrings = 1 << 1,
};

typedef struct DN_Str8TruncResult DN_Str8TruncResult;
struct DN_Str8TruncResult
{
  bool     truncated;
  DN_Str8  str8;
  DN_USize count_req; // Not including null-terminator
};

typedef struct DN_Str8SplitResult DN_Str8SplitResult;
struct DN_Str8SplitResult
{
  DN_Str8 *data;
  DN_USize count;
};

enum DN_Str8LineBreakMode
{
  DN_Str8LineBreakMode_AtWord,  // Add delimiter to string at ' ' and '\n' boundaries
  DN_Str8LineBreakMode_AtWidth, // Add delimiter to string at width intervals
};

typedef DN_USize DN_Str8TableFlags;
enum DN_Str8TableFlags_
{
  DN_Str8TableFlags_None      = 0,
  DN_Str8TableFlags_HasHeader = 1 << 0,
  DN_Str8TableFlags_RowLines  = 1 << 1,
};

typedef struct DN_Str8Link DN_Str8Link;
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
};

typedef struct DN_Str8Builder DN_Str8Builder;
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
};

#if !defined(DN_OsPathSeperator)
  #if defined(DN_PLATFORM_WIN32)
    #define DN_OsPathSeperator "\\"
  #else
    #define DN_OsPathSeperator "/"
  #endif
  #define DN_OsPathSeperatorStr8 DN_Str8Lit(DN_OsPathSeperator)
#endif

typedef struct DN_Path DN_Path;
struct DN_Path
{
  bool         has_prefix_path_seperator;
  DN_Str8Link *head;
  DN_Str8Link *tail;
  DN_USize     string_size;
  DN_U16       links_size;
};

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

typedef enum DN_ByteType {
  DN_ByteType_B,
  DN_ByteType_KiB,
  DN_ByteType_MiB,
  DN_ByteType_GiB,
  DN_ByteType_TiB,
  DN_ByteType_Count,
  DN_ByteType_Auto,
} DN_ByteType;

typedef struct DN_ByteCount DN_ByteCount;
struct DN_ByteCount
{
  DN_ByteType type;
  DN_Str8     suffix; // "KiB", "MiB", "GiB" .. e.t.c
  DN_F64      bytes;
};

typedef struct DN_Date DN_Date;
struct DN_Date
{
  DN_U8  day;
  DN_U8  month;
  DN_U16 year;
  DN_U8  hour;
  DN_U8  minutes;
  DN_U8  seconds;
  DN_U16 milliseconds;
};

typedef struct DN_FmtAppendResult DN_FmtAppendResult;
struct DN_FmtAppendResult
{
  DN_USize size_req;
  DN_Str8  str8;
  bool     truncated;
};

typedef struct DN_ProfilerAnchor DN_ProfilerAnchor;
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;
};

typedef struct DN_ProfilerZone DN_ProfilerZone;
struct DN_ProfilerZone
{
  struct DN_Profiler *profiler;
  DN_U16 anchor_index;
  DN_U64 begin_tsc;
  DN_U16 parent_zone;
  DN_U64 elapsed_tsc_at_zone_start;
};

typedef struct DN_ProfilerAnchorArray DN_ProfilerAnchorArray;
struct DN_ProfilerAnchorArray
{
  DN_ProfilerAnchor *data;
  DN_USize           count;
};

typedef DN_U64 (DN_ProfilerTscNowFunc)();
typedef struct DN_Profiler DN_Profiler;
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;
};

typedef bool (DN_QSortCompareFunc)   (void const *lhs, void const *rhs, void *user_context);
typedef bool (DN_BSearchLessThanFunc)(void const *lhs, void const *rhs, void *user_context);
enum DN_BSearchType
{
  // NOTE: 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_BSearchType_Match,

  // NOTE: 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_BSearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BSearchType_LowerBound);
  //   printf("%zu\n", result.index); // Prints index '4'
  DN_BSearchType_LowerBound,

  // NOTE: 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_BSearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BSearchType_UpperBound);
  //   printf("%zu\n", result.index); // Prints index '5'
  DN_BSearchType_UpperBound,
};

typedef struct DN_BSearchResult DN_BSearchResult;
struct DN_BSearchResult
{
  bool     found;
  DN_USize index;
};

typedef enum DN_ErrSinkMode {
  DN_ErrSinkMode_Nil,                  // Default behaviour to accumulate errors into the sink
  DN_ErrSinkMode_DebugBreakOnErrorLog, // Debug break (int3) when error is encountered and the sink is ended by the 'end and log' functions.
  DN_ErrSinkMode_ExitOnError,          // When an error is encountered, exit the program with the error code of the error that was caught.
} DN_ErrSinkMode;

typedef struct DN_ErrSinkMsg DN_ErrSinkMsg;
struct DN_ErrSinkMsg
{
  DN_I32         error_code;
  DN_Str8        msg;
  DN_CallSite    call_site;
  DN_ErrSinkMsg *next;
  DN_ErrSinkMsg *prev;
};

typedef struct DN_ErrSinkNode DN_ErrSinkNode;
struct DN_ErrSinkNode
{
  DN_CallSite    call_site;    // Call site that the node was created
  DN_ErrSinkMode mode;         // Controls how the sink behaves when an error is registered onto the sink.
  DN_ErrSinkMsg *msg_sentinel; // List of error messages accumulated for the current scope
  DN_U64         arena_pos;    // Position to reset the arena when the scope is ended
};

typedef struct DN_ErrSink DN_ErrSink;
struct DN_ErrSink
{
  DN_Arena*      arena;      // Dedicated allocator from the thread's local storage
  DN_ErrSinkNode stack[128]; // Each entry contains errors accumulated between a [begin, end] region of the active sink.
  DN_USize       stack_size;
};

typedef struct DN_TcScratch DN_TcScratch;
struct DN_TcScratch
{
  DN_Arena arena;
  DN_B32   destructed;
};

#if defined(__cplusplus)
typedef struct DN_TcScratchCpp DN_TcScratchCpp;
struct DN_TcScratchCpp
{
  DN_TcScratchCpp(DN_Arena **conflicts, DN_USize count);
  ~DN_TcScratchCpp();
  DN_TcScratch data;
};
#endif

typedef struct DN_TcInitArgs DN_TcInitArgs;
struct DN_TcInitArgs
{
  DN_U64 main_reserve;
  DN_U64 main_commit;
  DN_U64 temp_reserve;
  DN_U64 temp_commit;
  DN_U64 temp_count;
  DN_U64 err_sink_reserve;
  DN_U64 err_sink_commit;
};

typedef struct DN_TcCore DN_TcCore;
struct DN_TcCore // (T)hread (C)ontext sitting in thread-local storage
{
  DN_Str8x64  name;
  DN_U64      thread_id;
  DN_CallSite call_site;
  char        lane_opaque[sizeof(DN_U64) * 4];
  void*       user_context;

  DN_Arena*   main_arena;
  DN_Pool     main_pool;
  DN_Arena*   temp_arenas[4];
  DN_USize    temp_arenas_count;

  DN_ErrSink  err_sink;

  DN_Arena*   frame_arena;
};

typedef enum DN_TcDeinitArenas {
  DN_TcDeinitArenas_No,
  DN_TcDeinitArenas_Yes,
} DN_TcDeinitArenas;

typedef struct DN_Pcg32 DN_Pcg32;
struct DN_Pcg32       { DN_U64 state; };

typedef struct DN_Murmur3 DN_Murmur3;
struct DN_Murmur3 { DN_U64 e[2]; };

typedef enum DN_LogType {
  DN_LogType_Debug,
  DN_LogType_Info,
  DN_LogType_Warning,
  DN_LogType_Error,
  DN_LogType_Count,
} DN_LogType;

typedef enum DN_LogBold {
  DN_LogBold_No,
  DN_LogBold_Yes,
} DN_LogBold;

typedef struct DN_LogStyle DN_LogStyle;
struct DN_LogStyle
{
  DN_LogBold bold;
  bool       colour;
  DN_U8      r, g, b;
};

typedef struct DN_LogTypeParam DN_LogTypeParam;
struct DN_LogTypeParam
{
  bool    is_u32_enum;
  DN_U32  u32;
  DN_Str8 str8;
};

typedef enum DN_AnsiColourMode {
  DN_AnsiColourMode_Fg,
  DN_AnsiColourMode_Bg,
} DN_AnsiColourMode;

typedef struct DN_LogDate DN_LogDate;
struct DN_LogDate
{
  DN_U16 year;
  DN_U8  month;
  DN_U8  day;

  DN_U8 hour;
  DN_U8 minute;
  DN_U8 second;
};

typedef struct DN_LogPrefixSize DN_LogPrefixSize;
struct DN_LogPrefixSize
{
  DN_USize count;
  DN_USize padding;
};

typedef DN_U32 DN_LogFlags;
enum DN_LogFlags_
{
  DN_LogFlags_Nil       = 0,
  DN_LogFlags_NoNewLine = 1 << 0,
  DN_LogFlags_NoPrefix  = 1 << 1,
};

typedef void DN_LogPrintFunc(DN_LogTypeParam type, void *user_data, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, va_list args);

DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
typedef union DN_V2I32 DN_V2I32;
union DN_V2I32
{
  struct { DN_I32 x, y; };
  struct { DN_I32 w, h; };
  DN_I32 data[2];
};

typedef union DN_V2U16 DN_V2U16;
union DN_V2U16
{
  struct { DN_U16 x, y; };
  struct { DN_U16 w, h; };
  DN_U16 data[2];
};

typedef union DN_V2U32 DN_V2U32;
union DN_V2U32
{
  struct { DN_U32 x,   y; };
  struct { DN_U32 w,   h; };
  struct { DN_U32 min, max; };
  DN_U32 data[2];
};

typedef union DN_V2F32 DN_V2F32;
union DN_V2F32
{
  struct { DN_F32 x, y; };
  struct { DN_F32 w, h; };
  DN_F32 data[2];
};

typedef struct DN_2V2F32 DN_2V2F32;
struct DN_2V2F32
{
  DN_V2F32 min;
  DN_V2F32 max;
};

typedef struct DN_V2F32Array DN_V2F32Array;
struct DN_V2F32Array
{
  DN_V2F32 *data;
  DN_USize  count;
  DN_USize  max;
};

typedef union DN_V3F32 DN_V3F32;
union DN_V3F32
{
  struct { DN_F32 x, y, z; };
  struct { DN_F32 r, g, b; };
  DN_V2F32  xy;
  DN_F32    data[3];
};

typedef union DN_V4F32 DN_V4F32;
union DN_V4F32
{
  struct { DN_F32 x, y, z, w; };
  struct { DN_F32 r, g, b, a; };
  DN_V3F32  rgb;
  DN_V3F32  xyz;
  DN_F32    data[4];
};

typedef struct DN_V4F32Array DN_V4F32Array;
struct DN_V4F32Array
{
  DN_V4F32* data;
  DN_USize  count;
  DN_USize  max;
};
DN_MSVC_WARNING_POP

typedef struct DN_M4 DN_M4;
struct DN_M4
{
  DN_F32 columns[4][4]; // Column major matrix
};

typedef union DN_M2x3 DN_M2x3;
union DN_M2x3
{
  DN_F32 e[6];
  DN_F32 row[2][3];
};

typedef struct DN_M2x3XForm DN_M2x3XForm;
struct DN_M2x3XForm
{
  DN_M2x3 forward;
  DN_M2x3 inverse;
};

typedef enum DN_M2x3ProjOrigin {
  DN_M2x3ProjOrigin_TopLeft,
  DN_M2x3ProjOrigin_Center,
} DN_M2x3ProjOrigin;

typedef struct DN_Rect DN_Rect;
struct DN_Rect
{
  DN_V2F32 pos, size;
};

typedef enum DN_RectCutClip {
  DN_RectCutClip_No,
  DN_RectCutClip_Yes,
} DN_RectCutClip;

typedef enum DN_RectCutSide {
  DN_RectCutSide_Left,
  DN_RectCutSide_Right,
  DN_RectCutSide_Top,
  DN_RectCutSide_Bottom,
} DN_RectCutSide;

typedef struct DN_RectCut DN_RectCut;
struct DN_RectCut
{
  DN_Rect*       rect;
  DN_RectCutSide side;
};

typedef struct DN_RaycastV2 DN_RaycastV2;
struct DN_RaycastV2
{
  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)`
};

typedef struct DN_Ring DN_Ring;
struct DN_Ring
{
  DN_U64 size;
  char  *base;
  DN_U64 write_pos;
  DN_U64 read_pos;
};

typedef enum DN_ArrayErase {
  DN_ArrayErase_Unstable,
  DN_ArrayErase_Stable,
} DN_ArrayErase;

typedef struct DN_ArrayEraseResult DN_ArrayEraseResult;
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.count; 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
};

typedef struct DN_ArrayFindResult DN_ArrayFindResult;
struct DN_ArrayFindResult
{
  bool     success;
  DN_USize index;
  void    *value;
};
typedef bool (DN_ArrayFindEqFunc)(void const *lhs, void const *find);

typedef enum DN_HTableDeallocPoolOnDeinit {
  DN_HTableDeallocPoolOnDeinit_No,
  DN_HTableDeallocPoolOnDeinit_Yes,
} DN_HTableDeallocPoolOnDeinit;

typedef enum DN_HTableHashSentinel {
  DN_HTableHashSentinel_Empty = 0,
  DN_HTableHashSentinel_Tomb,
  DN_HTableHashSentinel_FirstValid,
} DN_HTableHashSentinel;

typedef DN_U32 DN_HTableFlags;
enum DN_HTableFlags_
{
  DN_HTableFlags_DeallocPoolOnDeinit = 1 << 0,
  DN_HTableFlags_UsingHeap           = 1 << 1,
};

typedef DN_U32 DN_HTableHashType; // NOTE: Alias for the hash table's `hash` (4 unsigned bytes)
typedef DN_U32(DN_HTableHashFunc) (void const *key, DN_USize size);
typedef bool  (DN_HTableKeyEqFunc)(void const *lhs, void const *rhs, DN_USize size);

typedef struct DN_HTable DN_HTable;
struct DN_HTable
{
  // NOTE: The total number of slots used by the table is defined as `count` + `tombs_count`
  DN_USize            count;        // Count of `kvs` that are filled with active values (e.g. hash >= `FirstValid`)
  DN_USize            tombs_count;  // Count of `kvs` that are tombstoned (e.g. hash == `Tomb`)
  DN_USize            max;          // Max capacity of `kvs`
  DN_F32              load_factor01;
  DN_HTableHashFunc*  hash_func;
  DN_HTableKeyEqFunc* key_eq_func;

  DN_HTableFlags flags;
  DN_Heap        heap;
  DN_Pool        pool;

  // NOTE: Metadata for handling genericism (in C that means type-erasure)
  void**   kvs;
  DN_USize offset_of_hash;
  DN_USize size_of_kv;
  DN_USize offset_of_key;
  DN_USize size_of_key;
  DN_USize offset_of_value;
  DN_USize size_of_value;
};

typedef struct DN_HTableInitArgs DN_HTableInitArgs;
struct DN_HTableInitArgs
{
  DN_F32              load_factor;
  DN_HTableHashFunc*  hash_func;
  DN_HTableKeyEqFunc* key_eq_func;
  DN_USize            max;
  void**              kvs;
  DN_USize            offset_of_hash;
  DN_USize            size_of_kv;
  DN_USize            offset_of_key;
  DN_USize            size_of_key;
  DN_USize            offset_of_value;
  DN_USize            size_of_value;
};

typedef struct DN_HTableSlot DN_HTableSlot;
struct DN_HTableSlot
{
  // NOTE: Type-erased lookup of the {hash, key, value} object in the hash table
  DN_HTableHashType hash_u32;  // Hash copied out to a U32 for convenience
  DN_USize          kvs_index; // Index into `kvs` that the following {hash, key, value} pointers are pointing to
  void*             kv_struct; // Pointer to the matched kv struct
  void*             hash;      // Pointer to the hash embedded in the user's `kvs` struct
  void*             key;       // Pointer to the key embedded in the user's `kvs` struct
  void*             value;     // Pointer to the value embedded in the user's `kvs` struct
};

typedef struct DN_HTableAddResult DN_HTableAddResult;
struct DN_HTableAddResult
{
  bool          success; // Adding fails if adding required a table resize and resizing failed (out-of-memory)
  bool          existed; // Set if the item you attempted to add, already existed in the table and it was updated instead
  DN_HTableSlot slot;    // Table slot containing the value you just added, zero initialised struct on failure
};

typedef struct DN_HTableLookupResult DN_HTableLookupResult;
struct DN_HTableLookupResult
{
  DN_HTableHashType key_hash; // hash(key) that was used to look up `slot`
  DN_HTableSlot     slot;     // KV object from `kvs`, always set (either empty, tombed, or active slot)
};

typedef struct DN_HTableResizeResult DN_HTablePrepareResult;
struct DN_HTableResizeResult
{
  bool success;
  bool needed_resize;
};

typedef struct DN_HTableInitResult DN_HTableInitResult;
struct DN_HTableInitResult
{
  DN_HTable table;
  bool      success; // Fails if the allocator could not allocate memory for the table
};

enum DN_HTableAllowTombstone
{
  DN_HTableAllowTombstone_No,
  DN_HTableAllowTombstone_Yes,
};

enum DN_LeakAllocFlag
{
  DN_LeakAllocFlag_Freed         = 1 << 0,
  DN_LeakAllocFlag_LeakPermitted = 1 << 1,
};

typedef struct DN_LeakAlloc DN_LeakAlloc;
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

typedef struct DN_LeakTrackerKV DN_LeakTrackerKV;
struct DN_LeakTrackerKV
{
  DN_HTableHashType hash;
  DN_UPtr           key;
  DN_LeakAlloc      value;
};

typedef struct DN_LeakTracker DN_LeakTracker;
struct DN_LeakTracker
{
  DN_LeakTrackerKV *alloc_table_kvs;
  DN_HTable         alloc_table;
  DN_TicketMutex    alloc_table_mutex;
  DN_U64            alloc_table_bytes_allocated_for_stack_traces;
};

typedef DN_USize DN_InitFlags;
enum DN_InitFlags_
{
  DN_InitFlags_Nil            = 0,

  // NOTE: Query the OS for information and enable functionality that requires the OS (like virtual
  // memory APIs, mutexes, secure RNG) as well as per-thread persistent and scratch allocators.
  DN_InitFlags_OS             = (1 << 0),
  DN_InitFlags_LeakTracker    = (1 << 1) | DN_InitFlags_OS,
  DN_InitFlags_LogLibFeatures = (1 << 2),
  DN_InitFlags_LogCPUFeatures = (1 << 3) | DN_InitFlags_OS,
  DN_InitFlags_LogAllFeatures = DN_InitFlags_LogLibFeatures | DN_InitFlags_LogCPUFeatures,
};

typedef enum DN_BinPackMode {
  DN_BinPackMode_Serialise,
  DN_BinPackMode_Deserialise,
} DN_BinPackMode;

typedef struct DN_BinPack DN_BinPack;
struct DN_BinPack
{
  DN_Str8Builder writer;
  DN_Str8        read;
  DN_USize       read_index;
};

typedef enum DN_CSVSerialise {
  DN_CSVSerialise_Read,
  DN_CSVSerialise_Write,
} DN_CSVSerialise;

typedef struct DN_CSVTokeniser DN_CSVTokeniser;
struct DN_CSVTokeniser
{
  bool        bad;
  DN_Str8     string;
  char        delimiter;
  char const *it;
  bool        end_of_line;
};

typedef struct DN_CSVPack DN_CSVPack;
struct DN_CSVPack
{
  DN_Str8Builder  write_builder;
  DN_USize        write_column;
  DN_CSVTokeniser read_tokeniser;
};

typedef struct DN_TestDiagnosticRow DN_TestDiagnosticRow;
struct DN_TestDiagnosticRow
{
  DN_Str8     expr;
  DN_Str8     invariant;
  DN_CallSite call_site;
  DN_Str8     message;
};

typedef struct DN_TestEntry DN_TestEntry;
struct DN_TestEntry
{
  bool                  failed;
  DN_Str8               name;
  DN_TestDiagnosticRow* diagnostics;
  DN_USize              diagnostics_count;
  DN_USize              diagnostics_max;
  DN_U64                ts_begin;
  DN_U64                ts_end;
};

typedef struct DN_TestGroup DN_TestGroup;
struct DN_TestGroup
{
  DN_Str8        name;
  DN_TestEntry*  entries;       // Array of tests cases that have been executed/are being executed
  DN_USize       entries_count;
  DN_USize       entries_max;
  DN_U64         ts_begin;      // Timestamp of when the test started (using DN_OS_PerfCounter)
  DN_U64         ts_end;        // Timestamp of when the test ended
  DN_TestEntry*  curr_entry;    // Active test being executed and results should be updated into
};

typedef struct DN_TestCore DN_TestCore;
struct DN_TestCore
{
  DN_Arena*      arena;
  DN_Pool        pool;
  DN_USize       total_passed; // Total number of tests (`TestEntry`) across all groups that passed
  DN_USize       total_failed; // Total number of tests (`TestEntry`) across all groups that failed
  DN_USize       total_count;  // Total number of tests (`TestEntry`) across all groups

  DN_TestGroup*  groups;
  DN_USize       groups_count;
  DN_USize       groups_max;

  DN_TestGroup*  curr_group;   // Active test group being executed and test entries are being added to
};

typedef enum DN_TestLogic {
  DN_TestLogic_GreaterThan,
  DN_TestLogic_GreaterThanEq,
  DN_TestLogic_LessThan,
  DN_TestLogic_LessThanEq,
  DN_TestLogic_Eq,
  DN_TestLogic_NotEq,
} DN_TestLogic;

typedef DN_U32 DN_Str8FromTestCoreFlags;
enum DN_Str8FromTestCoreFlags_
{
  DN_Str8FromTestCoreFlags_Nil,
  DN_Str8FromTestCoreFlags_Colour,
};

#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)
// NOTE: This depends on DN_Str8 because we've customised the library
#include "External/stb_sprintf.h"
DN_GCC_WARNING_POP
DN_MSVC_WARNING_POP

#if DN_WITH_OS
#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

extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];

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

typedef DN_U32 DN_OSFileRotateFlags;
enum DN_OSFileRotateFlags_
{
  DN_OSFileRotateFlags_Nil          = 0,

  // NOTE: All files are rotated as usual but when it's time to rotate the base file to index 1,
  // this flags causes the base file to be copied to index 1 instead of being moved. This may be
  // useful if you are looking to make rotating backups rather than rotating files.
  DN_OSFileRotateFlags_KeepBaseFile = 1 << 0,
};

enum DN_OSPathInfoType
{
  DN_OSPathInfoType_Unknown,
  DN_OSPathInfoType_Directory,
  DN_OSPathInfoType_File,
};

typedef struct DN_OSPathInfo DN_OSPathInfo;
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;
};

typedef struct DN_OSDirIterator DN_OSDirIterator;
struct DN_OSDirIterator
{
  void   *handle;
  DN_Str8 file_name;
  char    buffer[512];
};

// NOTE: R/W Stream API
typedef struct DN_OSFileRead DN_OSFileRead;
struct DN_OSFileRead
{
  bool     success;
  DN_USize bytes_read;
};

typedef struct DN_OSFile DN_OSFile;
struct DN_OSFile
{
  bool  error;
  void *handle;
};

typedef 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
} DN_OSFileOpen;

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

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

typedef struct DN_OSExecAsyncHandle DN_OSExecAsyncHandle;
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;
};

typedef struct DN_OSExecResult DN_OSExecResult;
struct DN_OSExecResult
{
  bool    finished;
  DN_Str8 stdout_text;
  DN_Str8 stderr_text;
  DN_U32  os_error_code;
  DN_U32  exit_code;
};

typedef struct DN_OSExecArgs DN_OSExecArgs;
struct DN_OSExecArgs
{
  DN_OSExecFlags flags;
  DN_Str8        working_dir;
  DN_Str8Slice   environment;
};

// NOTE: DN_OSSemaphore
DN_U32 const DN_OS_SEMAPHORE_INFINITE_TIMEOUT = UINT32_MAX;

typedef struct DN_OSSemaphore DN_OSSemaphore;
struct DN_OSSemaphore
{
  DN_U64 handle;
};

typedef struct DN_OSBarrier DN_OSBarrier;
struct DN_OSBarrier
{
  DN_U64 handle;
};

typedef enum DN_OSSemaphoreWaitResult {
  DN_OSSemaphoreWaitResult_Failed,
  DN_OSSemaphoreWaitResult_Success,
  DN_OSSemaphoreWaitResult_Timeout,
} DN_OSSemaphoreWaitResult;

typedef struct DN_OSMutex DN_OSMutex;
struct DN_OSMutex
{
  DN_U64 handle;
};

typedef struct DN_OSConditionVariable DN_OSConditionVariable;
struct DN_OSConditionVariable
{
  DN_U64 handle;
};

struct DN_OSWindow
{
  DN_UPtr handle;
};

enum DN_OSWindowShow
{
  DN_OSWindowShow_Nil,
  DN_OSWindowShow_Restore,
  DN_OSWindowShow_Minimise,
  DN_OSWindowShow_Maximise,
};

enum DN_OSWindowMaximise
{
  DN_OSWindowMaximise_No,
  DN_OSWindowMaximise_Yes,
};

enum DN_OSWindowMinimise
{
  DN_OSWindowMinimise_No,
  DN_OSWindowMinimise_Yes,
};

typedef DN_I32(DN_OSThreadFunc)(struct DN_OSThread *);

typedef DN_U32 DN_OSThreadFlags;
enum DN_OSThreadFlags_
{
  DN_OSThreadFlags_Nil      = 0,
  DN_OSThreadFlags_Detached = 1 << 0,
};

typedef struct DN_OSThreadInitArgs DN_OSThreadInitArgs;
struct DN_OSThreadInitArgs
{
  // NOTE: Customise much memory the thread's TLS context will be initialised with which contains
  // persistent/temporary allocators and misc facilities that each thread has exclusive access to.
   DN_TcInitArgs tc_args;

   // NOTE: Set how much stack space in bytes the thread will have. Leave this value to 0 to defer
   // to the OS's default stack size.
   DN_USize         stack_size;
   DN_OSThreadFlags flags;
};

typedef struct DN_OSThreadLane DN_OSThreadLane;
struct DN_OSThreadLane
{
  DN_USize     index;
  DN_USize     count;
  DN_OSBarrier barrier;
  void*        shared_mem;
};

typedef struct DN_OSThreadLaneway DN_OSThreadLaneway;
struct DN_OSThreadLaneway
{
  DN_OSThread* threads;
  DN_USize     threads_count;
  DN_UPtr*     shared_mem;
  DN_OSBarrier barrier;
};

typedef struct DN_OSThread DN_OSThread;
struct DN_OSThread
{
  DN_OSThreadFlags flags;
  DN_Str8x64       name;
  DN_TcCore        context;
  DN_OSThreadLane  lane;
  bool             is_lane_set;
  void            *handle;
  DN_U64           thread_id;
  void            *user_context;
  DN_OSThreadFunc *func;
  DN_OSSemaphore   thread_exec_wait_for_thread_id_sem;
  DN_OSSemaphore   caller_wait_for_thread_init_to_finish_sem;
#if !defined(DN_PLATFORM_WIN32)
  DN_OSSemaphore   join_done_sem;
#endif
  DN_TcInitArgs    tc_init_args;
};

typedef DN_U32 DN_OSLogFlags;
enum DN_OSLoggerFlags_
{
  DN_OSLoggerFlags_Nil       = 0,
  DN_OSLoggerFlags_File      = 1 << 0, // Write the log to disk
  DN_OSLoggerFlags_NoColour  = 1 << 2, // Prevent outputting logs with Ansi colour codes (note: logs written to disk never use colour)
  DN_OSLoggerFlags_NoOutput  = 1 << 3, // Prevent outputting to the terminal

  // NOTE: Internal flags, do not use
  DN_OSLoggerFlags_FileError = 1 << 4, // On error opening the log file, this flag is sticky until log file path is updated
};

typedef struct DN_OSLogger DN_OSLogger;
struct DN_OSLogger
{
  DN_OSLogFlags  flags;

  // NOTE: Enable log rotation by setting `rotate_every_n_bytes`, `rotate_count` to non-zero values
  // and set the `DN_OSLoggerFlags_File` flag in `flags`.
  DN_USize       rotate_every_n_bytes; // Set this to the number of bytes the file should be before rotating
  DN_USize       rotate_count;         // Set this to the number of files to have in rotation

  // NOTE: Internal fields, do not modify
  DN_OSFile      file;               // Handle to file managed by the logger internally
  DN_Str8        file_path;          // Set the file path to write to using `DN_OS_LoggerSetFilePath`
  DN_TicketMutex file_mutex;         // Mutex locked when writing to disk
  DN_TicketMutex mutex;              // Mutex locked when writing to standard-error
};

typedef struct DN_OSCore DN_OSCore;
struct DN_OSCore
{
  DN_CPUReport           cpu_report;
  DN_OSLogger            logger;

  // 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_MemList             mem;
  DN_Arena               arena;
  void*                  platform_context;
};

typedef struct DN_OSDiskSpace DN_OSDiskSpace;
struct DN_OSDiskSpace
{
  bool   success;
  DN_U64 avail;
  DN_U64 size;
};

typedef enum DN_OSAsyncPriority {
  DN_OSAsyncPriority_Low,
  DN_OSAsyncPriority_High,
  DN_OSAsyncPriority_Count,
} DN_OSAsyncPriority;

typedef struct DN_OSAsyncCore DN_OSAsyncCore;
struct DN_OSAsyncCore
{
  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;
};

typedef struct DN_OSAsyncWorkArgs DN_OSAsyncWorkArgs;
struct DN_OSAsyncWorkArgs
{
  DN_OSThread *thread;
  void        *input;
};

typedef void(DN_OSAsyncWorkFunc)(DN_OSAsyncWorkArgs work_args);

typedef struct DN_OSAsyncWork DN_OSAsyncWork;
struct DN_OSAsyncWork
{
  DN_OSAsyncWorkFunc *func;
  void             *input;
  void             *output;
};

typedef struct DN_OSAsyncTask DN_OSAsyncTask;
struct DN_OSAsyncTask
{
  bool           queued;
  DN_OSAsyncWork work;
  DN_OSSemaphore completion_sem;
};
#endif // #if DN_WITH_OS

typedef struct DN_Core DN_Core;
struct DN_Core
{
  DN_InitFlags     init_flags;
  DN_TcCore        main_tc;
  DN_USize         mem_allocs_frame;
  DN_LeakTracker   leak;

  DN_LogType       log_level_to_show_from;
  DN_LogPrintFunc* print_func;
  void*            print_func_context;

  bool             os_init;
  #if DN_WITH_OS
  DN_OSCore        os;
  #endif
};

// NOTE: Library initialisation
// Overview
//   You must initialise the library to setup pre-requisite data structures that some APIs in the
//   library take advantage of. The `DN_Core` object must remain valid for the lifetime of the
//   library.
//
// API
//   DN_Init
//     This must be called before using the library once to setup TLS and query the OS for
//     information (such as page size) for tuning allocations and so forth. The
//     caller should pass in a zero-initialised `DN_Core` that should persist for program lifetime.
//
//     A reference to the core passed in is kept which can be queried with `DN_Get` and may be used
//     internally by the library. If you have an application that has the concept of frames, you may
//     optionally call `DN_BeginFrame` which resets some metrics that are counted for example it
//     tracks the number of memory allocations for the current frame and that counter can be reset.
//
//     For convenience you can opt into some sane defaults for the `DN_TcInitArgs` which customises
//     how thread-local storage is setup with `DN_TcInitArgsDefault()` which produces a
//     `DN_TcInitArgs` object you can pass in by value.
DN_API void                     DN_Init                                                (DN_Core *dn, DN_InitFlags flags, DN_TcInitArgs args);
DN_API void                     DN_Set                                                 (DN_Core *dn);
DN_API DN_Core*                 DN_Get                                                 ();
DN_API void                     DN_BeginFrame                                          ();

DN_API bool                     DN_VerifyArgsF                                         (DN_VerifyType type, bool expr, DN_CallSite call_site, DN_Str8 expr_str8, char const *fmt, ...);
DN_API bool                     DN_VerifyArgs                                          (DN_VerifyType type, bool expr, DN_CallSite call_site, DN_Str8 expr_str8);

#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_MemStartsWith                                       (void const *lhs, DN_USize lhs_count, void const *rhs, DN_USize rhs_count);
DN_API bool                     DN_MemEq                                               (void const *lhs, DN_USize lhs_count, void const *rhs, DN_USize rhs_count);
DN_API bool                     DN_MemEqUnsafe                                         (void const *lhs, void const *rhs, DN_USize count);
#if defined(__cplusplus)
template <typename T> T*        DN_MemCopyObjT                                         (T *dest, T const *src, DN_USize count);
#define                         DN_MemCopyObj(dest, src, count)                        DN_MemCopyObjT(dest, src, count)
#else
#define                         DN_MemCopyObj(dest, src, count)                        DN_Memcpy(dest, src, sizeof(*src) * count)
#endif

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_USize                 DN_AlignUpPowerOfTwoUSize                              (DN_USize val);
DN_API DN_U64                   DN_AlignUpPowerOfTwoU64                                (DN_U64 val);
DN_API DN_U32                   DN_AlignUpPowerOfTwoU32                                (DN_U32 val);

DN_API void                     DN_ByteSwapU64Ptr                                      (DN_U8* dest, DN_U64 src);
#define                         DN_ByteSwap64(val)                                     ( \
                                                                                        ((((DN_U64)(val) >> 56) & 0xFF) <<  0) | \
                                                                                        ((((DN_U64)(val) >> 48) & 0xFF) <<  8) | \
                                                                                        ((((DN_U64)(val) >> 40) & 0xFF) << 16) | \
                                                                                        ((((DN_U64)(val) >> 32) & 0xFF) << 24) | \
                                                                                        ((((DN_U64)(val) >> 24) & 0xFF) << 32) | \
                                                                                        ((((DN_U64)(val) >> 16) & 0xFF) << 40) | \
                                                                                        ((((DN_U64)(val) >> 8)  & 0xFF) << 48) | \
                                                                                        ((((DN_U64)(val) >> 0)  & 0xFF) << 56)   \
                                                                                       )
#define                         DN_ByteSwap32(val)                                     ( \
                                                                                        ((((DN_U32)(val) >> 24) & 0xFF) << 0)  | \
                                                                                        ((((DN_U32)(val) >> 16) & 0xFF) << 8)  | \
                                                                                        ((((DN_U32)(val) >> 8)  & 0xFF) << 16) | \
                                                                                        ((((DN_U32)(val) >> 0)  & 0xFF) << 24)   \
                                                                                       )
#define                         DN_ByteSwap24(val)                                     ( \
                                                                                        ((((DN_U32)(val) >> 16) & 0xFF) << 0)  | \
                                                                                        ((((DN_U32)(val) >> 8)  & 0xFF) << 8)  | \
                                                                                        ((((DN_U32)(val) >> 0)  & 0xFF) << 16)   \
                                                                                       )
#define                         DN_ByteSwap16(val)                                     ( \
                                                                                        ((((DN_U16)(val) >> 8)  & 0xFF) << 0) | \
                                                                                        ((((DN_U16)(val) >> 0)  & 0xFF) << 8)   \
                                                                                       )
#if defined(DN_64_BIT)
  #define                       DN_ByteSwapUSize(val)                                  DN_ByteSwap64(val)
#else
  #define                       DN_ByteSwapUSize(val)                                  DN_ByteSwap32(val)
#endif

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_TicketMutexBegin                                    (DN_TicketMutex *mutex);
DN_API void                     DN_TicketMutexEnd                                      (DN_TicketMutex *mutex);
DN_API DN_UInt                  DN_TicketMutexMakeTicket                               (DN_TicketMutex *mutex);
DN_API void                     DN_TicketMutexBeginTicket                              (DN_TicketMutex const *mutex, DN_UInt ticket);
DN_API bool                     DN_TicketMutexCanLock                                  (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);
DN_API bool                     DN_BitIsAny                                            (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_USize                 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_MemStats              DN_MemStatsSum                                         (DN_MemStats lhs, DN_MemStats rhs);
DN_API DN_MemStats              DN_MemStatsSumArray                                    (DN_MemStats const *array, DN_USize count);

// NOTE: Heap
// Overview
//   Generic heap allocators that can do arbitrary memory allocations and deallocations. It's a very
//   thin wrapper that tracks the number of bytes allocated and deallocated and is accepted by some
//   data structures in the codebase that opt out or have an alternative to arena allocations (of
//   which arenas are the predominant where it makes sense to).
//
//   Basic heap takes an API for routines in the likes of malloc/free (CRT/POSIX) or
//   HeapAlloc/HeapFree (Win32) which take a size and a pointer to free.
//
//   Virtual heap takes an API for routines in the likes of VirtualAlloc (Win32) or mmap (POSIX) and
//   those family of functions.

// API
//   DN_HeapAlloc
//   DN_HeapDealloc
//     Allocate and deallocate memory. Note that `DN_HeapAlloc` takes reserve and commit even for
//     the basic allocator. In the basic allocator which does not have a concept of reserving and
//     committing, the `commit` value is ignored and the `reserve` amount is the amount that will be
//     allocated.
DN_API DN_Heap                  DN_HeapInitBasic                                       (DN_HeapBasicAllocFunc *basic_alloc, DN_HeapBasicDeallocFunc *basic_dealloc);
DN_API DN_Heap                  DN_HeapInitVirtual                                     (DN_U32 page_size, DN_HeapVirtualReserveFunc *virtual_reserve, DN_HeapVirtualCommitFunc *virtual_commit, DN_HeapVirtualReleaseFunc *virtual_release);
DN_API void*                    DN_HeapAlloc                                           (DN_Heap *heap, DN_USize reserve, DN_USize commit);
DN_API void                     DN_HeapDealloc                                         (DN_Heap *heap, void *ptr, DN_USize size);

// NOTE: MemList
// Overview
//   `MemList` is an implementation of a classical `Arena` (e.g. bump allocator, can dynamically
//   grow, frees by bumping pointer back, sub-divides a block of memory). The term `Arena` is
//   reserved as a thin-layer over the functionality here to provide some use-after-free protection.
//   See `Arena` for more info.
//
//   In general for 99% of use-cases you want an `Arena` and if some API is not exposed on the
//   `Arena` to then use the `MemList` API on the embedded `mem_list` backing the Arena.
DN_API DN_MemList               DN_MemListFromBuffer                                   (void *buffer, DN_USize size, DN_MemFlags flags);
DN_API DN_MemList               DN_MemListFromHeap                                     (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap);
DN_API void                     DN_MemListDeinit                                       (DN_MemList *mem);
DN_API bool                     DN_MemListCommit                                       (DN_MemList *mem, DN_U64 size);
DN_API bool                     DN_MemListCommitTo                                     (DN_MemList *mem, DN_U64 pos);
DN_API bool                     DN_MemListGrow                                         (DN_MemList *mem, DN_U64 reserve, DN_U64 commit);
DN_API void *                   DN_MemListAlloc                                        (DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem zmem);
DN_API void *                   DN_MemListAllocContiguous                              (DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem zmem);
DN_API void *                   DN_MemListCopy                                         (DN_MemList *mem, void const *data, DN_U64 size, uint8_t align);
DN_API void                     DN_MemListPopTo                                        (DN_MemList *mem, DN_U64 init_used);
DN_API void                     DN_MemListPop                                          (DN_MemList *mem, DN_U64 amount);
DN_API DN_U64                   DN_MemListPos                                          (DN_MemList const *mem);
DN_API void                     DN_MemListClear                                        (DN_MemList *mem);
DN_API bool                     DN_MemListOwnsPtr                                      (DN_MemList const *mem, void const *ptr);
DN_API DN_Str8x64               DN_MemListInfoStr8x64                                  (DN_MemListInfo info);
DN_API DN_MemListTemp           DN_MemListTempBegin                                    (DN_MemList *mem);
DN_API void                     DN_MemListTempEnd                                      (DN_MemListTemp mem);
#define                         DN_MemListNew(arena, T, zmem)                          (T *)DN_MemListAlloc(arena, sizeof(T), alignof(T), zmem)
#define                         DN_MemListNewZ(arena, T)                               (T *)DN_MemListAlloc(arena, sizeof(T), alignof(T), DN_ZMem_Yes)
#define                         DN_MemListNewContiguous(arena, T, zmem)                (T *)DN_MemListAllocContiguous(arena, sizeof(T), alignof(T), zmem)
#define                         DN_MemListNewContiguousZ(arena, T)                     (T *)DN_MemListAllocContiguous(arena, sizeof(T), alignof(T), DN_ZMem_Yes)
#define                         DN_MemListNewArray(arena, T, count, zmem)              (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), zmem)
#define                         DN_MemListNewArrayZ(arena, T, count)                   (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_Yes)
#define                         DN_MemListNewArrayNoZ(arena, T, count)                 (T *)DN_MemListAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_No)
#define                         DN_MemListNewCopy(arena, T, src)                       (T *)DN_MemListCopy(arena, (src), sizeof(T),            alignof(T))
#define                         DN_MemListNewArrayCopy(arena, T, src, count)           (T *)DN_MemListCopy(arena, (src), sizeof(T)  * (count), alignof(T))

// NOTE: Arena
// Overview
//   `Arena`'s in this codebase are thin-layers over `MemList` but additionally provide
//   use-after-free (UAF) protection when using temporary memory regions (e.g. thread context
//   scratch `TCScratch` or `Temp[Begin|End]` family of functions).
//
//   These arenas associate themselves with the temporary memory region they begin in if it is
//   constructed using the `Temp[Begin|End]` family of functions (TCScratch implicitly call these
//   for you before handing you the arena). If you attempt to allocate from a different arena bound
//   with a different temporary memory region than the active one an assertion is triggered. This
//   protection is gated by the presence of the preprocessor definition
//   `#define DN_ARENA_TEMP_MEM_UAF_GUARD 1`.
//
//   Without the preprocessor definition UAF protection is compiled out (e.g. no-op). UAF protection
//   is also not enabled if you use `ArenaFromMemList` which simply sets up a plain arena that
//   forwards all calls into the `MemList` API.
//
//   To get UAF protection, all allocations _must_ go through the `Arena` API, using the `MemList`
//   field directly in the `Arena` will bypass these checks and lead to unusual behaviour. If you
//   want to forgo any of this infrastructure store and use the `MemList` directly in your codebase.

// UAF Example
/*
   DN_Arena arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_Kilobytes(64), DN_MemFlags_Nil, DN_OS_HeapInitDefault());
   DN_Arena temp  = DN_ArenaTempBeginFromArena(&arena);
   {
     // NOTE: You can also `TempBegin` with `&temp`, either is valid. They both have pointers to
     // the same underlying memory block owned by `arena`.
     DN_Arena nested_temp = DN_ArenaTempBeginFromArena(&arena);

     // NOTE: This allocation triggers the UAF guard and asserts! An allocation into `temp`'s memory
     // region would be reset when we end `nested_temp`'s memory region since they are spawned from
     // the same underlying memory block sitting in `arena`.
     //
     // But the intent here is that the caller is resetting `nested_temp`'s allocations and not
     // `temp` hence the UAF protection triggers. The only permissible way to allocate in this
     // region is to allocate from `nested_temp` (the active region).
     DN_U64 *u64  = DN_ArenaNewZ(&temp, DN_U64);

     DN_ArenaTempEnd(&nested_temp);
   }
   DN_ArenaTempEnd(&temp);
   DN_ArenaDeinit(&arena); // Frees the memory
 */
DN_API DN_Arena                 DN_ArenaFromMemList                                    (DN_MemList *mem);
DN_API DN_Arena                 DN_ArenaFromHeap                                       (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags, DN_Heap heap);
DN_API DN_Arena                 DN_ArenaTempBeginFromMemList                           (DN_MemList *mem);
DN_API DN_Arena                 DN_ArenaTempBeginFromArena                             (DN_Arena *arena);
DN_API void                     DN_ArenaTempEnd                                        (DN_Arena *arena, DN_ArenaReset reset);
DN_API void*                    DN_ArenaAlloc                                          (DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_mem);
DN_API void*                    DN_ArenaAllocContiguous                                (DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_arena);
DN_API void*                    DN_ArenaCopy                                           (DN_Arena *arena, void const *data, DN_U64 size, DN_U8 align);
DN_API void                     DN_ArenaDeinit                                         (DN_Arena *arena);
DN_API bool                     DN_ArenaOwnsPtr                                        (DN_Arena const *arena, void *ptr);

#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_ArenaNewArrayNoZ(arena, T, count)                   (T *)DN_ArenaAlloc(arena, sizeof(T) * (count), alignof(T), DN_ZMem_No)
#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 DN_ErrSink*              DN_ErrSinkBegin_                                       (DN_ErrSink *err, DN_ErrSinkMode mode, DN_CallSite call_site);
#define                         DN_ErrSinkBegin(err, mode)                             DN_ErrSinkBegin_(err, mode, DN_CallSiteNow)
#define                         DN_ErrSinkBeginDefault(err)                            DN_ErrSinkBegin(err, DN_ErrSinkMode_Nil)
DN_API bool                     DN_ErrSinkHasError                                     (DN_ErrSink *err);
DN_API DN_ErrSinkMsg*           DN_ErrSinkEnd                                          (DN_Arena *arena, DN_ErrSink *err);
DN_API DN_Str8                  DN_ErrSinkEndStr8                                      (DN_Arena *arena, DN_ErrSink *err);
DN_API void                     DN_ErrSinkEndIgnore                                    (DN_ErrSink *err);
DN_API bool                     DN_ErrSinkEndLogError_                                 (DN_ErrSink *err, DN_CallSite call_site, DN_Str8 msg);
#define                         DN_ErrSinkEndLogError(err, err_msg)                    DN_ErrSinkEndLogError_(err, DN_CallSiteNow, err_msg)
DN_API bool                     DN_ErrSinkEndLogErrorFV_                               (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args);
#define                         DN_ErrSinkEndLogErrorFV(err, fmt, args)                DN_ErrSinkEndLogErrorFV_(err, DN_CallSiteNow, fmt, args)
DN_API bool                     DN_ErrSinkEndLogErrorF_                                (DN_ErrSink *err, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...);
#define                         DN_ErrSinkEndLogErrorF(err, fmt, ...)                  DN_ErrSinkEndLogErrorF_(err, DN_CallSiteNow, fmt, ##__VA_ARGS__)
DN_API void                     DN_ErrSinkEndExitIfErrorF_                             (DN_ErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, ...);
#define                         DN_ErrSinkEndExitIfErrorF(err, exit_val, fmt, ...)     DN_ErrSinkEndExitIfErrorF_(err, DN_CallSiteNow, exit_val, fmt, ##__VA_ARGS__)
DN_API void                     DN_ErrSinkEndExitIfErrorFV_                            (DN_ErrSink *err, DN_CallSite call_site, DN_U32 exit_val, DN_FMT_ATTRIB char const *fmt, va_list args);
#define                         DN_ErrSinkEndExitIfErrorFV(err, exit_val, fmt, args)   DN_ErrSinkEndExitIfErrorFV_(err, DN_CallSiteNow, exit_val, fmt, args)
DN_API void                     DN_ErrSinkAppendFV_                                    (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args);
#define                         DN_ErrSinkAppendFV(error, error_code, fmt, args)       DN_ErrSinkAppendFV_(error, error_code, DN_CallSiteNow, fmt, args)
DN_API void                     DN_ErrSinkAppendF_                                     (DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...);
#define                         DN_ErrSinkAppendF(error, error_code, fmt, ...)         DN_ErrSinkAppendF_(error, error_code, DN_CallSiteNow, fmt, ##__VA_ARGS__)

DN_API DN_TcInitArgs            DN_TcInitArgsDefault                                   ();
DN_API void                     DN_TcInit                                              (DN_TcCore *tc, DN_U64 thread_id, DN_Arena *main_arena, DN_Arena *temp_arenas, DN_USize temp_arenas_count, DN_Arena *err_sink_arena);
DN_API void                     DN_TcInitFromHeap                                      (DN_TcCore *tc, DN_U64 thread_id, DN_TcInitArgs args, DN_Heap heap);
DN_API void                     DN_TcDeinit                                            (DN_TcCore *tc, DN_TcDeinitArenas deinit_arenas);
DN_API void                     DN_TcEquip                                             (DN_TcCore *tc);
DN_API DN_TcCore*               DN_TcGet                                               ();
DN_API DN_Arena*                DN_TcMainArena                                         ();
DN_API DN_Pool*                 DN_TcMainPool                                          ();
DN_API DN_Arena                 DN_TcTempArenaFromAllocator                            (DN_Allocator *conflicts, DN_USize count);
DN_API DN_Arena                 DN_TcTempArenaFromArena                                (DN_Arena **conflicts, DN_USize count);
DN_API DN_TcScratch             DN_TcScratchBeginAllocator                             (DN_Allocator *conflicts, DN_USize count);
DN_API DN_TcScratch             DN_TcScratchBeginArena                                 (DN_Arena **conflicts, DN_USize count);
DN_API void                     DN_TcScratchEnd                                        (DN_TcScratch *scratch);
DN_API void                     DN_TcSetFrameArena                                     (DN_Arena *arena);
DN_API DN_Arena*                DN_TcFrameArena                                        ();
DN_API DN_ErrSink*              DN_TcErrSink                                           ();
#define                         DN_TcErrSinkBegin(mode)                                DN_ErrSinkBegin(DN_TcErrSink(), mode)
#define                         DN_TcErrSinkBeginDefault()                             DN_ErrSinkBeginDefault(DN_TcErrSink())

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_U64FromStr8Delimiters                               (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count);
DN_API DN_U64FromResult         DN_U64FromStr8Delimiter                                (DN_Str8 string, DN_Str8 delimiter);
DN_API DN_U64FromResult         DN_U64FromStr8                                         (DN_Str8 string);
DN_API DN_U64FromResult         DN_U64FromPtrDelimiter                                 (void const *data, DN_USize size, DN_Str8 delimiter);
DN_API DN_U64FromResult         DN_U64FromPtr                                          (void const *data, DN_USize size);
DN_API DN_U64                   DN_U64FromPtrUnsafeDelimiter                           (void const *data, DN_USize size, DN_Str8 delimiter);
DN_API DN_U64                   DN_U64FromPtrUnsafe                                    (void const *data, DN_USize size);
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_U64                   DN_U64FromU8x32HiBEUnsafe                              (DN_U8x32 const *val); // Get U64 stored in big-endian at the high bytes [24:32)
DN_API DN_U64FromResult         DN_U64FromU8x32HiBE                                    (DN_U8x32 const *val); // Checks [0:24) bytes aren't set before getting the U64
DN_API DN_USize                 DN_USizeFromU8x32HiBEUnsafe                            (DN_U8x32 const *val); // Get USize stored in big-endian at the high bytes [32 - sizeof USize:32)
DN_API DN_USizeFromResult       DN_USizeFromU8x32HiBE                                  (DN_U8x32 const *val); // Checks [0:sizeof USize) bytes aren't set before getting the U64
DN_API DN_U32FromResult         DN_U32FromHexStr8                                      (DN_Str8 hex);
DN_API DN_U32FromResult         DN_U32FromStr8Delimiters                               (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count);
DN_API DN_U32FromResult         DN_U32FromStr8Delimiter                                (DN_Str8 string, DN_Str8 delimiter);
DN_API DN_U32FromResult         DN_U32FromPtr                                          (void const *data, DN_USize size);
DN_API DN_I64FromResult         DN_I64FromStr8Delimiters                               (DN_Str8 string, DN_Str8 const *delimiters, DN_USize delimiters_count);
DN_API DN_I64FromResult         DN_I64FromStr8Delimiter                                (DN_Str8 string, DN_Str8 delimiter);
DN_API DN_I64FromResult         DN_I64FromStr8                                         (DN_Str8 string);
DN_API DN_I64FromResult         DN_I64FromPtr                                          (void const *data, DN_USize size);
DN_API DN_I64                   DN_I64FromPtrUnsafe                                    (void const *data, DN_USize size);

DN_API bool                     DN_U8x32Eq                                             (DN_U8x32 const *lhs, DN_U8x32 const *rhs);
DN_API DN_U8x32                 DN_U8x32FromBytesLeftPadZ                              (DN_U8 const *ptr, DN_USize size);
DN_API DN_U8x32                 DN_U8x32FromHexUnsafe                                  (DN_Str8 hex_32b);
DN_API DN_U8x32FromResult       DN_U8x32FromHex                                        (DN_Str8 hex_32b);
DN_API DN_U8x32FromResult       DN_U8x32FromDecimalStr8                                (DN_Str8 decimal); // Write decimal string (e.g. "12345") as big-endian 256-bit value

DN_API DN_Allocator             DN_AllocatorFromMemList                                (DN_MemList *mem);
DN_API DN_Allocator             DN_AllocatorFromArena                                  (DN_Arena *arena);
DN_API DN_Allocator             DN_AllocatorFromPool                                   (DN_Pool *pool);
DN_API void*                    DN_AllocatorAlloc                                      (DN_Allocator allocator, DN_USize count, DN_U8 align, DN_ZMem z_mem);

DN_API DN_USize                 DN_FmtVCount                                           (DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_USize                 DN_FmtCount                                            (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_CStr8Count                                          (char const *src);
DN_API DN_USize                 DN_CStr16Count                                         (wchar_t const *src);

#define                         DN_Str16Lit(string)                                    DN_Str16{(wchar_t *)(string), sizeof(string)/sizeof(string[0]) - 1}
#define                         DN_Str16FromPtr(data, count)                           DN_Literal(DN_Str16){(wchar_t *)(data), (DN_USize)(count)}

#define                         DN_Str8Lit(c_str)                                      DN_Literal(DN_Str8){(char *)(c_str), sizeof(c_str) - 1}
#define                         DN_Str8PrintFmt(string)                                (int)((string).count), (string).data

#define                         DN_Str8FromPtr(data, count)                            DN_Literal(DN_Str8){(char *)(data), (DN_USize)(count)}
#define                         DN_Str8FromStruct(ptr)                                 DN_Str8FromPtr((ptr)->data, (ptr)->count)
#define                         DN_Str8FromLitArray(c_array)                           DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array))
DN_API DN_Str8                  DN_Str8AllocAllocator                                  (DN_USize count, DN_ZMem z_mem, DN_Allocator allocator);
DN_API DN_Str8                  DN_Str8AllocArena                                      (DN_USize count, DN_ZMem z_mem, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8AllocPool                                       (DN_USize count, DN_Pool *pool);

DN_API DN_Str8                  DN_Str8FromCStr8                                       (char const *src);
DN_API DN_Str8                  DN_Str8FromCStr8Arena                                  (char const *src, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8FromPtrArena                                    (void const *data, DN_USize count, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8FromPtrPool                                     (void const *data, DN_USize count, DN_Pool *pool);
DN_API DN_Str8                  DN_Str8FromStr8Allocator                               (DN_Str8 string, DN_Allocator allocator);
DN_API DN_Str8                  DN_Str8FromStr8Arena                                   (DN_Str8 string, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8FromStr8Pool                                    (DN_Str8 string, DN_Pool *pool);
DN_API DN_Str8                  DN_Str8FmtVAllocator                                   (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_Str8                  DN_Str8FmtVArena                                       (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_Str8                  DN_Str8FmtAllocator                                    (DN_Allocator allocator, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8                  DN_Str8FmtArena                                        (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8                  DN_Str8FmtVPool                                        (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_Str8                  DN_Str8FmtPool                                         (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...);

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_Str8x512              DN_Str8x512FromFmt                                     (DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8x512              DN_Str8x512FromFmtV                                    (DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_Str8x1024             DN_Str8x1024FromFmt                                    (DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8x1024             DN_Str8x1024FromFmtV                                   (DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void                     DN_Str8x16AppendFmt                                    (DN_Str8x16 *str, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void                     DN_Str8x16AppendFmtV                                   (DN_Str8x16 *str, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void                     DN_Str8x32AppendFmt                                    (DN_Str8x32 *str, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void                     DN_Str8x32AppendFmtV                                   (DN_Str8x32 *str, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void                     DN_Str8x64AppendFmt                                    (DN_Str8x64 *str, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void                     DN_Str8x64AppendFmtV                                   (DN_Str8x64 *str, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void                     DN_Str8x128AppendFmt                                   (DN_Str8x128 *str, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void                     DN_Str8x128AppendFmtV                                  (DN_Str8x128 *str, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void                     DN_Str8x256AppendFmt                                   (DN_Str8x256 *str, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void                     DN_Str8x256AppendFmtV                                  (DN_Str8x256 *str, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void                     DN_Str8x512AppendFmt                                   (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void                     DN_Str8x512AppendFmtV                                  (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void                     DN_Str8x1024AppendFmt                                  (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void                     DN_Str8x1024AppendFmtV                                 (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, va_list args);

DN_API DN_Str8x32               DN_Str8x32FromU64                                      (DN_U64 val, char seperator);
DN_API bool                     DN_Str8Is                                              (DN_Str8 string, DN_Str8IsFlags flags);
DN_API char *                   DN_Str8End                                             (DN_Str8 string);
DN_API DN_Str8                  DN_Str8Subset                                          (DN_Str8 string, DN_USize offset, DN_USize count);
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_Str8SplitFlags mode);
DN_API DN_Str8SplitResult       DN_Str8SplitArena                                      (DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags mode, DN_Arena *arena);
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, DN_Str8FindFlag 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_API bool                     DN_Str8EqSensitive                                     (DN_Str8 lhs, DN_Str8 rhs);
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_API bool                     DN_Str8StartsWithSensitive                             (DN_Str8 string, DN_Str8 prefix);
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_API bool                     DN_Str8EndsWithSensitive                               (DN_Str8 string, DN_Str8 prefix);
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_API DN_Str8                  DN_Str8TrimPrefixSensitive                             (DN_Str8 string, DN_Str8 prefix);
DN_API DN_Str8                  DN_Str8TrimPrefixInsensitive                           (DN_Str8 string, DN_Str8 prefix);
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_API DN_Str8                  DN_Str8TrimSuffixSensitive                             (DN_Str8 string, DN_Str8 suffix);
DN_API DN_Str8                  DN_Str8TrimSuffixInsensitive                           (DN_Str8 string, DN_Str8 suffix);
DN_API DN_Str8                  DN_Str8TrimAround                                      (DN_Str8 string, DN_Str8 trim_string, DN_Str8EqCase eq_case);
DN_API DN_Str8                  DN_Str8TrimAroundSensitive                             (DN_Str8 string, DN_Str8 trim_string);
DN_API DN_Str8                  DN_Str8TrimAroundInsensitive                           (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 count);
DN_API DN_Str8                  DN_Str8TruncateArena                                   (DN_Str8 string, DN_USize max_count, DN_Str8 truncator, DN_Arena *arena);
DN_API DN_Str8TruncResult       DN_Str8TruncMiddlePtr                                  (DN_Str8 str8, DN_USize side_size, DN_Str8 truncator, char *dest, DN_USize dest_max);
DN_API DN_Str8TruncResult       DN_Str8TruncMiddle                                     (DN_Str8 str8, DN_USize side_size, DN_Str8 truncator, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8Lower                                           (DN_Str8 string, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8Upper                                           (DN_Str8 string, DN_Arena *arena);
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_API DN_Str8                  DN_Str8ReplaceSensitive                                (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8ReplaceInsensitive                              (DN_Str8 string, DN_Str8 find, DN_Str8 replace, DN_USize start_index, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8PadNewLinesAllocator                            (DN_Str8 string, DN_Str8 pad_string, DN_Allocator allocator);
DN_API DN_Str8                  DN_Str8PadNewLinesArena                                (DN_Str8 string, DN_Str8 pad_string, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8LineBreakAllocator                              (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Allocator allocator);
DN_API DN_Str8                  DN_Str8LineBreakArena                                  (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8Table                                           (DN_Str8 const* rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena);

#if DN_WITH_STR8_AVX512F
DN_API DN_Str8FindResult        DN_Str8FindStr8AVX512F                                 (DN_Str8 string, DN_Str8 find);
DN_API DN_Str8FindResult        DN_Str8FindLastStr8AVX512F                             (DN_Str8 string, DN_Str8 find);
DN_API DN_Str8BSplitResult      DN_Str8BSplitAVX512F                                   (DN_Str8 string, DN_Str8 find);
DN_API DN_Str8BSplitResult      DN_Str8BSplitLastAVX512F                               (DN_Str8 string, DN_Str8 find);
DN_API DN_USize                 DN_Str8SplitAVX512F                                    (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags flags);
DN_API DN_Str8Slice             DN_Str8SplitAllocAVX512F                               (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags flags);
#endif

DN_API DN_Str8                  DN_Str8SliceRender                                     (DN_Str8Slice array, DN_Str8 seperator, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8RenderSpaceSep                                  (DN_Str8Slice array, DN_Arena *arena);
DN_API int                      DN_Str8CompareNatural                                  (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case);
DN_API int                      DN_Str8CompareLexicographic                            (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case);

DN_API bool                     DN_Str16Eq                                             (DN_Str16 lhs, DN_Str16 rhs);
DN_API DN_Str16                 DN_Str16SliceRender                                    (DN_Str16Slice array, DN_Str16 seperator, DN_Arena *arena);
DN_API DN_Str16                 DN_Str16RenderSpaceSep                                 (DN_Str16Slice array, DN_Arena *arena);

DN_API DN_Str8Builder           DN_Str8BuilderFromArena                                (DN_Arena *arena);
DN_API DN_Str8Builder           DN_Str8BuilderFromStr8PtrRef                           (DN_Arena *arena, DN_Str8 const *strings, DN_USize count);
DN_API DN_Str8Builder           DN_Str8BuilderFromStr8PtrCopy                          (DN_Arena *arena, DN_Str8 const *strings, DN_USize count);
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 count, DN_AddType add);
DN_API bool                     DN_Str8BuilderAddArrayCopy                             (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize count, DN_AddType add);
DN_API bool                     DN_Str8BuilderAddFV                                    (DN_Str8Builder *builder, DN_AddType add, DN_FMT_ATTRIB char const *fmt, va_list args);
#define                         DN_Str8BuilderAppendArrayRef(builder, strings, size)   DN_Str8BuilderAddArrayRef(builder, strings, size, DN_AddType_Append)
#define                         DN_Str8BuilderAppendArrayCopy(builder, strings, size)  DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_AddType_Append)
#define                         DN_Str8BuilderAppendSliceRef(builder, slice)           DN_Str8BuilderAddArrayRef(builder, slice.data, slice.count, DN_AddType_Append)
#define                         DN_Str8BuilderAppendSliceCopy(builder, slice)          DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.count, DN_AddType_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_AddType_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 count);
DN_API bool                     DN_Str8BuilderAppendBytesCopy                          (DN_Str8Builder *builder, void const *ptr, DN_USize count);
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_AddType_Prepend)
#define                         DN_Str8BuilderPrependArrayCopy(builder, strings, size) DN_Str8BuilderAddArrayCopy(builder, strings, size, DN_AddType_Prepend)
#define                         DN_Str8BuilderPrependSliceRef(builder, slice)          DN_Str8BuilderAddArrayRef(builder, slice.data, slice.count, DN_AddType_Prepend)
#define                         DN_Str8BuilderPrependSliceCopy(builder, slice)         DN_Str8BuilderAddArrayCopy(builder, slice.data, slice.count, DN_AddType_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_AddType_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_Str8FromStr8BuilderAllocator                        (DN_Str8Builder const *builder, DN_Allocator allocator);
DN_API DN_Str8                  DN_Str8FromStr8BuilderArena                            (DN_Str8Builder const *builder, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8FromStr8BuilderDelimitAllocator                 (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Allocator allocator);
DN_API DN_Str8                  DN_Str8FromStr8BuilderDelimitArena                     (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena);

DN_API bool                     DN_PathAddRef                                          (DN_Path *path, DN_Str8 add, DN_Arena *arena);
DN_API bool                     DN_PathAdd                                             (DN_Path *path, DN_Str8 add, DN_Arena *arena);
DN_API bool                     DN_PathAddF                                            (DN_Path *path, DN_FMT_ATTRIB char const *fmt, ...);
DN_API bool                     DN_PathPop                                             (DN_Path *path);
DN_API DN_Str8                  DN_Str8FromPath                                        (DN_Path const *path, DN_Str8 path_seperator, DN_Allocator allocator);
DN_API DN_Str8                  DN_Str8FmtPathVArena                                   (DN_Str8 path_seperator, DN_Arena *arena, char const *fmt, va_list args);
DN_API DN_Str8                  DN_Str8FmtPathArena                                    (DN_Str8 path_seperator, DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8                  DN_Str8FmtPathVPool                                    (DN_Str8 path_seperator, DN_Pool  *pool,  char const *fmt, va_list args);
DN_API DN_Str8                  DN_Str8FmtPathPool                                     (DN_Str8 path_seperator, DN_Pool  *pool,  DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8                  DN_Str8FmtOsPathArena                                  (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8                  DN_Str8FmtOsPathPool                                   (DN_Pool  *pool,  DN_FMT_ATTRIB char const *fmt, ...);

DN_API DN_USize                 DN_Utf8FromUtf32                                       (DN_U8 utf8[4], DN_U32 codepoint);
DN_API bool                     DN_Utf32IsAlphabet                                     (DN_U32 codepoint);
DN_API bool                     DN_Utf32IsDigit                                        (DN_U32 codepoint);
DN_API bool                     DN_Utf32IsAlphaNum                                     (DN_U32 codepoint);
DN_API bool                     DN_Utf32IsWhitespace                                   (DN_U32 codepoint);
DN_API bool                     DN_Utf32IsHex                                          (DN_U32 codepoint);
DN_API bool                     DN_Utf32IsWordCharacter                                (DN_U32 codepoint);
DN_API DN_U32                   DN_Utf32ToLower                                        (DN_U32 codepoint);
DN_API DN_U32                   DN_Utf32ToUpper                                        (DN_U32 codepoint);
DN_API DN_USize                 DN_Utf32FromUtf16                                      (DN_U16 utf16[2], DN_U32 codepoint);
DN_API DN_Utf32FromResult       DN_Utf32FromUtf8Stream                                 (DN_Str8 stream);
DN_API bool                     DN_Utf32FromUtf8Iterate                                (DN_Utf32FromUtf8Iterator *it, DN_Utf32IterateDir dir, DN_Str8 utf8);
DN_API DN_USize                 DN_Utf32CodepointCountFromUtf8                         (DN_Str8 str, DN_CodepointCountFlags flags);

DN_API DN_U8                    DN_U8FromHexNibble                                     (char hex);
DN_API DN_NibbleFromU8Result    DN_NibbleFromU8                                        (DN_U8 u8);

DN_API DN_USize                 DN_PtrBytesFromStr8Hex                                 (DN_Str8 hex, void *dest, DN_USize dest_count);
DN_API DN_USize                 DN_PtrBytesFromPtrHex                                  (char const *hex, DN_USize hex_count, void *dest, DN_USize dest_count);
DN_API DN_Str8                  DN_Str8BytesFromStr8HexArena                           (DN_Str8 hex, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8BytesFromPtrHexArena                            (char const *hex, DN_USize hex_count, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8BytesFromPtrHexPool                             (char const *hex, DN_USize hex_count, DN_Pool *pool);
DN_API DN_U8x16                 DN_U8x16FromPtrHex32                                   (char const *hex, DN_USize hex_count);
DN_API DN_U8x32                 DN_U8x32FromPtrHex64                                   (char const *hex, DN_USize hex_count);

DN_API DN_HexU64                DN_HexU64FromU64                                       (DN_U64 value, DN_HexFromU64Type type);
DN_API DN_USize                 DN_PtrHexFromPtrBytes                                  (void const *bytes, DN_USize bytes_count, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z);
DN_API DN_USize                 DN_PtrHexFromStr8Bytes                                 (DN_Str8 bytes, void *hex, DN_USize hex_count, DN_TrimLeadingZero trim_leading_z);
DN_API DN_Str8                  DN_Str8HexFromPtrBytesArena                            (void const *bytes, DN_USize bytes_count, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z);
DN_API DN_Str8                  DN_Str8HexFromStr8BytesArena                           (DN_Str8 bytes, DN_Arena *arena, DN_TrimLeadingZero trim_leading_z);
DN_API DN_Hex32                 DN_Hex32FromPtrBytes16                                 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z);
DN_API DN_Hex64                 DN_Hex64FromPtrBytes32                                 (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z);
DN_API DN_Hex64                 DN_Hex64FromU8x32                                      (DN_U8x32 const *value, DN_TrimLeadingZero trim_leading_z);
DN_API DN_Hex128                DN_Hex128FromPtrBytes64                                (void const *bytes, DN_USize bytes_count, DN_TrimLeadingZero trim_leading_z);

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_Str8                  DN_Str8FromByteType                                    (DN_ByteType type);
DN_API DN_ByteCount             DN_ByteCountFromU64                                    (DN_U64 byte_count, DN_ByteType type);
DN_API DN_Str8x32               DN_Str8x32FromByteCountU64                             (DN_U64 byte_count, DN_ByteType type);
#define                         DN_Str8x32FromByteCountU64Auto(bytes)                  DN_Str8x32FromByteCountU64(bytes, DN_ByteType_Auto)

// NOTE: Profiler
// Overview
//   Basic profiler that tracks the duration of marked-up regions which are denoted by an
//   opening and closing `anchor`. The profiler works in "frame" life-cycles which can by cycled by
//   calling `DN_ProfilerNewFrame`. The number of frames that the profiler will persist is chosen by
//   `anchors_per_frame`.
//
//   For example if you pass a buffer of 1024 `anchors` and `anchors_per_frame = 128` then the
//   profiler will hold onto 8 (1024 anchors / 128 anchors per frame) frames of profiling
//   information. The profiler will cycle through the 8 frames of anchors and upon reaching the end
//   begin overwriting the oldest frames worth of anchors.
//
//   Once a frame has ended the just completed buffer of anchors that was just written to can be
//   read by the caller and visualised to find the timings of each region, relative to the duration
//   of the frame until it is eventually overwritten by calling `DN_ProfilerNewFrame` once the
//   profiler has cycled through all the other frames.
//
//   The caller must upfront determine the number of anchors and frames the profiler should have and
//   pass it in. The profiler does not allocate any memory.
//
//   When profiling functions that are invoked from different call-stacks the exclusive elapsed
//   duration can exceed its inclusive duration. This is by design as the profiler makes no
//   effort to distinguish between the different call-tree that a zone may have been triggered by.
//   The profiler simpler always updates the same static anchor assigned for that zone.
//
// API
//   DN_ProfilerInit
//     You can set `tsc_now` to NULL to use the default timer mechanic which relies on
//     DN_CPUGetTsc() which essentially uses __rdtsc. `tsc_frequency` must however always be
//     provided, for, DN_CPUGetTsc() you can use `DN_OS_EstimateTscPerSecond()` to calculate the
//     frequency of `__rdtsc`.
//   DN_ProfilerNewFrame
//     Always call `DN_ProfilerNewFrame` at-least once using the `Zone` family of functions as it
//     sets up
//   DN_ProfilerBeginZone
//     The zeroth anchor is reserved for profiling the begin and end of a profiler frame. If you are
//     manually specifying `anchor_index` (e.g. using an enum) ensure that the first anchor index
//     starts from `1`. Note that the `BeginZoneAuto` macro uses `__COUNTER__ + 1` to skip the 0th
//     zone.
//   DN_ProfilerFrameAnchors
//     Returns the current frame's (e.g. the anchors that the profiler is currently writing to)
//     worth of anchors
#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(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_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 DN_USize                 DN_ProfilerFmtAnchor                                   (DN_ProfilerAnchor anchor, DN_U64 tsc_frequency, char *buffer, DN_USize count);
DN_API DN_Str8                  DN_ProfilerFmtAnchorStr8                               (DN_ProfilerAnchor anchor, DN_U64 tsc_frequency, DN_Arena *arena);
DN_API void                     DN_ProfilerFmtToStdout                                 (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);

DN_API void                     DN_QSort_                                              (void *array, DN_USize array_size, DN_USize elem_size, void *user_context, DN_QSortCompareFunc *compare);
#if defined(__cplusplus)
template <typename T> void      DN_QSortT                                              (T *array, DN_USize array_size, void *user_context, DN_QSortCompareFunc *compare);
#define                         DN_QSort(array, array_size, user_context, compare)     DN_QSortT(array, array_size, user_context, compare)
#else
#define                         DN_QSort(array, array_size, user_context, compare)     DN_QSort_(array, array_size, sizeof(*array), user_context, compare)
#endif

DN_API bool                     DN_QSortCompareStr8NaturalAsc                          (void const* lhs, void const *rhs, void *user_context);
DN_API bool                     DN_QSortCompareStr8NaturalDesc                         (void const* lhs, void const *rhs, void *user_context);
DN_API bool                     DN_QSortCompareStr8LexicographicAsc                    (void const* lhs, void const *rhs, void *user_context);
DN_API bool                     DN_QSortCompareStr8LexicographicDesc                   (void const* lhs, void const *rhs, void *user_context);
DN_API bool                     DN_QSortCompareBytesLT                                 (void const* lhs, void const *rhs, void *user_context);
DN_API bool                     DN_QSortCompareBytesGT                                 (void const* lhs, void const *rhs, void *user_context);
DN_API void                     DN_QSortBytesLT                                        (void *array, DN_USize array_size, DN_USize elem_size);
DN_API void                     DN_QSortBytesGT                                        (void *array, DN_USize array_size, DN_USize elem_size);
DN_API void                     DN_QSortStr8NaturalAsc                                 (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case);
DN_API void                     DN_QSortStr8NaturalDesc                                (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case);
DN_API void                     DN_QSortStr8LexicographicAsc                           (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case);
DN_API void                     DN_QSortStr8LexicographicDesc                          (DN_Str8 *array, DN_USize array_size, DN_Str8EqCase eq_case);

DN_API bool                     DN_BSearchLessThanBytes                                (void const *lhs, void const *rhs, void *user_context);
DN_API DN_BSearchResult         DN_BSearch                                             (void const *array, DN_USize count, DN_USize elem_size, void const *find, DN_BSearchType type, void *user_context, DN_BSearchLessThanFunc *less_than);
DN_API DN_BSearchResult         DN_BSearchBytes                                        (void const *array, DN_USize count, DN_USize elem_size, void const *find, DN_BSearchType type);
DN_API DN_BSearchResult         DN_BSearchUSize                                        (DN_USize const *array, DN_USize count, DN_USize find, DN_BSearchType type);
DN_API DN_BSearchResult         DN_BSearchU64                                          (DN_U64 const *array, DN_USize count, DN_U64 find, DN_BSearchType type);
DN_API DN_BSearchResult         DN_BSearchU32                                          (DN_U32 const *array, DN_USize count, DN_U32 find, DN_BSearchType type);

DN_API DN_Pcg32                 DN_Pcg32Init                                           (DN_U64 seed);
DN_API DN_U32                   DN_Pcg32Next                                           (DN_Pcg32 *rng);
DN_API DN_U64                   DN_Pcg32Next64                                         (DN_Pcg32 *rng);
DN_API DN_U32                   DN_Pcg32Range                                          (DN_Pcg32 *rng, DN_U32 low, DN_U32 high);
DN_API DN_F32                   DN_Pcg32NextF32                                        (DN_Pcg32 *rng);
DN_API DN_F64                   DN_Pcg32NextF64                                        (DN_Pcg32 *rng);
DN_API void                     DN_Pcg32Advance                                        (DN_Pcg32 *rng, DN_U64 delta);

#if !defined(DN_FNV1A32_SEED)
  #define DN_FNV1A32_SEED 2166136261U
#endif

#if !defined(DN_FNV1A64_SEED)
  #define DN_FNV1A64_SEED 14695981039346656037ULL
#endif

DN_API DN_U32                   DN_Fnv1aHashU32FromBytes                               (void const *bytes, DN_USize count, DN_U32 seed);
DN_API DN_U64                   DN_Fnv1aHashU64FromBytes                               (void const *bytes, DN_USize count, DN_U64 seed);

DN_API DN_U32                   DN_Murmur3HashU32FromBytesX86                          (void const *bytes, int len, DN_U32 seed);
DN_API DN_Murmur3               DN_Murmur3HashU128FromBytesX64                         (void const *bytes, int len, DN_U32 seed);
DN_API DN_U64                   DN_Murmur3HashU64FromBytesX64                          (void const *bytes, int len, DN_U32 seed);
DN_API DN_U32                   DN_Murmur3HashU32FromBytesX64                          (void const *bytes, int len, DN_U32 seed);

#if defined(DN_64_BIT)
  #define                       DN_Murmur3HashU32FromBytes(bytes, len, seed)       DN_Murmur3HashU32FromBytesX64(bytes, len, seed)
#else
  #define                       DN_Murmur3HashU32FromBytes(bytes, len, seed)       DN_Murmur3HashU32FromBytesX86(bytes, len, seed)
#endif

#define                         DN_AnsiCodeBoldLit                                     "\x1b[1m"
#define                         DN_AnsiCodeResetLit                                    "\x1b[0m"
DN_API DN_Str8x32               DN_Str8x32FromAnsiColourCodeU8Rgb                      (DN_AnsiColourMode mode, DN_U8 r, DN_U8 g, DN_U8 b);
DN_API DN_Str8x32               DN_Str8x32FromAnsiColourCodeV3F32Rgb255                (DN_AnsiColourMode mode, DN_V3F32 rgb_255);
DN_API DN_Str8x32               DN_Str8x32FromAnsiColourCodeU32Rgb                     (DN_AnsiColourMode mode, DN_U32 value);
DN_API DN_Str8                  DN_Str8FromStr8AnsiColourU8RgbArena                    (DN_AnsiColourMode mode, DN_Str8 str8, DN_U8 r, DN_U8 g, DN_U8 b, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8FromStr8AnsiColourV3F32Rgb255Arena              (DN_AnsiColourMode mode, DN_Str8 str8, DN_V3F32 rgb_255, DN_Arena *arena);
DN_API DN_Str8                  DN_Str8FmtAnsiColourU8RgbArena                         (DN_AnsiColourMode mode, DN_U8 r, DN_U8 g, DN_U8 b, DN_Arena *arena, char const *fmt, ...);
DN_API DN_Str8                  DN_Str8FmtAnsiColourV3F32Rgb255Arena                   (DN_AnsiColourMode mode, DN_V3F32 rgb_255, DN_Arena *arena, char const *fmt, ...);

// NOTE: Create log printable lines with a date prefix, severity and message. The platform
// implementation should call `SetPrintFunc` to intercept the log messages and output to the desired
// destination (log file, standard out, e.t.c.). When the library is initialised `DN_Init` to with
// OS functionality enabled, the log callback is by default set to outputting via standard out.
DN_API DN_LogPrefixSize         DN_LogMakePrefix                                       (DN_LogStyle style, DN_LogTypeParam type, DN_CallSite call_site, DN_LogDate date, char *dest, DN_USize dest_size);
DN_API void                     DN_LogSetPrintFunc                                     (DN_LogPrintFunc *print_func, void *user_data);
DN_API void                     DN_LogPrintF                                           (DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void                     DN_LogPrintFV                                          (DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_LogTypeParam          DN_LogTypeParamFromType                                (DN_LogType type);
#define                         DN_LogF(type, fmt, ...)                                DN_LogPrintF(DN_LogTypeParamFromType(type), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__)

#define                         DN_LogDebugF(fmt, ...)                                 DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Debug),   DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__)
#define                         DN_LogInfoF(fmt, ...)                                  DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Info),    DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__)
#define                         DN_LogWarningF(fmt, ...)                               DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Warning), DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__)
#define                         DN_LogErrorF(fmt, ...)                                 DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Error),   DN_CallSiteNow, DN_LogFlags_Nil, fmt, ##__VA_ARGS__)

#define                         DN_LogFlagF(type, flags, fmt, ...)                     DN_LogPrintF(DN_LogTypeParamFromType(type),               DN_CallSiteNow, flags, fmt, ##__VA_ARGS__)
#define                         DN_LogFlagDebugF(flags, fmt, ...)                      DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Debug),   DN_CallSiteNow, flags, fmt, ##__VA_ARGS__)
#define                         DN_LogFlagInfoF(flags, fmt, ...)                       DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Info),    DN_CallSiteNow, flags, fmt, ##__VA_ARGS__)
#define                         DN_LogFlagWarningF(flags, fmt, ...)                    DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Warning), DN_CallSiteNow, flags, fmt, ##__VA_ARGS__)
#define                         DN_LogFlagErrorF(flags, fmt, ...)                      DN_LogPrintF(DN_LogTypeParamFromType(DN_LogType_Error),   DN_CallSiteNow, flags, fmt, ##__VA_ARGS__)


// NOTE: OS primitives that the OS layer can provide for the base layer but is optional.
#if defined(DN_FREESTANDING)
#define                        DN_StackTraceFromArena(...) {}
#define                        DN_StackTraceFromAllocator(...) {}
#define                        DN_StackTraceIterate(...) false
#define                        DN_Str8FromStackTraceAllocator(...) DN_Str8Lit("N/A")
#define                        DN_Str8FromStackTraceArena(...) DN_Str8Lit("N/A")
#define                        DN_Str8FromStackTraceNowAllocator(...) DN_Str8Lit("N/A")
#define                        DN_Str8FromStackTraceNowArena(...) DN_Str8Lit("N/A")
#define                        DN_Str8FromStackTraceNowHeap(...) DN_Str8Lit("N/A")
#define                        DN_StackTraceGetFrames(...)
#define                        DN_StackTraceRawFrameToFrame(...)
#define                        DN_StackTracePrint(...)
#define                        DN_StackTraceReloadSymbols(...)
#else
DN_API DN_StackTrace           DN_StackTraceFromArena                                  (DN_Arena *arena, DN_U16 limit);
DN_API DN_StackTrace           DN_StackTraceFromAllocator                              (DN_Allocator allocator, DN_U16 limit);
DN_API bool                    DN_StackTraceIterate                                    (DN_StackTraceIterator *it, DN_StackTrace const *walk);
DN_API DN_Str8                 DN_Str8FromStackTraceAllocator                          (DN_Allocator allocator, DN_StackTrace const *walk, DN_U16 skip);
DN_API DN_Str8                 DN_Str8FromStackTraceArena                              (DN_Arena *arena, DN_StackTrace const *walk, DN_U16 skip);
DN_API DN_Str8                 DN_Str8FromStackTraceNowAllocator                       (DN_Arena *arena, DN_U16 limit, DN_U16 skip);
DN_API DN_Str8                 DN_Str8FromStackTraceNowArena                           (DN_Arena *arena, DN_U16 limit, DN_U16 skip);
DN_API DN_Str8                 DN_Str8FromStackTraceNowHeap                            (DN_U16 limit, DN_U16 skip);
DN_API DN_StackTraceFrameSlice DN_StackTraceGetFrames                                  (DN_Arena *arena, DN_U16 limit);
DN_API DN_StackTraceFrame      DN_StackTraceRawFrameToFrame                            (DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
DN_API void                    DN_StackTracePrint                                      (DN_U16 limit);
DN_API void                    DN_StackTraceReloadSymbols                              ();
#endif

DN_API DN_F32                  DN_F32Lerp                                              (DN_F32 a, DN_F32 t, DN_F32 b);
DN_API DN_F32                  DN_F32Floor                                             (DN_F32 val);
DN_API DN_F32                  DN_F32Ceil                                              (DN_F32 val);
DN_API DN_F32                  DN_F32RoundHalfUp                                       (DN_F32 val);

#define                        DN_V2I32Zero                                            DN_Literal(DN_V2I32){{(DN_I32)(0),    (DN_I32)(0)}}
#define                        DN_V2I32One                                             DN_Literal(DN_V2I32){{(DN_I32)(1),    (DN_I32)(1)}}
#define                        DN_V2I32From1N(x)                                       DN_Literal(DN_V2I32){{(DN_I32)(x),    (DN_I32)(x)}}
#define                        DN_V2I32From2N(x, y)                                    DN_Literal(DN_V2I32){{(DN_I32)(x),    (DN_I32)(y)}}
#define                        DN_V2I32InitV2(xy)                                      DN_Literal(DN_V2I32){{(DN_I32)(xy).x, (DN_I32)(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, DN_I32   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, DN_I32   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, DN_I32   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, DN_I32   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_V2I32Min                                             (DN_V2I32 a, DN_V2I32 b);
DN_API DN_V2I32                DN_V2I32Max                                             (DN_V2I32 a, DN_V2I32 b);
DN_API DN_V2I32                DN_V2I32Abs                                             (DN_V2I32 a);

#define                        DN_V2U16Zero                                            DN_Literal(DN_V2U16){{(DN_U16)(0), (DN_U16)(0)}}
#define                        DN_V2U16One                                             DN_Literal(DN_V2U16){{(DN_U16)(1), (DN_U16)(1)}}
#define                        DN_V2U16From1N(x)                                       DN_Literal(DN_V2U16){{(DN_U16)(x), (DN_U16)(x)}}
#define                        DN_V2U16From2N(x, y)                                    DN_Literal(DN_V2U16){{(DN_U16)(x), (DN_U16)(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, DN_I32 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, DN_I32 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, DN_I32 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, DN_I32 rhs);
DN_API DN_V2U16&               operator-=                                              (DN_V2U16& lhs, DN_V2U16 rhs);
DN_API DN_V2U16&               operator+=                                              (DN_V2U16& lhs, DN_V2U16 rhs);

// NOTE: DN_V2F32
// API
//   DN_V2F32Rotate
//     Rotate a vector using the precomputed cosine and sine value of the angle (e.g.
//     `cos_a => cos(your_angle * 2.f * DN_PiF32)` and `sin_a => sinf(your_angle * 2.f * DN_PiF32)`)
#define                        DN_V2U32Zero                                            DN_Literal(DN_V2U32){{(DN_U32)(0),    (DN_U32)(0)}}
#define                        DN_V2U32One                                             DN_Literal(DN_V2U32){{(DN_U32)(1),    (DN_U32)(1)}}
#define                        DN_V2U32From1N(x)                                       DN_Literal(DN_V2U32){{(DN_U32)(x),    (DN_U32)(x)}}
#define                        DN_V2U32From2N(x, y)                                    DN_Literal(DN_V2U32){{(DN_U32)(x),    (DN_U32)(y)}}
#define                        DN_V2U32FromV2(v2)                                      DN_Literal(DN_V2U32){{(DN_U32)(v2.x), (DN_U32)(v2.y)}}

#define                        DN_V2F32Zero                                            DN_Literal(DN_V2F32){{(DN_F32)(0),    (DN_F32)(0)}}
#define                        DN_V2F32One                                             DN_Literal(DN_V2F32){{(DN_F32)(1),    (DN_F32)(1)}}
#define                        DN_V2F32From1N(x)                                       DN_Literal(DN_V2F32){{(DN_F32)(x),    (DN_F32)(x)}}
#define                        DN_V2F32From2N(x, y)                                    DN_Literal(DN_V2F32){{(DN_F32)(x),    (DN_F32)(y)}}
#define                        DN_V2F32FromV2(xy)                                      DN_Literal(DN_V2F32){{(DN_F32)(xy).x, (DN_F32)(xy).y}}

DN_API DN_V2F32                DN_V2F32Lerp                                            (DN_V2F32 a, DN_F32 t, DN_V2F32 b);
DN_API DN_V2F32                DN_V2F32Rotate                                          (DN_V2F32 v, DN_F32 cos_a, DN_F32 sin_a);

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, DN_I32    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, DN_I32    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, DN_I32    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, DN_I32    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, DN_I32   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, DN_I32   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, DN_I32   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, DN_I32   rhs);

DN_API DN_V2F32                DN_V2F32Min                                             (DN_V2F32 a, DN_V2F32 b);
DN_API DN_V2F32                DN_V2F32Max                                             (DN_V2F32 a, DN_V2F32 b);
DN_API DN_V2F32                DN_V2F32Abs                                             (DN_V2F32 a);
DN_API DN_F32                  DN_V2F32Dot                                             (DN_V2F32 a, DN_V2F32 b);
DN_API DN_F32                  DN_V2F32LengthSq2V2                                     (DN_V2F32 lhs, DN_V2F32 rhs);
DN_API bool                    DN_V2F32LengthSqIsWithin2V2                             (DN_V2F32 lhs, DN_V2F32 rhs, DN_F32 within_amount_sq);
DN_API DN_F32                  DN_V2F32Length2V2                                       (DN_V2F32 lhs, DN_V2F32 rhs);
DN_API DN_F32                  DN_V2F32LengthSq                                        (DN_V2F32 lhs);
DN_API DN_F32                  DN_V2F32Length                                          (DN_V2F32 lhs);
DN_API DN_V2F32                DN_V2F32Normalise                                       (DN_V2F32 a);
DN_API DN_V2F32                DN_V2F32Perpendicular                                   (DN_V2F32 a);
DN_API DN_V2F32                DN_V2F32Reflect                                         (DN_V2F32 in, DN_V2F32 surface);
DN_API DN_F32                  DN_V2F32Area                                            (DN_V2F32 a);

#define                        DN_V3F32From1N(x)                                       DN_Literal(DN_V3F32){{(DN_F32)(x),    (DN_F32)(x),    (DN_F32)(x)}}
#define                        DN_V3F32From3N(x, y, z)                                 DN_Literal(DN_V3F32){{(DN_F32)(x),    (DN_F32)(y),    (DN_F32)(z)}}
#define                        DN_V3F32FromV2F32And1N(xy, z)                           DN_Literal(DN_V3F32){{(DN_F32)(xy.x), (DN_F32)(xy.y), (DN_F32)(z)}}

// NOTE: Grayscale co-efficients from:
//   https://github.com/EpicGames/UnrealEngine/blob/260bb2e1c5610b31c63a36206eedd289409c5f11/Engine/Source/Runtime/Core/Private/Math/Color.cpp#L304
DN_V3F32 static const          DN_V3F32_RGB_LUMINANCE = DN_V3F32From3N(0.3f, 0.59f, 0.11f);

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, DN_I32   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, DN_I32   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, DN_I32   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, DN_I32   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_V3F32                DN_V3F32Lerp                                            (DN_V3F32 lhs, DN_F32 t01, DN_V3F32 rhs);
DN_API DN_F32                  DN_V3F32LengthSq                                        (DN_V3F32 a);
DN_API DN_F32                  DN_V3F32Length                                          (DN_V3F32 a);
DN_API DN_V3F32                DN_V3F32Normalise                                       (DN_V3F32 a);

#define                        DN_V4F32From1N(x)                                       DN_Literal(DN_V4F32){{(DN_F32)(x), (DN_F32)(x), (DN_F32)(x), (DN_F32)(x)}}
#define                        DN_V4F32From4N(x, y, z, w)                              DN_Literal(DN_V4F32){{(DN_F32)(x), (DN_F32)(y), (DN_F32)(z), (DN_F32)(w)}}
#define                        DN_V4F32FromV3And1N(xyz, w)                             DN_Literal(DN_V4F32){{xyz.x,        xyz.y,        xyz.z,        w}}
#define                        DN_V4F32Rgba01FromRgbaU8(r, g, b, a)                    DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f, a / 255.f}}
#define                        DN_V4F32Rgba01FromRgbU8(r, g, b)                        DN_Literal(DN_V4F32){{r / 255.f, g / 255.f, b / 255.f,       1.f}}

DN_API DN_V4F32                DN_V4F32Lerp                                            (DN_V4F32 lhs, DN_F32 t01, DN_V4F32 rhs);
DN_API bool                    DN_V4F32Rgba01IsValid                                   (DN_V4F32 rgba01);
DN_API DN_V4F32                DN_V4F32Rgba01FromRgbU32                                (DN_U32 u32);
DN_API DN_V4F32                DN_V4F32Rgba01FromRgbaU32                               (DN_U32 u32);
DN_API DN_V4F32                DN_V4F32Linear01FromSrgb01                              (DN_V4F32 rgb01);
DN_API DN_V4F32                DN_V4F32Linear01Desaturate                              (DN_V4F32 linear01, DN_F32 t01);
DN_API DN_V4F32                DN_V4F32Srgb01FromLinear01                              (DN_V4F32 linear01);

#define                        DN_V4F32FromV4Alpha(v4, alpha)                          DN_V4F32FromV3And1N(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, DN_I32   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, DN_I32   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_V4F32Dot                                             (DN_V4F32 a, DN_V4F32 b);

DN_API DN_M4                   DN_M4Identity                                           ();
DN_API DN_M4                   DN_M4ScaleF                                             (DN_F32 x, DN_F32 y, DN_F32 z);
DN_API DN_M4                   DN_M4Scale                                              (DN_V3F32 xyz);
DN_API DN_M4                   DN_M4TranslateF                                         (DN_F32 x, DN_F32 y, DN_F32 z);
DN_API DN_M4                   DN_M4Translate                                          (DN_V3F32 xyz);
DN_API DN_M4                   DN_M4Transpose                                          (DN_M4 mat);
DN_API DN_M4                   DN_M4Rotate                                             (DN_V3F32 axis, DN_F32 radians);
DN_API DN_M4                   DN_M4Orthographic                                       (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_M4Perspective                                        (DN_F32 fov /*radians*/, DN_F32 aspect, DN_F32 z_near, DN_F32 z_far);
DN_API DN_M4                   DN_M4Add                                                (DN_M4 lhs, DN_M4 rhs);
DN_API DN_M4                   DN_M4Sub                                                (DN_M4 lhs, DN_M4 rhs);
DN_API DN_M4                   DN_M4Mul                                                (DN_M4 lhs, DN_M4 rhs);
DN_API DN_M4                   DN_M4Div                                                (DN_M4 lhs, DN_M4 rhs);
DN_API DN_M4                   DN_M4AddF                                               (DN_M4 lhs, DN_F32 rhs);
DN_API DN_M4                   DN_M4SubF                                               (DN_M4 lhs, DN_F32 rhs);
DN_API DN_M4                   DN_M4MulF                                               (DN_M4 lhs, DN_F32 rhs);
DN_API DN_M4                   DN_M4DivF                                               (DN_M4 lhs, DN_F32 rhs);
DN_API DN_Str8x256             DN_M4ColumnMajorString                                  (DN_M4 mat);

DN_API bool                    DN_M2x3Eq                                               (DN_M2x3 const *lhs, DN_M2x3 const *rhs);
DN_API bool                    DN_M2x3NotEq                                            (DN_M2x3 const *lhs, DN_M2x3 const *rhs);
DN_API DN_M2x3                 DN_M2x3Identity                                         ();
DN_API DN_M2x3                 DN_M2x3Translate                                        (DN_V2F32 offset);
DN_API DN_V2F32                DN_M2x3ScaleGet                                         (DN_M2x3 m2x3);
DN_API DN_M2x3                 DN_M2x3Scale                                            (DN_V2F32 scale);
DN_API DN_M2x3                 DN_M2x3Rotate                                           (DN_F32 radians);
DN_API DN_M2x3                 DN_M2x3ProjFromV2F32                                    (DN_V2F32 size, DN_M2x3ProjOrigin origin);
DN_API DN_M2x3XForm            DN_M2x3XFormFrom2M2x3                                   (DN_M2x3 forward, DN_M2x3 inverse);
DN_API DN_M2x3XForm            DN_M2x3XFormFromTRS                                     (DN_V2F32 pos, DN_V2F32 scale, DN_F32 rotate_rads, DN_V2F32 pivot_pos);
DN_API DN_M2x3XForm            DN_M2x3XFormIdentity                                    ();
DN_API DN_M2x3XForm            DN_M2x3XFormMul                                         (DN_M2x3XForm m1, DN_M2x3XForm m2);
DN_API DN_M2x3                 DN_M2x3Mul                                              (DN_M2x3 m1, DN_M2x3 m2);
DN_API DN_V2F32                DN_M2x3Mul2F32                                          (DN_M2x3 m1, DN_F32 x, DN_F32 y);
DN_API DN_V2F32                DN_M2x3MulV2F32                                         (DN_M2x3 m1, DN_V2F32 v2);
DN_API DN_Rect                 DN_M2x3MulRect                                          (DN_M2x3 m1, DN_Rect rect);

#define                        DN_RectFrom2V2(pos, size)                               DN_Literal(DN_Rect){(pos), (size)}
#define                        DN_RectFrom4N(x, y, w, h)                               DN_Literal(DN_Rect){DN_Literal(DN_V2F32){{x, y}}, DN_Literal(DN_V2F32){{w, h}}}
#define                        DN_RectZero                                             DN_RectFrom4N(0, 0, 0, 0)

DN_API DN_V2F32                DN_RectCenter                                           (DN_Rect rect);
DN_API bool                    DN_RectContainsPoint                                    (DN_Rect rect, DN_V2F32 p);
DN_API bool                    DN_RectContainsRect                                     (DN_Rect a, DN_Rect b);
DN_API DN_Rect                 DN_RectExpand                                           (DN_Rect a, DN_F32 amount);
DN_API DN_Rect                 DN_RectExpandV2                                         (DN_Rect a, DN_V2F32 amount);
DN_API bool                    DN_RectIntersects                                       (DN_Rect a, DN_Rect b);
DN_API DN_Rect                 DN_RectIntersection                                     (DN_Rect a, DN_Rect b);
DN_API DN_Rect                 DN_RectUnion                                            (DN_Rect a, DN_Rect b);
DN_API DN_2V2F32               DN_RectRange                                            (DN_Rect a);
DN_API bool                    DN_RectEq                                               (DN_Rect lhs, DN_Rect rhs);
DN_API DN_F32                  DN_RectArea                                             (DN_Rect a);
DN_API DN_V2F32                DN_RectInterpV2F32                                      (DN_Rect rect, DN_V2F32 t01);
DN_API DN_V2F32                DN_RectTopLeft                                          (DN_Rect rect);
DN_API DN_V2F32                DN_RectTopRight                                         (DN_Rect rect);
DN_API DN_V2F32                DN_RectBottomLeft                                       (DN_Rect rect);
DN_API DN_V2F32                DN_RectBottomRight                                      (DN_Rect rect);

DN_API DN_Rect                 DN_RectCutLeftClip                                      (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);
DN_API DN_Rect                 DN_RectCutRightClip                                     (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);
DN_API DN_Rect                 DN_RectCutTopClip                                       (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);
DN_API DN_Rect                 DN_RectCutBottomClip                                    (DN_Rect *rect, DN_F32 amount, DN_RectCutClip clip);

#define                        DN_RectCutLeft(rect, amount)                            DN_RectCutLeftClip(rect, amount, DN_RectCutClip_Yes)
#define                        DN_RectCutRight(rect, amount)                           DN_RectCutRightClip(rect, amount, DN_RectCutClip_Yes)
#define                        DN_RectCutTop(rect, amount)                             DN_RectCutTopClip(rect, amount, DN_RectCutClip_Yes)
#define                        DN_RectCutBottom(rect, amount)                          DN_RectCutBottomClip(rect, amount, DN_RectCutClip_Yes)

#define                        DN_RectCutLeftNoClip(rect, amount)                      DN_RectCutLeftClip(rect, amount, DN_RectCutClip_No)
#define                        DN_RectCutRightNoClip(rect, amount)                     DN_RectCutRightClip(rect, amount, DN_RectCutClip_No)
#define                        DN_RectCutTopNoClip(rect, amount)                       DN_RectCutTopClip(rect, amount, DN_RectCutClip_No)
#define                        DN_RectCutBottomNoClip(rect, amount)                    DN_RectCutBottomClip(rect, amount, DN_RectCutClip_No)

DN_API DN_Rect                 DN_RectCutCut                                           (DN_RectCut rect_cut, DN_V2F32 size, DN_RectCutClip clip);
#define                        DN_RectCutInit(rect, side)                              DN_Literal(DN_RectCut){rect, side}

DN_API DN_RaycastV2            DN_RaycastLineIntersectV2                               (DN_V2F32 origin_a, DN_V2F32 dir_a, DN_V2F32 origin_b, DN_V2F32 dir_b);

// NOTE: Containers that are imlpemented using primarily 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.

// NOTE: Intrusive Singly Linked List
//   Define a struct with the members `next`:
//
//     struct MyLinkItem {
//       int         data;
//       MyLinkItem *next;
//     } my_link = {};
//
//     MyLinkItem *first_item = DN_ISinglyLLDetach(&my_link, MyLinkItem);
#define DN_ISinglyLLDetach(list) (decltype(list))DN_SinglyLLDetach((void **)&(list), (void **)&(list)->next)

// NOTE: Singly Linked List with Head and Tail pointer
/*
  struct MyLinkItem {
    int         data;
    MyLinkItem *next;
  } my_list = {};

  struct MyContainer {
    MyLinkItem *head;
    MyLinkItem *tail;
  };

  MyLinkItem  item = {};
  MyContainer container = {};
  DN_ISinglyHeadTailLLAppend(container, item);
  // ... or alternatively, DN_SinglyHeadTailLLAppend(container.head, container.tail, item);

  for (MyLinkItem *it = container.head; it; it = it->next) { }
*/
#define DN_SinglyHeadTailLLAppend(head, tail, to_append) \
  do {                                                   \
    if (!head)                                           \
      head = to_append;                                  \
    if (tail)                                            \
      tail->next = to_append;                            \
    tail = to_append;                                    \
  } while (0)
#define DN_ISinglyHeadTailLLAppend(container_ptr, to_append) DN_SinglyHeadTailLLAppend((container_ptr)->head, (container_ptr)->tail, to_append)

// NOTE: Sentinel Doubly Linked List
//   Uses a sentinel/dummy node as the list head. The sentinel points to itself when empty.
//   Define a struct with the members `next` and `prev`:
//
//     struct MyLinkItem {
//       int         data;
//       MyLinkItem *next;
//       MyLinkItem *prev;
//     } my_list = {};
//
//     DN_SentinelDoublyLLInit(&my_list);
//     DN_SentinelDoublyLLAppend(&my_list, &new_item);
//     DN_SentinelDoublyLLForEach(it, &my_list) { /* ... */ }
//
#define DN_SentinelDoublyLLInit(list)             (list)->next = (list)->prev = (list)
#define DN_SentinelDoublyLLIsSentinel(list, item) ((list) == (item))
#define DN_SentinelDoublyLLIsEmpty(list)          (!(list) || ((list) == (list)->next))
#define DN_SentinelDoublyLLIsInit(list)           ((list)->next && (list)->prev)
#define DN_SentinelDoublyLLHasItems(list)         ((list) && ((list) != (list)->next))
#define DN_SentinelDoublyLLForEach(it, list)      auto *it = (list)->next; (it) != (list); (it) = (it)->next

#define DN_SentinelDoublyLLInitArena(list, T, ptr_arena)   \
  do {                                                     \
    (list) = DN_ArenaNew(ptr_arena, T, DN_ZMem_Yes); \
    DN_SentinelDoublyLLInit(list);                         \
  } while (0)

#define DN_SentinelDoublyLLInitPool(list, T, pool) \
  do {                                             \
    (list) = DN_PoolNew(pool, T);                  \
    DN_SentinelDoublyLLInit(list);                 \
  } while (0)

#define DN_SentinelDoublyLLDetach(item)  \
  do {                                   \
    if (item) {                          \
      (item)->prev->next = (item)->next; \
      (item)->next->prev = (item)->prev; \
      (item)->next       = nullptr;      \
      (item)->prev       = nullptr;      \
    }                                    \
  } while (0)

#define DN_SentinelDoublyLLDequeue(list, dest_ptr) \
  if (DN_SentinelDoublyLLHasItems(list)) {         \
    dest_ptr = (list)->next;                       \
    DN_SentinelDoublyLLDetach(dest_ptr);           \
  }

#define DN_SentinelDoublyLLAppend(list, item) \
  do {                                        \
    if (item) {                               \
      if ((item)->next)                       \
        DN_SentinelDoublyLLDetach(item);      \
      (item)->next       = (list)->next;      \
      (item)->prev       = (list);            \
      (item)->next->prev = (item);            \
      (item)->prev->next = (item);            \
    }                                         \
  } while (0)

#define DN_SentinelDoublyLLPrepend(list, item) \
  do {                                         \
    if (item) {                                \
      if ((item)->next)                        \
        DN_SentinelDoublyLLDetach(item);       \
      (item)->next       = (list);             \
      (item)->prev       = (list)->prev;       \
      (item)->next->prev = (item);             \
      (item)->prev->next = (item);             \
    }                                          \
  } while (0)

// NOTE: Doubly Linked List
//   Define a struct with the members `next` and `prev`. This list has null pointers for head->prev
//   and tail->next.
//
//     struct MyLinkItem {
//       int         data;
//       MyLinkItem *next;
//       MyLinkItem *prev;
//     } my_link = {};
//
//     MyLinkItem first_item = {}, second_item = {};
//     DN_DoublyLLAppend(&first_item, &second_item); // first_item -> second_item
//
#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);                                                       \
        if ((ptr)->next)                                                            \
          (ptr)->next->prev = (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);                                                       \
        if ((ptr)->prev)                                                            \
          (ptr)->prev->next = (ptr);                                                \
      } else {                                                                      \
        (head) = (ptr);                                                             \
      }                                                                             \
    }                                                                               \
  } while (0)

// NOTE: For C++ we need to cast the void* returned in these functions to the concrete type. In C,
// no cast is needed.
#if defined(__cplusplus)
  #define DN_CppDeclType(x) decltype(x)
#else
  #define DN_CppDeclType
#endif

// NOTE: Arrays
//   Data structures that have a `T *data`, `DN_USize count` and `DN_USize max` capacity that can be
//   dynamically shrunk or expanded.
//
//   API
//     Resize:  Resizes the array to `new_max` erase elements if resizing to a smaller size
//     Reserve: Expands the capacity of the array if `new_max > max` otherwise no-op
//     Prepare: Expands the capacity of the array if `count + add_count > max` otherwise no-op
//
//   Variants
//     PArray => Pointer (to) Array
//     LArray => Literal Array
//       Define a C array and size. (P) array macros take a pointer to the aray, its size and its max
//       capacity. The (L) array macros take the literal array and derives the max capacity
//       automatically using DN_ArrayCountU(l_array).
//
//         MyStruct buffer[TB_ASType_Count] = {};
//         DN_USize count                    = 0;
//         MyStruct *item_0                 = DN_PArrayMake(buffer, &size, DN_ArrayCountU(buffer), DN_ZMem_No);
//         MyStruct *item_1                 = DN_LArrayMake(buffer, &size, DN_ZMem_No);
//
//     IArray => Intrusive Array
//       Define a struct with the members `data`, `count` and `max`:
//
//         struct MyArray {
//           MyStruct *data;
//           DN_USize  count;
//           DN_USize  max;
//         } my_array = {};
//         DN_Arena arena = {};
//         DN_IArrayResizeArena(&my_array, &arena, 256);
//         MyStruct *item = DN_IArrayMake(&my_array, DN_ZMem_No);
//
#if defined(__cplusplus)
  #define DN_PArrayFind(ptr, size, ptr_find, eq_func)                              DN_TArrayFind(ptr, size, ptr_find, eq_func)
  #define DN_PArrayFindMemEq(ptr, size, ptr_find)                                  DN_TArrayFindMemEq(ptr, size, ptr_find)
  #define DN_PArrayResizePool(ptr, ptr_size, ptr_max, pool, new_max)               DN_TArrayResizePool(&(ptr), ptr_size, ptr_max, pool, new_max)
  #define DN_PArrayResizeArena(ptr, ptr_size, ptr_max, arena, new_max)             DN_TArrayResizeArena(&(ptr), ptr_size, ptr_max, arena, new_max)
  #define DN_PArrayReservePool(ptr, ptr_max, pool, new_max)                        DN_TArrayReservePool(&(ptr), ptr_max, pool, new_max)
  #define DN_PArrayReserveArena(ptr, ptr_max, arena, new_max)                      DN_TArrayReserveArena(&(ptr), ptr_max, arena, new_max)
  #define DN_PArrayPreparePool(ptr, size, ptr_max, pool, add_count)                DN_TArrayPreparePool(&(ptr), size, ptr_max, pool, add_count)
  #define DN_PArrayPrepareArena(ptr, size, ptr_max, arena, add_count)              DN_TArrayPrepareArena(&(ptr), size, ptr_max, arena, add_count)
  #define DN_PArrayCopyPtrPool(dest, src, count_, pool)                            DN_TArrayCopyPtrPool(&(dest), src, count_, pool)
  #define DN_PArrayCopyPtrPoolAssert(dest, src, count_, pool)                      DN_TArrayCopyPtrPoolAssert(&(dest), src, count_, pool, DN_CallSiteNow)
  #define DN_PArrayCopyPtrArena(dest, src, count_, arena)                          DN_TArrayCopyPtrArena(&(dest), src, count_, arena)
  #define DN_PArrayCopyPtrArenaAssert(dest, src, count_, arena)                    DN_TArrayCopyPtrArenaAssert(&(dest), src, count_, arena, DN_CallSiteNow)

  #define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem)                     DN_TArrayMakeArray(ptr, ptr_size, max, count, z_mem)
  #define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count)                           DN_TArrayMakeArray(ptr, ptr_size, max, count, DN_ZMem_Yes)
  #define DN_PArrayMakeArrayNoZ(ptr, ptr_size, max, count)                         DN_TArrayMakeArray(ptr, ptr_size, max, count, DN_ZMem_No)
  #define DN_PArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem)               DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem, DN_CallSiteNow)
  #define DN_PArrayMakeArrayAssertZ(ptr, ptr_size, max, count)                     DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, DN_ZMem_Yes, DN_CallSiteNow)
  #define DN_PArrayMakeArrayAssertNoZ(ptr, ptr_size, max, count)                   DN_TArrayMakeArrayAssert(ptr, ptr_size, max, count, DN_ZMem_No, DN_CallSiteNow)
  #define DN_PArrayMakeArrayArena(ptr, ptr_size, ptr_max, arena, count, z_mem)     DN_TArrayMakeArrayArena(&(ptr), ptr_size, ptr_max, arena, count, z_mem)
  #define DN_PArrayMakeArrayArenaZ(ptr, ptr_size, ptr_max, arena, count)           DN_TArrayMakeArrayArena(&(ptr), ptr_size, ptr_max, arena, count, DN_ZMem_Yes)
  #define DN_PArrayMakeArrayArenaNoZ(ptr, ptr_size, ptr_max, arena, count)         DN_TArrayMakeArrayArena(&(ptr), ptr_size, ptr_max, arena, count, DN_ZMem_No)
  #define DN_PArrayMakeArrayPool(ptr, ptr_size, ptr_max, pool, count, z_mem)       DN_TArrayMakeArrayPool(&(ptr), ptr_size, ptr_max, pool, count, z_mem)
  #define DN_PArrayMakeArrayPoolZ(ptr, ptr_size, ptr_max, pool, count)             DN_TArrayMakeArrayPool(&(ptr), ptr_size, ptr_max, pool, count, DN_ZMem_Yes)
  #define DN_PArrayMakeArrayPoolNoZ(ptr, ptr_size, ptr_max, pool, count)           DN_TArrayMakeArrayPool(&(ptr), ptr_size, ptr_max, pool, count, DN_ZMem_No)

  #define DN_PArrayMake(ptr, ptr_size, max, z_mem)                                 DN_TArrayMakeArray(ptr, ptr_size, max, 1, z_mem)
  #define DN_PArrayMakeZ(ptr, ptr_size, max)                                       DN_TArrayMakeArray(ptr, ptr_size, max, 1, DN_ZMem_Yes)
  #define DN_PArrayMakeNoZ(ptr, ptr_size, max)                                     DN_TArrayMakeArray(ptr, ptr_size, max, 1, DN_ZMem_No)
  #define DN_PArrayMakeAssert(ptr, ptr_size, max, z_mem)                           DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, z_mem, DN_CallSiteNow)
  #define DN_PArrayMakeAssertZ(ptr, ptr_size, max)                                 DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, DN_ZMem_Yes, DN_CallSiteNow)
  #define DN_PArrayMakeAssertNoZ(ptr, ptr_size, max)                               DN_TArrayMakeArrayAssert(ptr, ptr_size, max, 1, DN_ZMem_No, DN_CallSiteNow)
  #define DN_PArrayMakeArena(ptr, ptr_size, ptr_max, arena, z_mem)                 DN_TArrayMakeArrayArena(&(ptr), ptr_size, ptr_max, arena, 1, z_mem)
  #define DN_PArrayMakeArenaZ(ptr, ptr_size, ptr_max, arena)                       DN_TArrayMakeArrayArena(&(ptr), ptr_size, ptr_max, arena, 1, DN_ZMem_Yes)
  #define DN_PArrayMakeArenaNoZ(ptr, ptr_size, ptr_max, arena)                     DN_TArrayMakeArrayArena(&(ptr), ptr_size, ptr_max, arena, 1, DN_ZMem_No)
  #define DN_PArrayMakePool(ptr, ptr_size, ptr_max, pool, z_mem)                   DN_TArrayMakeArrayPool(&(ptr), ptr_size, ptr_max, pool, 1, z_mem)
  #define DN_PArrayMakePoolZ(ptr, ptr_size, ptr_max, pool)                         DN_TArrayMakeArrayPool(&(ptr), ptr_size, ptr_max, pool, 1, DN_ZMem_Yes)
  #define DN_PArrayMakePoolNoZ(ptr, ptr_size, ptr_max, pool)                       DN_TArrayMakeArrayPool(&(ptr), ptr_size, ptr_max, pool, 1, DN_ZMem_No)

  #define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add)                 DN_TArrayAddArray(ptr, ptr_size, max, items, count, add)
  #define DN_PArrayAddArrayArena(ptr, ptr_size, ptr_max, arena, items, count, add) DN_TArrayAddArrayArena(&(ptr), ptr_size, ptr_max, arena, items, count, add)
  #define DN_PArrayAddArrayPool(ptr, ptr_size, ptr_max, pool, items, count, add)   DN_TArrayAddArrayPool(&(ptr), ptr_size, ptr_max, pool, items, count, add)
  #define DN_PArrayAdd(ptr, ptr_size, max, item, add)                              DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, add)
  #define DN_PArrayAddArena(ptr, ptr_size, ptr_max, arena, item, add)              DN_TArrayAddArrayArena(&(ptr), ptr_size, ptr_max, arena, &item, 1, add)
  #define DN_PArrayAddPool(ptr, ptr_size, ptr_max, pool, item, add)                DN_TArrayAddArrayPool(&(ptr), ptr_size, ptr_max, pool, &item, 1, add)
  #define DN_PArrayAppendArray(ptr, ptr_size, max, items, count)                   DN_TArrayAddArray(ptr, ptr_size, max, items, count, DN_AddType_Append)
  #define DN_PArrayAppend(ptr, ptr_size, max, item)                                DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, DN_AddType_Append)
  #define DN_PArrayAppendArena(ptr, ptr_size, ptr_max, arena, item)                DN_TArrayAddArrayArena(&(ptr), ptr_size, ptr_max, arena, &item, 1, DN_AddType_Append)
  #define DN_PArrayAppendPool(ptr, ptr_size, ptr_max, pool, item)                  DN_TArrayAddArrayPool(&(ptr), ptr_size, ptr_max, pool, &item, 1, DN_AddType_Append)
  #define DN_PArrayAppendAssert(ptr, ptr_size, max, item)                          DN_TArrayAddArrayAssert(ptr, ptr_size, max, &item, 1, DN_AddType_Append, DN_CallSiteNow)
  #define DN_PArrayPrependArray(ptr, ptr_size, max, items, count)                  DN_TArrayAddArray(ptr, ptr_size, max, items, count, DN_AddType_Prepend)
  #define DN_PArrayPrepend(ptr, ptr_size, max, item)                               DN_TArrayAddArray(ptr, ptr_size, max, &item, 1, DN_AddType_Prepend)
  #define DN_PArrayPrependArena(ptr, ptr_size, ptr_max, arena, item)               DN_TArrayAddArrayArena(&(ptr), ptr_size, ptr_max, arena, &item, 1, DN_AddType_Prepend)
  #define DN_PArrayPrependPool(ptr, ptr_size, ptr_max, pool, item)                 DN_TArrayAddArrayPool(&(ptr), ptr_size, ptr_max, pool, &item, 1, DN_AddType_Prepend)

  #define DN_PArrayEraseRange(ptr, ptr_size, begin_index, count, erase)            DN_TArrayEraseRange(ptr, ptr_size, begin_index, count, erase)
  #define DN_PArrayErase(ptr, ptr_size, index, erase)                              DN_TArrayEraseRange(ptr, ptr_size, index, 1, erase)
  #define DN_PArrayInsertArray(ptr, ptr_size, max, index, items, count)            DN_TArrayInsertArray(ptr, ptr_size, max, index, items, count)
  #define DN_PArrayInsert(ptr, ptr_size, max, index, item)                         DN_TArrayInsertArray(ptr, ptr_size, max, index, &item, 1)
  #define DN_PArrayPopFront(ptr, ptr_size, max, count)                             DN_TArrayPopFront(ptr, ptr_size, count)
  #define DN_PArrayPopBack(ptr, ptr_size, max, count)                              DN_TArrayPopBack(ptr, ptr_size, count)
#else
  #define DN_PArrayFind(ptr, size, ptr_find, eq_func)                          DN_ArrayFind(ptr, size, sizeof(*(ptr)), ptr_find, eq_func)
  #define DN_PArrayFindMemEq(ptr, size, ptr_find)                              DN_ArrayFindMemEq(ptr, size, sizeof(*(ptr)), ptr_find)
  #define DN_PArrayResizePool(ptr, ptr_size, ptr_max, pool, new_max)           DN_ArrayResizePool((void **)&(ptr), ptr_size, ptr_max, sizeof((ptr)[0]), pool, new_max)
  #define DN_PArrayResizeArena(ptr, ptr_size, ptr_max, arena, new_max)         DN_ArrayResizeArena((void **)&(ptr), ptr_size, ptr_max, sizeof((ptr)[0]), arena, new_max)
  #define DN_PArrayReservePool(ptr, ptr_max, pool, new_max)                    DN_ArrayReservePool((void **)&(ptr), ptr_max, sizeof((ptr)[0]), pool, new_max)
  #define DN_PArrayReserveArena(ptr, ptr_max, arena, new_max)                  DN_ArrayReserveArena((void **)&(ptr), ptr_max, sizeof((ptr)[0]), arena, new_max)
  #define DN_PArrayPreparePool(ptr, size, ptr_max, pool, add_count)            DN_ArrayPreparePool((void **)(ptr), size, ptr_max, sizeof((*ptr)[0]), pool, add_count)
  #define DN_PArrayPrepareArena(ptr, size, ptr_max, arena, add_count)          DN_ArrayPrepareArena((void **)(ptr), size, ptr_max, sizeof((*ptr)[0]), arena, add_count)
  #define DN_PArrayCopyPtrPool(ptr_array, src, count_, pool)                   DN_ArrayCopyPtrPool(ptr_array, src, count_, sizeof((*ptr)[0]), pool)
  #define DN_PArrayCopyPtrPoolAssert(ptr_array, src, count_, pool)             DN_ArrayCopyPtrPool(ptr_array, src, count_, sizeof((*ptr)[0]), pool, DN_CallSiteNow)
  #define DN_PArrayCopyPtrArena(ptr_array, src, count_, arena)                 DN_ArrayCopyPtrArena(ptr_array, src, count_, sizeof((*ptr)[0]), arena)
  #define DN_PArrayCopyPtrArenaAssert(ptr_array, src, count_, arena)           DN_ArrayCopyPtrArena(ptr_array, src, count_, sizeof((*ptr)[0]), arena, DN_CallSiteNow)

  #define DN_PArrayMakeArray(ptr, ptr_size, max, count, z_mem)                 (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem)
  #define DN_PArrayMakeArrayZ(ptr, ptr_size, max, count)                       (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes)
  #define DN_PArrayMakeArrayNoZ(ptr, ptr_size, max, count)                     (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_No)
  #define DN_PArrayMakeArrayAssert(ptr, ptr_size, max, count, z_mem)           (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, z_mem, DN_CallSiteNow)
  #define DN_PArrayMakeArrayAssertZ(ptr, ptr_size, max, count)                 (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_Yes, DN_CallSiteNow)
  #define DN_PArrayMakeArrayAssertNoZ(ptr, ptr_size, max, count)               (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), count, DN_ZMem_No, DN_CallSiteNow)
  #define DN_PArrayMakeArrayArena(ptr, ptr_size, max, arena, count, z_mem)     (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, count, z_mem)
  #define DN_PArrayMakeArrayArenaZ(ptr, ptr_size, max, arena, count)           (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, count, DN_ZMem_Yes)
  #define DN_PArrayMakeArrayArenaNoZ(ptr, ptr_size, max, arena, count)         (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, count, DN_ZMem_No)
  #define DN_PArrayMakeArrayPool(ptr, ptr_size, max, pool, count, z_mem)       (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, count, z_mem)
  #define DN_PArrayMakeArrayPoolZ(ptr, ptr_size, max, pool, count)             (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, count, DN_ZMem_Yes)
  #define DN_PArrayMakeArrayPoolNoZ(ptr, ptr_size, max, pool, count)           (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, count, DN_ZMem_No)

  #define DN_PArrayMake(ptr, ptr_size, max, z_mem)                             (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem)
  #define DN_PArrayMakeZ(ptr, ptr_size, max)                                   (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes)
  #define DN_PArrayMakeNoZ(ptr, ptr_size, max)                                 (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArray(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_No)
  #define DN_PArrayMakeAssert(ptr, ptr_size, max, z_mem)                       (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, z_mem, DN_CallSiteNow)
  #define DN_PArrayMakeAssertZ(ptr, ptr_size, max)                             (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_Yes, DN_CallSiteNow)
  #define DN_PArrayMakeAssertNoZ(ptr, ptr_size, max)                           (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), 1, DN_ZMem_No, DN_CallSiteNow)
  #define DN_PArrayMakeArena(ptr, ptr_size, max, arena, z_mem)                 (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, 1, z_mem)
  #define DN_PArrayMakeArenaZ(ptr, ptr_size, max, arena)                       (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, 1, DN_ZMem_Yes)
  #define DN_PArrayMakeArenaNoZ(ptr, ptr_size, max, arena)                     (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, 1, DN_ZMem_No)
  #define DN_PArrayMakePool(ptr, ptr_size, max, pool, z_mem)                   (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, 1, z_mem)
  #define DN_PArrayMakePoolZ(ptr, ptr_size, max, pool)                         (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, 1, DN_ZMem_Yes)
  #define DN_PArrayMakePoolNoZ(ptr, ptr_size, max, pool)                       (DN_CppDeclType(&(ptr)[0]))DN_ArrayMakeArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, 1, DN_ZMem_No)

  #define DN_PArrayAddArray(ptr, ptr_size, max, items, count, add)             (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, add)
  #define DN_PArrayAddArrayArena(ptr, ptr_size, max, arena, items, count, add) (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, items, count, add)
  #define DN_PArrayAddArrayPool(ptr, ptr_size, max, pool, items, count, add)   (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, items, count, add)
  #define DN_PArrayAdd(ptr, ptr_size, max, item, add)                          (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, add)
  #define DN_PArrayAddArena(ptr, ptr_size, max, arena, item, add)              (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, &item, 1, add)
  #define DN_PArrayAddPool(ptr, ptr_size, max, pool, item, add)                (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, &item, 1, add)
  #define DN_PArrayAppendArray(ptr, ptr_size, max, items, count)               (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_AddType_Append)
  #define DN_PArrayAppend(ptr, ptr_size, max, item)                            (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_AddType_Append)
  #define DN_PArrayAppendArena(ptr, ptr_size, max, arena, item)                (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, &item, 1, DN_AddType_Append)
  #define DN_PArrayAppendPool(ptr, ptr_size, max, pool, item)                  (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, &item, 1, DN_AddType_Append)
  #define DN_PArrayAppendAssert(ptr, ptr_size, max, item)                      (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayAssert(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_AddType_Append, DN_CallSiteNow)
  #define DN_PArrayPrependArray(ptr, ptr_size, max, items, count)              (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), items, count, DN_AddType_Prepend)
  #define DN_PArrayPrepend(ptr, ptr_size, max, item)                           (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArray(ptr, ptr_size, max, sizeof((ptr)[0]), &item, 1, DN_AddType_Prepend)
  #define DN_PArrayPrependArena(ptr, ptr_size, max, arena, item)               (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayArena((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), arena, &item, 1, DN_AddType_Prepend)
  #define DN_PArrayPrependPool(ptr, ptr_size, max, pool, item)                 (DN_CppDeclType(&(ptr)[0]))DN_ArrayAddArrayPool((void **)&(ptr), ptr_size, max, sizeof((ptr)[0]), pool, &item, 1, DN_AddType_Prepend)

  #define DN_PArrayEraseRange(ptr, ptr_size, begin_index, count, erase)        DN_ArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), begin_index, count, erase)
  #define DN_PArrayErase(ptr, ptr_size, index, erase)                          DN_ArrayEraseRange(ptr, ptr_size, sizeof((ptr)[0]), index, 1, erase)
  #define DN_PArrayInsertArray(ptr, ptr_size, max, index, items, count)        (DN_CppDeclType(&(ptr)[0]))DN_ArrayInsertArray(ptr, ptr_size, max, sizeof((ptr)[0]), index, items, count)
  #define DN_PArrayInsert(ptr, ptr_size, max, index, item)                     (DN_CppDeclType(&(ptr)[0]))DN_ArrayInsertArray(ptr, ptr_size, max, sizeof((ptr)[0]), index, &item, 1)
  #define DN_PArrayPopFront(ptr, ptr_size, max, count)                         (DN_CppDeclType(&(ptr)[0]))DN_ArrayPopFront(ptr, ptr_size, sizeof((ptr)[0]), count)
  #define DN_PArrayPopBack(ptr, ptr_size, max, count)                          (DN_CppDeclType(&(ptr)[0]))DN_ArrayPopBack(ptr, ptr_size, sizeof((ptr)[0]), count)
#endif

#define                                   DN_LArrayFind(c_array, size, ptr_find, eq_func)                      DN_PArrayFind(c_array, size, ptr_find, eq_func)
#define                                   DN_LArrayFindMemEq(c_array, size, ptr_find)                          DN_PArrayFindMemEq(c_array, size, ptr_find)

#define                                   DN_LArrayMakeArray(c_array, ptr_size, count, z_mem)                  DN_PArrayMakeArray(c_array, ptr_size, DN_ArrayCountU(c_array), count, z_mem)
#define                                   DN_LArrayMakeArrayZ(c_array, ptr_size, count)                        DN_PArrayMakeArrayZ(c_array, ptr_size, DN_ArrayCountU(c_array), count)
#define                                   DN_LArrayMakeArrayNoZ(c_array, ptr_size, count)                      DN_PArrayMakeArrayNoZ(c_array, ptr_size, DN_ArrayCountU(c_array), count)
#define                                   DN_LArrayMakeArrayAssert(c_array, ptr_size, count, z_mem)            DN_PArrayMakeArrayAssert(c_array, ptr_size, DN_ArrayCountU(c_array), count, z_mem)
#define                                   DN_LArrayMakeArrayAssertZ(c_array, ptr_size, count)                  DN_PArrayMakeArrayAssertZ(c_array, ptr_size, DN_ArrayCountU(c_array), count)
#define                                   DN_LArrayMakeArrayAssertNoZ(c_array, ptr_size, count)                DN_PArrayMakeArrayAssertNoZ(c_array, ptr_size, DN_ArrayCountU(c_array), count)

#define                                   DN_LArrayMake(c_array, ptr_size, z_mem)                              DN_PArrayMake(c_array, ptr_size, DN_ArrayCountU(c_array), z_mem)
#define                                   DN_LArrayMakeZ(c_array, ptr_size)                                    DN_PArrayMakeZ(c_array, ptr_size, DN_ArrayCountU(c_array))
#define                                   DN_LArrayMakeNoZ(c_array, ptr_size)                                  DN_PArrayMakeNoZ(c_array, ptr_size, DN_ArrayCountU(c_array))
#define                                   DN_LArrayMakeAssert(c_array, ptr_size, z_mem)                        DN_PArrayMakeAssert(c_array, ptr_size, DN_ArrayCountU(c_array), z_mem)
#define                                   DN_LArrayMakeAssertZ(c_array, ptr_size)                              DN_PArrayMakeAssertZ(c_array, ptr_size, DN_ArrayCountU(c_array))
#define                                   DN_LArrayMakeAssertNoZ(c_array, ptr_size)                            DN_PArrayMakeAssertNoZ(c_array, ptr_size, DN_ArrayCountU(c_array))

#define                                   DN_LArrayAddArray(c_array, ptr_size, items, count, add)              DN_PArrayAddArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count, add)
#define                                   DN_LArrayAdd(c_array, ptr_size, item, add)                           DN_PArrayAdd(c_array, ptr_size, DN_ArrayCountU(c_array), item, add)
#define                                   DN_LArrayAppendArray(c_array, ptr_size, items, count)                DN_PArrayAppendArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count)
#define                                   DN_LArrayAppend(c_array, ptr_size, item)                             DN_PArrayAppend(c_array, ptr_size, DN_ArrayCountU(c_array), item)
#define                                   DN_LArrayAppendAssert(c_array, ptr_size, item)                       DN_PArrayAppendAssert(c_array, ptr_size, DN_ArrayCountU(c_array), item)
#define                                   DN_LArrayPrependArray(c_array, ptr_size, items, count)               DN_PArrayPrependArray(c_array, ptr_size, DN_ArrayCountU(c_array), items, count)
#define                                   DN_LArrayPrepend(c_array, ptr_size, item)                            DN_PArrayPrepend(c_array, ptr_size, DN_ArrayCountU(c_array), item)
#define                                   DN_LArrayEraseRange(c_array, ptr_size, begin_index, count, erase)    DN_PArrayEraseRange(c_array, ptr_size, begin_index, count, erase)
#define                                   DN_LArrayErase(c_array, ptr_size, index, erase)                      DN_PArrayErase(c_array, ptr_size, index, erase)
#define                                   DN_LArrayInsertArray(c_array, ptr_size, index, items, count)         DN_PArrayInsertArray(c_array, ptr_size, DN_ArrayCountU(c_array), index, items, count)
#define                                   DN_LArrayInsert(c_array, ptr_size, index, item)                      DN_PArrayInsert(c_array, ptr_size, DN_ArrayCountU(c_array), index, item)
#define                                   DN_LArrayPopFront(c_array, ptr_size, count)                          DN_PArrayPopFront(c_array, ptr_size, DN_ArrayCountU(c_array), count)
#define                                   DN_LArrayPopBack(c_array, ptr_size, count)                           DN_PArrayPopBack(c_array, ptr_size, DN_ArrayCountU(c_array), count)

#define                                   DN_IArrayFind(ptr_array, ptr_find, eq_func)                          DN_PArrayFind((ptr_array)->data, (ptr_array)->count, ptr_find, eq_func)
#define                                   DN_IArrayFindMemEq(ptr_array, ptr_find)                              DN_PArrayFindMemEq((ptr_array)->data, (ptr_array)->count, ptr_find)
#define                                   DN_IArrayResizePool(ptr_array, pool, new_max)                        DN_PArrayResizePool((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool, new_max)
#define                                   DN_IArrayResizeArena(ptr_array, arena, new_max)                      DN_PArrayResizeArena((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena, new_max)
#define                                   DN_IArrayReservePool(ptr_array, pool, new_max)                       DN_PArrayReservePool((ptr_array)->data, &(ptr_array)->max, pool, new_max)
#define                                   DN_IArrayReserveArena(ptr_array, arena, new_max)                     DN_PArrayReserveArena((ptr_array)->data, &(ptr_array)->max, arena, new_max)
#define                                   DN_IArrayPreparePool(ptr_array, pool, add_count)                     DN_PArrayPreparePool(&(ptr_array)->data, (ptr_array)->count, &(ptr_array)->max, pool, add_count)
#define                                   DN_IArrayPrepareArena(ptr_array, arena, add_count)                   DN_PArrayPrepareArena(&(ptr_array)->data, (ptr_array)->count, &(ptr_array)->max, arena, add_count)
#define                                   DN_IArrayCopyPtrPool(ptr_array, src, count_, pool)                   (ptr_array)->max = (ptr_array)->count = DN_PArrayCopyPtrPool((ptr_array), src, count_, pool)
#define                                   DN_IArrayCopyPtrPoolAssert(ptr_array, src, count_, pool)             (ptr_array)->max = (ptr_array)->count = DN_PArrayCopyPtrPoolAssert((ptr_array), src, count_, pool)
#define                                   DN_IArrayCopyPtrArena(ptr_array, src, count_, arena)                 (ptr_array)->max = (ptr_array)->count = DN_PArrayCopyPtrArena((ptr_array), src, count_, arena)
#define                                   DN_IArrayCopyPtrArenaAssert(ptr_array, src, count_, arena)           (ptr_array)->max = (ptr_array)->count = DN_PArrayCopyPtrArenaAssert((ptr_array), src, count_, arena)

#define                                   DN_IArrayMakeArray(ptr_array, count_, z_mem)                         DN_PArrayMakeArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count_, z_mem)
#define                                   DN_IArrayMakeArrayZ(ptr_array, count_)                               DN_PArrayMakeArrayZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count_)
#define                                   DN_IArrayMakeArrayNoZ(ptr_array, count_)                             DN_PArrayMakeArrayNoZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count_)
#define                                   DN_IArrayMakeArrayAssert(ptr_array, count_, z_mem)                   DN_PArrayMakeArrayAssert((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count_, z_mem)
#define                                   DN_IArrayMakeArrayAssertZ(ptr_array, count_)                         DN_PArrayMakeArrayAssertZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count_)
#define                                   DN_IArrayMakeArrayAssertNoZ(ptr_array, count_)                       DN_PArrayMakeArrayAssertNoZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count_)
#define                                   DN_IArrayMakeArrayArena(ptr_array, arena, count_, z_mem)             DN_PArrayMakeArrayArena((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena, count_, z_mem)
#define                                   DN_IArrayMakeArrayArenaZ(ptr_array, arena, count_)                   DN_PArrayMakeArrayArenaZ((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena, count_)
#define                                   DN_IArrayMakeArrayArenaNoZ(ptr_array, arena, count_)                 DN_PArrayMakeArrayArenaNoZ((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena, count_)
#define                                   DN_IArrayMakeArrayPool(ptr_array, pool, count_, z_mem)               DN_PArrayMakeArrayPool((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool, count_, z_mem)
#define                                   DN_IArrayMakeArrayPoolZ(ptr_array, pool, count_)                     DN_PArrayMakeArrayPoolZ((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool, count_)
#define                                   DN_IArrayMakeArrayPoolNoZ(ptr_array, pool, count_)                   DN_PArrayMakeArrayPoolNoZ((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool, count_)

#define                                   DN_IArrayMake(ptr_array, z_mem)                                      DN_PArrayMake((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, z_mem)
#define                                   DN_IArrayMakeZ(ptr_array)                                            DN_PArrayMakeZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max)
#define                                   DN_IArrayMakeNoZ(ptr_array)                                          DN_PArrayMakeNoZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max)
#define                                   DN_IArrayMakeAssert(ptr_array, z_mem)                                DN_PArrayMakeAssert((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, z_mem)
#define                                   DN_IArrayMakeAssertZ(ptr_array)                                      DN_PArrayMakeAssertZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max)
#define                                   DN_IArrayMakeAssertNoZ(ptr_array)                                    DN_PArrayMakeAssertNoZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max)
#define                                   DN_IArrayMakeArena(ptr_array, arena, z_mem)                          DN_PArrayMakeArena((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena, z_mem)
#define                                   DN_IArrayMakeArenaZ(ptr_array, arena)                                DN_PArrayMakeArenaZ((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena)
#define                                   DN_IArrayMakeArenaNoZ(ptr_array, arena)                              DN_PArrayMakeArenaNoZ((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena)
#define                                   DN_IArrayMakePool(ptr_array, pool, z_mem)                            DN_PArrayMakePool((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool, z_mem)
#define                                   DN_IArrayMakePoolZ(ptr_array, pool)                                  DN_PArrayMakePoolZ((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool)
#define                                   DN_IArrayMakePoolNoZ(ptr_array, pool)                                DN_PArrayMakePoolNoZ((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool)
#define                                   DN_IArrayMakeNoZ(ptr_array)                                          DN_PArrayMakeNoZ((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max)

#define                                   DN_IArrayAddArray(ptr_array, items, count_, add)                     DN_PArrayAddArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, items, count_, add)
#define                                   DN_IArrayAddArrayArena(ptr_array, arena, items, count_, add)         DN_PArrayAddArrayArena((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena, items, count_, add)
#define                                   DN_IArrayAddArrayPool(ptr_array, pool, items, count_, add)           DN_PArrayAddArrayPool((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool, items, count_, add)
#define                                   DN_IArrayAdd(ptr_array, item, add)                                   DN_PArrayAdd((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, item, add)
#define                                   DN_IArrayAddArena(ptr_array, arena, item, add)                       DN_PArrayAddArena((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena, item, add)
#define                                   DN_IArrayAddPool(ptr_array, pool, item, add)                         DN_PArrayAddPool((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool, item, add)
#define                                   DN_IArrayAppendArray(ptr_array, items, count_)                       DN_PArrayAppendArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, items, count_)
#define                                   DN_IArrayAppend(ptr_array, item)                                     DN_PArrayAppend((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, item)
#define                                   DN_IArrayAppendArena(ptr_array, arena, item)                         DN_PArrayAppendArena((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena, item)
#define                                   DN_IArrayAppendPool(ptr_array, pool, item)                           DN_PArrayAppendPool((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool, item)
#define                                   DN_IArrayAppendAssert(ptr_array, item)                               DN_PArrayAppendAssert((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, item)
#define                                   DN_IArrayPrependArray(ptr_array, items, count_)                      DN_PArrayPrependArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, items, count_)
#define                                   DN_IArrayPrepend(ptr_array, item)                                    DN_PArrayPrepend((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, item)
#define                                   DN_IArrayPrependArena(ptr_array, arena, item)                        DN_PArrayPrependArena((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, arena, item)
#define                                   DN_IArrayPrependPool(ptr_array, pool, item)                          DN_PArrayPrependPool((ptr_array)->data, &(ptr_array)->count, &(ptr_array)->max, pool, item)
#define                                   DN_IArrayEraseRange(ptr_array, begin_index, count_, erase)           DN_PArrayEraseRange((ptr_array)->data, &(ptr_array)->count, begin_index, count_, erase)
#define                                   DN_IArrayErase(ptr_array, index, erase)                              DN_PArrayErase((ptr_array)->data, &(ptr_array)->count, index, erase)
#define                                   DN_IArrayInsertArray(ptr_array, index, items, count_)                DN_PArrayInsertArray((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, index, items, count_)
#define                                   DN_IArrayInsert(ptr_array, index, item)                              DN_PArrayInsert((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, index, item)
#define                                   DN_IArrayPopFront(ptr_array, count_)                                 DN_PArrayPopFront((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count_)
#define                                   DN_IArrayPopBack(ptr_array, count_)                                  DN_PArrayPopBack((ptr_array)->data, &(ptr_array)->count, (ptr_array)->max, count_)

// NOTE: Slices
//
//   Fixed size container allocated up front that have a `T *data` and `DN_USize count` elements.
//
//   API
//     AllocArena: Allocates the container with the requested `count` elements
//
#define                                   DN_ISliceAllocArena(slice_ptr, count_, zmem, arena)                  (DN_CppDeclType(&((slice_ptr)->data[0])))DN_SliceAllocArena((void **)&((slice_ptr)->data), &((slice_ptr)->count), count_, sizeof((slice_ptr)->data[0]), alignof(DN_CppDeclType((slice_ptr)->data[0])), zmem, arena)


DN_API void*                              DN_SliceAllocArena             (void **data, DN_USize *slice_size_field, DN_USize count, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena);

DN_API DN_ArrayFindResult                 DN_ArrayFind                   (void *data, DN_USize count, DN_USize elem_size, void const *find, DN_ArrayFindEqFunc *eq_func);
DN_API DN_ArrayFindResult                 DN_ArrayFindMemEq              (void *data, DN_USize count, DN_USize elem_size, void const *find);
DN_API void*                              DN_ArrayInsertArray            (void *data, DN_USize *count, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize items_count);
DN_API void*                              DN_ArrayPopFront               (void *data, DN_USize *count, DN_USize elem_size, DN_USize pop_count);
DN_API void*                              DN_ArrayPopBack                (void *data, DN_USize *count, DN_USize elem_size, DN_USize pop_count);
DN_API DN_ArrayEraseResult                DN_ArrayEraseRange             (void *data, DN_USize *count, DN_USize elem_size, DN_USize begin_index, DN_ISize erase_count, DN_ArrayErase erase);
DN_API void*                              DN_ArrayMakeArray              (void *data, DN_USize *count, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem);
DN_API void*                              DN_ArrayMakeArrayAssert        (void *data, DN_USize *count, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem, DN_CallSite call_site);
DN_API void*                              DN_ArrayMakeArrayArena         (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize make_count, DN_ZMem z_mem);
DN_API void*                              DN_ArrayMakeArrayPool          (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize make_count, DN_ZMem z_mem);
DN_API void*                              DN_ArrayAddArray               (void *data, DN_USize *count, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_AddType add);
DN_API void*                              DN_ArrayAddArrayAssert         (void *data, DN_USize *count, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_AddType add, DN_CallSite call_site);
DN_API void*                              DN_ArrayAddArrayArena          (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_Arena *arena, DN_AddType add);
DN_API void*                              DN_ArrayAddArrayPool           (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_Pool *pool, DN_AddType add);
DN_API bool                               DN_ArrayResizePool             (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max);
DN_API bool                               DN_ArrayResizeArena            (void **data, DN_USize *count, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max);
DN_API bool                               DN_ArrayReservePool            (void **data, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max);
DN_API bool                               DN_ArrayReserveArena           (void **data, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max);
DN_API bool                               DN_ArrayPreparePool            (void **data, DN_USize count, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize add_count);
DN_API bool                               DN_ArrayPrepareArena           (void **data, DN_USize count, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize add_count);
DN_API DN_USize                           DN_ArrayCopyPtrArena           (void **data, void const *src, DN_USize count, DN_USize elem_size, DN_Arena *arena);
DN_API DN_USize                           DN_ArrayCopyPtrArenaAssert     (void **data, void const *src, DN_USize count, DN_USize elem_size, DN_Arena *arena, DN_CallSite call_site);
DN_API DN_USize                           DN_ArrayCopyPtrPool            (void **data, void const *src, DN_USize count, DN_USize elem_size, DN_Pool *pool);
DN_API DN_USize                           DN_ArrayCopyPtrPoolAssert      (void **data, void const *src, DN_USize count, DN_USize elem_size, DN_Pool *pool, DN_CallSite call_site);

#if defined (__cplusplus)
template <typename T> DN_ArrayFindResult  DN_TArrayFind                  (T *data, DN_USize count, void const *find, DN_ArrayFindEqFunc *eq_func);
template <typename T> DN_ArrayFindResult  DN_TArrayFindMemEq             (T *data, DN_USize count, void const *find, DN_ArrayFindEqFunc *eq_func);
template <typename T> T*                  DN_TArrayInsertArray           (T *data, DN_USize *count, DN_USize max, DN_USize index, void const *items, DN_USize pop_count);
template <typename T> T*                  DN_TArrayPopFront              (T *data, DN_USize *count, DN_USize pop_count);
template <typename T> T*                  DN_TArrayPopBack               (T *data, DN_USize *count, DN_USize pop_count);
template <typename T> DN_ArrayEraseResult DN_TArrayEraseRange            (T *data, DN_USize *count, DN_USize begin_index, DN_ISize erase_count, DN_ArrayErase erase);
template <typename T> T*                  DN_TArrayMakeArray             (T *data, DN_USize *count, DN_USize max, DN_USize make_count, DN_ZMem z_mem);
template <typename T> T*                  DN_TArrayMakeArrayAssert       (T *data, DN_USize *count, DN_USize max, DN_USize make_count, DN_ZMem z_mem, DN_CallSite call_site);
template <typename T> T*                  DN_TArrayMakeArrayAssertZ      (T *data, DN_USize *count, DN_USize max, DN_USize make_count, DN_CallSite call_site);
template <typename T> T*                  DN_TArrayMakeArrayAssertNoZ    (T *data, DN_USize *count, DN_USize max, DN_USize make_count, DN_CallSite call_site);
template <typename T> T*                  DN_TArrayAddArray              (T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_AddType add);
template <typename T> T*                  DN_TArrayAddArrayAssert        (T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_AddType add, DN_CallSite call_site);
template <typename T> T*                  DN_TArrayMakeArrayArena        (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize make_count, DN_ZMem z_mem);
template <typename T> T*                  DN_TArrayMakeArrayArenaZ       (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize make_count);
template <typename T> T*                  DN_TArrayMakeArrayArenaNoZ     (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize make_count);
template <typename T> T*                  DN_TArrayMakeArrayPool         (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize make_count, DN_ZMem z_mem);
template <typename T> T*                  DN_TArrayMakeArrayPoolZ        (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize make_count);
template <typename T> T*                  DN_TArrayMakeArrayPoolNoZ      (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize make_count);
template <typename T> T*                  DN_TArrayAddArrayArena         (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_AddType add);
template <typename T> T*                  DN_TArrayAddArrayPool          (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_AddType add);
template <typename T> T*                  DN_TArrayMakeArena             (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_ZMem z_mem);
template <typename T> T*                  DN_TArrayMakeArenaZ            (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena);
template <typename T> T*                  DN_TArrayMakeArenaNoZ          (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena);
template <typename T> T*                  DN_TArrayMakePool              (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_ZMem z_mem);
template <typename T> T*                  DN_TArrayMakePoolZ             (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool);
template <typename T> T*                  DN_TArrayMakePoolNoZ           (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool);
template <typename T> T*                  DN_TArrayAddArena              (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_AddType add);
template <typename T> T*                  DN_TArrayAddPool               (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_AddType add);
template <typename T> bool                DN_TArrayResizePool            (T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize new_max);
template <typename T> bool                DN_TArrayResizeArena           (T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize new_max);
template <typename T> bool                DN_TArrayReservePool           (T **data, DN_USize *max, DN_Pool *pool, DN_USize new_max);
template <typename T> bool                DN_TArrayReserveArena          (T **data, DN_USize *max, DN_Pool *pool, DN_USize new_max);
template <typename T> bool                DN_TArrayPreparePool           (T **data, DN_USize count, DN_USize *max, DN_Pool *pool, DN_USize add_count);
template <typename T> bool                DN_TArrayPrepareArena          (T **data, DN_USize count, DN_USize *max, DN_Arena *pool, DN_USize add_count);
template <typename T> DN_USize            DN_TArrayCopyPtrArena          (T **data, T const *src, DN_USize count, DN_Arena *arena);
template <typename T> DN_USize            DN_TArrayCopyPtrArenaAssert    (T **data, T const *src, DN_USize count, DN_Arena *arena, DN_CallSite call_site);
template <typename T> DN_USize            DN_TArrayCopyPtrPool           (T **data, T const *src, DN_USize count, DN_Pool *pool);
template <typename T> DN_USize            DN_TArrayCopyPtrPoolAssert     (T **data, T const *src, DN_USize count, DN_Pool *pool, DN_CallSite call_site);
#endif

DN_API void*                              DN_SinglyLLDetach                      (void **link, void **next);
DN_API bool                               DN_RingHasSpace                        (DN_Ring const *ring, DN_U64 size);
DN_API bool                               DN_RingHasData                         (DN_Ring const *ring, DN_U64 size);
DN_API void                               DN_RingWrite                           (DN_Ring *ring, void const *src, DN_U64 src_size);
#define                                   DN_RingWriteStruct(ring_ptr, item_ptr) DN_RingWrite((ring_ptr), (item_ptr), sizeof(*(item_ptr)))
DN_API void                               DN_RingRead                            (DN_Ring *ring, void *dest, DN_U64 dest_size);
#define                                   DN_RingReadStruct(ring_ptr, dest_ptr)  DN_RingRead((ring_ptr), (dest_ptr), sizeof(*(dest_ptr)))

// NOTE: Hash Table
// Overview
//   A hash table implemented in C-style C++ that is based off and heavily derivative of Jonathan
//   Blow's "simple hash table" implementation from JAI that he posted on Pastebin
//   https://pastebin.com/raw/xMUQXshn The goal of this hash table is to provide a relatively
//   generic table that covers the 99% of use cases. For the 1% you should design a custom table
//   suited for that workload.
//
//   The key features of the simple hash-table, paraphrased
//
//     Open-addressed table with each table slot consists of {hash, key, value} for
//     cache-coherency. Default load factor of 0.7 and uses a 4 byte hash
//
//     Tombstoning with quadratic probing using triangular numbers. Less cache friendly than linear
//     probing but more cache friendly than double hashing. It mitigates clustering of collisions
//     around the insertion point but early probes maintain cache locality to the initial collision.
//     Quote from the reference implementation:
//
//       We use the 'triangular numbers' version of quadratic probing. It yields more collisions per
//       insertion than double hashing (double hashing gives us 0.74, triangular numbers give us
//       0.84) but the collisions are cheaper because the initial probes are cache-coherent, unlike
//       with double hashing. At large table sizes this ends up mattering substantially.
//       (examples/hash_table_test.jai, at the 128 million entry table size, takes 15.5 seconds with
//       triangular numbers, 17.8 seconds with double hashing.)
//
//       Triangular numbers are guaranteed to hit every entry in the table:
//       https://fgiesen.wordpress.com/2015/02/22/triangular-numbers-mod-2n/
//
//   This hash table requires that the caller sets up their own struct with the key, value and hash
//   located in the struct and pass the offsets to these fields into the table such that it can
//   updated the values in a type-erased manner. By keeping all the table slot data into one struct
//   ensures that the optimal path of looking up an item requires exactly one cache miss to fetch
//   the necessary data. As long as the table maintains a 0.7 load factor, probabilistically all
//   lookups are resolved in 1 shot.
//
//   Other tables that opt for {key, key...} {value, value...} {hash, hash...} have benefits and
//   make varying trade-offs. But assuming that the user is not memory bound and most lookups are
//   resolved in 1 lookup this optimises for the common case where a simple hash table covers the
//   99% of use-cases.
//
//   Note that this table requires that the user passes a double pointer to where store key-value
//   structs that the table maintains on their behalf. This is an oddity necessitated because of the
//   table being implemented using C constructs e.g.:
//
//     MyKeyValue *my_kvs = NULL;
//     DN_HTableInitArgsDefault(MyKeyValue, &my_kvs, ...)
//
//   By forcing the user to store the pointer of `my_kvs` we allow you to have a type-safe view
//   into the objects that the table is manipulating. This is a useful default mode of operation
//   that makes it easier to debug having a type-safe pointer on-hand to inspect the contents of the
//   table. This is rather than having the user need to cast `table.kvs` back to its concrete type
//   every time you inspect the table.
//
//   This would not be necessary if this was implemented in basically any other language that
//   supports generics as a first class citizen.
//
// Example
/*
  // NOTE: A hash table that maps (U64 -> bool) using the OS heap allocator
  struct MyKeyValue
  {
    DN_HTableHashType hash;  // Must always be `DN_HTableHashType` which is a 4 byte hash
    DN_U64            key;   // Any arbitrary key as long as it's in the same struct
    bool              value; // Any arbitrary value as long as it's in the same struct
  };

  MyKeyValue*         table_kvs  = {};
  DN_HTableInitArgs   table_args = DN_HTableInitArgsDefault(MyKeyValue, table_kvs, hash, key, value);
  DN_HTableInitResult table_init = DN_HTableInitHeap(table_args, DN_OS_HeapInitDefault());
  if (table_init.success) {
    DN_HTable table = table_init.table;
    P0I_U8x20 key   = {};
    bool      value = true;
    DN_HTableAdd(&table, &key, &value);
  }
*/
DN_API DN_U32                 DN_HTableHashFuncMurmur3KeyBytes(void const* key, DN_USize size);
DN_API DN_U32                 DN_HTableHashFuncMurmur3KeyStr8 (void const* key, DN_USize size);
DN_API bool                   DN_HTableKeyEqFuncMemcmp        (void const* lhs, void const* rhs, DN_USize size);
DN_API bool                   DN_HTableKeyEqFuncStr8Eq        (void const* lhs, void const* rhs, DN_USize size);

#define                       DN_HTableInitArgsDefault(KeyValueT, kvs_ptr, HashField, KeyField, ValueField) DN_HTableInitArgsDefault_(DN_Cast(void **)&kvs_ptr, sizeof(*kvs_ptr), offsetof(KeyValueT, HashField), sizeof(kvs_ptr->KeyField), offsetof(KeyValueT, KeyField), sizeof(kvs_ptr->ValueField), offsetof(KeyValueT, ValueField))
DN_API DN_HTableInitArgs      DN_HTableInitArgsDefault_      (void** kvs, DN_USize size_of_kvs, DN_USize offset_of_hash, DN_USize size_of_key, DN_USize offset_of_key, DN_USize size_of_value, DN_USize offset_of_value);
DN_API DN_HTableInitResult    DN_HTableInitHeap              (DN_HTableInitArgs args, DN_Heap heap);
DN_API DN_HTable              DN_HTableInitHeapAssert        (DN_HTableInitArgs args, DN_Heap heap);
DN_API DN_HTableInitResult    DN_HTableInitPool              (DN_HTableInitArgs args, DN_Pool pool, DN_HTableDeallocPoolOnDeinit dealloc_pool);
DN_API DN_HTable              DN_HTableInitPoolAssert        (DN_HTableInitArgs args, DN_Pool pool, DN_HTableDeallocPoolOnDeinit dealloc_pool);
DN_API void                   DN_HTableDeinit                (DN_HTable* table);

DN_API DN_HTableLookupResult  DN_HTableLookup                (DN_HTable const* table, void const* key, DN_HTableAllowTombstone allow_tombstone);
DN_API DN_HTableSlot          DN_HTableFind                  (DN_HTable const* table, void const* key);
DN_API void*                  DN_HTableValueFromFind         (DN_HTable const* table, void const* key);
DN_API DN_HTableSlot          DN_HTableSlotFromIndex         (DN_HTable const* table, DN_USize index);
#define                       DN_HTableSlotIsValue(slot)     ((slot).hash_u32 >= DN_HTableHashSentinel_FirstValid)
#define                       DN_HTableSlotIsEmpty(slot)     ((slot).hash_u32 == DN_HTableHashSentinel_Empty)
#define                       DN_HTableSlotIsTomb(slot)      ((slot).hash_u32 == DN_HTableHashSentinel_Tomb)
#define                       DN_HTableHashIsValue(hash_u32) ((hash_u32) >= DN_HTableHashSentinel_FirstValid)
#define                       DN_HTableHashIsEmpty(hash_u32) ((hash_u32) == DN_HTableHashSentinel_Empty)
#define                       DN_HTableHashIsTomb(hash_u32)  ((hash_u32) == DN_HTableHashSentinel_Tomb)

DN_API bool                   DN_HTableResize                (DN_HTable* table, DN_USize new_max);
DN_API DN_HTablePrepareResult DN_HTablePrepare               (DN_HTable* table, DN_USize add_count);
DN_API void                   DN_HTableClear                 (DN_HTable* table);
DN_API DN_HTableAddResult     DN_HTableMake                  (DN_HTable* table, void* key);
DN_API DN_HTableAddResult     DN_HTableAdd                   (DN_HTable* table, void* key, void* value);
DN_API bool                   DN_HTableDel                   (DN_HTable* table, void* key);

// NOTE: Binary Packer
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);
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);
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);

// NOTE: CSV Tokeniser/Packer
// Overview
//   Data structures to create and parse CSV files, supports Python style escaped quotes (e.g. Using
//   "" to escape quotes inside a quoted string).

// API
//  DN_CSVTokeniserNextN
//  Reads the next N consecutive fields from the parser. If `column_iterator` is `false` then the
//  read of the N consecutive fields does not proceed past the end of the current CSV row. If `true`
//  then it reads the next N fields even if reading would progress onto the next row.
DN_API DN_CSVTokeniser       DN_CSVTokeniserInit        (DN_Str8 string, char delimiter);
DN_API bool                  DN_CSVTokeniserValid       (DN_CSVTokeniser *tokeniser);
DN_API bool                  DN_CSVTokeniserNextRow     (DN_CSVTokeniser *tokeniser);
DN_API DN_Str8               DN_CSVTokeniserNextField   (DN_CSVTokeniser *tokeniser);
DN_API DN_Str8               DN_CSVTokeniserNextColumn  (DN_CSVTokeniser *tokeniser);
DN_API void                  DN_CSVTokeniserSkipLine    (DN_CSVTokeniser *tokeniser);
DN_API int                   DN_CSVTokeniserNextN       (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator);
DN_API int                   DN_CSVTokeniserNextColumnN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size);
DN_API int                   DN_CSVTokeniserNextFieldN  (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size);
DN_API void                  DN_CSVTokeniserSkipLineN   (DN_CSVTokeniser *tokeniser, int count);
DN_API void                  DN_CSVPackU64              (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value);
DN_API void                  DN_CSVPackI64              (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value);
DN_API void                  DN_CSVPackI32              (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value);
DN_API void                  DN_CSVPackI16              (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value);
DN_API void                  DN_CSVPackI8               (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value);
DN_API void                  DN_CSVPackU32              (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value);
DN_API void                  DN_CSVPackU16              (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value);
DN_API void                  DN_CSVPackBoolAsU64        (DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value);
DN_API void                  DN_CSVPackStr8             (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena);
DN_API void                  DN_CSVPackBuffer           (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size);
DN_API void                  DN_CSVPackBufferWithMax    (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max);
DN_API bool                  DN_CSVPackNewLine          (DN_CSVPack *pack, DN_CSVSerialise serialise);

// NOTE: Test Framework
// Overview
//   A simple barebones testing framework that we use to unit test this library. This framework
//   allows grouping of unit tests into 1 top-level group with an arbitrary number of tests inside
//   the group. It uses a begin and end pattern to scope tests into groups.

// Example
/*
  DN_Arena    arena  = DN_ArenaFromHeap(DN_Megabytes(1), DN_Kilobytes(64), DN_MemFlags_Nil, DN_OS_HeapInitDefault());
  DN_TestCore result = DN_TestInit(&arena);
  for (DN_TestGroupScopeF(&result, "Base")) {
    for (DN_TestScopeF(&result, "Check that foo != bar!"))
      DN_TestVerifyStr8NotEq(&result, DN_Str8Lit("foo"), DN_Str8Lit("bar")); // Test passes
    for (DN_TestScopeF(&result, "Does 1 equal 2?"))
      DN_TestVerifyUSizeEq(&result,  1, 2); // This test will fail!
    for (DN_TestScopeF(&result, "Float test, 1 is definitely less than 2"))
      DN_TestVerifyF64LessThan(&result,  1.f, 2.f); // Floats work as well
  }
  DN_Str8 test_output = DN_Str8FromTestCore(&result, &arena, DN_Str8FromTestCoreFlags_Colour);
  printf("%.*s", DN_Str8PrintFmt(test_output));
  DN_ArenaDeinit(&arena);
 */

DN_API DN_TestCore           DN_TestInit                                                       (DN_Arena *arena);
DN_API void                  DN_TestGroupBeginF                                                (DN_TestCore *test, char const *fmt, ...);
DN_API void                  DN_TestGroupEnd                                                   (DN_TestCore *test);
#define                      DN_TestGroupScopeF(test_ptr, fmt, ...)                            bool group_once_ = (DN_TestGroupBeginF(test_ptr, fmt, ##__VA_ARGS__), false); !group_once_; DN_TestGroupEnd(test_ptr), group_once_ = true
DN_API void                  DN_TestBeginF                                                     (DN_TestCore *test, char const *fmt, ...);
DN_API void                  DN_TestEnd                                                        (DN_TestCore *test);
#define                      DN_TestScopeF(test_ptr, fmt, ...)                                 bool test_once_  = (DN_TestBeginF(test_ptr, fmt, ##__VA_ARGS__),      false); !test_once_;  DN_TestEnd(test_ptr),      test_once_  = true
DN_API DN_Str8               DN_Str8FromTestCore                                               (DN_TestCore const *test, DN_Arena *arena, DN_Str8FromTestCoreFlags flags);

DN_API void                  DN_TestVerifyExprF_                                               (DN_TestCore *test, DN_CallSite call_site, DN_Str8 expr, bool expr_result, char const *fmt, ...);
#define                      DN_TestVerifyExprF(test_ptr, expr, fmt, ...)                      DN_TestVerifyExprF_(test_ptr, DN_CallSiteNow, DN_Str8Lit(#expr), (expr), fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyExpr(test_ptr, expr)                                 DN_TestVerifyExprF_(test_ptr, DN_CallSiteNow, DN_Str8Lit(#expr), (expr), "")

DN_API void                  DN_TestVerifyF64Fmt                                               (DN_TestCore *test, DN_CallSite call_site, DN_Str8 val_str8, DN_Str8 expect_str8, DN_F64 val, DN_F64 expect, DN_TestLogic logic, char const *fmt, ...);
#define                      DN_TestVerifyF64GreaterThanEq(test_ptr, val, expect)              DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThanEq, "")
#define                      DN_TestVerifyF64GreaterThanEqF(test_ptr, val, expect, fmt, ...)   DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThanEq, fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyF64GreaterThan(test_ptr, val, expect)                DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThan,   "")
#define                      DN_TestVerifyF64GreaterThanF(test_ptr, val, expect, fmt, ...)     DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThan,   fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyF64LessThan(test_ptr, val, expect)                   DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThan,      "")
#define                      DN_TestVerifyF64LessThanF(test_ptr, val, expect, fmt, ...)        DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThan,      fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyF64LessThanEq(test_ptr, val, expect)                 DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThanEq,    "")
#define                      DN_TestVerifyF64LessThanEqF(test_ptr, val, expect, fmt, ...)      DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThanEq,    fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyF64Eq(test_ptr, val, expect)                         DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_Eq,            "")
#define                      DN_TestVerifyF64EqF(test_ptr, val, expect, fmt, ...)              DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_Eq,            fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyF64NotEq(test_ptr, val, expect)                      DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_NotEq,         "")
#define                      DN_TestVerifyF64NotEqF(test_ptr, val, expect, fmt, ...)           DN_TestVerifyF64Fmt(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_NotEq,         fmt, ##__VA_ARGS__)

DN_API void                  DN_TestVerifyISizeF                                               (DN_TestCore *test, DN_CallSite call_site, DN_Str8 val_str8, DN_Str8 expect_str8, DN_ISize val, DN_ISize expect, DN_TestLogic logic, char const *fmt, ...);
#define                      DN_TestVerifyISizeGreaterThanEq(test_ptr, val, expect)            DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThanEq, "")
#define                      DN_TestVerifyISizeGreaterThanEqF(test_ptr, val, expect, fmt, ...) DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThanEq, fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyISizeGreaterThan(test_ptr, val, expect)              DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThan,   "")
#define                      DN_TestVerifyISizeGreaterThanF(test_ptr, val, expect, fmt, ...)   DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThan,   fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyISizeLessThan(test_ptr, val, expect)                 DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThan,      "")
#define                      DN_TestVerifyISizeLessThanF(test_ptr, val, expect, fmt, ...)      DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThan,      fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyISizeLessThanEq(test_ptr, val, expect)               DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThanEq,    "")
#define                      DN_TestVerifyISizeLessThanEqF(test_ptr, val, expect, fmt, ...)    DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThanEq,    fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyISizeEq(test_ptr, val, expect)                       DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_Eq,            "")
#define                      DN_TestVerifyISizeEqF(test_ptr, val, expect, fmt, ...)            DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_Eq,            fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyISizeNotEq(test_ptr, val, expect)                    DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_NotEq,         "")
#define                      DN_TestVerifyISizeNotEqF(test_ptr, val, expect, fmt, ...)         DN_TestVerifyISizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_NotEq,         fmt, ##__VA_ARGS__)

DN_API void                  DN_TestVerifyUSizeF                                               (DN_TestCore *test, DN_CallSite call_site, DN_Str8 val_str8, DN_Str8 expect_str8, DN_USize val, DN_USize expect, DN_TestLogic logic, char const *fmt, ...);
#define                      DN_TestVerifyUSizeGreaterThanEq(test_ptr, val, expect)            DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThanEq, "")
#define                      DN_TestVerifyUSizeGreaterThanEqF(test_ptr, val, expect, fmt, ...) DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThanEq, fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyUSizeGreaterThan(test_ptr, val, expect)              DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThan,   "")
#define                      DN_TestVerifyUSizeGreaterThanF(test_ptr, val, expect, fmt, ...)   DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_GreaterThan,   fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyUSizeLessThan(test_ptr, val, expect)                 DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThan,      "")
#define                      DN_TestVerifyUSizeLessThanF(test_ptr, val, expect, fmt, ...)      DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThan,      fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyUSizeLessThanEq(test_ptr, val, expect)               DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThanEq,    "")
#define                      DN_TestVerifyUSizeLessThanEqF(test_ptr, val, expect, fmt, ...)    DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_LessThanEq,    fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyUSizeEq(test_ptr, val, expect)                       DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_Eq,            "")
#define                      DN_TestVerifyUSizeEqF(test_ptr, val, expect, fmt, ...)            DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_Eq,            fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyUSizeNotEq(test_ptr, val, expect)                    DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_NotEq,         "")
#define                      DN_TestVerifyUSizeNotEqF(test_ptr, val, expect, fmt, ...)         DN_TestVerifyUSizeF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#val), DN_Str8Lit(#expect), val, expect, DN_TestLogic_NotEq,         fmt, ##__VA_ARGS__)

DN_API void                  DN_TestVerifyStr8F                                                (DN_TestCore *test, DN_CallSite call_site, DN_Str8 expr, DN_Str8 str8, DN_Str8 expect, bool expect_eq, char const *fmt, ...);
#define                      DN_TestVerifyStr8EqF(test_ptr, str8, expect, fmt, ...)            DN_TestVerifyStr8F(test_ptr, DN_CallSiteNow, DN_Str8Lit(#str8 " == " #expect), (str8), (expect), /*expect_eq=*/ true, fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyStr8Eq(test_ptr, str8, expect)                       DN_TestVerifyStr8F(test_ptr, DN_CallSiteNow, DN_Str8Lit(#str8 " == " #expect), (str8), (expect), /*expect_eq=*/ true, "")
#define                      DN_TestVerifyStr8NotEqF(test_ptr, str8, expect, fmt, ...)         DN_TestVerifyStr8F(test_ptr, DN_CallSiteNow, DN_Str8Lit(#str8 " != " #expect), (str8), (expect), /*expect_eq=*/ false, fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyStr8NotEq(test_ptr, str8, expect)                    DN_TestVerifyStr8F(test_ptr, DN_CallSiteNow, DN_Str8Lit(#str8 " != " #expect), (str8), (expect), /*expect_eq=*/ false, "")

DN_API void                  DN_TestVerifyBytesF                                               (DN_TestCore *test, DN_CallSite call_site, DN_Str8 bytes, DN_Str8 expect, bool expect_eq, char const *fmt, ...);
#define                      DN_TestVerifyBytesEqF(test_ptr, bytes, expect, fmt, ...)          DN_TestVerifyBytesF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#bytes " == " #expect), (bytes), (expect), /*expect_eq=*/ true, fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyBytesEq(test_ptr, bytes, expect)                     DN_TestVerifyBytesF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#bytes " == " #expect), (bytes), (expect), /*expect_eq=*/ true, "")
#define                      DN_TestVerifyBytesNotEqF(test_ptr, bytes, expect, fmt, ...)       DN_TestVerifyBytesF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#bytes " != " #expect), (bytes), (expect), /*expect_eq=*/ false, fmt, ##__VA_ARGS__)
#define                      DN_TestVerifyBytesNotEq(test_ptr, bytes, expect)                  DN_TestVerifyBytesF(test_ptr, DN_CallSiteNow, DN_Str8Lit(#bytes " != " #expect), (bytes), (expect), /*expect_eq=*/ false, "")

#if DN_WITH_TESTS
DN_API DN_TestCore           DN_TestSuite                                                      (DN_Arena *arena_);
#endif

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

#if DN_WITH_OS
DN_API DN_Str8               DN_OS_Str8FromStr8BuilderHeap                                     (DN_Str8Builder const *builder);
DN_API void                  DN_OS_LogPrintFV                                                  (DN_LogTypeParam type, void *user_data, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void                  DN_OS_LogPrintF                                                   (DN_LogTypeParam type, void *user_data, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void                  DN_OS_SetLogPrintFuncToOS                                         ();

// NOTE: Logger
// Overview
//   Logger that utilises the OS facilities to log to standard-error or to a file. Can optionally
//   rotate log files by setting the `rotate_` prefixed member functions. The logger can be used
//   across multiple threads without issue.
//
//   A rotating logger will write to the file specified by `DN_OS_LoggerSetFilePath`. When that log
//   file exceeds `rotate_every_n_bytes` in size, this file will be moved to
//   `<file_path>.<rotate_index>`. All other files in that sequence, if they exist, are shifted
//   down one. The last file in the rotation gets deleted. If there are any OS issues with
//   rotating a file (like permissions or directories with the same name as the rotation target)
//   then it will output a warning to standard-error and disable logging to disk.
//
//   Rotating errors can be flushed by fixing the issue at the OS level and clearing the flag
//   `DN_OSLoggerFlags_FileError` from the logger before attempting to log again. Setting a new
//   file path via `DN_OS_LoggerSetFilePath` will also clear this flag.
//
//   See `DN_OS_FileRotate` for more information on the rotating scheme. The files are rotated with
//   a suffix of ".".
//
//   The target byte count for rotation is a hint. A large log message on a file teetering against
//   the maximum allowed byte size will cause the log file to exceed the target size before being
//   rotated on the next log invocation.

DN_API void                  DN_OS_LoggerSetFilePath                                           (DN_OSLogger *logger, DN_Pool *pool, DN_Str8 file_path);
DN_API void                  DN_OS_LoggerFV                                                    (DN_OSLogger *logger, DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void                  DN_OS_LoggerF                                                     (DN_OSLogger *logger, DN_LogTypeParam type, DN_CallSite call_site, DN_LogFlags flags, DN_FMT_ATTRIB char const *fmt, ...);

// NOTE: Heap
// Overview
//   Helper functions to create a heap allocator that utilises OS memory allocators.

// API
//   DN_OS_HeapBasicAlloc
//   DN_OS_HeapBasicDealloc
//     Helper functions that can be used with as the `basic_alloc` and `basic_dealloc` functions for
//     `DN_Heap`. These functions essentially call directly into `DN_OS_MemAlloc` and
//     `DN_OS_MemDealloc`

//   DN_OS_HeapInitVirtual
//     Create a heap allocator using the OS's native virtual memory APIs (e.g.: reserve, commit,
//     release)

//   DN_OS_HeapInitBasic
//     Create a heap allocator using the OS's native malloc-like APIs (e.g.: HeapAlloc on Windows,
//     malloc on linux)

//   DN_OS_HeapInitDefault
//     Create a heap allocator using the OS's native memory APIs. Defaults to virtual memory
//     always unless you are on Emscripten where it'll use basic allocator (virtual memory is not
//     supported through Emscripten).

DN_API void *                    DN_OS_HeapBasicAlloc                         (DN_USize size);
DN_API void                      DN_OS_HeapBasicDealloc                       (void *ptr);
DN_API DN_Heap                   DN_OS_HeapInitBasic                          ();
DN_API DN_Heap                   DN_OS_HeapInitVirtual                        ();
DN_API DN_Heap                   DN_OS_HeapInitDefault                        ();

DN_API DN_Arena                  DN_OS_ArenaFromHeapVirtual                   (DN_U64 reserve, DN_U64 commit, DN_MemFlags flags);
DN_API DN_Arena                  DN_OS_ArenaFromHeapBasic                     (DN_U64 size, DN_MemFlags flags);

DN_API void *                    DN_OS_MemReserve                             (DN_USize count, DN_MemCommit commit, DN_MemPage page_flags);
DN_API bool                      DN_OS_MemCommit                              (void *ptr, DN_USize count, DN_U32 page_flags);
DN_API void                      DN_OS_MemDecommit                            (void *ptr, DN_USize count);
DN_API void                      DN_OS_MemRelease                             (void *ptr, DN_USize count);
DN_API int                       DN_OS_MemProtect                             (void *ptr, DN_USize count, DN_U32 page_flags);

DN_API void *                    DN_OS_MemAlloc                               (DN_USize count, 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_seperator = '-', char hms_seperator = ':');
DN_API DN_Str8x32                DN_OS_DateLocalTimeStr8                      (DN_Date time, char date_seperator = '-', char hms_seperator = ':');
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);
DN_API void                      DN_OS_OpenUrl                                (DN_Str8 url);
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);

// NOTE: OS File
// Overview
//   File system manipulation functionality. Implemented using Win32 API and POSIX API on linux
//   machines.

// API
//   DN_OS_FileRotate
//     Rotate the file at `base_file_path` by moving it to `<base_file_path><suffix><rotate_index>`
//     and then moving the file at `rotate_index+1` to `rotate_index+2` up until `rotate_count`.
//
//     For example:
//
//       base_file_path: "log.txt"
//       rotate_count:   4
//       suffix:         ".bak."
//
//     Will execute the rotate operation, in this order:
//
//       log.txt.bak.2 moves to => log.txt.bak.3
//       log.txt.bak.1 moves to => log.txt.bak.2
//       log.txt       moves to => log.txt.bak.1
//
//     Set the flag `DN_OSFileRotateFlags_KeepBaseFile` to preserve the `base_file_path` by copying
//     it instead of moving it, e.g the example changes to:
//
//       log.txt.bak.2 moves  to => log.txt.bak.3
//       log.txt.bak.1 moves  to => log.txt.bak.2
//       log.txt       copied to => log.txt.bak.1
//
//     This function is a no-op if `rotate_count <= 0`. If `rotate_count == 1` we interpret that as
//     we want to cycle the singular file specified at `base_file_path` which translates to just
//     deleting the file specified at `base_file_path` unless `DN_OSFileRotateFlags_KeepBaseFile`
//     is set. In that case this function also becomes a no-op.
//
//     If any of the files to rotate are directories this function will stop and return false. If
//     the files to rotate does not exist, the function returns true.

DN_API bool                      DN_OS_FileCopy                               (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err);
DN_API bool                      DN_OS_FileMove                               (DN_Str8 src, DN_Str8 dest, bool overwrite, DN_ErrSink *err);
DN_API bool                      DN_OS_FileRotate                             (DN_Str8 base_file_path, DN_USize rotate_count, DN_Str8 suffix, DN_OSFileRotateFlags flags);

DN_API DN_OSFile                 DN_OS_FileOpen                               (DN_Str8 path, DN_OSFileOpen open_mode, DN_OSFileAccess access, DN_ErrSink *err);
DN_API DN_OSFileRead             DN_OS_FileRead                               (DN_OSFile *file, void *buffer, DN_USize count, DN_ErrSink *err);
DN_API bool                      DN_OS_FileWritePtr                           (DN_OSFile *file, void const *data, DN_USize count, DN_ErrSink *err);
DN_API bool                      DN_OS_FileWrite                              (DN_OSFile *file, DN_Str8 buffer, DN_ErrSink *err);
DN_API bool                      DN_OS_FileWriteFV                            (DN_OSFile *file, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API bool                      DN_OS_FileWriteF                             (DN_OSFile *file, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
DN_API bool                      DN_OS_FileFlush                              (DN_OSFile *file, DN_ErrSink *err);
DN_API void                      DN_OS_FileClose                              (DN_OSFile *file);

DN_API DN_Str8                   DN_OS_FileReadAll                            (DN_Allocator allocator, DN_Str8 path, DN_ErrSink *err);
DN_API DN_Str8                   DN_OS_FileReadAllArena                       (DN_Arena *arena, DN_Str8 path, DN_ErrSink *err);
DN_API DN_Str8                   DN_OS_FileReadAllPool                        (DN_Pool *pool, DN_Str8 path, DN_ErrSink *err);

DN_API bool                      DN_OS_FileWriteAll                           (DN_Str8 path, DN_Str8 buffer, DN_ErrSink *err);
DN_API bool                      DN_OS_FileWriteAllFV                         (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API bool                      DN_OS_FileWriteAllF                          (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);
DN_API bool                      DN_OS_FileWriteAllSafe                       (DN_Str8 path, DN_Str8 buffer, DN_ErrSink *err);
DN_API bool                      DN_OS_FileWriteAllSafeFV                     (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API bool                      DN_OS_FileWriteAllSafeF                      (DN_Str8 path, DN_ErrSink *err, DN_FMT_ATTRIB char const *fmt, ...);

DN_API DN_Str8                   DN_OS_Str8FromPathInfoType                   (DN_OSPathInfoType type);
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 void                      DN_OS_Exit                                   (int32_t exit_code);
DN_API DN_OSExecArgs             DN_OS_ExecArgsDefault                        ();
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_ErrSink  *err);
DN_API DN_OSExecResult           DN_OS_ExecWait                               (DN_OSExecAsyncHandle handle, DN_Arena *arena, DN_ErrSink *err);
DN_API DN_OSExecAsyncHandle      DN_OS_ExecAsync                              (DN_Str8Slice cmd_line, DN_OSExecArgs args, DN_ErrSink *err);
DN_API DN_OSExecResult           DN_OS_Exec                                   (DN_Str8Slice cmd_line, DN_OSExecArgs args, DN_Arena *arena, DN_ErrSink *err);
DN_API DN_OSExecResult           DN_OS_ExecOrAbort                            (DN_Str8Slice cmd_line, DN_OSExecArgs args, DN_Arena *arena);

DN_API DN_OSSemaphore            DN_OS_SemaphoreInit                          (DN_U32 initial_count);
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_OSBarrier              DN_OS_BarrierInit                            (DN_U32 thread_count);
DN_API void                      DN_OS_BarrierDeinit                          (DN_OSBarrier *barrier);
DN_API void                      DN_OS_BarrierWait                            (DN_OSBarrier *barrier);

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

// NOTE: Windowing
DN_API DN_OSWindowMinimise       DN_OS_WindowIsMinimised                      (DN_OSWindow *window);
DN_API DN_OSWindowMaximise       DN_OS_WindowIsMaximised                      (DN_OSWindow *window);
DN_API void                      DN_OS_WindowSetTitle                         (DN_OSWindow *window, DN_Str8 title);
DN_API void                      DN_OS_WindowShow                             (DN_OSWindow *window, DN_OSWindowShow min);
DN_API void                      DN_OS_WindowMaximise                         (DN_OSWindow *window, DN_OSWindowMaximise maximise);
DN_API void                      DN_OS_WindowMinimise                         (DN_OSWindow *window, DN_OSWindowMinimise minimise);
DN_API void                      DN_OS_WindowBringToFront                     (DN_OSWindow *window);
DN_API void                      DN_OS_WindowFocus                            (DN_OSWindow *window);
DN_API bool                      DN_OS_WindowIsFocused                        (DN_OSWindow *window);

// NOTE: Thread
// Overview
//   Threading primitives that uses CreateThread on Win32 and pthreads everywhere else. Threads
//   are also the primitives to implement lanes, a multi-core by default paradigm. More
//   information is detailed in the OS's Thread Lane APIs.
// API
//   DN_OS_ThreadInit
//     Create a thread that will immediately execute `func`. Pass in a zero initialised `thread`
//     that will get its fields initialised with the thread information.
//
//     `tc_init_args` customises how much memory is allocated for the thread's TLS context which
//     contains allocators that are exclusively owned by the thread. You may use
//     DN_TcInitArgsDefault() for sensible default values.
//
//     Note not all fields in the thread will be initialised until the thread has itself run its
//     initialisation code which is entirely dependent on the OS scheduler, scheduling the thread to
//     execute.
//
//     TODO: Allow the caller to wait on the thread initialisation's completion

//   DN_OS_ThreadInitLane
//     The same as `DN_OS_ThreadInit` but allows setting the lane information. `DN_OS_ThreadInit`
//     calls into this function with a `lane == NULL` value.
//
//     If you are intended to execute code in a lane-esque manner (e.g. GPU style programming on the
//     CPU, multiple threads, single/linear program execution) pass in `lane` with the fields
//     populated (the thread's index, how many threads there are in the laneway and the barrier to
//     share across lanes must be initialised to synchronise different threads).
//
//     For the general use-case if you wish to use lanes then prefer the `DN_OS_ThreadLane` APIs
//     below which create the threads with the lane information setup accordingly.

//   DN_OS_ThreadJoin
//     Returns true if the thread joined successfully or it is already joined. False if timed out
//     waiting for the thread to join.
DN_API bool                      DN_OS_ThreadInitLane                         (DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadLane *lane, DN_OSThreadInitArgs init_args, void *user_context);
DN_API bool                      DN_OS_ThreadInit                             (DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadInitArgs init_args, void *user_context);
DN_API bool                      DN_OS_ThreadJoin                             (DN_OSThread *thread, DN_U32 timeout_ms, DN_TcDeinitArenas deinit_arenas);
DN_API DN_U32                    DN_OS_ThreadID                               ();
DN_API DN_OSThreadInitArgs       DN_OS_ThreadInitArgsDefault                  ();
DN_API void                      DN_OS_ThreadSetNameFmt                       (char const *fmt, ...);

// NOTE: Thread Lanes
// Overview
//   Thread lanes provide an abstraction to represent the concept of programming a CPU like a
//   GPU, e.g. SIMT (Single Instruction Multiple Threads). The lane terminology is popularised by Ryan
//   Fleury. SIMT is formally defined as
//
//    Threads are grouped into warps/wavefronts (typically 32 or 64 threads) that execute the same
//    instruction in lockstep, but each thread operates on different data and maintains its own state
//
//   The individual threads in a wavefront on the CPU side are colloquially dubbed "lanes" and a
//   thread lane here contains the necessary state to facilitate this such as the current index in the
//   wavefront and synchronisation primitives to coordinate the different lanes together.
//
//   The idea is to write code in a single-threaded manner (linear execution) but across multiple
//   threads so that the default is all execution paths are inherently multi-threaded by default. Opt
//   out of parallelism instead of opt in. This optimises for the trend of core counts increasing
//   whilst clock counts remain static.
//
//   A laneway is a helper function to initialise the number of requested OS threads/lanes upfront and
//   setup the required synchronisation primitives. It can then be dispatched all the threads which
//   start executing the `entry_point` in parallel.
//
// API
//   DN_OS_ThreadLaneSync
//     A blocking call to synchronise the program-counter of all other lanes in the laneway to this
//     function call invocation (using an OS barrier). Optionally pass in the pointer to a pointer
//     `ptr_to_share` to broadcast the pointer from one lanes to the others. The lane that wishes
//     to broadcast the pointer must have a non-null pointer, all other lanes must pass in a
//     non-null pointer. A typical use case might look like:
/*
         DN_OSThreadLane *lane = DN_OS_TcThreadLane(); // Get lane from current (t)hread (c)context

         // NOTE: Allocate buffer in lane 0
         DN_U8 *buffer         = nullptr;
         if (lane->index == 0)
           buffer = DN_ArenaNewArray(DN_TcMainArena(), DN_U8, DN_Gigabytes(1), DN_ZMem_No);

         // NOTE: Lane 0 broadcasts the `buffer` pointer to lane 1..N
         DN_OS_ThreadLaneSync(lane, &buffer);

         // NOTE: We use LaneRange to divide the buffer into equal sized chunks that each lane can
         // write into without clobbering over each other.
         DN_V2USize range = DN_OS_ThreadLaneRange(lane, DN_Gigabytes(1));
         for (DN_USize index = range.begin; index < range.end; index++) { buffer[index] = index; }
*/
//     In this example, lane 0 will allocate a 1GiB buffer pass in a `buffer` to
//     DN_OS_ThreadLaneSync` that is non-null. Lanes 1->N will skip the branch (because their lanes
//     indexes are 1..N) and invoke `DN_OS_ThreadLaneSync` with a nullptr `buffer`. After the
//     blocking call is complete, lanes 0->N will now have synchronised the `buffer` pointer and all
//     lanes point to the 1GiB range allocated in lane 0's allocator.
//
//     Additionally we demonstrate `DN_OS_ThreadLaneRange` which does math behind the scenes to
//     divide the buffer up and assign each lane their own indices in the buffer that they can work
//     on in parallel without clobbering each others work.
//
//   DN_OS_ThreadLaneRange
//     Calculates the range of values the current lane in the laneway should execute. For example if
//     you have 128 items and 16 threads each lane will receive the following `DN_V2USize` range:
//       Lane 0  => [0,   8)
//       Lane 1  => [8,   16)
//       ...
//       Lane 16 => [120, 128)
DN_API DN_OSThreadLane           DN_OS_ThreadLaneInit                         (DN_USize index, DN_USize thread_count, DN_OSBarrier barrier, DN_UPtr *share_mem);
DN_API void                      DN_OS_ThreadLaneSync                         (DN_OSThreadLane *lane, void **ptr_to_share);
DN_API DN_V2USize                DN_OS_ThreadLaneRange                        (DN_OSThreadLane const *lane, DN_USize values_count);

DN_API DN_OSThreadLaneway        DN_OS_ThreadLanewayFromArgs                  (DN_OSThread* threads, DN_USize threads_count, DN_UPtr* shared_mem);
DN_API DN_OSThreadLaneway        DN_OS_ThreadLanewayFromArena                 (DN_USize threads_count, DN_Arena* arena);
DN_API void                      DN_OS_ThreadLanewayDispatch                  (DN_OSThreadLaneway *laneway, DN_OSThreadFunc *entry_point, DN_OSThreadInitArgs init_args, void *user_context);
DN_API void                      DN_OS_ThreadLanewayJoin                      (DN_OSThreadLaneway *laneway, DN_U32 timeout_ms, DN_TcDeinitArenas deinit_arenas);

DN_API DN_OSThreadLane*          DN_OS_TcThreadLane                           ();
DN_API void                      DN_OS_TcThreadLaneSync                       (void **ptr_to_share);
DN_API DN_OSThreadLane           DN_OS_TcThreadLaneEquip                      (DN_OSThreadLane lane);

DN_API void                      DN_OS_AsyncInit                              (DN_OSAsyncCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size);
DN_API void                      DN_OS_AsyncDeinit                            (DN_OSAsyncCore *async);
DN_API bool                      DN_OS_AsyncQueueWork                         (DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms);
DN_API DN_OSAsyncTask            DN_OS_AsyncQueueTask                         (DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms);
DN_API bool                      DN_OS_AsyncWaitTask                          (DN_OSAsyncTask *task, DN_U32 timeout_ms);

// NOTE: DN_OSPrint
typedef enum DN_OSPrintDest {
  DN_OSPrintDest_Out,
  DN_OSPrintDest_Err,
} DN_OSPrintDest;

// 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 // DN_WITH_OS

// NOTE: Template implementations
#if defined(__cplusplus)
template <typename T> T *DN_MemCopyObjT(T *dest, T const *src, DN_USize count)
{
  T* result = dest;
  DN_Memcpy(dest, src, sizeof(T) * count);
  return result;
}

template <typename T>
DN_ArrayFindResult DN_TArrayFind(T *data, DN_USize count, void const *find, DN_ArrayFindEqFunc *eq_func)
{
  DN_ArrayFindResult result = DN_ArrayFind(data, count, sizeof(*data), find, eq_func);
  return result;
}

template <typename T>
DN_ArrayFindResult DN_TArrayFindMemEq(T *data, DN_USize count, void const *find)
{
  DN_ArrayFindResult result = DN_ArrayFindMemEq(data, count, sizeof(*data), find);
  return result;
}

template <typename T>
T *DN_TArrayInsertArray(T *data, DN_USize *count, DN_USize max, DN_USize index, T const *items, DN_USize items_count)
{
  T *result = DN_Cast(T *)DN_ArrayInsertArray(data, count, max, sizeof(*data), index, items, items_count);
  return result;
}

template <typename T>
T *DN_TArrayPopFront(T *data, DN_USize *count, DN_USize pop_count)
{
  T *result = DN_Cast(T *)DN_ArrayPopFront(data, count, sizeof(*data), pop_count);
  return result;
}

template <typename T>
T *DN_TArrayPopBack(T *data, DN_USize *count, DN_USize pop_count)
{
  T *result = DN_Cast(T *)DN_ArrayPopBack(data, count, sizeof(*data), pop_count);
  return result;
}

template <typename T>
DN_ArrayEraseResult DN_TArrayEraseRange(T *data, DN_USize *count, DN_USize begin_index, DN_ISize erase_count, DN_ArrayErase erase)
{
  DN_ArrayEraseResult result = DN_ArrayEraseRange(data, count, sizeof(*data), begin_index, erase_count, erase);
  return result;
}

template <typename T>
T *DN_TArrayMakeArray(T *data, DN_USize *count, DN_USize max, DN_USize make_count, DN_ZMem z_mem)
{
  T *result = DN_Cast(T *)DN_ArrayMakeArray(data, count, max, sizeof(*data), make_count, z_mem);
  return result;
}

template <typename T>
T *DN_TArrayMakeArrayAssert(T *data, DN_USize *count, DN_USize max, DN_USize make_count, DN_ZMem z_mem, DN_CallSite call_site)
{
  T *result = DN_Cast(T *)DN_ArrayMakeArrayAssert(data, count, max, sizeof(*data), make_count, z_mem, call_site);
  return result;
}

template <typename T>
T *DN_TArrayMakeArrayAssertZ(T *data, DN_USize *count, DN_USize max, DN_USize make_count, DN_CallSite call_site)
{
  T* result = DN_TArrayMakeArrayAssert(data, count, max, make_count, DN_ZMem_Yes, call_site);
  return result;
}

template <typename T>
T *DN_TArrayMakeArrayAssertNoZ(T *data, DN_USize *count, DN_USize max, DN_USize make_count, DN_CallSite call_site)
{
  T* result = DN_TArrayMakeArrayAssert(data, count, max, make_count, DN_ZMem_No, call_site);
  return result;
}

template <typename T>
T *DN_TArrayAddArray(T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_AddType add)
{
  T* result = DN_Cast(T *)DN_ArrayAddArray(data, count, max, sizeof(*elems), elems, elems_count, add);
  return result;
}

template <typename T>
T *DN_TArrayAddArrayAssert(T *data, DN_USize *count, DN_USize max, T const *elems, DN_USize elems_count, DN_AddType add, DN_CallSite call_site)
{
  T* result = DN_Cast(T *)DN_ArrayAddArrayAssert(data, count, max, sizeof(*elems), elems, elems_count, add, call_site);
  return result;
}

template <typename T>
T *DN_TArrayMakeArrayArena(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize make_count, DN_ZMem z_mem)
{
  T *result = DN_Cast(T *)DN_ArrayMakeArrayArena(DN_Cast(void **)data, count, max, sizeof(**data), arena, make_count, z_mem);
  return result;
}

template <typename T>
T *DN_TArrayMakeArrayArenaZ(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize make_count)
{
  T *result = DN_TArrayMakeArrayArena(data, count, max, arena, make_count, DN_ZMem_Yes);
  return result;
}

template <typename T>
T *DN_TArrayMakeArrayArenaNoZ(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize make_count)
{
  T *result = DN_TArrayMakeArrayArena(data, count, max, arena, make_count, DN_ZMem_No);
  return result;
}

template <typename T>
T *DN_TArrayMakeArrayPool(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize make_count, DN_ZMem z_mem)
{
  T *result = DN_Cast(T *)DN_ArrayMakeArrayPool(DN_Cast(void **)data, count, max, sizeof(**data), pool, make_count, z_mem);
  return result;
}

template <typename T>
T *DN_TArrayMakeArrayPoolZ(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize make_count)
{
  T *result = DN_TArrayMakeArrayPool(data, count, max, pool, make_count, DN_ZMem_Yes);
  return result;
}

template <typename T>
T *DN_TArrayMakeArrayPoolNoZ(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize make_count)
{
  T *result = DN_TArrayMakeArrayPool(data, count, max, pool, make_count, DN_ZMem_No);
  return result;
}

template <typename T>
T *DN_TArrayAddArrayArena(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_AddType add)
{
  T *result = DN_Cast(T *)DN_ArrayAddArrayArena(DN_Cast(void **)data, count, max, sizeof(**data), arena, elems, elems_count, add);
  return result;
}

template <typename T>
T *DN_TArrayAddArrayPool(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_AddType add)
{
  T *result = DN_Cast(T *)DN_ArrayAddArrayPool(DN_Cast(void **)data, count, max, sizeof(**data), pool, elems, elems_count, add);
  return result;
}

template <typename T>
T *DN_TArrayMakeArena(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_ZMem z_mem)
{
  T *result = DN_TArrayMakeArrayArena(data, count, max, arena, 1, z_mem);
  return result;
}

template <typename T>
T *DN_TArrayMakeArenaZ(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena)
{
  T *result = DN_TArrayMakeArrayArena(data, count, max, arena, 1, DN_ZMem_Yes);
  return result;
}

template <typename T>
T *DN_TArrayMakeArenaNoZ(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena)
{
  T *result = DN_TArrayMakeArrayArena(data, count, max, arena, 1, DN_ZMem_No);
  return result;
}

template <typename T>
T *DN_TArrayMakePool(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_ZMem z_mem)
{
  T *result = DN_TArrayMakeArrayPool(data, count, max, pool, 1, z_mem);
  return result;
}

template <typename T>
T *DN_TArrayMakePoolZ(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool)
{
  T *result = DN_TArrayMakeArrayPool(data, count, max, pool, 1, DN_ZMem_Yes);
  return result;
}

template <typename T>
T *DN_TArrayMakePoolNoZ(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool)
{
  T *result = DN_TArrayMakeArrayPool(data, count, max, pool, 1, DN_ZMem_No);
  return result;
}

template <typename T>
T *DN_TArrayAddArena(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, T const *elems, DN_USize elems_count, DN_AddType add)
{
  T *result = DN_TArrayAddArrayArena(data, count, max, arena, elems, elems_count, add);
  return result;
}

template <typename T>
T *DN_TArrayAddPool(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, T const *elems, DN_USize elems_count, DN_AddType add)
{
  T *result = DN_TArrayAddArrayPool(data, count, max, pool, elems, elems_count, add);
  return result;
}

template <typename T>
bool DN_TArrayResizePool(T **data, DN_USize *count, DN_USize *max, DN_Pool *pool, DN_USize new_max)
{
  bool result = DN_ArrayResizePool(DN_Cast(void **)data, count, max, sizeof(**data), pool, new_max);
  return result;
}

template <typename T>
bool DN_TArrayResizeArena(T **data, DN_USize *count, DN_USize *max, DN_Arena *arena, DN_USize new_max)
{
  bool result = DN_ArrayResizeArena(DN_Cast(void **)data, count, max, sizeof(**data), arena, new_max);
  return result;
}

template <typename T>
bool DN_TArrayReservePool(T **data, DN_USize *max, DN_Pool *pool, DN_USize new_max)
{
  bool result = DN_ArrayReservePool(DN_Cast(void **)data, max, sizeof(**data), pool, new_max);
  return result;
}

template <typename T>
bool DN_TArrayReserveArena(T **data, DN_USize *max, DN_Arena *arena, DN_USize new_max)
{
  bool result = DN_ArrayReserveArena(DN_Cast(void **)data, max, sizeof(**data), arena, new_max);
  return result;
}

template <typename T>
bool DN_TArrayPreparePool(T **data, DN_USize count, DN_USize *max, DN_Pool *pool, DN_USize add_count)
{
  bool result = DN_ArrayPreparePool(DN_Cast(void **)data, count, max, sizeof(**data), pool, add_count);
  return result;
}

template <typename T>
bool DN_TArrayPrepareArena(T **data, DN_USize count, DN_USize *max, DN_Arena *arena, DN_USize add_count)
{
  bool result = DN_ArrayPrepareArena(DN_Cast(void **)data, count, max, sizeof(**data), arena, add_count);
  return result;
}

template <typename T>
DN_USize DN_TArrayCopyPtrArena(T **data, T const *src, DN_USize count, DN_Arena *arena)
{
  DN_USize result = DN_ArrayCopyPtrArena(DN_Cast(void **)data, src, count, sizeof(T), arena);
  return result;
}

template <typename T>
DN_USize DN_TArrayCopyPtrArenaAssert(T **data, T const *src, DN_USize count, DN_Arena *arena, DN_CallSite call_site)
{
  DN_USize result = DN_ArrayCopyPtrArenaAssert(DN_Cast(void **)data, src, count, sizeof(T), arena, call_site);
  return result;
}

template <typename T>
DN_USize DN_TArrayCopyPtrPool(T **data, T const *src, DN_USize count, DN_Pool *pool)
{
  DN_USize result = DN_ArrayCopyPtrPool(DN_Cast(void **)data, src, count, sizeof(T), pool);
  return result;
}

template <typename T>
DN_USize DN_TArrayCopyPtrPoolAssert(T **data, T const *src, DN_USize count, DN_Pool *pool, DN_CallSite call_site)
{
  DN_USize result = DN_ArrayCopyPtrPoolAssert(DN_Cast(void **)data, src, count, sizeof(T), pool, call_site);
  return result;
}
#endif // defined(__cplusplus)

#if DN_WITH_NET
typedef enum DN_NETRequestType {
  DN_NETRequestType_Nil,
  DN_NETRequestType_HTTP,
  DN_NETRequestType_WS,
} DN_NETRequestType;

typedef enum DN_NETResponseState {
  DN_NETResponseState_Nil,
  DN_NETResponseState_Error,
  DN_NETResponseState_HTTP,
  DN_NETResponseState_WSOpen,
  DN_NETResponseState_WSText,
  DN_NETResponseState_WSBinary,
  DN_NETResponseState_WSClose,
  DN_NETResponseState_WSPing,
  DN_NETResponseState_WSPong,
} DN_NETResponseState;

typedef enum DN_NETWSSend {
  DN_NETWSSend_Text,
  DN_NETWSSend_Binary,
  DN_NETWSSend_Close,
  DN_NETWSSend_Ping,
  DN_NETWSSend_Pong,
} DN_NETWSSend;

typedef DN_U32 DN_NETDoHTTPFlags;
enum DN_NETDoHTTPFlags_
{
  DN_NETDoHTTPFlags_Nil              = 0,
  DN_NETDoHTTPFlags_BasicAuth        = 1 << 0,
  DN_NETDoHTTPFlags_DisableSSLVerify = 1 << 1,
};

typedef struct DN_NETDoHTTPArgs DN_NETDoHTTPArgs;
struct DN_NETDoHTTPArgs
{
  // NOTE: WS and HTTP args
  DN_NETDoHTTPFlags  flags;
  DN_Str8            username;
  DN_Str8            password;
  DN_Str8           *headers;
  DN_U16             headers_size;

  // NOTE: HTTP args only
  DN_Str8            payload;
};

typedef struct DN_NETRequestHandle DN_NETRequestHandle;
struct DN_NETRequestHandle
{
  DN_UPtr handle;
  DN_U64  gen;
};

typedef struct DN_NETResponse DN_NETResponse;
struct DN_NETResponse
{
  // NOTE: When filling these fields, all their values are copied internally in the library so the
  // values do not need to persist past the initial invocation of the HTTP/WS request.
  // NOTE: Common to WS and HTTP responses
  DN_NETRequestType   type;
  DN_NETResponseState state;
  DN_NETRequestHandle request;
  DN_Str8             error_str8;
  DN_Str8             body;

  // NOTE: HTTP responses only
  DN_U32              http_status;
};

typedef struct DN_NETRequest DN_NETRequest;
struct DN_NETRequest
{
  DN_MemList        mem;
  DN_Arena          arena;
  DN_Arena          start_response_arena;
  DN_NETRequestType type;
  DN_U64            gen;
  DN_Str8           url;
  DN_Str8           method;
  DN_OSSemaphore    completion_sem;
  DN_NETDoHTTPArgs  args;
  DN_NETResponse    response;
  DN_NETRequest    *next;
  DN_NETRequest    *prev;
  DN_U64            context[2];
};

typedef void               (DN_NETInitFunc)              (struct DN_NETCore *net, char *base, DN_U64 base_size);
typedef void               (DN_NETDeinitFunc)            (struct DN_NETCore *net);
typedef DN_NETRequestHandle(DN_NETDoHTTPFunc)            (struct DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
typedef DN_NETRequestHandle(DN_NETDoWSFunc)              (struct DN_NETCore *net, DN_Str8 url);
typedef void               (DN_NETDoWSSendFunc)          (DN_NETRequestHandle handle, DN_Str8 data, DN_NETWSSend send);
typedef DN_NETResponse     (DN_NETWaitForResponseFunc)   (DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms);
typedef DN_NETResponse     (DN_NETWaitForAnyResponseFunc)(struct DN_NETCore *net, DN_Arena *arena, DN_U32 timeout_ms);

typedef struct DN_NETInterface DN_NETInterface;
struct DN_NETInterface
{
  DN_NETInitFunc*               init;
  DN_NETDeinitFunc*             deinit;
  DN_NETDoHTTPFunc*             do_http;
  DN_NETDoWSFunc*               do_ws;
  DN_NETDoWSSendFunc*           do_ws_send;
  DN_NETWaitForResponseFunc*    wait_for_response;
  DN_NETWaitForAnyResponseFunc* wait_for_any_response;
};

typedef struct DN_NETCore DN_NETCore;
struct DN_NETCore
{
  char           *base;
  DN_U64          base_size;
  DN_MemList      mem;
  DN_Arena        arena;
  DN_OSSemaphore  completion_sem;
  void           *context;
  DN_NETInterface api;
};

// NOTE: NET
// Overview
//   Defines a multi-threaded interface for doing network requests (via the `DN_NETInterface`
//   object) in the core struct. On top of this is a asynchronous API provided to the caller along
//   the lines of, do http/ws followed to dispatch a request followed by a blocking wait for
//   response function.
//
// API
//   DN_NET_ResponseHasFailed
//     After a response has been dispatched via do http/ws the returned handle's status can be
//     queried using this function. A request has failed if the `state` has returned
//     `DN_NETResponseState_Error`, or the returned HTTP status code was `>= 400`.
DN_Str8             DN_NET_Str8FromResponseState     (DN_NETResponseState state);
DN_NETRequest *     DN_NET_RequestFromHandle         (DN_NETRequestHandle handle);
DN_NETRequestHandle DN_NET_HandleFromRequest         (DN_NETRequest *request);
bool                DN_NET_ResponseHasFailed         (DN_NETResponse const* resp);
bool                DN_NET_ResponseHasSucceeded      (DN_NETResponse const* resp);
bool                DN_NET_ResponseIsReady           (DN_NETResponse const* resp);
DN_Str8             DN_NET_Str8DiagnosticFromResponse(DN_NETResponse const* resp, DN_Arena *arena);

// NOTE: Internal functions for different networking implementations to use
void                DN_NET_BaseInit             (DN_NETCore *net, char *base, DN_U64 base_size);
DN_NETRequestHandle DN_NET_SetupRequest         (DN_NETRequest *request, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type);
#endif

#if DN_WITH_NET_CURL
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
#pragma comment(lib, "advapi32")
#pragma comment(lib, "ws2_32")
#pragma comment(lib, "wldap32")
#pragma comment(lib, "crypt32")
#pragma comment(lib, "secur32")
#pragma comment(lib, "Iphlpapi")
#endif

#if !DN_WITH_OS || !DN_WITH_NET
  #error "NET API with CURL requires #define DN_WITH_NET 1 and #define DN_WITH_OS 1"
#endif
typedef struct DN_NETCurlCore DN_NETCurlCore;
struct DN_NETCurlCore
{
  // NOTE: Shared w/ user and networking thread
  DN_Ring        ring;
  DN_OSMutex     ring_mutex;
  bool           kill_thread;

  DN_OSMutex     list_mutex;          // Lock for request, response, deinit, free list
  DN_NETRequest *request_list;        // Current requests submitted by the user thread awaiting to move into the thread request list
  DN_NETRequest *response_list;       // Finished requests that are to be deqeued by the user via wait for response
  DN_NETRequest *deinit_list;         // Requests that are finished and are awaiting to be de-initialised by the CURL thread
  DN_NETRequest *free_list;           // Request pool that new requests will use before allocating
  DN_USize       request_count;
  DN_USize       response_count;
  DN_USize       deinit_count;
  DN_USize       free_count;
  DN_USize       thread_request_count;

  // NOTE: Networking thread only
  DN_NETRequest *thread_request_list; // Current requests being executed by the CURL thread.
                                      // This list is exclusively owned by the CURL thread so no locking is needed
  DN_OSThread    thread;
  void          *thread_curlm;
};

#define             DN_NET_CurlCoreFromNet(net)  ((net) ? (DN_Cast(DN_NETCurlCore *)(net)->context) : nullptr);
DN_NETInterface     DN_NET_CurlInterface         ();
void                DN_NET_CurlInit              (DN_NETCore *net, char *base, DN_U64 base_size);
void                DN_NET_CurlDeinit            (DN_NETCore *net);
DN_NETRequestHandle DN_NET_CurlDoHTTP            (DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
DN_NETRequestHandle DN_NET_CurlDoWSArgs          (DN_NETCore *net, DN_Str8 url, DN_NETDoHTTPArgs const *args);
DN_NETRequestHandle DN_NET_CurlDoWS              (DN_NETCore *net, DN_Str8 url);
void                DN_NET_CurlDoWSSend          (DN_NETRequestHandle handle, DN_Str8 payload, DN_NETWSSend send);
DN_NETResponse      DN_NET_CurlWaitForResponse   (DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms);
DN_NETResponse      DN_NET_CurlWaitForAnyResponse(DN_NETCore *net, DN_Arena *arena, DN_U32 timeout_ms);
#endif // #if DN_WITH_NET_CURL

#if DN_WITH_NET_EMSCRIPTEN
#if !DN_WITH_OS || !DN_WITH_NET
  #error "NET API with Emscripten requires #define DN_WITH_NET 1 and #define DN_WITH_OS 1"
#endif
#if !defined(__EMSCRIPTEN__)
  #error "NET API with Esmcripten can only be compiled with emcc which defines __EMSCRIPTEN__"
#endif

DN_NETInterface     DN_NET_EmcInterface();
void                DN_NET_EmcInit              (DN_NETCore *net, char *base, DN_U64 base_size);
void                DN_NET_EmcDeinit            (DN_NETCore *net);
DN_NETRequestHandle DN_NET_EmcDoHTTP            (DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args);
DN_NETRequestHandle DN_NET_EmcDoWS              (DN_NETCore *net, DN_Str8 url);
void                DN_NET_EmcDoWSSend          (DN_NETRequestHandle handle, DN_Str8 data, DN_NETWSSend send);
DN_NETResponse      DN_NET_EmcWaitForResponse   (DN_NETRequestHandle handle, DN_Arena *arena, DN_U32 timeout_ms);
DN_NETResponse      DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *arena, DN_U32 timeout_ms);
#endif // #if DN_WITH_NET_EMSCRIPTEN

#if DN_WITH_OS
  #if defined(DN_PLATFORM_WIN32)
    #include "OS/dn_os_windows.h"
    #include "OS/dn_os_w32.h"
  #elif defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_EMSCRIPTEN)
    #include "OS/dn_os_posix.h"
  #else
    #error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
  #endif
#endif // #if DN_WITH_OS

#endif // #if !defined(DN_H)
