Move source code into Source folder and add a single header generator"

This commit is contained in:
2025-06-26 22:14:18 +10:00
parent a41a9d550d
commit 7259cbf296
80 changed files with 18259 additions and 47 deletions
+728
View File
@@ -0,0 +1,728 @@
#define DN_BASE_CPP
#include "../dn_clangd.h"
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
#if !defined(DN_PLATFORM_ARM64) && !defined(DN_PLATFORM_EMSCRIPTEN)
#if defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
#include <cpuid.h>
#endif
DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
DN_API DN_CPUIDResult DN_CPU_ID(DN_CPUIDArgs args)
{
DN_CPUIDResult result = {};
__cpuidex(result.values, args.eax, args.ecx);
return result;
}
DN_API DN_USize DN_CPU_HasFeatureArray(DN_CPUReport const *report, DN_CPUFeatureQuery *features, DN_USize features_size)
{
DN_USize result = 0;
DN_USize const BITS = sizeof(report->features[0]) * 8;
for (DN_ForIndexU(feature_index, features_size)) {
DN_CPUFeatureQuery *query = features + feature_index;
DN_USize chunk_index = query->feature / BITS;
DN_USize chunk_bit = query->feature % BITS;
DN_U64 chunk = report->features[chunk_index];
query->available = chunk & (1ULL << chunk_bit);
result += DN_CAST(int) query->available;
}
return result;
}
DN_API bool DN_CPU_HasFeature(DN_CPUReport const *report, DN_CPUFeature feature)
{
DN_CPUFeatureQuery query = {};
query.feature = feature;
bool result = DN_CPU_HasFeatureArray(report, &query, 1) == 1;
return result;
}
DN_API bool DN_CPU_HasAllFeatures(DN_CPUReport const *report, DN_CPUFeature const *features, DN_USize features_size)
{
bool result = true;
for (DN_USize index = 0; result && index < features_size; index++)
result &= DN_CPU_HasFeature(report, features[index]);
return result;
}
DN_API void DN_CPU_SetFeature(DN_CPUReport *report, DN_CPUFeature feature)
{
DN_Assert(feature < DN_CPUFeature_Count);
DN_USize const BITS = sizeof(report->features[0]) * 8;
DN_USize chunk_index = feature / BITS;
DN_USize chunk_bit = feature % BITS;
report->features[chunk_index] |= (1ULL << chunk_bit);
}
DN_API DN_CPUReport DN_CPU_Report()
{
DN_CPUReport result = {};
DN_CPUIDResult fn_0000_[500] = {};
DN_CPUIDResult fn_8000_[500] = {};
int const EXTENDED_FUNC_BASE_EAX = 0x8000'0000;
int const REGISTER_SIZE = sizeof(fn_0000_[0].reg.eax);
// NOTE: Query standard/extended numbers ///////////////////////////////////////////////////////
{
DN_CPUIDArgs args = {};
// NOTE: Query standard function (e.g. eax = 0x0) for function count + cpu vendor
args = {};
fn_0000_[0] = DN_CPU_ID(args);
// NOTE: Query extended function (e.g. eax = 0x8000'0000) for function count + cpu vendor
args = {};
args.eax = DN_CAST(int) EXTENDED_FUNC_BASE_EAX;
fn_8000_[0] = DN_CPU_ID(args);
}
// NOTE: Extract function count ////////////////////////////////////////////////////////////////
int const STANDARD_FUNC_MAX_EAX = fn_0000_[0x0000].reg.eax;
int const EXTENDED_FUNC_MAX_EAX = fn_8000_[0x0000].reg.eax;
// NOTE: Enumerate all CPUID results for the known function counts /////////////////////////////
{
DN_AssertF((STANDARD_FUNC_MAX_EAX + 1) <= DN_ArrayCountI(fn_0000_),
"Max standard count is %d",
STANDARD_FUNC_MAX_EAX + 1);
DN_AssertF((DN_CAST(DN_ISize) EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1) <= DN_ArrayCountI(fn_8000_),
"Max extended count is %zu",
DN_CAST(DN_ISize) EXTENDED_FUNC_MAX_EAX - EXTENDED_FUNC_BASE_EAX + 1);
for (int eax = 1; eax <= STANDARD_FUNC_MAX_EAX; eax++) {
DN_CPUIDArgs args = {};
args.eax = eax;
fn_0000_[eax] = DN_CPU_ID(args);
}
for (int eax = EXTENDED_FUNC_BASE_EAX + 1, index = 1; eax <= EXTENDED_FUNC_MAX_EAX; eax++, index++) {
DN_CPUIDArgs args = {};
args.eax = eax;
fn_8000_[index] = DN_CPU_ID(args);
}
}
// NOTE: Query CPU vendor //////////////////////////////////////////////////////////////////////
{
DN_Memcpy(result.vendor + 0, &fn_8000_[0x0000].reg.ebx, REGISTER_SIZE);
DN_Memcpy(result.vendor + 4, &fn_8000_[0x0000].reg.edx, REGISTER_SIZE);
DN_Memcpy(result.vendor + 8, &fn_8000_[0x0000].reg.ecx, REGISTER_SIZE);
}
// NOTE: Query CPU brand ///////////////////////////////////////////////////////////////////////
if (EXTENDED_FUNC_MAX_EAX >= (EXTENDED_FUNC_BASE_EAX + 4)) {
DN_Memcpy(result.brand + 0, &fn_8000_[0x0002].reg.eax, REGISTER_SIZE);
DN_Memcpy(result.brand + 4, &fn_8000_[0x0002].reg.ebx, REGISTER_SIZE);
DN_Memcpy(result.brand + 8, &fn_8000_[0x0002].reg.ecx, REGISTER_SIZE);
DN_Memcpy(result.brand + 12, &fn_8000_[0x0002].reg.edx, REGISTER_SIZE);
DN_Memcpy(result.brand + 16, &fn_8000_[0x0003].reg.eax, REGISTER_SIZE);
DN_Memcpy(result.brand + 20, &fn_8000_[0x0003].reg.ebx, REGISTER_SIZE);
DN_Memcpy(result.brand + 24, &fn_8000_[0x0003].reg.ecx, REGISTER_SIZE);
DN_Memcpy(result.brand + 28, &fn_8000_[0x0003].reg.edx, REGISTER_SIZE);
DN_Memcpy(result.brand + 32, &fn_8000_[0x0004].reg.eax, REGISTER_SIZE);
DN_Memcpy(result.brand + 36, &fn_8000_[0x0004].reg.ebx, REGISTER_SIZE);
DN_Memcpy(result.brand + 40, &fn_8000_[0x0004].reg.ecx, REGISTER_SIZE);
DN_Memcpy(result.brand + 44, &fn_8000_[0x0004].reg.edx, REGISTER_SIZE);
DN_Assert(result.brand[sizeof(result.brand) - 1] == 0);
}
// NOTE: Query CPU features //////////////////////////////////////////////////////////////////
for (DN_USize ext_index = 0; ext_index < DN_CPUFeature_Count; ext_index++) {
bool available = false;
// NOTE: Mask bits taken from various manuals
// - AMD64 Architecture Programmer's Manual, Volumes 1-5
// - https://en.wikipedia.org/wiki/CPUID#Calling_CPUID
switch (DN_CAST(DN_CPUFeature) ext_index) {
case DN_CPUFeature_3DNow: available = (fn_8000_[0x0001].reg.edx & (1 << 31)); break;
case DN_CPUFeature_3DNowExt: available = (fn_8000_[0x0001].reg.edx & (1 << 30)); break;
case DN_CPUFeature_ABM: available = (fn_8000_[0x0001].reg.ecx & (1 << 5)); break;
case DN_CPUFeature_AES: available = (fn_0000_[0x0001].reg.ecx & (1 << 25)); break;
case DN_CPUFeature_AVX: available = (fn_0000_[0x0001].reg.ecx & (1 << 28)); break;
case DN_CPUFeature_AVX2: available = (fn_0000_[0x0007].reg.ebx & (1 << 0)); break;
case DN_CPUFeature_AVX512F: available = (fn_0000_[0x0007].reg.ebx & (1 << 16)); break;
case DN_CPUFeature_AVX512DQ: available = (fn_0000_[0x0007].reg.ebx & (1 << 17)); break;
case DN_CPUFeature_AVX512IFMA: available = (fn_0000_[0x0007].reg.ebx & (1 << 21)); break;
case DN_CPUFeature_AVX512PF: available = (fn_0000_[0x0007].reg.ebx & (1 << 26)); break;
case DN_CPUFeature_AVX512ER: available = (fn_0000_[0x0007].reg.ebx & (1 << 27)); break;
case DN_CPUFeature_AVX512CD: available = (fn_0000_[0x0007].reg.ebx & (1 << 28)); break;
case DN_CPUFeature_AVX512BW: available = (fn_0000_[0x0007].reg.ebx & (1 << 30)); break;
case DN_CPUFeature_AVX512VL: available = (fn_0000_[0x0007].reg.ebx & (1 << 31)); break;
case DN_CPUFeature_AVX512VBMI: available = (fn_0000_[0x0007].reg.ecx & (1 << 1)); break;
case DN_CPUFeature_AVX512VBMI2: available = (fn_0000_[0x0007].reg.ecx & (1 << 6)); break;
case DN_CPUFeature_AVX512VNNI: available = (fn_0000_[0x0007].reg.ecx & (1 << 11)); break;
case DN_CPUFeature_AVX512BITALG: available = (fn_0000_[0x0007].reg.ecx & (1 << 12)); break;
case DN_CPUFeature_AVX512VPOPCNTDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 14)); break;
case DN_CPUFeature_AVX5124VNNIW: available = (fn_0000_[0x0007].reg.edx & (1 << 2)); break;
case DN_CPUFeature_AVX5124FMAPS: available = (fn_0000_[0x0007].reg.edx & (1 << 3)); break;
case DN_CPUFeature_AVX512VP2INTERSECT: available = (fn_0000_[0x0007].reg.edx & (1 << 8)); break;
case DN_CPUFeature_AVX512FP16: available = (fn_0000_[0x0007].reg.edx & (1 << 23)); break;
case DN_CPUFeature_CLZERO: available = (fn_8000_[0x0008].reg.ebx & (1 << 0)); break;
case DN_CPUFeature_CMPXCHG8B: available = (fn_0000_[0x0001].reg.edx & (1 << 8)); break;
case DN_CPUFeature_CMPXCHG16B: available = (fn_0000_[0x0001].reg.ecx & (1 << 13)); break;
case DN_CPUFeature_F16C: available = (fn_0000_[0x0001].reg.ecx & (1 << 29)); break;
case DN_CPUFeature_FMA: available = (fn_0000_[0x0001].reg.ecx & (1 << 12)); break;
case DN_CPUFeature_FMA4: available = (fn_8000_[0x0001].reg.ecx & (1 << 16)); break;
case DN_CPUFeature_FP128: available = (fn_8000_[0x001A].reg.eax & (1 << 0)); break;
case DN_CPUFeature_FP256: available = (fn_8000_[0x001A].reg.eax & (1 << 2)); break;
case DN_CPUFeature_FPU: available = (fn_0000_[0x0001].reg.edx & (1 << 0)); break;
case DN_CPUFeature_MMX: available = (fn_0000_[0x0001].reg.edx & (1 << 23)); break;
case DN_CPUFeature_MONITOR: available = (fn_0000_[0x0001].reg.ecx & (1 << 3)); break;
case DN_CPUFeature_MOVBE: available = (fn_0000_[0x0001].reg.ecx & (1 << 22)); break;
case DN_CPUFeature_MOVU: available = (fn_8000_[0x001A].reg.eax & (1 << 1)); break;
case DN_CPUFeature_MmxExt: available = (fn_8000_[0x0001].reg.edx & (1 << 22)); break;
case DN_CPUFeature_PCLMULQDQ: available = (fn_0000_[0x0001].reg.ecx & (1 << 1)); break;
case DN_CPUFeature_POPCNT: available = (fn_0000_[0x0001].reg.ecx & (1 << 23)); break;
case DN_CPUFeature_RDRAND: available = (fn_0000_[0x0001].reg.ecx & (1 << 30)); break;
case DN_CPUFeature_RDSEED: available = (fn_0000_[0x0007].reg.ebx & (1 << 18)); break;
case DN_CPUFeature_RDTSCP: available = (fn_8000_[0x0001].reg.edx & (1 << 27)); break;
case DN_CPUFeature_SHA: available = (fn_0000_[0x0007].reg.ebx & (1 << 29)); break;
case DN_CPUFeature_SSE: available = (fn_0000_[0x0001].reg.edx & (1 << 25)); break;
case DN_CPUFeature_SSE2: available = (fn_0000_[0x0001].reg.edx & (1 << 26)); break;
case DN_CPUFeature_SSE3: available = (fn_0000_[0x0001].reg.ecx & (1 << 0)); break;
case DN_CPUFeature_SSE41: available = (fn_0000_[0x0001].reg.ecx & (1 << 19)); break;
case DN_CPUFeature_SSE42: available = (fn_0000_[0x0001].reg.ecx & (1 << 20)); break;
case DN_CPUFeature_SSE4A: available = (fn_8000_[0x0001].reg.ecx & (1 << 6)); break;
case DN_CPUFeature_SSSE3: available = (fn_0000_[0x0001].reg.ecx & (1 << 9)); break;
case DN_CPUFeature_TSC: available = (fn_0000_[0x0001].reg.edx & (1 << 4)); break;
case DN_CPUFeature_TscInvariant: available = (fn_8000_[0x0007].reg.edx & (1 << 8)); break;
case DN_CPUFeature_VAES: available = (fn_0000_[0x0007].reg.ecx & (1 << 9)); break;
case DN_CPUFeature_VPCMULQDQ: available = (fn_0000_[0x0007].reg.ecx & (1 << 10)); break;
case DN_CPUFeature_Count: DN_InvalidCodePath; break;
}
if (available)
DN_CPU_SetFeature(&result, DN_CAST(DN_CPUFeature) ext_index);
}
return result;
}
#endif // !defined(DN_PLATFORM_ARM64) && !defined(DN_PLATFORM_EMSCRIPTEN)
// NOTE: DN_TicketMutex ////////////////////////////////////////////////////////////////////////////
DN_API void DN_TicketMutex_Begin(DN_TicketMutex *mutex)
{
unsigned int ticket = DN_Atomic_AddU32(&mutex->ticket, 1);
DN_TicketMutex_BeginTicket(mutex, ticket);
}
DN_API void DN_TicketMutex_End(DN_TicketMutex *mutex)
{
DN_Atomic_AddU32(&mutex->serving, 1);
}
DN_API DN_UInt DN_TicketMutex_MakeTicket(DN_TicketMutex *mutex)
{
DN_UInt result = DN_Atomic_AddU32(&mutex->ticket, 1);
return result;
}
DN_API void DN_TicketMutex_BeginTicket(DN_TicketMutex const *mutex, DN_UInt ticket)
{
DN_AssertF(mutex->serving <= ticket,
"Mutex skipped ticket? Was ticket generated by the correct mutex via MakeTicket? ticket = %u, "
"mutex->serving = %u",
ticket,
mutex->serving);
while (ticket != mutex->serving) {
// NOTE: Use spinlock intrinsic
_mm_pause();
}
}
DN_API bool DN_TicketMutex_CanLock(DN_TicketMutex const *mutex, DN_UInt ticket)
{
bool result = (ticket == mutex->serving);
return result;
}
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
#if !defined(DN_CRT_SECURE_NO_WARNINGS_PREVIOUSLY_DEFINED)
#undef _CRT_SECURE_NO_WARNINGS
#endif
#endif
// NOTE: DN_Bit ////////////////////////////////////////////////////////////////////////////////////
DN_API void DN_Bit_UnsetInplace(DN_USize *flags, DN_USize bitfield)
{
*flags = (*flags & ~bitfield);
}
DN_API void DN_Bit_SetInplace(DN_USize *flags, DN_USize bitfield)
{
*flags = (*flags | bitfield);
}
DN_API bool DN_Bit_IsSet(DN_USize bits, DN_USize bits_to_set)
{
auto result = DN_CAST(bool)((bits & bits_to_set) == bits_to_set);
return result;
}
DN_API bool DN_Bit_IsNotSet(DN_USize bits, DN_USize bits_to_check)
{
auto result = !DN_Bit_IsSet(bits, bits_to_check);
return result;
}
// NOTE: DN_Safe ///////////////////////////////////////////////////////////////////////////////////
DN_API DN_I64 DN_Safe_AddI64(int64_t a, int64_t b)
{
DN_I64 result = DN_CheckF(a <= INT64_MAX - b, "a=%zd, b=%zd", a, b) ? (a + b) : INT64_MAX;
return result;
}
DN_API DN_I64 DN_Safe_MulI64(int64_t a, int64_t b)
{
DN_I64 result = DN_CheckF(a <= INT64_MAX / b, "a=%zd, b=%zd", a, b) ? (a * b) : INT64_MAX;
return result;
}
DN_API DN_U64 DN_Safe_AddU64(DN_U64 a, DN_U64 b)
{
DN_U64 result = DN_CheckF(a <= UINT64_MAX - b, "a=%zu, b=%zu", a, b) ? (a + b) : UINT64_MAX;
return result;
}
DN_API DN_U64 DN_Safe_SubU64(DN_U64 a, DN_U64 b)
{
DN_U64 result = DN_CheckF(a >= b, "a=%zu, b=%zu", a, b) ? (a - b) : 0;
return result;
}
DN_API DN_U64 DN_Safe_MulU64(DN_U64 a, DN_U64 b)
{
DN_U64 result = DN_CheckF(a <= UINT64_MAX / b, "a=%zu, b=%zu", a, b) ? (a * b) : UINT64_MAX;
return result;
}
DN_API DN_U32 DN_Safe_SubU32(DN_U32 a, DN_U32 b)
{
DN_U32 result = DN_CheckF(a >= b, "a=%u, b=%u", a, b) ? (a - b) : 0;
return result;
}
// NOTE: DN_SaturateCastUSizeToI* ////////////////////////////////////////////////////////////
// INT*_MAX literals will be promoted to the type of uintmax_t as uintmax_t is
// the highest possible rank (unsigned > signed).
DN_API int DN_SaturateCastUSizeToInt(DN_USize val)
{
int result = DN_Check(DN_CAST(uintmax_t) val <= INT_MAX) ? DN_CAST(int) val : INT_MAX;
return result;
}
DN_API int8_t DN_SaturateCastUSizeToI8(DN_USize val)
{
int8_t result = DN_Check(DN_CAST(uintmax_t) val <= INT8_MAX) ? DN_CAST(int8_t) val : INT8_MAX;
return result;
}
DN_API DN_I16 DN_SaturateCastUSizeToI16(DN_USize val)
{
DN_I16 result = DN_Check(DN_CAST(uintmax_t) val <= INT16_MAX) ? DN_CAST(DN_I16) val : INT16_MAX;
return result;
}
DN_API DN_I32 DN_SaturateCastUSizeToI32(DN_USize val)
{
DN_I32 result = DN_Check(DN_CAST(uintmax_t) val <= INT32_MAX) ? DN_CAST(DN_I32) val : INT32_MAX;
return result;
}
DN_API int64_t DN_SaturateCastUSizeToI64(DN_USize val)
{
int64_t result = DN_Check(DN_CAST(uintmax_t) val <= INT64_MAX) ? DN_CAST(int64_t) val : INT64_MAX;
return result;
}
// NOTE: DN_SaturateCastUSizeToU* ////////////////////////////////////////////////////////////
// Both operands are unsigned and the lowest rank operand will be promoted to
// match the highest rank operand.
DN_API DN_U8 DN_SaturateCastUSizeToU8(DN_USize val)
{
DN_U8 result = DN_Check(val <= UINT8_MAX) ? DN_CAST(DN_U8) val : UINT8_MAX;
return result;
}
DN_API DN_U16 DN_SaturateCastUSizeToU16(DN_USize val)
{
DN_U16 result = DN_Check(val <= UINT16_MAX) ? DN_CAST(DN_U16) val : UINT16_MAX;
return result;
}
DN_API DN_U32 DN_SaturateCastUSizeToU32(DN_USize val)
{
DN_U32 result = DN_Check(val <= UINT32_MAX) ? DN_CAST(DN_U32) val : UINT32_MAX;
return result;
}
DN_API DN_U64 DN_SaturateCastUSizeToU64(DN_USize val)
{
DN_U64 result = DN_Check(DN_CAST(DN_U64) val <= UINT64_MAX) ? DN_CAST(DN_U64) val : UINT64_MAX;
return result;
}
// NOTE: DN_SaturateCastU64To* ///////////////////////////////////////////////////////////////
DN_API int DN_SaturateCastU64ToInt(DN_U64 val)
{
int result = DN_Check(val <= INT_MAX) ? DN_CAST(int) val : INT_MAX;
return result;
}
DN_API int8_t DN_SaturateCastU64ToI8(DN_U64 val)
{
int8_t result = DN_Check(val <= INT8_MAX) ? DN_CAST(int8_t) val : INT8_MAX;
return result;
}
DN_API DN_I16 DN_SaturateCastU64ToI16(DN_U64 val)
{
DN_I16 result = DN_Check(val <= INT16_MAX) ? DN_CAST(DN_I16) val : INT16_MAX;
return result;
}
DN_API DN_I32 DN_SaturateCastU64ToI32(DN_U64 val)
{
DN_I32 result = DN_Check(val <= INT32_MAX) ? DN_CAST(DN_I32) val : INT32_MAX;
return result;
}
DN_API int64_t DN_SaturateCastU64ToI64(DN_U64 val)
{
int64_t result = DN_Check(val <= INT64_MAX) ? DN_CAST(int64_t) val : INT64_MAX;
return result;
}
// Both operands are unsigned and the lowest rank operand will be promoted to
// match the highest rank operand.
DN_API unsigned int DN_SaturateCastU64ToUInt(DN_U64 val)
{
unsigned int result = DN_Check(val <= UINT8_MAX) ? DN_CAST(unsigned int) val : UINT_MAX;
return result;
}
DN_API DN_U8 DN_SaturateCastU64ToU8(DN_U64 val)
{
DN_U8 result = DN_Check(val <= UINT8_MAX) ? DN_CAST(DN_U8) val : UINT8_MAX;
return result;
}
DN_API DN_U16 DN_SaturateCastU64ToU16(DN_U64 val)
{
DN_U16 result = DN_Check(val <= UINT16_MAX) ? DN_CAST(DN_U16) val : UINT16_MAX;
return result;
}
DN_API DN_U32 DN_SaturateCastU64ToU32(DN_U64 val)
{
DN_U32 result = DN_Check(val <= UINT32_MAX) ? DN_CAST(DN_U32) val : UINT32_MAX;
return result;
}
// NOTE: DN_SaturateCastISizeToI* ////////////////////////////////////////////////////////////
// Both operands are signed so the lowest rank operand will be promoted to
// match the highest rank operand.
DN_API int DN_SaturateCastISizeToInt(DN_ISize val)
{
DN_Assert(val >= INT_MIN && val <= INT_MAX);
int result = DN_CAST(int) DN_Clamp(val, INT_MIN, INT_MAX);
return result;
}
DN_API int8_t DN_SaturateCastISizeToI8(DN_ISize val)
{
DN_Assert(val >= INT8_MIN && val <= INT8_MAX);
int8_t result = DN_CAST(int8_t) DN_Clamp(val, INT8_MIN, INT8_MAX);
return result;
}
DN_API DN_I16 DN_SaturateCastISizeToI16(DN_ISize val)
{
DN_Assert(val >= INT16_MIN && val <= INT16_MAX);
DN_I16 result = DN_CAST(DN_I16) DN_Clamp(val, INT16_MIN, INT16_MAX);
return result;
}
DN_API DN_I32 DN_SaturateCastISizeToI32(DN_ISize val)
{
DN_Assert(val >= INT32_MIN && val <= INT32_MAX);
DN_I32 result = DN_CAST(DN_I32) DN_Clamp(val, INT32_MIN, INT32_MAX);
return result;
}
DN_API int64_t DN_SaturateCastISizeToI64(DN_ISize val)
{
DN_Assert(DN_CAST(int64_t) val >= INT64_MIN && DN_CAST(int64_t) val <= INT64_MAX);
int64_t result = DN_CAST(int64_t) DN_Clamp(DN_CAST(int64_t) val, INT64_MIN, INT64_MAX);
return result;
}
// NOTE: DN_SaturateCastISizeToU* ////////////////////////////////////////////////////////////
// If the value is a negative integer, we clamp to 0. Otherwise, we know that
// the value is >=0, we can upcast safely to bounds check against the maximum
// allowed value.
DN_API unsigned int DN_SaturateCastISizeToUInt(DN_ISize val)
{
unsigned int result = 0;
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT_MAX))
result = DN_CAST(unsigned int) val;
else
result = UINT_MAX;
}
return result;
}
DN_API DN_U8 DN_SaturateCastISizeToU8(DN_ISize val)
{
DN_U8 result = 0;
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT8_MAX))
result = DN_CAST(DN_U8) val;
else
result = UINT8_MAX;
}
return result;
}
DN_API DN_U16 DN_SaturateCastISizeToU16(DN_ISize val)
{
DN_U16 result = 0;
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT16_MAX))
result = DN_CAST(DN_U16) val;
else
result = UINT16_MAX;
}
return result;
}
DN_API DN_U32 DN_SaturateCastISizeToU32(DN_ISize val)
{
DN_U32 result = 0;
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT32_MAX))
result = DN_CAST(DN_U32) val;
else
result = UINT32_MAX;
}
return result;
}
DN_API DN_U64 DN_SaturateCastISizeToU64(DN_ISize val)
{
DN_U64 result = 0;
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT64_MAX))
result = DN_CAST(DN_U64) val;
else
result = UINT64_MAX;
}
return result;
}
// NOTE: DN_SaturateCastI64To* ///////////////////////////////////////////////////////////////
// Both operands are signed so the lowest rank operand will be promoted to
// match the highest rank operand.
DN_API DN_ISize DN_SaturateCastI64ToISize(int64_t val)
{
DN_Check(val >= DN_ISIZE_MIN && val <= DN_ISIZE_MAX);
DN_ISize result = DN_CAST(int64_t) DN_Clamp(val, DN_ISIZE_MIN, DN_ISIZE_MAX);
return result;
}
DN_API int8_t DN_SaturateCastI64ToI8(int64_t val)
{
DN_Check(val >= INT8_MIN && val <= INT8_MAX);
int8_t result = DN_CAST(int8_t) DN_Clamp(val, INT8_MIN, INT8_MAX);
return result;
}
DN_API DN_I16 DN_SaturateCastI64ToI16(int64_t val)
{
DN_Check(val >= INT16_MIN && val <= INT16_MAX);
DN_I16 result = DN_CAST(DN_I16) DN_Clamp(val, INT16_MIN, INT16_MAX);
return result;
}
DN_API DN_I32 DN_SaturateCastI64ToI32(int64_t val)
{
DN_Check(val >= INT32_MIN && val <= INT32_MAX);
DN_I32 result = DN_CAST(DN_I32) DN_Clamp(val, INT32_MIN, INT32_MAX);
return result;
}
DN_API unsigned int DN_SaturateCastI64ToUInt(int64_t val)
{
unsigned int result = 0;
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT_MAX))
result = DN_CAST(unsigned int) val;
else
result = UINT_MAX;
}
return result;
}
DN_API DN_ISize DN_SaturateCastI64ToUSize(int64_t val)
{
DN_USize result = 0;
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= DN_USIZE_MAX))
result = DN_CAST(DN_USize) val;
else
result = DN_USIZE_MAX;
}
return result;
}
DN_API DN_U8 DN_SaturateCastI64ToU8(int64_t val)
{
DN_U8 result = 0;
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT8_MAX))
result = DN_CAST(DN_U8) val;
else
result = UINT8_MAX;
}
return result;
}
DN_API DN_U16 DN_SaturateCastI64ToU16(int64_t val)
{
DN_U16 result = 0;
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT16_MAX))
result = DN_CAST(DN_U16) val;
else
result = UINT16_MAX;
}
return result;
}
DN_API DN_U32 DN_SaturateCastI64ToU32(int64_t val)
{
DN_U32 result = 0;
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT32_MAX))
result = DN_CAST(DN_U32) val;
else
result = UINT32_MAX;
}
return result;
}
DN_API DN_U64 DN_SaturateCastI64ToU64(int64_t val)
{
DN_U64 result = 0;
if (DN_Check(val >= DN_CAST(int64_t) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT64_MAX))
result = DN_CAST(DN_U64) val;
else
result = UINT64_MAX;
}
return result;
}
DN_API int8_t DN_SaturateCastIntToI8(int val)
{
DN_Check(val >= INT8_MIN && val <= INT8_MAX);
int8_t result = DN_CAST(int8_t) DN_Clamp(val, INT8_MIN, INT8_MAX);
return result;
}
DN_API DN_I16 DN_SaturateCastIntToI16(int val)
{
DN_Check(val >= INT16_MIN && val <= INT16_MAX);
DN_I16 result = DN_CAST(DN_I16) DN_Clamp(val, INT16_MIN, INT16_MAX);
return result;
}
DN_API DN_U8 DN_SaturateCastIntToU8(int val)
{
DN_U8 result = 0;
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT8_MAX))
result = DN_CAST(DN_U8) val;
else
result = UINT8_MAX;
}
return result;
}
DN_API DN_U16 DN_SaturateCastIntToU16(int val)
{
DN_U16 result = 0;
if (DN_Check(val >= DN_CAST(DN_ISize) 0)) {
if (DN_Check(DN_CAST(uintmax_t) val <= UINT16_MAX))
result = DN_CAST(DN_U16) val;
else
result = UINT16_MAX;
}
return result;
}
DN_API DN_U32 DN_SaturateCastIntToU32(int val)
{
static_assert(sizeof(val) <= sizeof(DN_U32), "Sanity check to allow simplifying of casting");
DN_U32 result = 0;
if (DN_Check(val >= 0))
result = DN_CAST(DN_U32) val;
return result;
}
DN_API DN_U64 DN_SaturateCastIntToU64(int val)
{
static_assert(sizeof(val) <= sizeof(DN_U64), "Sanity check to allow simplifying of casting");
DN_U64 result = 0;
if (DN_Check(val >= 0))
result = DN_CAST(DN_U64) val;
return result;
}
// NOTE: DN_Asan ////////////////////////////////////////////////////////////////////////// ////////
static_assert(DN_IsPowerOfTwoAligned(DN_ASAN_POISON_GUARD_SIZE, DN_ASAN_POISON_ALIGNMENT),
"ASAN poison guard size must be a power-of-two and aligned to ASAN's alignment"
"requirement (8 bytes)");
DN_API void DN_ASAN_PoisonMemoryRegion(void const volatile *ptr, DN_USize size)
{
if (!ptr || !size)
return;
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
DN_AssertF(DN_IsPowerOfTwoAligned(ptr, 8),
"Poisoning requires the pointer to be aligned on an 8 byte boundary");
__asan_poison_memory_region(ptr, size);
if (DN_ASAN_VET_POISON) {
DN_HardAssert(__asan_address_is_poisoned(ptr));
DN_HardAssert(__asan_address_is_poisoned((char *)ptr + (size - 1)));
}
#else
(void)ptr;
(void)size;
#endif
}
DN_API void DN_ASAN_UnpoisonMemoryRegion(void const volatile *ptr, DN_USize size)
{
if (!ptr || !size)
return;
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
__asan_unpoison_memory_region(ptr, size);
if (DN_ASAN_VET_POISON)
DN_HardAssert(__asan_region_is_poisoned((void *)ptr, size) == 0);
#else
(void)ptr;
(void)size;
#endif
}
+612
View File
@@ -0,0 +1,612 @@
#if !defined(DN_BASE_H)
#define DN_BASE_H
// NOTE: Macros ////////////////////////////////////////////////////////////////////////////////////
#define DN_Stringify(x) #x
#define DN_TokenCombine2(x, y) x ## y
#define DN_TokenCombine(x, y) DN_TokenCombine2(x, y)
#include <stdarg.h> // va_list
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#include <inttypes.h> // PRIu64...
#if !defined(DN_OS_WIN32)
#include <stdlib.h> // exit()
#endif
#define DN_ForIndexU(index, size) DN_USize index = 0; index < size; index++
#define DN_ForIndexI(index, size) DN_ISize index = 0; index < size; index++
#define DN_ForItSize(it, T, array, size) struct { DN_USize index; T *data; } it = {0, &(array)[0]}; it.index < (size); it.index++, it.data++
#define DN_ForIt(it, T, array) struct { DN_USize index; T *data; } it = {0, &(array)->data[0]}; it.index < (array)->size; it.index++, it.data++
#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++
#define DN_AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1))
#define DN_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ~((uintptr_t)(pot) - 1))
#define DN_IsPowerOfTwo(value) ((((uintptr_t)(value)) & (((uintptr_t)(value)) - 1)) == 0)
#define DN_IsPowerOfTwoAligned(value, pot) ((((uintptr_t)value) & (((uintptr_t)pot) - 1)) == 0)
// NOTE: String.h Dependencies /////////////////////////////////////////////////////////////////////
#if !defined(DN_Memcpy) || !defined(DN_Memset) || !defined(DN_Memcmp) || !defined(DN_Memmove)
#include <string.h>
#if !defined(DN_Memcpy)
#define DN_Memcpy(dest, src, count) memcpy((dest), (src), (count))
#endif
#if !defined(DN_Memset)
#define DN_Memset(dest, value, count) memset((dest), (value), (count))
#endif
#if !defined(DN_Memcmp)
#define DN_Memcmp(lhs, rhs, count) memcmp((lhs), (rhs), (count))
#endif
#if !defined(DN_Memmove)
#define DN_Memmove(dest, src, count) memmove((dest), (src), (count))
#endif
#endif
// NOTE: Math.h Dependencies ///////////////////////////////////////////////////////////////////////
#if !defined(DN_SqrtF32) || !defined(DN_SinF32) || !defined(DN_CosF32) || !defined(DN_TanF32)
#include <math.h>
#if !defined(DN_SqrtF32)
#define DN_SqrtF32(val) sqrtf(val)
#endif
#if !defined(DN_SinF32)
#define DN_SinF32(val) sinf(val)
#endif
#if !defined(DN_CosF32)
#define DN_CosF32(val) cosf(val)
#endif
#if !defined(DN_TanF32)
#define DN_TanF32(val) tanf(val)
#endif
#endif
// NOTE: Math //////////////////////////////////////////////////////////////////////////////////////
#define DN_PiF32 3.14159265359f
#define DN_DegreesToRadians(degrees) ((degrees) * (DN_PI / 180.0f))
#define DN_RadiansToDegrees(radians) ((radians) * (180.f * DN_PI))
#define DN_Abs(val) (((val) < 0) ? (-(val)) : (val))
#define DN_Max(a, b) (((a) > (b)) ? (a) : (b))
#define DN_Min(a, b) (((a) < (b)) ? (a) : (b))
#define DN_Clamp(val, lo, hi) DN_Max(DN_Min(val, hi), lo)
#define DN_Squared(val) ((val) * (val))
#define DN_Swap(a, b) \
do { \
auto temp = a; \
a = b; \
b = temp; \
} while (0)
// NOTE: Size //////////////////////////////////////////////////////////////////////////////////////
#define DN_SizeOfI(val) DN_CAST(ptrdiff_t)sizeof(val)
#define DN_ArrayCountU(array) (sizeof(array)/(sizeof((array)[0])))
#define DN_ArrayCountI(array) (DN_ISize)DN_ArrayCountU(array)
#define DN_CharCountU(string) (sizeof(string) - 1)
// NOTE: SI Byte ///////////////////////////////////////////////////////////////////////////////////
#define DN_Bytes(val) ((DN_U64)val)
#define DN_Kilobytes(val) ((DN_U64)1024 * DN_Bytes(val))
#define DN_Megabytes(val) ((DN_U64)1024 * DN_Kilobytes(val))
#define DN_Gigabytes(val) ((DN_U64)1024 * DN_Megabytes(val))
// NOTE: Time //////////////////////////////////////////////////////////////////////////////////////
#define DN_SecondsToMs(val) ((val) * 1000)
#define DN_MinutesToSec(val) ((val) * 60ULL)
#define DN_HoursToSec(val) (DN_MinutesToSec(val) * 60ULL)
#define DN_DaysToSec(val) (DN_HoursToSec(val) * 24ULL)
#define DN_WeeksToSec(val) (DN_DaysToSec(val) * 7ULL)
#define DN_YearsToSec(val) (DN_WeeksToSec(val) * 52ULL)
// NOTE: Debug Break ///////////////////////////////////////////////////////////////////////////////
#if !defined(DN_DebugBreak)
#if defined(NDEBUG)
#define DN_DebugBreak
#else
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
#define DN_DebugBreak __debugbreak()
#elif DN_HAS_BUILTIN(__builtin_debugtrap)
#define DN_DebugBreak __builtin_debugtrap()
#elif DN_HAS_BUILTIN(__builtin_trap) || defined(DN_COMPILER_GCC)
#define DN_DebugBreak __builtin_trap()
#else
#include <signal.h>
#if defined(SIGTRAP)
#define DN_DebugBreak raise(SIGTRAP)
#else
#define DN_DebugBreak raise(SIGABRT)
#endif
#endif
#endif
#endif
// NOTE: Byte swaps ////////////////////////////////////////////////////////////////////////////////
#define DN_ByteSwap64(u64) (((((u64) >> 56) & 0xFF) << 0) | \
((((u64) >> 48) & 0xFF) << 8) | \
((((u64) >> 40) & 0xFF) << 16) | \
((((u64) >> 32) & 0xFF) << 24) | \
((((u64) >> 24) & 0xFF) << 32) | \
((((u64) >> 16) & 0xFF) << 40) | \
((((u64) >> 8) & 0xFF) << 48) | \
((((u64) >> 0) & 0xFF) << 56))
// NOTE: Defer Macro ///////////////////////////////////////////////////////////////////////////////
#if defined(__cplusplus)
template <typename Procedure>
struct DN_Defer
{
Procedure proc;
DN_Defer(Procedure p) : proc(p) {}
~DN_Defer() { proc(); }
};
struct DN_DeferHelper
{
template <typename Lambda>
DN_Defer<Lambda> operator+(Lambda lambda) { return DN_Defer<Lambda>(lambda); };
};
#define DN_UniqueName(prefix) DN_TokenCombine(prefix, __LINE__)
#define DN_DEFER const auto DN_UniqueName(defer_lambda_) = DN_DeferHelper() + [&]()
#endif // defined(__cplusplus)
#define DN_DeferLoop(begin, end) \
bool DN_UniqueName(once) = (begin, true); \
DN_UniqueName(once); \
end, DN_UniqueName(once) = false
// NOTE: Types /////////////////////////////////////////////////////////////////////////////////////
typedef intptr_t DN_ISize;
typedef uintptr_t DN_USize;
typedef int8_t DN_I8;
typedef int16_t DN_I16;
typedef int32_t DN_I32;
typedef int64_t DN_I64;
typedef uint8_t DN_U8;
typedef uint16_t DN_U16;
typedef uint32_t DN_U32;
typedef uint64_t DN_U64;
typedef float DN_F32;
typedef double DN_F64;
typedef unsigned int DN_UInt;
typedef DN_I32 DN_B32;
#define DN_F32_MAX 3.402823466e+38F
#define DN_F32_MIN 1.175494351e-38F
#define DN_F64_MAX 1.7976931348623158e+308
#define DN_F64_MIN 2.2250738585072014e-308
#define DN_USIZE_MAX UINTPTR_MAX
#define DN_ISIZE_MAX INTPTR_MAX
#define DN_ISIZE_MIN INTPTR_MIN
enum DN_ZeroMem
{
DN_ZeroMem_No, // Memory can be handed out without zero-ing it out
DN_ZeroMem_Yes, // Memory should be zero-ed out before giving to the callee
};
struct DN_Str8
{
char *data; // The bytes of the string
DN_USize size; // The number of bytes in the string
char const *begin() const { return data; }
char const *end() const { return data + size; }
char *begin() { return data; }
char *end() { return data + size; }
};
struct DN_Str16 // A pointer and length style string that holds slices to UTF16 bytes.
{
wchar_t *data; // The UTF16 bytes of the string
DN_USize size; // The number of characters in the string
#if defined(__cplusplus)
wchar_t const *begin() const { return data; } // Const begin iterator for range-for loops
wchar_t const *end() const { return data + size; } // Const end iterator for range-for loops
wchar_t *begin() { return data; } // Begin iterator for range-for loops
wchar_t *end() { return data + size; } // End iterator for range-for loops
#endif
};
template <typename T>
struct DN_Slice // A pointer and length container of data
{
T *data;
DN_USize size;
T *begin() { return data; }
T *end() { return data + size; }
T const *begin() const { return data; }
T const *end() const { return data + size; }
};
// NOTE: DN_CallSite ///////////////////////////////////////////////////////////////////////////////
struct DN_CallSite
{
DN_Str8 file;
DN_Str8 function;
DN_U32 line;
};
#define DN_CALL_SITE \
DN_CallSite \
{ \
DN_STR8(__FILE__), DN_STR8(__func__), __LINE__ \
}
// NOTE: Intrinsics ////////////////////////////////////////////////////////////////////////////////
// NOTE: DN_Atomic_Add/Exchange return the previous value store in the target
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
#include <intrin.h>
#define DN_Atomic_CompareExchange64(dest, desired_val, prev_val) _InterlockedCompareExchange64((__int64 volatile *)dest, desired_val, prev_val)
#define DN_Atomic_CompareExchange32(dest, desired_val, prev_val) _InterlockedCompareExchange((long volatile *)dest, desired_val, prev_val)
#define DN_Atomic_LoadU64(target) *(target)
#define DN_Atomic_LoadU32(target) *(target)
#define DN_Atomic_AddU32(target, value) _InterlockedExchangeAdd((long volatile *)target, value)
#define DN_Atomic_AddU64(target, value) _InterlockedExchangeAdd64((__int64 volatile *)target, value)
#define DN_Atomic_SubU32(target, value) DN_Atomic_AddU32(DN_CAST(long volatile *) target, (long)-value)
#define DN_Atomic_SubU64(target, value) DN_Atomic_AddU64(target, (DN_U64) - value)
#define DN_CountLeadingZerosU64(value) __lzcnt64(value)
#define DN_CPU_TSC() __rdtsc()
#define DN_CompilerReadBarrierAndCPUReadFence _ReadBarrier(); _mm_lfence()
#define DN_CompilerWriteBarrierAndCPUWriteFence _WriteBarrier(); _mm_sfence()
#elif defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
#if defined(__ANDROID__)
#elif defined(DN_PLATFORM_EMSCRIPTEN)
#include <emmintrin.h>
#else
#include <x86intrin.h>
#endif
#define DN_Atomic_LoadU64(target) __atomic_load_n(x, __ATOMIC_SEQ_CST)
#define DN_Atomic_LoadU32(target) __atomic_load_n(x, __ATOMIC_SEQ_CST)
#define DN_Atomic_AddU32(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
#define DN_Atomic_AddU64(target, value) __atomic_fetch_add(target, value, __ATOMIC_ACQ_REL)
#define DN_Atomic_SubU32(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
#define DN_Atomic_SubU64(target, value) __atomic_fetch_sub(target, value, __ATOMIC_ACQ_REL)
#define DN_CountLeadingZerosU64(value) __builtin_clzll(value)
#if defined(DN_COMPILER_GCC)
#define DN_CPU_TSC() __rdtsc()
#else
#define DN_CPU_TSC() __builtin_readcyclecounter()
#endif
#if defined(DN_PLATFORM_EMSCRIPTEN)
#define DN_CompilerReadBarrierAndCPUReadFence
#define DN_CompilerWriteBarrierAndCPUWriteFence
#else
#define DN_CompilerReadBarrierAndCPUReadFence asm volatile("lfence" ::: "memory")
#define DN_CompilerWriteBarrierAndCPUWriteFence asm volatile("sfence" ::: "memory")
#endif
#else
#error "Compiler not supported"
#endif
#if !defined(DN_PLATFORM_ARM64)
struct DN_CPURegisters
{
int eax;
int ebx;
int ecx;
int edx;
};
union DN_CPUIDResult
{
DN_CPURegisters reg;
int values[4];
};
struct DN_CPUIDArgs
{
int eax;
int ecx;
};
#define DN_CPU_FEAT_XMACRO \
DN_CPU_FEAT_XENTRY(3DNow) \
DN_CPU_FEAT_XENTRY(3DNowExt) \
DN_CPU_FEAT_XENTRY(ABM) \
DN_CPU_FEAT_XENTRY(AES) \
DN_CPU_FEAT_XENTRY(AVX) \
DN_CPU_FEAT_XENTRY(AVX2) \
DN_CPU_FEAT_XENTRY(AVX512F) \
DN_CPU_FEAT_XENTRY(AVX512DQ) \
DN_CPU_FEAT_XENTRY(AVX512IFMA) \
DN_CPU_FEAT_XENTRY(AVX512PF) \
DN_CPU_FEAT_XENTRY(AVX512ER) \
DN_CPU_FEAT_XENTRY(AVX512CD) \
DN_CPU_FEAT_XENTRY(AVX512BW) \
DN_CPU_FEAT_XENTRY(AVX512VL) \
DN_CPU_FEAT_XENTRY(AVX512VBMI) \
DN_CPU_FEAT_XENTRY(AVX512VBMI2) \
DN_CPU_FEAT_XENTRY(AVX512VNNI) \
DN_CPU_FEAT_XENTRY(AVX512BITALG) \
DN_CPU_FEAT_XENTRY(AVX512VPOPCNTDQ) \
DN_CPU_FEAT_XENTRY(AVX5124VNNIW) \
DN_CPU_FEAT_XENTRY(AVX5124FMAPS) \
DN_CPU_FEAT_XENTRY(AVX512VP2INTERSECT) \
DN_CPU_FEAT_XENTRY(AVX512FP16) \
DN_CPU_FEAT_XENTRY(CLZERO) \
DN_CPU_FEAT_XENTRY(CMPXCHG8B) \
DN_CPU_FEAT_XENTRY(CMPXCHG16B) \
DN_CPU_FEAT_XENTRY(F16C) \
DN_CPU_FEAT_XENTRY(FMA) \
DN_CPU_FEAT_XENTRY(FMA4) \
DN_CPU_FEAT_XENTRY(FP128) \
DN_CPU_FEAT_XENTRY(FP256) \
DN_CPU_FEAT_XENTRY(FPU) \
DN_CPU_FEAT_XENTRY(MMX) \
DN_CPU_FEAT_XENTRY(MONITOR) \
DN_CPU_FEAT_XENTRY(MOVBE) \
DN_CPU_FEAT_XENTRY(MOVU) \
DN_CPU_FEAT_XENTRY(MmxExt) \
DN_CPU_FEAT_XENTRY(PCLMULQDQ) \
DN_CPU_FEAT_XENTRY(POPCNT) \
DN_CPU_FEAT_XENTRY(RDRAND) \
DN_CPU_FEAT_XENTRY(RDSEED) \
DN_CPU_FEAT_XENTRY(RDTSCP) \
DN_CPU_FEAT_XENTRY(SHA) \
DN_CPU_FEAT_XENTRY(SSE) \
DN_CPU_FEAT_XENTRY(SSE2) \
DN_CPU_FEAT_XENTRY(SSE3) \
DN_CPU_FEAT_XENTRY(SSE41) \
DN_CPU_FEAT_XENTRY(SSE42) \
DN_CPU_FEAT_XENTRY(SSE4A) \
DN_CPU_FEAT_XENTRY(SSSE3) \
DN_CPU_FEAT_XENTRY(TSC) \
DN_CPU_FEAT_XENTRY(TscInvariant) \
DN_CPU_FEAT_XENTRY(VAES) \
DN_CPU_FEAT_XENTRY(VPCMULQDQ)
enum DN_CPUFeature
{
#define DN_CPU_FEAT_XENTRY(label) DN_CPUFeature_##label,
DN_CPU_FEAT_XMACRO
#undef DN_CPU_FEAT_XENTRY
DN_CPUFeature_Count,
};
struct DN_CPUFeatureDecl
{
DN_CPUFeature value;
DN_Str8 label;
};
struct DN_CPUFeatureQuery
{
DN_CPUFeature feature;
bool available;
};
struct DN_CPUReport
{
char vendor[4 /*bytes*/ * 3 /*EDX, ECX, EBX*/ + 1 /*null*/];
char brand[48];
DN_U64 features[(DN_CPUFeature_Count / (sizeof(DN_U64) * 8)) + 1];
};
extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
#endif // DN_PLATFORM_ARM64
// NOTE: DN_TicketMutex ////////////////////////////////////////////////////////////////////////////
struct DN_TicketMutex
{
unsigned int volatile ticket; // The next ticket to give out to the thread taking the mutex
unsigned int volatile serving; // The ticket ID to block the mutex on until it is returned
};
// NOTE: Intrinsics ////////////////////////////////////////////////////////////////////////////////
DN_FORCE_INLINE DN_U64 DN_Atomic_SetValue64 (DN_U64 volatile *target, DN_U64 value);
DN_FORCE_INLINE DN_U32 DN_Atomic_SetValue32 (DN_U32 volatile *target, DN_U32 value);
#if !defined (DN_PLATFORM_ARM64)
DN_API DN_CPUIDResult DN_CPU_ID (DN_CPUIDArgs args);
DN_API DN_USize DN_CPU_HasFeatureArray (DN_CPUReport const *report, DN_CPUFeatureQuery *features, DN_USize features_size);
DN_API bool DN_CPU_HasFeature (DN_CPUReport const *report, DN_CPUFeature feature);
DN_API bool DN_CPU_HasAllFeatures (DN_CPUReport const *report, DN_CPUFeature const *features, DN_USize features_size);
template <DN_USize N>
bool DN_CPU_HasAllFeaturesCArray(DN_CPUReport const *report, DN_CPUFeature const (&features)[N]);
DN_API void DN_CPU_SetFeature (DN_CPUReport *report, DN_CPUFeature feature);
DN_API DN_CPUReport DN_CPU_Report ();
#endif
// NOTE: DN_TicketMutex ////////////////////////////////////////////////////////////////////////////
DN_API void DN_TicketMutex_Begin (DN_TicketMutex *mutex);
DN_API void DN_TicketMutex_End (DN_TicketMutex *mutex);
DN_API DN_UInt DN_TicketMutex_MakeTicket (DN_TicketMutex *mutex);
DN_API void DN_TicketMutex_BeginTicket(DN_TicketMutex const *mutex, DN_UInt ticket);
DN_API bool DN_TicketMutex_CanLock (DN_TicketMutex const *mutex, DN_UInt ticket);
// NOTE: DN_DLList /////////////////////////////////////////////////////////////////////////////////
#define DN_DLList_Init(list) \
(list)->next = (list)->prev = (list)
#define DN_DLList_IsSentinel(list, item) ((list) == (item))
#define DN_DLList_InitArena(list, T, arena) \
do { \
(list) = DN_Arena_New(arena, T, DN_ZeroMem_Yes); \
DN_DLList_Init(list); \
} while (0)
#define DN_DLList_InitPool(list, T, pool) \
do { \
(list) = DN_Pool_New(pool, T); \
DN_DLList_Init(list); \
} while (0)
#define DN_DLList_Detach(item) \
do { \
if (item) { \
(item)->prev->next = (item)->next; \
(item)->next->prev = (item)->prev; \
(item)->next = nullptr; \
(item)->prev = nullptr; \
} \
} while (0)
#define DN_DLList_Dequeue(list, dest_ptr) \
if (DN_DLList_HasItems(list)) { \
dest_ptr = (list)->next; \
DN_DLList_Detach(dest_ptr); \
}
#define DN_DLList_Append(list, item) \
do { \
if (item) { \
if ((item)->next) \
DN_DLList_Detach(item); \
(item)->next = (list)->next; \
(item)->prev = (list); \
(item)->next->prev = (item); \
(item)->prev->next = (item); \
} \
} while (0)
#define DN_DLList_Prepend(list, item) \
do { \
if (item) { \
if ((item)->next) \
DN_DLList_Detach(item); \
(item)->next = (list); \
(item)->prev = (list)->prev; \
(item)->next->prev = (item); \
(item)->prev->next = (item); \
} \
} while (0)
#define DN_DLList_IsEmpty(list) \
(!(list) || ((list) == (list)->next))
#define DN_DLList_IsInit(list) \
((list)->next && (list)->prev)
#define DN_DLList_HasItems(list) \
((list) && ((list) != (list)->next))
#define DN_DLList_ForEach(it, list) \
auto *it = (list)->next; (it) != (list); (it) = (it)->next
// NOTE: Intrinsics ////////////////////////////////////////////////////////////////////////////////
DN_FORCE_INLINE DN_U64 DN_Atomic_SetValue64(DN_U64 volatile *target, DN_U64 value)
{
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
__int64 result;
do {
result = *target;
} while (DN_Atomic_CompareExchange64(target, value, result) != result);
return DN_CAST(DN_U64) result;
#elif defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
DN_U64 result = __sync_lock_test_and_set(target, value);
return result;
#else
#error Unsupported compiler
#endif
}
DN_FORCE_INLINE DN_U32 DN_Atomic_SetValue32(DN_U32 volatile *target, DN_U32 value)
{
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
long result;
do {
result = *target;
} while (DN_Atomic_CompareExchange32(target, value, result) != result);
return result;
#elif defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG)
long result = __sync_lock_test_and_set(target, value);
return result;
#else
#error Unsupported compiler
#endif
}
template <DN_USize N>
bool DN_CPU_HasAllFeaturesCArray(DN_CPUReport const *report, DN_CPUFeature const (&features)[N])
{
bool result = DN_CPU_HasAllFeatures(report, features, N);
return result;
}
// NOTE: DN_Bit ////////////////////////////////////////////////////////////////////////////////////
DN_API void DN_Bit_UnsetInplace (DN_USize *flags, DN_USize bitfield);
DN_API void DN_Bit_SetInplace (DN_USize *flags, DN_USize bitfield);
DN_API bool DN_Bit_IsSet (DN_USize bits, DN_USize bits_to_set);
DN_API bool DN_Bit_IsNotSet (DN_USize bits, DN_USize bits_to_check);
#define DN_Bit_ClearNextLSB(value) (value) & ((value) - 1)
// NOTE: DN_Safe ///////////////////////////////////////////////////////////////////////////////////
DN_API DN_I64 DN_Safe_AddI64 (DN_I64 a, DN_I64 b);
DN_API DN_I64 DN_Safe_MulI64 (DN_I64 a, DN_I64 b);
DN_API DN_U64 DN_Safe_AddU64 (DN_U64 a, DN_U64 b);
DN_API DN_U64 DN_Safe_MulU64 (DN_U64 a, DN_U64 b);
DN_API DN_U64 DN_Safe_SubU64 (DN_U64 a, DN_U64 b);
DN_API DN_U32 DN_Safe_SubU32 (DN_U32 a, DN_U32 b);
DN_API int DN_SaturateCastUSizeToInt (DN_USize val);
DN_API DN_I8 DN_SaturateCastUSizeToI8 (DN_USize val);
DN_API DN_I16 DN_SaturateCastUSizeToI16 (DN_USize val);
DN_API DN_I32 DN_SaturateCastUSizeToI32 (DN_USize val);
DN_API DN_I64 DN_SaturateCastUSizeToI64 (DN_USize val);
DN_API int DN_SaturateCastU64ToInt (DN_U64 val);
DN_API DN_I8 DN_SaturateCastU8ToI8 (DN_U64 val);
DN_API DN_I16 DN_SaturateCastU16ToI16 (DN_U64 val);
DN_API DN_I32 DN_SaturateCastU32ToI32 (DN_U64 val);
DN_API DN_I64 DN_SaturateCastU64ToI64 (DN_U64 val);
DN_API DN_UInt DN_SaturateCastU64ToUInt (DN_U64 val);
DN_API DN_U8 DN_SaturateCastU64ToU8 (DN_U64 val);
DN_API DN_U16 DN_SaturateCastU64ToU16 (DN_U64 val);
DN_API DN_U32 DN_SaturateCastU64ToU32 (DN_U64 val);
DN_API DN_U8 DN_SaturateCastUSizeToU8 (DN_USize val);
DN_API DN_U16 DN_SaturateCastUSizeToU16 (DN_USize val);
DN_API DN_U32 DN_SaturateCastUSizeToU32 (DN_USize val);
DN_API DN_U64 DN_SaturateCastUSizeToU64 (DN_USize val);
DN_API int DN_SaturateCastISizeToInt (DN_ISize val);
DN_API DN_I8 DN_SaturateCastISizeToI8 (DN_ISize val);
DN_API DN_I16 DN_SaturateCastISizeToI16 (DN_ISize val);
DN_API DN_I32 DN_SaturateCastISizeToI32 (DN_ISize val);
DN_API DN_I64 DN_SaturateCastISizeToI64 (DN_ISize val);
DN_API DN_UInt DN_SaturateCastISizeToUInt (DN_ISize val);
DN_API DN_U8 DN_SaturateCastISizeToU8 (DN_ISize val);
DN_API DN_U16 DN_SaturateCastISizeToU16 (DN_ISize val);
DN_API DN_U32 DN_SaturateCastISizeToU32 (DN_ISize val);
DN_API DN_U64 DN_SaturateCastISizeToU64 (DN_ISize val);
DN_API DN_ISize DN_SaturateCastI64ToISize (DN_I64 val);
DN_API DN_I8 DN_SaturateCastI64ToI8 (DN_I64 val);
DN_API DN_I16 DN_SaturateCastI64ToI16 (DN_I64 val);
DN_API DN_I32 DN_SaturateCastI64ToI32 (DN_I64 val);
DN_API DN_UInt DN_SaturateCastI64ToUInt (DN_I64 val);
DN_API DN_ISize DN_SaturateCastI64ToUSize (DN_I64 val);
DN_API DN_U8 DN_SaturateCastI64ToU8 (DN_I64 val);
DN_API DN_U16 DN_SaturateCastI64ToU16 (DN_I64 val);
DN_API DN_U32 DN_SaturateCastI64ToU32 (DN_I64 val);
DN_API DN_U64 DN_SaturateCastI64ToU64 (DN_I64 val);
DN_API DN_I8 DN_SaturateCastIntToI8 (int val);
DN_API DN_I16 DN_SaturateCastIntToI16 (int val);
DN_API DN_U8 DN_SaturateCastIntToU8 (int val);
DN_API DN_U16 DN_SaturateCastIntToU16 (int val);
DN_API DN_U32 DN_SaturateCastIntToU32 (int val);
DN_API DN_U64 DN_SaturateCastIntToU64 (int val);
// NOTE: DN_Asan ///////////////////////////////////////////////////////////////////////////////////
DN_API void DN_ASAN_PoisonMemoryRegion (void const volatile *ptr, DN_USize size);
DN_API void DN_ASAN_UnpoisonMemoryRegion (void const volatile *ptr, DN_USize size);
#endif // !defined(DN_BASE_H)
+90
View File
@@ -0,0 +1,90 @@
#if !defined(DN_BASE_ASSERT_H)
#define DN_BASE_ASSERT_H
#define DN_HardAssert(expr) DN_HardAssertF(expr, "")
#if defined(DN_FREESTANDING)
#define DN_HardAssertF(expr, fmt, ...) \
do { \
if (!(expr)) { \
DN_DebugBreak; \
(*(int *)0) = 0; \
} \
} while (0)
#else
#define DN_HardAssertF(expr, fmt, ...) \
do { \
if (!(expr)) { \
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_LOG_ErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DN_STR_FMT(stack_trace_), \
##__VA_ARGS__); \
DN_DebugBreak; \
} \
} while (0)
#endif
#if defined(DN_NO_ASSERT)
#define DN_Assert(...)
#define DN_AssertOnce(...)
#define DN_AssertF(...)
#define DN_AssertFOnce(...)
#else
#if defined(DN_FREESTANDING)
#define DN_AssertF(expr, fmt, ...) \
do { \
if (!(expr)) { \
DN_DebugBreak; \
(*(int *)0) = 0; \
} \
} while (0)
#else
#define DN_AssertF(expr, fmt, ...) \
do { \
if (!(expr)) { \
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DN_STR_FMT(stack_trace_), \
##__VA_ARGS__); \
DN_DebugBreak; \
} \
} while (0)
#endif
#define DN_AssertFOnce(expr, fmt, ...) \
do { \
static bool once = true; \
if (!(expr) && once) { \
once = false; \
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DN_STR_FMT(stack_trace_), \
##__VA_ARGS__); \
DN_DebugBreak; \
} \
} while (0)
#define DN_Assert(expr) DN_AssertF((expr), "")
#define DN_AssertOnce(expr) DN_AssertFOnce((expr), "")
#endif
#define DN_InvalidCodePathF(fmt, ...) DN_HardAssertF(0, fmt, ##__VA_ARGS__)
#define DN_InvalidCodePath DN_InvalidCodePathF("Invalid code path triggered")
// NOTE: Check macro ///////////////////////////////////////////////////////////////////////////////
#define DN_Check(expr) DN_CheckF(expr, "")
#if defined(DN_FREESTANDING)
#define DN_CheckF(expr, fmt, ...) (expr)
#else
#if defined(DN_NO_CHECK_BREAK)
#define DN_CheckF(expr, fmt, ...) \
((expr) ? true : (DN_LOG_WarningF(fmt, ##__VA_ARGS__), false))
#else
#define DN_CheckF(expr, fmt, ...) \
((expr) ? true : (DN_LOG_ErrorF(fmt, ##__VA_ARGS__), DN_StackTrace_Print(128 /*limit*/), DN_DebugBreak, false))
#endif
#endif
#endif
+160
View File
@@ -0,0 +1,160 @@
#if !defined(DN_BASE_COMPILER_H)
#define DN_BASE_COMPILER_H
// NOTE: Compiler identification ///////////////////////////////////////////////////////////////////
// Warning! Order is important here, clang-cl on Windows defines _MSC_VER
#if defined(_MSC_VER)
#if defined(__clang__)
#define DN_COMPILER_CLANG_CL
#define DN_COMPILER_CLANG
#else
#define DN_COMPILER_MSVC
#endif
#elif defined(__clang__)
#define DN_COMPILER_CLANG
#elif defined(__GNUC__)
#define DN_COMPILER_GCC
#endif
// NOTE: __has_feature /////////////////////////////////////////////////////////////////////////////
// MSVC for example does not support the feature detection macro for instance so we compile it out
#if defined(__has_feature)
#define DN_HAS_FEATURE(expr) __has_feature(expr)
#else
#define DN_HAS_FEATURE(expr) 0
#endif
// NOTE: __has_builtin /////////////////////////////////////////////////////////////////////////////
// MSVC for example does not support the feature detection macro for instance so we compile it out
#if defined(__has_builtin)
#define DN_HAS_BUILTIN(expr) __has_builtin(expr)
#else
#define DN_HAS_BUILTIN(expr) 0
#endif
// NOTE: Warning suppression macros ////////////////////////////////////////////////////////////////
#if defined(DN_COMPILER_MSVC)
#define DN_MSVC_WARNING_PUSH __pragma(warning(push))
#define DN_MSVC_WARNING_DISABLE(...) __pragma(warning(disable :##__VA_ARGS__))
#define DN_MSVC_WARNING_POP __pragma(warning(pop))
#else
#define DN_MSVC_WARNING_PUSH
#define DN_MSVC_WARNING_DISABLE(...)
#define DN_MSVC_WARNING_POP
#endif
#if defined(DN_COMPILER_CLANG) || defined(DN_COMPILER_GCC) || defined(DN_COMPILER_CLANG_CL)
#define DN_GCC_WARNING_PUSH _Pragma("GCC diagnostic push")
#define DN_GCC_WARNING_DISABLE_HELPER_0(x) #x
#define DN_GCC_WARNING_DISABLE_HELPER_1(y) DN_GCC_WARNING_DISABLE_HELPER_0(GCC diagnostic ignored #y)
#define DN_GCC_WARNING_DISABLE(warning) _Pragma(DN_GCC_WARNING_DISABLE_HELPER_1(warning))
#define DN_GCC_WARNING_POP _Pragma("GCC diagnostic pop")
#else
#define DN_GCC_WARNING_PUSH
#define DN_GCC_WARNING_DISABLE(...)
#define DN_GCC_WARNING_POP
#endif
// NOTE: Host OS identification ////////////////////////////////////////////////////////////////////
#if defined(_WIN32)
#define DN_OS_WIN32
#elif defined(__gnu_linux__) || defined(__linux__)
#define DN_OS_UNIX
#endif
// NOTE: Platform identification ///////////////////////////////////////////////////////////////////
#if !defined(DN_PLATFORM_EMSCRIPTEN) && \
!defined(DN_PLATFORM_POSIX) && \
!defined(DN_PLATFORM_WIN32)
#if defined(__aarch64__) || defined(_M_ARM64)
#define DN_PLATFORM_ARM64
#elif defined(__EMSCRIPTEN__)
#define DN_PLATFORM_EMSCRIPTEN
#elif defined(DN_OS_WIN32)
#define DN_PLATFORM_WIN32
#else
#define DN_PLATFORM_POSIX
#endif
#endif
// NOTE: Windows crap //////////////////////////////////////////////////////////////////////////////
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
#if defined(_CRT_SECURE_NO_WARNINGS)
#define DN_CRT_SECURE_NO_WARNINGS_PREVIOUSLY_DEFINED
#else
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif
// NOTE: Force Inline //////////////////////////////////////////////////////////////////////////////
#if defined(DN_COMPILER_MSVC) || defined(DN_COMPILER_CLANG_CL)
#define DN_FORCE_INLINE __forceinline
#else
#define DN_FORCE_INLINE inline __attribute__((always_inline))
#endif
// NOTE: Function/Variable Annotations /////////////////////////////////////////////////////////////
#if defined(DN_STATIC_API)
#define DN_API static
#else
#define DN_API
#endif
// NOTE: C/CPP Literals ////////////////////////////////////////////////////////////////////////////
// Declare struct literals that work in both C and C++ because the syntax is different between
// languages.
#if 0
struct Foo { int a; }
struct Foo foo = DN_LITERAL(Foo){32}; // Works on both C and C++
#endif
#if defined(__cplusplus)
#define DN_LITERAL(T) T
#else
#define DN_LITERAL(T) (T)
#endif
// NOTE: Thread Locals /////////////////////////////////////////////////////////////////////////////
#if defined(__cplusplus)
#define DN_THREAD_LOCAL thread_local
#else
#define DN_THREAD_LOCAL _Thread_local
#endif
// NOTE: C variadic argument annotations ///////////////////////////////////////////////////////////
// TODO: Other compilers
#if defined(DN_COMPILER_MSVC)
#define DN_FMT_ATTRIB _Printf_format_string_
#else
#define DN_FMT_ATTRIB
#endif
// NOTE: Type Cast /////////////////////////////////////////////////////////////////////////////////
#define DN_CAST(val) (val)
// NOTE: Zero initialisation macro /////////////////////////////////////////////////////////////////
#if defined(__cplusplus)
#define DN_ZeroInit {}
#else
#define DN_ZeroInit {0}
#endif
// NOTE: Address sanitizer /////////////////////////////////////////////////////////////////////////
#if !defined(DN_ASAN_POISON)
#define DN_ASAN_POISON 0
#endif
#if !defined(DN_ASAN_VET_POISON)
#define DN_ASAN_VET_POISON 0
#endif
#define DN_ASAN_POISON_ALIGNMENT 8
#if !defined(DN_ASAN_POISON_GUARD_SIZE)
#define DN_ASAN_POISON_GUARD_SIZE 128
#endif
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
#include <sanitizer/asan_interface.h>
#endif
#endif // !defined(DN_BASE_COMPILER_H)
File diff suppressed because it is too large Load Diff
+372
View File
@@ -0,0 +1,372 @@
#if !defined(DN_CONTAINERS_H)
#define DN_CONTAINERS_H
#include "../dn_base_inc.h"
struct DN_Ring
{
DN_U64 size;
char *base;
DN_U64 write_pos;
DN_U64 read_pos;
};
// NOTE: DN_CArray /////////////////////////////////////////////////////////////////////////////////
enum DN_ArrayErase
{
DN_ArrayErase_Unstable,
DN_ArrayErase_Stable,
};
enum DN_ArrayAdd
{
DN_ArrayAdd_Append,
DN_ArrayAdd_Prepend,
};
struct DN_ArrayEraseResult
{
// The next index your for-index should be set to such that you can continue
// to iterate the remainder of the array, e.g:
//
// for (DN_USize index = 0; index < array.size; index++) {
// if (erase)
// index = DN_FArray_EraseRange(&array, index, -3, DN_ArrayErase_Unstable);
// }
DN_USize it_index;
DN_USize items_erased; // The number of items erased
};
template <typename T> struct DN_ArrayFindResult
{
T *data; // Pointer to the value if a match is found, null pointer otherwise
DN_USize index; // Index to the value if a match is found, 0 otherwise
};
#if !defined(DN_NO_SARRAY)
// NOTE: DN_SArray /////////////////////////////////////////////////////////////////////////////////
template <typename T> struct DN_SArray
{
T *data; // Pointer to the start of the array items in the block of memory
DN_USize size; // Number of items currently in the array
DN_USize max; // Maximum number of items this array can store
T *begin() { return data; }
T *end () { return data + size; }
T const *begin() const { return data; }
T const *end () const { return data + size; }
};
#endif // !defined(DN_NO_SARRAY)
#if !defined(DN_NO_FARRAY)
// NOTE: DN_FArray /////////////////////////////////////////////////////////////////////////////////
template <typename T, DN_USize N> struct DN_FArray
{
T data[N]; // Pointer to the start of the array items in the block of memory
DN_USize size; // Number of items currently in the array
T *begin() { return data; }
T *end () { return data + size; }
T const *begin() const { return data; }
T const *end () const { return data + size; }
};
template <typename T> using DN_FArray8 = DN_FArray<T, 8>;
template <typename T> using DN_FArray16 = DN_FArray<T, 16>;
template <typename T> using DN_FArray32 = DN_FArray<T, 32>;
template <typename T> using DN_FArray64 = DN_FArray<T, 64>;
#endif // !defined(DN_NO_FARRAY)
#if !defined(DN_NO_DSMAP)
// NOTE: DN_DSMap //////////////////////////////////////////////////////////////////////////////////
enum DN_DSMapKeyType
{
// Key | Key Hash | Map Index
DN_DSMapKeyType_Invalid,
DN_DSMapKeyType_U64, // U64 | Hash(U64) | Hash(U64) % map_size
DN_DSMapKeyType_U64NoHash, // U64 | U64 | U64 % map_size
DN_DSMapKeyType_Buffer, // Buffer | Hash(buffer) | Hash(buffer) % map_size
DN_DSMapKeyType_BufferAsU64NoHash, // Buffer | U64(buffer[0:4]) | U64(buffer[0:4]) % map_size
};
struct DN_DSMapKey
{
DN_DSMapKeyType type;
DN_U32 hash; // Hash to lookup in the map. If it equals, we check that the original key payload matches
void const *buffer_data;
DN_U32 buffer_size;
DN_U64 u64;
bool no_copy_buffer;
};
template <typename T>
struct DN_DSMapSlot
{
DN_DSMapKey key; // Hash table lookup key
T value; // Hash table value
};
typedef DN_U32 DN_DSMapFlags;
enum DN_DSMapFlags_
{
DN_DSMapFlags_Nil = 0,
DN_DSMapFlags_DontFreeArenaOnResize = 1 << 0,
};
using DN_DSMapHashFunction = DN_U32(DN_DSMapKey key, DN_U32 seed);
template <typename T> struct DN_DSMap
{
DN_U32 *hash_to_slot; // Mapping from hash to a index in the slots array
DN_DSMapSlot<T> *slots; // Values of the array stored contiguously, non-sorted order
DN_U32 size; // Total capacity of the map and is a power of two
DN_U32 occupied; // Number of slots used in the hash table
DN_Arena *arena; // Backing arena for the hash table
DN_Pool pool; // Allocator for keys that are variable-sized buffers
DN_U32 initial_size; // Initial map size, map cannot shrink on erase below this size
DN_DSMapHashFunction *hash_function; // Custom hashing function to use if field is set
DN_U32 hash_seed; // Seed for the hashing function, when 0, DN_DS_MAP_DEFAULT_HASH_SEED is used
DN_DSMapFlags flags;
};
template <typename T> struct DN_DSMapResult
{
bool found;
DN_DSMapSlot<T> *slot;
T *value;
};
#endif // !defined(DN_NO_DSMAP)
#if !defined(DN_NO_LIST)
// NOTE: DN_List ///////////////////////////////////////////////////////////////////////////////////
template <typename T> struct DN_ListChunk
{
T *data;
DN_USize size;
DN_USize count;
DN_ListChunk<T> *next;
DN_ListChunk<T> *prev;
};
template <typename T> struct DN_ListIterator
{
DN_B32 init; // True if DN_List_Iterate has been called at-least once on this iterator
DN_ListChunk<T> *chunk; // The chunk that the iterator is reading from
DN_USize chunk_data_index; // The index in the chunk the iterator is referencing
DN_USize index; // The index of the item in the list as if it was flat array
T *data; // Pointer to the data the iterator is referencing. Nullptr if invalid.
};
template <typename T> struct DN_List
{
DN_USize count; // Cumulative count of all items made across all list chunks
DN_USize chunk_size; // When new ListChunk's are required, the minimum 'data' entries to allocate for that node.
DN_ListChunk<T> *head;
DN_ListChunk<T> *tail;
};
#endif // !defined(DN_NO_LIST)
// NOTE: Macros for operating on data structures that are embedded into a C style struct or from a
// set of defined variables from the available scope. Keep it stupid simple, structs and functions.
// Minimal amount of container types with flexible construction leads to less duplicated container
// code and less template meta-programming.
//
// LArray => Literal Array
// Define a C array and size:
//
// ```
// MyStruct buffer[TB_ASType_Count] = {};
// DN_USize size = 0;
// MyStruct *item = DN_LArray_Make(buffer, size, DN_ArrayCountU(buffer), DN_ZeroMem_No);
// ```
//
// IArray => Intrusive Array
// Define a struct with the members 'data', 'size' and 'max':
//
// ```
// struct MyArray {
// MyStruct *data;
// DN_USize size;
// DN_USize max;
// } my_array = {};
//
// MyStruct *item = DN_IArray_Make(&my_array, MyArray, DN_ZeroMem_No);
// ```
// ISLList => Intrusive Singly Linked List
// Define a struct with the members 'next':
//
// ```
// struct MyLinkItem {
// int data;
// MyLinkItem *next;
// } my_link = {};
//
// MyLinkItem *first_item = DN_ISLList_Detach(&my_link, MyLinkItem);
// ```
#define DN_ISLList_Detach(list) (decltype(list)) DN_CSLList_Detach((void **)&(list), (void **)&(list)->next)
#define DN_LArray_MakeArray(c_array, size, max, count, zero_mem) (decltype(&(c_array)[0])) DN_CArray2_MakeArray(c_array, size, max, sizeof((c_array)[0]), count, zero_mem)
#define DN_LArray_MakeArrayZ(c_array, size, max, count) (decltype(&(c_array)[0])) DN_CArray2_MakeArray(c_array, size, max, sizeof((c_array)[0]), count, DN_ZeroMem_Yes)
#define DN_LArray_Make(c_array, size, max, zero_mem) (decltype(&(c_array)[0])) DN_CArray2_MakeArray(c_array, size, max, sizeof((c_array)[0]), 1, zero_mem)
#define DN_LArray_MakeZ(c_array, size, max) (decltype(&(c_array)[0])) DN_CArray2_MakeArray(c_array, size, max, sizeof((c_array)[0]), 1, DN_ZeroMem_Yes)
#define DN_LArray_AddArray(c_array, size, max, items, count, add) (decltype(&(c_array)[0])) DN_CArray2_AddArray (c_array, size, max, sizeof((c_array)[0]), items, count, add)
#define DN_LArray_Add(c_array, size, max, item, add) (decltype(&(c_array)[0])) DN_CArray2_AddArray (c_array, size, max, sizeof((c_array)[0]), &item, 1, add)
#define DN_LArray_AppendArray(c_array, size, max, items, count) (decltype(&(c_array)[0])) DN_CArray2_AddArray (c_array, size, max, sizeof((c_array)[0]), items, count, DN_ArrayAdd_Append)
#define DN_LArray_Append(c_array, size, max, item) (decltype(&(c_array)[0])) DN_CArray2_AddArray (c_array, size, max, sizeof((c_array)[0]), &item, 1, DN_ArrayAdd_Append)
#define DN_LArray_PrependArray(c_array, size, max, items, count) (decltype(&(c_array)[0])) DN_CArray2_AddArray (c_array, size, max, sizeof((c_array)[0]), items, count, DN_ArrayAdd_Prepend)
#define DN_LArray_Prepend(c_array, size, max, item) (decltype(&(c_array)[0])) DN_CArray2_AddArray (c_array, size, max, sizeof((c_array)[0]), &item, 1, DN_ArrayAdd_Prepend)
#define DN_LArray_EraseRange(c_array, size, begin_index, count, erase) DN_CArray2_EraseRange(c_array, size, sizeof((c_array)[0]), begin_index, count, erase)
#define DN_IArray_Front(array) (array)->data
#define DN_IArray_GrowIfNeededFromPool(array, pool) DN_CArray2_GrowIfNeededFromPool((void **)(&(array)->data), (array)->size, &(array)->max, sizeof((array)->data[0]), pool)
#define DN_IArray_MakeArray(array, count, zero_mem) (decltype(&((array)->data)[0])) DN_CArray2_MakeArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), count, zero_mem)
#define DN_IArray_MakeArrayZ(array, count) (decltype(&((array)->data)[0])) DN_CArray2_MakeArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), count, DN_ZeroMem_Yes)
#define DN_IArray_Make(array, zero_mem) (decltype(&((array)->data)[0])) DN_CArray2_MakeArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), 1, zero_mem)
#define DN_IArray_MakeZ(array) (decltype(&((array)->data)[0])) DN_CArray2_MakeArray((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), 1, DN_ZeroMem_Yes)
#define DN_IArray_AddArray(array, items, count, add) (decltype(&((array)->data)[0])) DN_CArray2_AddArray ((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), items, count, add)
#define DN_IArray_Add(array, item, add) (decltype(&((array)->data)[0])) DN_CArray2_AddArray ((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), &item, 1, add)
#define DN_IArray_AppendArray(array, items, count) (decltype(&((array)->data)[0])) DN_CArray2_AddArray ((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), items, count, DN_ArrayAdd_Append)
#define DN_IArray_Append(array, item) (decltype(&((array)->data)[0])) DN_CArray2_AddArray ((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), &item, 1, DN_ArrayAdd_Append)
#define DN_IArray_PrependArray(array, items, count) (decltype(&((array)->data)[0])) DN_CArray2_AddArray ((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), items, count, DN_ArrayAdd_Prepend)
#define DN_IArray_Prepend(array, item) (decltype(&((array)->data)[0])) DN_CArray2_AddArray ((array)->data, &(array)->size, (array)->max, sizeof(((array)->data)[0]), &item, 1, DN_ArrayAdd_Prepend)
#define DN_IArray_EraseRange(array, size, begin_index, count, erase) DN_CArray2_EraseRange((array)->data, &(array)->size, sizeof(((array)->data)[0]), begin_index, count, erase)
DN_API DN_ArrayEraseResult DN_CArray2_EraseRange (void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
DN_API void *DN_CArray2_MakeArray (void *data, DN_USize *size, DN_USize max, DN_USize data_size, DN_USize make_size, DN_ZeroMem zero_mem);
DN_API void *DN_CArray2_AddArray (void *data, DN_USize *size, DN_USize max, DN_USize data_size, void *elems, DN_USize elems_count, DN_ArrayAdd add);
DN_API bool DN_CArray2_GrowIfNeededFromPool (void **data, DN_USize size, DN_USize *max, DN_USize data_size, DN_Pool *pool);
DN_API void *DN_CSLList_Detach (void **link, void **next);
DN_API bool DN_Ring_HasSpace (DN_Ring const *ring, DN_U64 size);
DN_API bool DN_Ring_HasData (DN_Ring const *ring, DN_U64 size);
DN_API void DN_Ring_Write (DN_Ring *ring, void const *src, DN_U64 src_size);
#define DN_Ring_WriteStruct(ring, item) DN_Ring_Write((ring), (item), sizeof(*(item)))
DN_API void DN_Ring_Read (DN_Ring *ring, void *dest, DN_U64 dest_size);
#define DN_Ring_ReadStruct(ring, dest) DN_Ring_Read((ring), (dest), sizeof(*(dest)))
template <typename T> DN_ArrayEraseResult DN_CArray_EraseRange (T *data, DN_USize *size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
template <typename T> T * DN_CArray_MakeArray (T *data, DN_USize *size, DN_USize max, DN_USize count, DN_ZeroMem zero_mem);
template <typename T> T * DN_CArray_InsertArray (T *data, DN_USize *size, DN_USize max, DN_USize index, T const *items, DN_USize count);
template <typename T> T DN_CArray_PopFront (T *data, DN_USize *size, DN_USize count);
template <typename T> T DN_CArray_PopBack (T *data, DN_USize *size, DN_USize count);
template <typename T> DN_ArrayFindResult<T> DN_CArray_Find (T *data, DN_USize size, T const &value);
// NOTE: DN_SArray /////////////////////////////////////////////////////////////////////////////////
#if !defined(DN_NO_SARRAY)
template <typename T> DN_SArray<T> DN_SArray_Init (DN_Arena *arena, DN_USize size, DN_ZeroMem zero_mem);
template <typename T> DN_SArray<T> DN_SArray_InitSlice (DN_Arena *arena, DN_Slice<T> slice, DN_USize size, DN_ZeroMem zero_mem);
template <typename T, size_t N> DN_SArray<T> DN_SArray_InitCArray (DN_Arena *arena, T const (&array)[N], DN_USize size, DN_ZeroMem);
template <typename T> DN_SArray<T> DN_SArray_InitBuffer (T* buffer, DN_USize size);
template <typename T> bool DN_SArray_IsValid (DN_SArray<T> const *array);
template <typename T> DN_Slice<T> DN_SArray_Slice (DN_SArray<T> const *array);
template <typename T> T * DN_SArray_AddArray (DN_SArray<T> *array, T const *items, DN_USize count);
template <typename T, DN_USize N> T * DN_SArray_AddCArray (DN_SArray<T> *array, T const (&items)[N]);
template <typename T> T * DN_SArray_Add (DN_SArray<T> *array, T const &item);
#define DN_SArray_AddArrayAssert(...) DN_HardAssert(DN_SArray_AddArray(__VA_ARGS__))
#define DN_SArray_AddCArrayAssert(...) DN_HardAssert(DN_SArray_AddCArray(__VA_ARGS__))
#define DN_SArray_AddAssert(...) DN_HardAssert(DN_SArray_Add(__VA_ARGS__))
template <typename T> T * DN_SArray_MakeArray (DN_SArray<T> *array, DN_USize count, DN_ZeroMem zero_mem);
template <typename T> T * DN_SArray_Make (DN_SArray<T> *array, DN_ZeroMem zero_mem);
#define DN_SArray_MakeArrayAssert(...) DN_HardAssert(DN_SArray_MakeArray(__VA_ARGS__))
#define DN_SArray_MakeAssert(...) DN_HardAssert(DN_SArray_Make(__VA_ARGS__))
template <typename T> T * DN_SArray_InsertArray (DN_SArray<T> *array, DN_USize index, T const *items, DN_USize count);
template <typename T, DN_USize N> T * DN_SArray_InsertCArray (DN_SArray<T> *array, DN_USize index, T const (&items)[N]);
template <typename T> T * DN_SArray_Insert (DN_SArray<T> *array, DN_USize index, T const &item);
#define DN_SArray_InsertArrayAssert(...) DN_HardAssert(DN_SArray_InsertArray(__VA_ARGS__))
#define DN_SArray_InsertCArrayAssert(...) DN_HardAssert(DN_SArray_InsertCArray(__VA_ARGS__))
#define DN_SArray_InsertAssert(...) DN_HardAssert(DN_SArray_Insert(__VA_ARGS__))
template <typename T> T DN_SArray_PopFront (DN_SArray<T> *array, DN_USize count);
template <typename T> T DN_SArray_PopBack (DN_SArray<T> *array, DN_USize count);
template <typename T> DN_ArrayEraseResult DN_SArray_EraseRange (DN_SArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
template <typename T> void DN_SArray_Clear (DN_SArray<T> *array);
#endif // !defined(DN_NO_SARRAY)
#if !defined(DN_NO_FARRAY)
#define DN_FArray_ToSArray(array) DN_SArray_InitBuffer((array)->data, DN_ArrayCountU((array)->data))
template <typename T, DN_USize N> DN_FArray<T, N> DN_FArray_Init (T const *array, DN_USize count);
#define DN_FArray_HasData(array) ((array).data && (array).size)
template <typename T, DN_USize N> DN_FArray<T, N> DN_FArray_InitSlice (DN_Slice<T> slice);
template <typename T, DN_USize N, DN_USize K> DN_FArray<T, N> DN_FArray_InitCArray (T const (&items)[K]);
template <typename T, DN_USize N> bool DN_FArray_IsValid (DN_FArray<T, N> const *array);
template <typename T, DN_USize N> DN_USize DN_FArray_Max (DN_FArray<T, N> const *) { return N; }
template <typename T, DN_USize N> DN_Slice<T> DN_FArray_Slice (DN_FArray<T, N> const *array);
template <typename T, DN_USize N> T * DN_FArray_AddArray (DN_FArray<T, N> *array, T const *items, DN_USize count);
template <typename T, DN_USize N, DN_USize K> T * DN_FArray_AddCArray (DN_FArray<T, N> *array, T const (&items)[K]);
template <typename T, DN_USize N> T * DN_FArray_Add (DN_FArray<T, N> *array, T const &item);
#define DN_FArray_AddArrayAssert(...) DN_HardAssert(DN_FArray_AddArray(__VA_ARGS__))
#define DN_FArray_AddCArrayAssert(...) DN_HardAssert(DN_FArray_AddCArray(__VA_ARGS__))
#define DN_FArray_AddAssert(...) DN_HardAssert(DN_FArray_Add(__VA_ARGS__))
template <typename T, DN_USize N> T * DN_FArray_MakeArray (DN_FArray<T, N> *array, DN_USize count, DN_ZeroMem zero_mem);
template <typename T, DN_USize N> T * DN_FArray_Make (DN_FArray<T, N> *array, DN_ZeroMem zero_mem);
#define DN_FArray_MakeArrayAssert(...) DN_HardAssert(DN_FArray_MakeArray(__VA_ARGS__))
#define DN_FArray_MakeAssert(...) DN_HardAssert(DN_FArray_Make(__VA_ARGS__))
template <typename T, DN_USize N> T * DN_FArray_InsertArray (DN_FArray<T, N> *array, T const &item, DN_USize index);
template <typename T, DN_USize N, DN_USize K> T * DN_FArray_InsertCArray (DN_FArray<T, N> *array, DN_USize index, T const (&items)[K]);
template <typename T, DN_USize N> T * DN_FArray_Insert (DN_FArray<T, N> *array, DN_USize index, T const &item);
#define DN_FArray_InsertArrayAssert(...) DN_HardAssert(DN_FArray_InsertArray(__VA_ARGS__))
#define DN_FArray_InsertAssert(...) DN_HardAssert(DN_FArray_Insert(__VA_ARGS__))
template <typename T, DN_USize N> T DN_FArray_PopFront (DN_FArray<T, N> *array, DN_USize count);
template <typename T, DN_USize N> T DN_FArray_PopBack (DN_FArray<T, N> *array, DN_USize count);
template <typename T, DN_USize N> DN_ArrayFindResult<T> DN_FArray_Find (DN_FArray<T, N> *array, T const &find);
template <typename T, DN_USize N> DN_ArrayEraseResult DN_FArray_EraseRange (DN_FArray<T, N> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
template <typename T, DN_USize N> void DN_FArray_Clear (DN_FArray<T, N> *array);
#endif // !defined(DN_NO_FARRAY)
#if !defined(DN_NO_SLICE)
#define DN_TO_SLICE(val) DN_Slice_Init((val)->data, (val)->size)
#define DN_Slice_InitCArray(array) DN_Slice_Init(array, DN_ArrayCountU(array))
template <typename T> DN_Slice<T> DN_Slice_Init (T* const data, DN_USize size);
template <typename T, DN_USize N> DN_Slice<T> DN_Slice_InitCArrayCopy (DN_Arena *arena, T const (&array)[N]);
template <typename T> DN_Slice<T> DN_Slice_Copy (DN_Arena *arena, DN_Slice<T> slice);
template <typename T> DN_Slice<T> DN_Slice_CopyPtr (DN_Arena *arena, T* const data, DN_USize size);
template <typename T> DN_Slice<T> DN_Slice_Alloc (DN_Arena *arena, DN_USize size, DN_ZeroMem zero_mem);
DN_Str8 DN_Slice_Str8Render (DN_Arena *arena, DN_Slice<DN_Str8> array, DN_Str8 separator);
DN_Str8 DN_Slice_Str8RenderSpaceSeparated (DN_Arena *arena, DN_Slice<DN_Str8> array);
DN_Str16 DN_Slice_Str16Render (DN_Arena *arena, DN_Slice<DN_Str16> array, DN_Str16 separator);
DN_Str16 DN_Slice_Str16RenderSpaceSeparated(DN_Arena *arena, DN_Slice<DN_Str16> array);
#endif // !defined(DN_NO_SLICE)
#if !defined(DN_NO_DSMAP)
template <typename T> DN_DSMap<T> DN_DSMap_Init (DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags);
template <typename T> void DN_DSMap_Deinit (DN_DSMap<T> *map, DN_ZeroMem zero_mem);
template <typename T> bool DN_DSMap_IsValid (DN_DSMap<T> const *map);
template <typename T> DN_U32 DN_DSMap_Hash (DN_DSMap<T> const *map, DN_DSMapKey key);
template <typename T> DN_U32 DN_DSMap_HashToSlotIndex (DN_DSMap<T> const *map, DN_DSMapKey key);
template <typename T> DN_DSMapResult<T> DN_DSMap_Find (DN_DSMap<T> const *map, DN_DSMapKey key);
template <typename T> DN_DSMapResult<T> DN_DSMap_Make (DN_DSMap<T> *map, DN_DSMapKey key);
template <typename T> DN_DSMapResult<T> DN_DSMap_Set (DN_DSMap<T> *map, DN_DSMapKey key, T const &value);
template <typename T> DN_DSMapResult<T> DN_DSMap_FindKeyU64 (DN_DSMap<T> const *map, DN_U64 key);
template <typename T> DN_DSMapResult<T> DN_DSMap_MakeKeyU64 (DN_DSMap<T> *map, DN_U64 key);
template <typename T> DN_DSMapResult<T> DN_DSMap_SetKeyU64 (DN_DSMap<T> *map, DN_U64 key, T const &value);
template <typename T> DN_DSMapResult<T> DN_DSMap_FindKeyStr8 (DN_DSMap<T> const *map, DN_Str8 key);
template <typename T> DN_DSMapResult<T> DN_DSMap_MakeKeyStr8 (DN_DSMap<T> *map, DN_Str8 key);
template <typename T> DN_DSMapResult<T> DN_DSMap_SetKeyStr8 (DN_DSMap<T> *map, DN_Str8 key, T const &value);
template <typename T> bool DN_DSMap_Resize (DN_DSMap<T> *map, DN_U32 size);
template <typename T> bool DN_DSMap_Erase (DN_DSMap<T> *map, DN_DSMapKey key);
template <typename T> bool DN_DSMap_EraseKeyU64 (DN_DSMap<T> *map, DN_U64 key);
template <typename T> bool DN_DSMap_EraseKeyStr8 (DN_DSMap<T> *map, DN_Str8 key);
template <typename T> DN_DSMapKey DN_DSMap_KeyBuffer (DN_DSMap<T> const *map, void const *data, DN_U32 size);
template <typename T> DN_DSMapKey DN_DSMap_KeyBufferAsU64NoHash (DN_DSMap<T> const *map, void const *data, DN_U32 size);
template <typename T> DN_DSMapKey DN_DSMap_KeyU64 (DN_DSMap<T> const *map, DN_U64 u64);
template <typename T> DN_DSMapKey DN_DSMap_KeyStr8 (DN_DSMap<T> const *map, DN_Str8 string);
#define DN_DSMap_KeyCStr8(map, string) DN_DSMap_KeyBuffer(map, string, sizeof((string))/sizeof((string)[0]) - 1)
DN_API DN_DSMapKey DN_DSMap_KeyU64NoHash (DN_U64 u64);
DN_API bool DN_DSMap_KeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs);
DN_API bool operator== (DN_DSMapKey lhs, DN_DSMapKey rhs);
#endif // !defined(DN_NO_DSMAP)
#if !defined(DN_NO_LIST)
template <typename T> DN_List<T> DN_List_Init (DN_USize chunk_size);
template <typename T, size_t N> DN_List<T> DN_List_InitCArray (DN_Arena *arena, DN_USize chunk_size, T const (&array)[N]);
template <typename T> T * DN_List_At (DN_List<T> *list, DN_USize index, DN_ListChunk<T> **at_chunk);
template <typename T> void DN_List_Clear (DN_List<T> *list);
template <typename T> bool DN_List_Iterate (DN_List<T> *list, DN_ListIterator<T> *it, DN_USize start_index);
template <typename T> T * DN_List_MakeArena (DN_List<T> *list, DN_Arena *arena, DN_USize count);
template <typename T> T * DN_List_MakePool (DN_List<T> *list, DN_Pool *pool, DN_USize count);
template <typename T> T * DN_List_AddArena (DN_List<T> *list, DN_Arena *arena, T const &value);
template <typename T> T * DN_List_AddPool (DN_List<T> *list, DN_Pool *pool, T const &value);
template <typename T> void DN_List_AddListArena (DN_List<T> *list, DN_Arena *arena, DN_List<T> other);
template <typename T> void DN_List_AddListArena (DN_List<T> *list, DN_Pool *pool, DN_List<T> other);
template <typename T> DN_Slice<T> DN_List_ToSliceCopy (DN_List<T> const *list, DN_Arena* arena);
#endif // !defined(DN_NO_LIST)
#endif // !defined(DN_CONTAINER_H)
+364
View File
@@ -0,0 +1,364 @@
#define DN_CONVERT_CPP
DN_API int DN_CVT_FmtBuffer3DotTruncate(char *buffer, int size, DN_FMT_ATTRIB char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
int size_required = DN_VSNPrintF(buffer, size, fmt, args);
int result = DN_Max(DN_Min(size_required, size - 1), 0);
if (result == size - 1) {
buffer[size - 2] = '.';
buffer[size - 3] = '.';
}
va_end(args);
return result;
}
DN_API DN_CVTU64Str8 DN_CVT_U64ToStr8(uint64_t val, char separator)
{
DN_CVTU64Str8 result = {};
if (val == 0) {
result.data[result.size++] = '0';
} else {
// NOTE: The number is written in reverse because we form the string by
// dividing by 10, so we write it in, then reverse it out after all is
// done.
DN_CVTU64Str8 temp = {};
for (DN_USize digit_count = 0; val > 0; digit_count++) {
if (separator && (digit_count != 0) && (digit_count % 3 == 0))
temp.data[temp.size++] = separator;
auto digit = DN_CAST(char)(val % 10);
temp.data[temp.size++] = '0' + digit;
val /= 10;
}
// NOTE: Reverse the string
DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(6293) // Ill-defined for-loop
DN_MSVC_WARNING_DISABLE(6385) // Reading invalid data from 'temp.data' unsigned overflow is valid for loop termination
for (DN_USize temp_index = temp.size - 1; temp_index < temp.size; temp_index--) {
char ch = temp.data[temp_index];
result.data[result.size++] = ch;
}
DN_MSVC_WARNING_POP
}
return result;
}
DN_API DN_CVTU64ByteSize DN_CVT_U64ToByteSize(uint64_t bytes, DN_CVTU64ByteSizeType desired_type)
{
DN_CVTU64ByteSize result = {};
result.bytes = DN_CAST(DN_F64) bytes;
if (!DN_Check(desired_type != DN_CVTU64ByteSizeType_Count)) {
result.suffix = DN_CVT_U64ByteSizeTypeString(result.type);
return result;
}
if (desired_type == DN_CVTU64ByteSizeType_Auto)
for (; result.type < DN_CVTU64ByteSizeType_Count && result.bytes >= 1024.0; result.type = DN_CAST(DN_CVTU64ByteSizeType)(DN_CAST(DN_USize) result.type + 1))
result.bytes /= 1024.0;
else
for (; result.type < desired_type; result.type = DN_CAST(DN_CVTU64ByteSizeType)(DN_CAST(DN_USize) result.type + 1))
result.bytes /= 1024.0;
result.suffix = DN_CVT_U64ByteSizeTypeString(result.type);
return result;
}
DN_API DN_Str8 DN_CVT_U64ToByteSizeStr8(DN_Arena *arena, uint64_t bytes, DN_CVTU64ByteSizeType desired_type)
{
DN_CVTU64ByteSize byte_size = DN_CVT_U64ToByteSize(bytes, desired_type);
DN_Str8 result = DN_Str8_InitF(arena, "%.2f%.*s", byte_size.bytes, DN_STR_FMT(byte_size.suffix));
return result;
}
DN_API DN_Str8 DN_CVT_U64ByteSizeTypeString(DN_CVTU64ByteSizeType type)
{
DN_Str8 result = DN_STR8("");
switch (type) {
case DN_CVTU64ByteSizeType_B: result = DN_STR8("B"); break;
case DN_CVTU64ByteSizeType_KiB: result = DN_STR8("KiB"); break;
case DN_CVTU64ByteSizeType_MiB: result = DN_STR8("MiB"); break;
case DN_CVTU64ByteSizeType_GiB: result = DN_STR8("GiB"); break;
case DN_CVTU64ByteSizeType_TiB: result = DN_STR8("TiB"); break;
case DN_CVTU64ByteSizeType_Count: result = DN_STR8(""); break;
case DN_CVTU64ByteSizeType_Auto: result = DN_STR8(""); break;
}
return result;
}
DN_API DN_Str8 DN_CVT_U64ToAge(DN_Arena *arena, DN_U64 age_s, DN_CVTU64AgeUnit unit)
{
DN_Str8 result = {};
if (!arena)
return result;
char buffer[512];
DN_Arena stack_arena = DN_Arena_InitFromBuffer(buffer, sizeof(buffer), DN_ArenaFlags_NoPoison);
DN_Str8Builder builder = DN_Str8Builder_Init(&stack_arena);
DN_U64 remainder = age_s;
if (unit & DN_CVTU64AgeUnit_Year) {
DN_USize value = remainder / DN_YearsToSec(1);
remainder -= DN_YearsToSec(value);
if (value)
DN_Str8Builder_AppendF(&builder, "%s%zuyr", builder.string_size ? " " : "", value);
}
if (unit & DN_CVTU64AgeUnit_Week) {
DN_USize value = remainder / DN_WeeksToSec(1);
remainder -= DN_WeeksToSec(value);
if (value)
DN_Str8Builder_AppendF(&builder, "%s%zuw", builder.string_size ? " " : "", value);
}
if (unit & DN_CVTU64AgeUnit_Day) {
DN_USize value = remainder / DN_DaysToSec(1);
remainder -= DN_DaysToSec(value);
if (value)
DN_Str8Builder_AppendF(&builder, "%s%zud", builder.string_size ? " " : "", value);
}
if (unit & DN_CVTU64AgeUnit_Hr) {
DN_USize value = remainder / DN_HoursToSec(1);
remainder -= DN_HoursToSec(value);
if (value)
DN_Str8Builder_AppendF(&builder, "%s%zuh", builder.string_size ? " " : "", value);
}
if (unit & DN_CVTU64AgeUnit_Min) {
DN_USize value = remainder / DN_MinutesToSec(1);
remainder -= DN_MinutesToSec(value);
if (value)
DN_Str8Builder_AppendF(&builder, "%s%zum", builder.string_size ? " " : "", value);
}
if (unit & DN_CVTU64AgeUnit_Sec) {
DN_USize value = remainder;
if (value || builder.string_size == 0)
DN_Str8Builder_AppendF(&builder, "%s%zus", builder.string_size ? " " : "", value);
}
result = DN_Str8Builder_Build(&builder, arena);
return result;
}
DN_API DN_Str8 DN_CVT_F64ToAge(DN_Arena *arena, DN_F64 age_s, DN_CVTU64AgeUnit unit)
{
DN_Str8 result = {};
if (!arena)
return result;
char buffer[256];
DN_Arena stack_arena = DN_Arena_InitFromBuffer(buffer, sizeof(buffer), DN_ArenaFlags_NoPoison);
DN_Str8Builder builder = DN_Str8Builder_Init(&stack_arena);
DN_F64 remainder = age_s;
if (unit & DN_CVTU64AgeUnit_Year) {
DN_F64 value = remainder / DN_CAST(DN_F64) DN_YearsToSec(1);
if (value >= 1.0) {
remainder -= DN_YearsToSec(value);
DN_Str8Builder_AppendF(&builder, "%s%.1fyr", builder.string_size ? " " : "", value);
}
}
if (unit & DN_CVTU64AgeUnit_Week) {
DN_F64 value = remainder / DN_CAST(DN_F64) DN_WeeksToSec(1);
if (value >= 1.0) {
remainder -= DN_WeeksToSec(value);
DN_Str8Builder_AppendF(&builder, "%s%.1fw", builder.string_size ? " " : "", value);
}
}
if (unit & DN_CVTU64AgeUnit_Day) {
DN_F64 value = remainder / DN_CAST(DN_F64) DN_DaysToSec(1);
if (value >= 1.0) {
remainder -= DN_WeeksToSec(value);
DN_Str8Builder_AppendF(&builder, "%s%.1fd", builder.string_size ? " " : "", value);
}
}
if (unit & DN_CVTU64AgeUnit_Hr) {
DN_F64 value = remainder / DN_CAST(DN_F64) DN_HoursToSec(1);
if (value >= 1.0) {
remainder -= DN_HoursToSec(value);
DN_Str8Builder_AppendF(&builder, "%s%.1fh", builder.string_size ? " " : "", value);
}
}
if (unit & DN_CVTU64AgeUnit_Min) {
DN_F64 value = remainder / DN_CAST(DN_F64) DN_MinutesToSec(1);
if (value >= 1.0) {
remainder -= DN_MinutesToSec(value);
DN_Str8Builder_AppendF(&builder, "%s%.1fm", builder.string_size ? " " : "", value);
}
}
if (unit & DN_CVTU64AgeUnit_Sec) {
DN_F64 value = remainder;
DN_Str8Builder_AppendF(&builder, "%s%.1fs", builder.string_size ? " " : "", value);
}
result = DN_Str8Builder_Build(&builder, arena);
return result;
}
DN_API uint64_t DN_CVT_HexToU64(DN_Str8 hex)
{
DN_Str8 real_hex = DN_Str8_TrimPrefix(DN_Str8_TrimPrefix(hex, DN_STR8("0x")), DN_STR8("0X"));
DN_USize max_hex_size = sizeof(uint64_t) * 2 /*hex chars per byte*/;
DN_Assert(real_hex.size <= max_hex_size);
DN_USize size = DN_Min(max_hex_size, real_hex.size);
uint64_t result = 0;
for (DN_USize index = 0; index < size; index++) {
char ch = real_hex.data[index];
DN_CharHexToU8 val = DN_Char_HexToU8(ch);
if (!val.success)
break;
result = (result << 4) | val.value;
}
return result;
}
DN_API DN_Str8 DN_CVT_U64ToHex(DN_Arena *arena, uint64_t number, uint32_t flags)
{
DN_Str8 prefix = {};
if ((flags & DN_CVTHexU64Str8Flags_0xPrefix))
prefix = DN_STR8("0x");
char const *fmt = (flags & DN_CVTHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
DN_USize required_size = DN_CStr8_FSize(fmt, number) + prefix.size;
DN_Str8 result = DN_Str8_Alloc(arena, required_size, DN_ZeroMem_No);
if (DN_Str8_HasData(result)) {
DN_Memcpy(result.data, prefix.data, prefix.size);
int space = DN_CAST(int) DN_Max((result.size - prefix.size) + 1, 0); /*null-terminator*/
DN_SNPrintF(result.data + prefix.size, space, fmt, number);
}
return result;
}
DN_API DN_CVTU64HexStr8 DN_CVT_U64ToHexStr8(uint64_t number, DN_CVTHexU64Str8Flags flags)
{
DN_Str8 prefix = {};
if (flags & DN_CVTHexU64Str8Flags_0xPrefix)
prefix = DN_STR8("0x");
DN_CVTU64HexStr8 result = {};
DN_Memcpy(result.data, prefix.data, prefix.size);
result.size += DN_CAST(int8_t) prefix.size;
char const *fmt = (flags & DN_CVTHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
int size = DN_SNPrintF(result.data + result.size, DN_ArrayCountU(result.data) - result.size, fmt, number);
result.size += DN_CAST(uint8_t) size;
DN_Assert(result.size < DN_ArrayCountU(result.data));
// NOTE: snprintf returns the required size of the format string
// irrespective of if there's space or not, but, always null terminates so
// the last byte is wasted.
result.size = DN_Min(result.size, DN_ArrayCountU(result.data) - 1);
return result;
}
DN_API bool DN_CVT_BytesToHexPtr(void const *src, DN_USize src_size, char *dest, DN_USize dest_size)
{
if (!src || !dest)
return false;
if (!DN_Check(dest_size >= src_size * 2))
return false;
char const *HEX = "0123456789abcdef";
unsigned char const *src_u8 = DN_CAST(unsigned char const *) src;
for (DN_USize src_index = 0, dest_index = 0; src_index < src_size; src_index++) {
char byte = src_u8[src_index];
char hex01 = (byte >> 4) & 0b1111;
char hex02 = (byte >> 0) & 0b1111;
dest[dest_index++] = HEX[(int)hex01];
dest[dest_index++] = HEX[(int)hex02];
}
return true;
}
DN_API DN_Str8 DN_CVT_BytesToHex(DN_Arena *arena, void const *src, DN_USize size)
{
DN_Str8 result = {};
if (!src || size <= 0)
return result;
result = DN_Str8_Alloc(arena, size * 2, DN_ZeroMem_No);
result.data[result.size] = 0;
bool converted = DN_CVT_BytesToHexPtr(src, size, result.data, result.size);
DN_Assert(converted);
return result;
}
DN_API DN_USize DN_CVT_HexToBytesPtrUnchecked(DN_Str8 hex, void *dest, DN_USize dest_size)
{
DN_USize result = 0;
unsigned char *dest_u8 = DN_CAST(unsigned char *) dest;
for (DN_USize hex_index = 0; hex_index < hex.size; hex_index += 2, result += 1) {
char hex01 = hex.data[hex_index];
char hex02 = (hex_index + 1 < hex.size) ? hex.data[hex_index + 1] : 0;
char bit4_01 = DN_Char_HexToU8(hex01).value;
char bit4_02 = DN_Char_HexToU8(hex02).value;
char byte = (bit4_01 << 4) | (bit4_02 << 0);
dest_u8[result] = byte;
}
DN_Assert(result <= dest_size);
return result;
}
DN_API DN_USize DN_CVT_HexToBytesPtr(DN_Str8 hex, void *dest, DN_USize dest_size)
{
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0x"));
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0X"));
DN_USize result = 0;
if (!DN_Str8_HasData(hex))
return result;
// NOTE: Trimmed hex can be "0xf" -> "f" or "0xAB" -> "AB"
// Either way, the size can be odd or even, hence we round up to the nearest
// multiple of two to ensure that we calculate the min buffer size orrectly.
DN_USize hex_size_rounded_up = hex.size + (hex.size % 2);
DN_USize min_buffer_size = hex_size_rounded_up / 2;
if (hex.size <= 0 || !DN_Check(dest_size >= min_buffer_size))
return result;
result = DN_CVT_HexToBytesPtrUnchecked(hex, dest, dest_size);
return result;
}
DN_API DN_Str8 DN_CVT_HexToBytesUnchecked(DN_Arena *arena, DN_Str8 hex)
{
DN_USize hex_size_rounded_up = hex.size + (hex.size % 2);
DN_Str8 result = DN_Str8_Alloc(arena, (hex_size_rounded_up / 2), DN_ZeroMem_No);
if (result.data) {
DN_USize bytes_written = DN_CVT_HexToBytesPtr(hex, result.data, result.size);
DN_Assert(bytes_written == result.size);
}
return result;
}
DN_API DN_Str8 DN_CVT_HexToBytes(DN_Arena *arena, DN_Str8 hex)
{
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0x"));
hex = DN_Str8_TrimPrefix(hex, DN_STR8("0X"));
DN_Str8 result = {};
if (!DN_Str8_HasData(hex))
return result;
if (!DN_Check(DN_Str8_IsAll(hex, DN_Str8IsAll_Hex)))
return result;
result = DN_CVT_HexToBytesUnchecked(arena, hex);
return result;
}
+77
View File
@@ -0,0 +1,77 @@
#if !defined(DN_BASE_CONVERT_H)
#define DN_BASE_CONVERT_H
struct DN_CVTU64Str8
{
char data[27 + 1]; // NOTE(dn): 27 is the maximum size of DN_U64 including a separator
DN_U8 size;
};
enum DN_CVTU64ByteSizeType
{
DN_CVTU64ByteSizeType_B,
DN_CVTU64ByteSizeType_KiB,
DN_CVTU64ByteSizeType_MiB,
DN_CVTU64ByteSizeType_GiB,
DN_CVTU64ByteSizeType_TiB,
DN_CVTU64ByteSizeType_Count,
DN_CVTU64ByteSizeType_Auto,
};
struct DN_CVTU64ByteSize
{
DN_CVTU64ByteSizeType type;
DN_Str8 suffix; // "KiB", "MiB", "GiB" .. e.t.c
DN_F64 bytes;
};
struct DN_CVTU64HexStr8
{
char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/];
DN_U8 size;
};
typedef DN_U32 DN_CVTHexU64Str8Flags;
enum DN_CVTHexU64Str8Flags_
{
DN_CVTHexU64Str8Flags_Nil = 0,
DN_CVTHexU64Str8Flags_0xPrefix = 1 << 0, /// Add the '0x' prefix from the string
DN_CVTHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
};
typedef DN_U32 DN_CVTU64AgeUnit;
enum DN_CVTU64AgeUnit_
{
DN_CVTU64AgeUnit_Sec = 1 << 0,
DN_CVTU64AgeUnit_Min = 1 << 1,
DN_CVTU64AgeUnit_Hr = 1 << 2,
DN_CVTU64AgeUnit_Day = 1 << 3,
DN_CVTU64AgeUnit_Week = 1 << 4,
DN_CVTU64AgeUnit_Year = 1 << 5,
DN_CVTU64AgeUnit_HMS = DN_CVTU64AgeUnit_Sec | DN_CVTU64AgeUnit_Min | DN_CVTU64AgeUnit_Hr,
DN_CVTU64AgeUnit_All = DN_CVTU64AgeUnit_HMS | DN_CVTU64AgeUnit_Day | DN_CVTU64AgeUnit_Week | DN_CVTU64AgeUnit_Year,
};
DN_API int DN_CVT_FmtBuffer3DotTruncate (char *buffer, int size, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_CVTU64Str8 DN_CVT_U64ToStr8 (DN_U64 val, char separator);
DN_API DN_CVTU64ByteSize DN_CVT_U64ToByteSize (DN_U64 bytes, DN_CVTU64ByteSizeType type);
DN_API DN_Str8 DN_CVT_U64ToByteSizeStr8 (DN_Arena *arena, DN_U64 bytes, DN_CVTU64ByteSizeType desired_type);
DN_API DN_Str8 DN_CVT_U64ByteSizeTypeString (DN_CVTU64ByteSizeType type);
DN_API DN_Str8 DN_CVT_U64ToAge (DN_Arena *arena, DN_U64 age_s, DN_CVTU64AgeUnit unit);
DN_API DN_Str8 DN_CVT_F64ToAge (DN_Arena *arena, DN_F64 age_s, DN_CVTU64AgeUnit unit);
DN_API DN_U64 DN_CVT_HexToU64 (DN_Str8 hex);
DN_API DN_Str8 DN_CVT_U64ToHex (DN_Arena *arena, DN_U64 number, DN_CVTHexU64Str8Flags flags);
DN_API DN_CVTU64HexStr8 DN_CVT_U64ToHexStr8 (DN_U64 number, DN_U32 flags);
DN_API bool DN_CVT_BytesToHexPtr (void const *src, DN_USize src_size, char *dest, DN_USize dest_size);
DN_API DN_Str8 DN_CVT_BytesToHex (DN_Arena *arena, void const *src, DN_USize size);
#define DN_CVT_BytesToHexFromTLS(...) DN_CVT_BytesToHex(DN_OS_TLSTopArena(), __VA_ARGS__)
DN_API DN_USize DN_CVT_HexToBytesPtrUnchecked (DN_Str8 hex, void *dest, DN_USize dest_size);
DN_API DN_USize DN_CVT_HexToBytesPtr (DN_Str8 hex, void *dest, DN_USize dest_size);
DN_API DN_Str8 DN_CVT_HexToBytesUnchecked (DN_Arena *arena, DN_Str8 hex);
#define DN_CVT_HexToBytesUncheckedFromTLS(...) DN_CVT_HexToBytesUnchecked(DN_OS_TLSTopArena(), __VA_ARGS__)
DN_API DN_Str8 DN_CVT_HexToBytes (DN_Arena *arena, DN_Str8 hex);
#define DN_CVT_HexToBytesFromTLS(...) DN_CVT_HexToBytes(DN_OS_TLSTopArena(), __VA_ARGS__)
#endif // defined(DN_BASE_CONVERT_H)
+128
View File
@@ -0,0 +1,128 @@
#define DN_BASE_LOG_CPP
#include "../dn_clangd.h"
static DN_LOGEmitFromTypeFVFunc *g_dn_base_log_emit_from_type_fv_func_;
static void *g_dn_base_log_emit_from_type_fv_user_context_;
DN_API DN_Str8 DN_LOG_ColourEscapeCodeStr8FromRGB(DN_LOGColourType colour, DN_U8 r, DN_U8 g, DN_U8 b)
{
DN_THREAD_LOCAL char buffer[32];
buffer[0] = 0;
DN_Str8 result = {};
result.size = DN_SNPrintF(buffer,
DN_ArrayCountU(buffer),
"\x1b[%d;2;%u;%u;%um",
colour == DN_LOGColourType_Fg ? 38 : 48,
r,
g,
b);
result.data = buffer;
return result;
}
DN_API DN_Str8 DN_LOG_ColourEscapeCodeStr8FromU32(DN_LOGColourType colour, DN_U32 value)
{
DN_U8 r = DN_CAST(DN_U8)(value >> 24);
DN_U8 g = DN_CAST(DN_U8)(value >> 16);
DN_U8 b = DN_CAST(DN_U8)(value >> 8);
DN_Str8 result = DN_LOG_ColourEscapeCodeStr8FromRGB(colour, r, g, b);
return result;
}
DN_API DN_LOGPrefixSize DN_LOG_MakePrefix(DN_LOGStyle style, DN_LOGTypeParam type, DN_CallSite call_site, DN_LOGDate date, char *dest, DN_USize dest_size)
{
DN_Str8 type_str8 = type.str8;
if (type.is_u32_enum) {
switch (type.u32) {
case DN_LOGType_Debug: type_str8 = DN_STR8("DEBUG"); break;
case DN_LOGType_Info: type_str8 = DN_STR8("INFO "); break;
case DN_LOGType_Warning: type_str8 = DN_STR8("WARN"); break;
case DN_LOGType_Error: type_str8 = DN_STR8("ERROR"); break;
case DN_LOGType_Count: type_str8 = DN_STR8("BADXX"); break;
}
}
static DN_USize max_type_length = 0;
max_type_length = DN_Max(max_type_length, type_str8.size);
int type_padding = DN_CAST(int)(max_type_length - type_str8.size);
DN_Str8 colour_esc = {};
DN_Str8 bold_esc = {};
DN_Str8 reset_esc = {};
if (style.colour) {
bold_esc = DN_STR8(DN_LOG_BoldEscapeCode);
reset_esc = DN_STR8(DN_LOG_ResetEscapeCode);
colour_esc = DN_LOG_ColourEscapeCodeStr8FromRGB(DN_LOGColourType_Fg, style.r, style.g, style.b);
}
DN_Str8 file_name = DN_Str8_FileNameFromPath(call_site.file);
DN_GCC_WARNING_PUSH
DN_GCC_WARNING_DISABLE(-Wformat)
DN_GCC_WARNING_DISABLE(-Wformat-extra-args)
DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(4477)
int size = DN_SNPrintF(dest,
DN_CAST(int)dest_size,
"%04u-%02u-%02uT%02u:%02u:%02u" // date
"%S" // colour
"%S" // bold
" %S" // type
"%.*s" // type padding
"%S" // reset
" %S" // file name
":%05I32u " // line number
,
date.year,
date.month,
date.day,
date.hour,
date.minute,
date.second,
colour_esc, // colour
bold_esc, // bold
type_str8, // type
DN_CAST(int) type_padding,
"", // type padding
reset_esc, // reset
file_name, // file name
call_site.line); // line number
DN_MSVC_WARNING_POP // '%S' requires an argument of type 'wchar_t *', but variadic argument 7 has type 'DN_Str8'
DN_GCC_WARNING_POP
static DN_USize max_header_length = 0;
DN_USize size_no_ansi_codes = size - colour_esc.size - reset_esc.size - bold_esc.size;
max_header_length = DN_Max(max_header_length, size_no_ansi_codes);
DN_USize header_padding = max_header_length - size_no_ansi_codes;
DN_LOGPrefixSize result = {};
result.size = size;
result.padding = header_padding;
return result;
}
DN_API void DN_LOG_SetEmitFromTypeFVFunc(DN_LOGEmitFromTypeFVFunc *print_func, void *user_data)
{
g_dn_base_log_emit_from_type_fv_func_ = print_func;
g_dn_base_log_emit_from_type_fv_user_context_ = user_data;
}
DN_API void DN_LOG_EmitFromType(DN_LOGTypeParam type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...)
{
DN_LOGEmitFromTypeFVFunc *func = g_dn_base_log_emit_from_type_fv_func_;
void *user_context = g_dn_base_log_emit_from_type_fv_user_context_;
if (func) {
va_list args;
va_start(args, fmt);
func(type, user_context, call_site, fmt, args);
va_end(args);
}
}
DN_API DN_LOGTypeParam DN_LOG_MakeU32LogTypeParam(DN_LOGType type)
{
DN_LOGTypeParam result = {};
result.is_u32_enum = true;
result.u32 = type;
return result;
}
+72
View File
@@ -0,0 +1,72 @@
#if !defined(DN_BASE_LOG_H)
#define DN_BASE_LOG_H
enum DN_LOGType
{
DN_LOGType_Debug,
DN_LOGType_Info,
DN_LOGType_Warning,
DN_LOGType_Error,
DN_LOGType_Count,
};
enum DN_LOGBold
{
DN_LOGBold_No,
DN_LOGBold_Yes,
};
struct DN_LOGStyle
{
DN_LOGBold bold;
bool colour;
DN_U8 r, g, b;
};
struct DN_LOGTypeParam
{
bool is_u32_enum;
DN_U32 u32;
DN_Str8 str8;
};
enum DN_LOGColourType
{
DN_LOGColourType_Fg,
DN_LOGColourType_Bg,
};
struct DN_LOGDate
{
DN_U16 year;
DN_U8 month;
DN_U8 day;
DN_U8 hour;
DN_U8 minute;
DN_U8 second;
};
struct DN_LOGPrefixSize
{
DN_USize size;
DN_USize padding;
};
typedef void DN_LOGEmitFromTypeFVFunc(DN_LOGTypeParam type, void *user_data, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args);
#define DN_LOG_ResetEscapeCode "\x1b[0m"
#define DN_LOG_BoldEscapeCode "\x1b[1m"
DN_API DN_Str8 DN_LOG_ColourEscapeCodeStr8FromRGB(DN_LOGColourType colour, DN_U8 r, DN_U8 g, DN_U8 b);
DN_API DN_Str8 DN_LOG_ColourEscapeCodeStr8FromU32(DN_LOGColourType colour, DN_U32 value);
DN_API DN_LOGPrefixSize DN_LOG_MakePrefix (DN_LOGStyle style, DN_LOGTypeParam type, DN_CallSite call_site, DN_LOGDate date, char *dest, DN_USize dest_size);
DN_API void DN_LOG_SetEmitFromTypeFVFunc (DN_LOGEmitFromTypeFVFunc *print_func, void *user_data);
DN_API void DN_LOG_EmitFromType (DN_LOGTypeParam type, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_LOGTypeParam DN_LOG_MakeU32LogTypeParam (DN_LOGType type);
#define DN_LOG_DebugF(fmt, ...) DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Debug), DN_CALL_SITE, fmt, ##__VA_ARGS__)
#define DN_LOG_InfoF(fmt, ...) DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Info), DN_CALL_SITE, fmt, ##__VA_ARGS__)
#define DN_LOG_WarningF(fmt, ...) DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Warning), DN_CALL_SITE, fmt, ##__VA_ARGS__)
#define DN_LOG_ErrorF(fmt, ...) DN_LOG_EmitFromType(DN_LOG_MakeU32LogTypeParam(DN_LOGType_Error), DN_CALL_SITE, fmt, ##__VA_ARGS__)
#endif // !defined(DN_BASE_LOG_H)
+552
View File
@@ -0,0 +1,552 @@
#define DN_BASE_MEM_CPP
static DN_ArenaBlock *DN_Arena_BlockInitFromMemFuncs_(DN_U64 reserve, DN_U64 commit, bool track_alloc, bool alloc_can_leak, DN_ArenaMemFuncs mem_funcs)
{
DN_ArenaBlock *result = nullptr;
switch (mem_funcs.type) {
case DN_ArenaMemFuncType_Nil:
break;
case DN_ArenaMemFuncType_Basic: {
DN_AssertF(reserve > DN_ARENA_HEADER_SIZE, "%I64u > %I64u", reserve, DN_ARENA_HEADER_SIZE);
result = DN_CAST(DN_ArenaBlock *) mem_funcs.basic_alloc(reserve);
if (!result)
return result;
result->used = DN_ARENA_HEADER_SIZE;
result->commit = reserve;
result->reserve = reserve;
} break;
case DN_ArenaMemFuncType_VMem: {
DN_AssertF(mem_funcs.vmem_page_size, "Page size must be set to a non-zero, power of two value");
DN_Assert(DN_IsPowerOfTwo(mem_funcs.vmem_page_size));
DN_USize const page_size = mem_funcs.vmem_page_size;
DN_U64 real_reserve = reserve ? reserve : DN_ARENA_RESERVE_SIZE;
DN_U64 real_commit = commit ? commit : DN_ARENA_COMMIT_SIZE;
real_reserve = DN_AlignUpPowerOfTwo(real_reserve, page_size);
real_commit = DN_Min(DN_AlignUpPowerOfTwo(real_commit, page_size), real_reserve);
DN_AssertF(DN_ARENA_HEADER_SIZE < real_commit && real_commit <= real_reserve, "%I64u < %I64u <= %I64u", DN_ARENA_HEADER_SIZE, real_commit, real_reserve);
DN_MemCommit mem_commit = real_reserve == real_commit ? DN_MemCommit_Yes : DN_MemCommit_No;
result = DN_CAST(DN_ArenaBlock *) mem_funcs.vmem_reserve(real_reserve, mem_commit, DN_MemPage_ReadWrite);
if (!result)
return result;
if (mem_commit == DN_MemCommit_No && !mem_funcs.vmem_commit(result, real_commit, DN_MemPage_ReadWrite)) {
mem_funcs.vmem_release(result, real_reserve);
return result;
}
result->used = DN_ARENA_HEADER_SIZE;
result->commit = real_commit;
result->reserve = real_reserve;
} break;
}
if (track_alloc && result)
DN_Debug_TrackAlloc(result, result->reserve, alloc_can_leak);
return result;
}
static DN_ArenaBlock *DN_Arena_BlockInitFlagsFromMemFuncs_(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags, DN_ArenaMemFuncs mem_funcs)
{
bool track_alloc = (flags & DN_ArenaFlags_NoAllocTrack) == 0;
bool alloc_can_leak = flags & DN_ArenaFlags_AllocCanLeak;
DN_ArenaBlock *result = DN_Arena_BlockInitFromMemFuncs_(reserve, commit, track_alloc, alloc_can_leak, mem_funcs);
if (result && ((flags & DN_ArenaFlags_NoPoison) == 0))
DN_ASAN_PoisonMemoryRegion(DN_CAST(char *) result + DN_ARENA_HEADER_SIZE, result->commit - DN_ARENA_HEADER_SIZE);
return result;
}
static void DN_Arena_UpdateStatsOnNewBlock_(DN_Arena *arena, DN_ArenaBlock const *block)
{
DN_Assert(arena);
if (block) {
arena->stats.info.used += block->used;
arena->stats.info.commit += block->commit;
arena->stats.info.reserve += block->reserve;
arena->stats.info.blocks += 1;
arena->stats.hwm.used = DN_Max(arena->stats.hwm.used, arena->stats.info.used);
arena->stats.hwm.commit = DN_Max(arena->stats.hwm.commit, arena->stats.info.commit);
arena->stats.hwm.reserve = DN_Max(arena->stats.hwm.reserve, arena->stats.info.reserve);
arena->stats.hwm.blocks = DN_Max(arena->stats.hwm.blocks, arena->stats.info.blocks);
}
}
DN_API DN_Arena DN_Arena_InitFromBuffer(void *buffer, DN_USize size, DN_ArenaFlags flags)
{
DN_Assert(buffer);
DN_AssertF(DN_ARENA_HEADER_SIZE < size, "Buffer (%zu bytes) too small, need atleast %zu bytes to store arena metadata", size, DN_ARENA_HEADER_SIZE);
DN_AssertF(DN_IsPowerOfTwo(size), "Buffer (%zu bytes) must be a power-of-two", size);
// NOTE: Init block
DN_ArenaBlock *block = DN_CAST(DN_ArenaBlock *) buffer;
block->commit = size;
block->reserve = size;
block->used = DN_ARENA_HEADER_SIZE;
if (block && ((flags & DN_ArenaFlags_NoPoison) == 0))
DN_ASAN_PoisonMemoryRegion(DN_CAST(char *) block + DN_ARENA_HEADER_SIZE, block->commit - DN_ARENA_HEADER_SIZE);
DN_Arena result = {};
result.flags = flags | DN_ArenaFlags_NoGrow | DN_ArenaFlags_NoAllocTrack | DN_ArenaFlags_AllocCanLeak | DN_ArenaFlags_UserBuffer;
result.curr = block;
DN_Arena_UpdateStatsOnNewBlock_(&result, result.curr);
return result;
}
DN_API DN_Arena DN_Arena_InitFromMemFuncs(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags, DN_ArenaMemFuncs mem_funcs)
{
DN_Arena result = {};
result.flags = flags;
result.mem_funcs = mem_funcs;
result.flags |= DN_ArenaFlags_MemFuncs;
result.curr = DN_Arena_BlockInitFlagsFromMemFuncs_(reserve, commit, flags, mem_funcs);
DN_Arena_UpdateStatsOnNewBlock_(&result, result.curr);
return result;
}
static void DN_Arena_BlockDeinit_(DN_Arena const *arena, DN_ArenaBlock *block)
{
DN_USize release_size = block->reserve;
if (DN_Bit_IsNotSet(arena->flags, DN_ArenaFlags_NoAllocTrack))
DN_Debug_TrackDealloc(block);
DN_ASAN_UnpoisonMemoryRegion(block, block->commit);
if (arena->flags & DN_ArenaFlags_MemFuncs) {
if (arena->mem_funcs.type == DN_ArenaMemFuncType_Basic)
arena->mem_funcs.basic_dealloc(block);
else
arena->mem_funcs.vmem_release(block, release_size);
}
}
DN_API void DN_Arena_Deinit(DN_Arena *arena)
{
for (DN_ArenaBlock *block = arena ? arena->curr : nullptr; block;) {
DN_ArenaBlock *block_to_free = block;
block = block->prev;
DN_Arena_BlockDeinit_(arena, block_to_free);
}
if (arena)
*arena = {};
}
DN_API bool DN_Arena_CommitTo(DN_Arena *arena, DN_U64 pos)
{
if (!arena || !arena->curr)
return false;
DN_ArenaBlock *curr = arena->curr;
if (pos <= curr->commit)
return true;
DN_U64 real_pos = pos;
if (!DN_Check(pos <= curr->reserve))
real_pos = curr->reserve;
DN_Assert(arena->mem_funcs.vmem_page_size);
DN_USize end_commit = DN_AlignUpPowerOfTwo(real_pos, arena->mem_funcs.vmem_page_size);
DN_USize commit_size = end_commit - curr->commit;
char *commit_ptr = DN_CAST(char *) curr + curr->commit;
if (!arena->mem_funcs.vmem_commit(commit_ptr, commit_size, DN_MemPage_ReadWrite))
return false;
bool poison = DN_ASAN_POISON && ((arena->flags & DN_ArenaFlags_NoPoison) == 0);
if (poison)
DN_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
curr->commit = end_commit;
return true;
}
DN_API bool DN_Arena_Commit(DN_Arena *arena, DN_U64 size)
{
if (!arena || !arena->curr)
return false;
DN_U64 pos = DN_Min(arena->curr->reserve, arena->curr->commit + size);
bool result = DN_Arena_CommitTo(arena, pos);
return result;
}
DN_API bool DN_Arena_Grow(DN_Arena *arena, DN_U64 reserve, DN_U64 commit)
{
if (arena->flags & (DN_ArenaFlags_NoGrow | DN_ArenaFlags_UserBuffer))
return false;
bool result = false;
DN_ArenaBlock *new_block = DN_Arena_BlockInitFlagsFromMemFuncs_(reserve, commit, arena->flags, arena->mem_funcs);
if (new_block) {
result = true;
new_block->prev = arena->curr;
arena->curr = new_block;
new_block->reserve_sum = new_block->prev->reserve_sum + new_block->prev->reserve;
DN_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
}
return result;
}
DN_API void *DN_Arena_Alloc(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem)
{
if (!arena)
return nullptr;
if (!arena->curr) {
arena->curr = DN_Arena_BlockInitFlagsFromMemFuncs_(DN_ARENA_RESERVE_SIZE, DN_ARENA_COMMIT_SIZE, arena->flags, arena->mem_funcs);
DN_Arena_UpdateStatsOnNewBlock_(arena, arena->curr);
}
if (!arena->curr)
return nullptr;
try_alloc_again:
DN_ArenaBlock *curr = arena->curr;
bool poison = DN_ASAN_POISON && ((arena->flags & DN_ArenaFlags_NoPoison) == 0);
uint8_t real_align = poison ? DN_Max(align, DN_ASAN_POISON_ALIGNMENT) : align;
DN_U64 offset_pos = DN_AlignUpPowerOfTwo(curr->used, real_align) + (poison ? DN_ASAN_POISON_GUARD_SIZE : 0);
DN_U64 end_pos = offset_pos + size;
DN_U64 alloc_size = end_pos - curr->used;
if (end_pos > curr->reserve) {
if (arena->flags & (DN_ArenaFlags_NoGrow | DN_ArenaFlags_UserBuffer))
return nullptr;
DN_USize new_reserve = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_RESERVE_SIZE);
DN_USize new_commit = DN_Max(DN_ARENA_HEADER_SIZE + alloc_size, DN_ARENA_COMMIT_SIZE);
if (!DN_Arena_Grow(arena, new_reserve, new_commit))
return nullptr;
goto try_alloc_again;
}
DN_USize prev_arena_commit = curr->commit;
if (end_pos > curr->commit) {
DN_Assert(arena->mem_funcs.vmem_page_size);
DN_Assert(arena->mem_funcs.type == DN_ArenaMemFuncType_VMem);
DN_Assert((arena->flags & DN_ArenaFlags_UserBuffer) == 0);
DN_USize end_commit = DN_AlignUpPowerOfTwo(end_pos, arena->mem_funcs.vmem_page_size);
DN_USize commit_size = end_commit - curr->commit;
char *commit_ptr = DN_CAST(char *) curr + curr->commit;
if (!arena->mem_funcs.vmem_commit(commit_ptr, commit_size, DN_MemPage_ReadWrite))
return nullptr;
if (poison)
DN_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
curr->commit = end_commit;
arena->stats.info.commit += commit_size;
arena->stats.hwm.commit = DN_Max(arena->stats.hwm.commit, arena->stats.info.commit);
}
void *result = DN_CAST(char *) curr + offset_pos;
curr->used += alloc_size;
arena->stats.info.used += alloc_size;
arena->stats.hwm.used = DN_Max(arena->stats.hwm.used, arena->stats.info.used);
DN_ASAN_UnpoisonMemoryRegion(result, size);
if (zero_mem == DN_ZeroMem_Yes) {
DN_USize reused_bytes = DN_Min(prev_arena_commit - offset_pos, size);
DN_Memset(result, 0, reused_bytes);
}
DN_Assert(arena->stats.hwm.used >= arena->stats.info.used);
DN_Assert(arena->stats.hwm.commit >= arena->stats.info.commit);
DN_Assert(arena->stats.hwm.reserve >= arena->stats.info.reserve);
DN_Assert(arena->stats.hwm.blocks >= arena->stats.info.blocks);
return result;
}
DN_API void *DN_Arena_AllocContiguous(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem)
{
DN_ArenaFlags prev_flags = arena->flags;
arena->flags |= (DN_ArenaFlags_NoGrow | DN_ArenaFlags_NoPoison);
void *memory = DN_Arena_Alloc(arena, size, align, zero_mem);
arena->flags = prev_flags;
return memory;
}
DN_API void *DN_Arena_Copy(DN_Arena *arena, void const *data, DN_U64 size, uint8_t align)
{
if (!arena || !data || size == 0)
return nullptr;
void *result = DN_Arena_Alloc(arena, size, align, DN_ZeroMem_No);
if (result)
DN_Memcpy(result, data, size);
return result;
}
DN_API void DN_Arena_PopTo(DN_Arena *arena, DN_U64 init_used)
{
if (!arena || !arena->curr)
return;
DN_U64 used = DN_Max(DN_ARENA_HEADER_SIZE, init_used);
DN_ArenaBlock *curr = arena->curr;
while (curr->reserve_sum >= used) {
DN_ArenaBlock *block_to_free = curr;
arena->stats.info.used -= block_to_free->used;
arena->stats.info.commit -= block_to_free->commit;
arena->stats.info.reserve -= block_to_free->reserve;
arena->stats.info.blocks -= 1;
if (arena->flags & DN_ArenaFlags_UserBuffer)
break;
curr = curr->prev;
DN_Arena_BlockDeinit_(arena, block_to_free);
}
arena->stats.info.used -= curr->used;
arena->curr = curr;
curr->used = used - curr->reserve_sum;
char *poison_ptr = (char *)curr + DN_AlignUpPowerOfTwo(curr->used, DN_ASAN_POISON_ALIGNMENT);
DN_USize poison_size = ((char *)curr + curr->commit) - poison_ptr;
DN_ASAN_PoisonMemoryRegion(poison_ptr, poison_size);
arena->stats.info.used += curr->used;
}
DN_API void DN_Arena_Pop(DN_Arena *arena, DN_U64 amount)
{
DN_ArenaBlock *curr = arena->curr;
DN_USize used_sum = curr->reserve_sum + curr->used;
if (!DN_Check(amount <= used_sum))
amount = used_sum;
DN_USize pop_to = used_sum - amount;
DN_Arena_PopTo(arena, pop_to);
}
DN_API DN_U64 DN_Arena_Pos(DN_Arena const *arena)
{
DN_U64 result = (arena && arena->curr) ? arena->curr->reserve_sum + arena->curr->used : 0;
return result;
}
DN_API void DN_Arena_Clear(DN_Arena *arena)
{
DN_Arena_PopTo(arena, 0);
}
DN_API bool DN_Arena_OwnsPtr(DN_Arena const *arena, void *ptr)
{
bool result = false;
uintptr_t uint_ptr = DN_CAST(uintptr_t) ptr;
for (DN_ArenaBlock const *block = arena ? arena->curr : nullptr; !result && block; block = block->prev) {
uintptr_t begin = DN_CAST(uintptr_t) block + DN_ARENA_HEADER_SIZE;
uintptr_t end = begin + block->reserve;
result = uint_ptr >= begin && uint_ptr <= end;
}
return result;
}
DN_API DN_ArenaStats DN_Arena_SumStatsArray(DN_ArenaStats const *array, DN_USize size)
{
DN_ArenaStats result = {};
for (DN_ForItSize(it, DN_ArenaStats const, array, size)) {
DN_ArenaStats stats = *it.data;
result.info.used += stats.info.used;
result.info.commit += stats.info.commit;
result.info.reserve += stats.info.reserve;
result.info.blocks += stats.info.blocks;
result.hwm.used = DN_Max(result.hwm.used, result.info.used);
result.hwm.commit = DN_Max(result.hwm.commit, result.info.commit);
result.hwm.reserve = DN_Max(result.hwm.reserve, result.info.reserve);
result.hwm.blocks = DN_Max(result.hwm.blocks, result.info.blocks);
}
return result;
}
DN_API DN_ArenaStats DN_Arena_SumStats(DN_ArenaStats lhs, DN_ArenaStats rhs)
{
DN_ArenaStats array[] = {lhs, rhs};
DN_ArenaStats result = DN_Arena_SumStatsArray(array, DN_ArrayCountU(array));
return result;
}
DN_API DN_ArenaStats DN_Arena_SumArenaArrayToStats(DN_Arena const *array, DN_USize size)
{
DN_ArenaStats result = {};
for (DN_USize index = 0; index < size; index++) {
DN_Arena const *arena = array + index;
result = DN_Arena_SumStats(result, arena->stats);
}
return result;
}
DN_API DN_ArenaTempMem DN_Arena_TempMemBegin(DN_Arena *arena)
{
DN_ArenaTempMem result = {};
if (arena) {
DN_ArenaBlock *curr = arena->curr;
result = {arena, curr ? curr->reserve_sum + curr->used : 0};
}
return result;
};
DN_API void DN_Arena_TempMemEnd(DN_ArenaTempMem mem)
{
DN_Arena_PopTo(mem.arena, mem.used_sum);
};
DN_ArenaTempMemScope::DN_ArenaTempMemScope(DN_Arena *arena)
{
mem = DN_Arena_TempMemBegin(arena);
}
DN_ArenaTempMemScope::~DN_ArenaTempMemScope()
{
DN_Arena_TempMemEnd(mem);
}
// NOTE: DN_Pool ///////////////////////////////////////////////////////////////////////////////////
DN_API DN_Pool DN_Pool_Init(DN_Arena *arena, uint8_t align)
{
DN_Pool result = {};
if (arena) {
result.arena = arena;
result.align = align ? align : DN_POOL_DEFAULT_ALIGN;
}
return result;
}
DN_API bool DN_Pool_IsValid(DN_Pool const *pool)
{
bool result = pool && pool->arena && pool->align;
return result;
}
DN_API void *DN_Pool_Alloc(DN_Pool *pool, DN_USize size)
{
void *result = nullptr;
if (!DN_Pool_IsValid(pool))
return result;
DN_USize const required_size = sizeof(DN_PoolSlot) + pool->align + size;
DN_USize const size_to_slot_offset = 5; // __lzcnt64(32) e.g. DN_PoolSlotSize_32B
DN_USize slot_index = 0;
if (required_size > 32) {
// NOTE: Round up if not PoT as the low bits are set.
DN_USize dist_to_next_msb = DN_CountLeadingZerosU64(required_size) + 1;
dist_to_next_msb -= DN_CAST(DN_USize)(!DN_IsPowerOfTwo(required_size));
DN_USize const register_size = sizeof(DN_USize) * 8;
DN_Assert(register_size >= dist_to_next_msb + size_to_slot_offset);
slot_index = register_size - dist_to_next_msb - size_to_slot_offset;
}
if (!DN_CheckF(slot_index < DN_PoolSlotSize_Count, "Chunk pool does not support the requested allocation size"))
return result;
DN_USize slot_size_in_bytes = 1ULL << (slot_index + size_to_slot_offset);
DN_Assert(required_size <= (slot_size_in_bytes << 0));
DN_Assert(required_size >= (slot_size_in_bytes >> 1));
DN_PoolSlot *slot = nullptr;
if (pool->slots[slot_index]) {
slot = pool->slots[slot_index];
pool->slots[slot_index] = slot->next;
DN_Memset(slot->data, 0, size);
DN_Assert(DN_IsPowerOfTwoAligned(slot->data, pool->align));
} else {
void *bytes = DN_Arena_Alloc(pool->arena, slot_size_in_bytes, alignof(DN_PoolSlot), DN_ZeroMem_Yes);
slot = DN_CAST(DN_PoolSlot *) bytes;
// NOTE: The raw pointer is round up to the next 'pool->align'-ed
// address ensuring at least 1 byte of padding between the raw pointer
// and the pointer given to the user and that the user pointer is
// aligned to the pool's alignment.
//
// This allows us to smuggle 1 byte behind the user pointer that has
// the offset to the original pointer.
slot->data = DN_CAST(void *) DN_AlignDownPowerOfTwo(DN_CAST(uintptr_t) slot + sizeof(DN_PoolSlot) + pool->align, pool->align);
uintptr_t offset_to_original_ptr = DN_CAST(uintptr_t) slot->data - DN_CAST(uintptr_t) bytes;
DN_Assert(slot->data > bytes);
DN_Assert(offset_to_original_ptr <= sizeof(DN_PoolSlot) + pool->align);
// NOTE: Store the offset to the original pointer behind the user's
// pointer.
char *offset_to_original_storage = DN_CAST(char *) slot->data - 1;
DN_Memcpy(offset_to_original_storage, &offset_to_original_ptr, 1);
}
// NOTE: Smuggle the slot type in the next pointer so that we know, when the
// pointer gets returned which free list to return the pointer to.
result = slot->data;
slot->next = DN_CAST(DN_PoolSlot *) slot_index;
return result;
}
DN_API DN_Str8 DN_Pool_AllocStr8FV(DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args)
{
DN_Str8 result = {};
if (!DN_Pool_IsValid(pool))
return result;
DN_USize size_required = DN_CStr8_FVSize(fmt, args);
result.data = DN_CAST(char *) DN_Pool_Alloc(pool, size_required + 1);
if (result.data) {
result.size = size_required;
DN_VSNPrintF(result.data, DN_CAST(int)(result.size + 1), fmt, args);
}
return result;
}
DN_API DN_Str8 DN_Pool_AllocStr8F(DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
DN_Str8 result = DN_Pool_AllocStr8FV(pool, fmt, args);
va_end(args);
return result;
}
DN_API DN_Str8 DN_Pool_AllocStr8Copy(DN_Pool *pool, DN_Str8 string)
{
DN_Str8 result = {};
if (!DN_Pool_IsValid(pool))
return result;
if (!DN_Str8_HasData(string))
return result;
char *data = DN_CAST(char *) DN_Pool_Alloc(pool, string.size + 1);
if (!data)
return result;
DN_Memcpy(data, string.data, string.size);
data[string.size] = 0;
result = DN_Str8_Init(data, string.size);
return result;
}
DN_API void DN_Pool_Dealloc(DN_Pool *pool, void *ptr)
{
if (!DN_Pool_IsValid(pool) || !ptr)
return;
DN_Assert(DN_Arena_OwnsPtr(pool->arena, ptr));
char const *one_byte_behind_ptr = DN_CAST(char *) ptr - 1;
DN_USize offset_to_original_ptr = 0;
DN_Memcpy(&offset_to_original_ptr, one_byte_behind_ptr, 1);
DN_Assert(offset_to_original_ptr <= sizeof(DN_PoolSlot) + pool->align);
char *original_ptr = DN_CAST(char *) ptr - offset_to_original_ptr;
DN_PoolSlot *slot = DN_CAST(DN_PoolSlot *) original_ptr;
DN_PoolSlotSize slot_index = DN_CAST(DN_PoolSlotSize)(DN_CAST(uintptr_t) slot->next);
DN_Assert(slot_index < DN_PoolSlotSize_Count);
slot->next = pool->slots[slot_index];
pool->slots[slot_index] = slot;
}
DN_API void *DN_Pool_Copy(DN_Pool *pool, void const *data, DN_U64 size, uint8_t align)
{
if (!pool || !data || size == 0)
return nullptr;
// TODO: Hmm should align be part of the alloc interface in general? I'm not going to worry
// about this until we crash because of misalignment.
DN_Assert(pool->align >= align);
void *result = DN_Pool_Alloc(pool, size);
if (result)
DN_Memcpy(result, data, size);
return result;
}
+253
View File
@@ -0,0 +1,253 @@
#if !defined(DN_BASE_MEM_H)
#define DN_BASE_MEM_H
#include "../dn_clangd.h"
enum DN_MemCommit
{
DN_MemCommit_No,
DN_MemCommit_Yes,
};
typedef DN_U32 DN_MemPage;
enum DN_MemPage_
{
// Exception on read/write with a page. This flag overrides the read/write
// access.
DN_MemPage_NoAccess = 1 << 0,
DN_MemPage_Read = 1 << 1, // Only read permitted on the page.
// Only write permitted on the page. On Windows this is not supported and
// will be promoted to read+write permissions.
DN_MemPage_Write = 1 << 2,
DN_MemPage_ReadWrite = DN_MemPage_Read | DN_MemPage_Write,
// Modifier used in conjunction with previous flags. Raises exception on
// first access to the page, then, the underlying protection flags are
// active. This is supported on Windows, on other OS's using this flag will
// set the OS equivalent of DN_MemPage_NoAccess.
// This flag must only be used in DN_Mem_Protect
DN_MemPage_Guard = 1 << 3,
// If leak tracing is enabled, this flag will allow the allocation recorded
// from the reserve call to be leaked, e.g. not printed when leaks are
// dumped to the console.
DN_MemPage_AllocRecordLeakPermitted = 1 << 4,
// If leak tracing is enabled this flag will prevent any allocation record
// from being created in the allocation table at all. If this flag is
// enabled, 'OSMemPage_AllocRecordLeakPermitted' has no effect since the
// record will never be created.
DN_MemPage_NoAllocRecordEntry = 1 << 5,
// [INTERNAL] Do not use. All flags together do not constitute a correct
// configuration of pages.
DN_MemPage_All = DN_MemPage_NoAccess |
DN_MemPage_ReadWrite |
DN_MemPage_Guard |
DN_MemPage_AllocRecordLeakPermitted |
DN_MemPage_NoAllocRecordEntry,
};
#if !defined(DN_ARENA_RESERVE_SIZE)
#define DN_ARENA_RESERVE_SIZE DN_Megabytes(64)
#endif
#if !defined(DN_ARENA_COMMIT_SIZE)
#define DN_ARENA_COMMIT_SIZE DN_Kilobytes(64)
#endif
struct DN_ArenaBlock
{
DN_ArenaBlock *prev;
DN_U64 used;
DN_U64 commit;
DN_U64 reserve;
DN_U64 reserve_sum;
};
typedef uint32_t DN_ArenaFlags;
enum DN_ArenaFlags_
{
DN_ArenaFlags_Nil = 0,
DN_ArenaFlags_NoGrow = 1 << 0,
DN_ArenaFlags_NoPoison = 1 << 1,
DN_ArenaFlags_NoAllocTrack = 1 << 2,
DN_ArenaFlags_AllocCanLeak = 1 << 3,
// NOTE: Internal flags. Do not use
DN_ArenaFlags_UserBuffer = 1 << 4,
DN_ArenaFlags_MemFuncs = 1 << 5,
};
struct DN_ArenaInfo
{
DN_U64 used;
DN_U64 commit;
DN_U64 reserve;
DN_U64 blocks;
};
struct DN_ArenaStats
{
DN_ArenaInfo info;
DN_ArenaInfo hwm;
};
enum DN_ArenaMemFuncType
{
DN_ArenaMemFuncType_Nil,
DN_ArenaMemFuncType_Basic,
DN_ArenaMemFuncType_VMem,
};
typedef void *(DN_ArenaMemBasicAllocFunc)(DN_USize size);
typedef void (DN_ArenaMemBasicDeallocFunc)(void *ptr);
typedef void *(DN_ArenaMemVMemReserveFunc)(DN_USize size, DN_MemCommit commit, DN_MemPage page_flags);
typedef bool (DN_ArenaMemVMemCommitFunc)(void *ptr, DN_USize size, DN_U32 page_flags);
typedef void (DN_ArenaMemVMemReleaseFunc)(void *ptr, DN_USize size);
struct DN_ArenaMemFuncs
{
DN_ArenaMemFuncType type;
DN_ArenaMemBasicAllocFunc *basic_alloc;
DN_ArenaMemBasicDeallocFunc *basic_dealloc;
DN_U32 vmem_page_size;
DN_ArenaMemVMemReserveFunc *vmem_reserve;
DN_ArenaMemVMemCommitFunc *vmem_commit;
DN_ArenaMemVMemReleaseFunc *vmem_release;
};
struct DN_Arena
{
DN_ArenaMemFuncs mem_funcs;
DN_ArenaBlock *curr;
DN_ArenaStats stats;
DN_ArenaFlags flags;
DN_Str8 label;
DN_Arena *prev, *next;
};
struct DN_ArenaTempMem
{
DN_Arena *arena;
DN_U64 used_sum;
};
struct DN_ArenaTempMemScope
{
DN_ArenaTempMemScope(DN_Arena *arena);
~DN_ArenaTempMemScope();
DN_ArenaTempMem mem;
};
DN_USize const DN_ARENA_HEADER_SIZE = DN_AlignUpPowerOfTwo(sizeof(DN_Arena), 64);
// NOTE: DN_Arena //////////////////////////////////////////////////////////////////////////////////
DN_API DN_Arena DN_Arena_InitFromBuffer (void *buffer, DN_USize size, DN_ArenaFlags flags);
DN_API DN_Arena DN_Arena_InitFromMemFuncs (DN_U64 reserve, DN_U64 commit, DN_ArenaFlags flags, DN_ArenaMemFuncs mem_funcs);
DN_API void DN_Arena_Deinit (DN_Arena *arena);
DN_API bool DN_Arena_Commit (DN_Arena *arena, DN_U64 size);
DN_API bool DN_Arena_CommitTo (DN_Arena *arena, DN_U64 pos);
DN_API bool DN_Arena_Grow (DN_Arena *arena, DN_U64 reserve, DN_U64 commit);
DN_API void * DN_Arena_Alloc (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem);
DN_API void * DN_Arena_AllocContiguous (DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZeroMem zero_mem);
DN_API void * DN_Arena_Copy (DN_Arena *arena, void const *data, DN_U64 size, uint8_t align);
DN_API void DN_Arena_PopTo (DN_Arena *arena, DN_U64 init_used);
DN_API void DN_Arena_Pop (DN_Arena *arena, DN_U64 amount);
DN_API DN_U64 DN_Arena_Pos (DN_Arena const *arena);
DN_API void DN_Arena_Clear (DN_Arena *arena);
DN_API bool DN_Arena_OwnsPtr (DN_Arena const *arena, void *ptr);
DN_API DN_ArenaStats DN_Arena_SumStatsArray (DN_ArenaStats const *array, DN_USize size);
DN_API DN_ArenaStats DN_Arena_SumStats (DN_ArenaStats lhs, DN_ArenaStats rhs);
DN_API DN_ArenaStats DN_Arena_SumArenaArrayToStats (DN_Arena const *array, DN_USize size);
DN_API DN_ArenaTempMem DN_Arena_TempMemBegin (DN_Arena *arena);
DN_API void DN_Arena_TempMemEnd (DN_ArenaTempMem mem);
#define DN_Arena_New_Frame(T, zero_mem) (T *)DN_Arena_Alloc(DN_OS_TLSGet()->frame_arena, sizeof(T), alignof(T), zero_mem)
#define DN_Arena_New(arena, T, zero_mem) (T *)DN_Arena_Alloc(arena, sizeof(T), alignof(T), zero_mem)
#define DN_Arena_NewArray(arena, T, count, zero_mem) (T *)DN_Arena_Alloc(arena, sizeof(T) * (count), alignof(T), zero_mem)
#define DN_Arena_NewCopy(arena, T, src) (T *)DN_Arena_Copy (arena, (src), sizeof(T), alignof(T))
#define DN_Arena_NewArrayCopy(arena, T, src, count) (T *)DN_Arena_Copy (arena, (src), sizeof(T) * (count), alignof(T))
#if !defined(DN_POOL_DEFAULT_ALIGN)
#define DN_POOL_DEFAULT_ALIGN 16
#endif
struct DN_PoolSlot
{
void *data;
DN_PoolSlot *next;
};
enum DN_PoolSlotSize
{
DN_PoolSlotSize_32B,
DN_PoolSlotSize_64B,
DN_PoolSlotSize_128B,
DN_PoolSlotSize_256B,
DN_PoolSlotSize_512B,
DN_PoolSlotSize_1KiB,
DN_PoolSlotSize_2KiB,
DN_PoolSlotSize_4KiB,
DN_PoolSlotSize_8KiB,
DN_PoolSlotSize_16KiB,
DN_PoolSlotSize_32KiB,
DN_PoolSlotSize_64KiB,
DN_PoolSlotSize_128KiB,
DN_PoolSlotSize_256KiB,
DN_PoolSlotSize_512KiB,
DN_PoolSlotSize_1MiB,
DN_PoolSlotSize_2MiB,
DN_PoolSlotSize_4MiB,
DN_PoolSlotSize_8MiB,
DN_PoolSlotSize_16MiB,
DN_PoolSlotSize_32MiB,
DN_PoolSlotSize_64MiB,
DN_PoolSlotSize_128MiB,
DN_PoolSlotSize_256MiB,
DN_PoolSlotSize_512MiB,
DN_PoolSlotSize_1GiB,
DN_PoolSlotSize_2GiB,
DN_PoolSlotSize_4GiB,
DN_PoolSlotSize_8GiB,
DN_PoolSlotSize_16GiB,
DN_PoolSlotSize_32GiB,
DN_PoolSlotSize_Count,
};
struct DN_Pool
{
DN_Arena *arena;
DN_PoolSlot *slots[DN_PoolSlotSize_Count];
uint8_t align;
};
// NOTE: DN_Pool ///////////////////////////////////////////////////////////////////////////////////
DN_API DN_Pool DN_Pool_Init (DN_Arena *arena, uint8_t align);
DN_API bool DN_Pool_IsValid (DN_Pool const *pool);
DN_API void * DN_Pool_Alloc (DN_Pool *pool, DN_USize size);
DN_API DN_Str8 DN_Pool_AllocStr8FV (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_Str8 DN_Pool_AllocStr8F (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8 DN_Pool_AllocStr8Copy (DN_Pool *pool, DN_Str8 string);
DN_API void DN_Pool_Dealloc (DN_Pool *pool, void *ptr);
DN_API void * DN_Pool_Copy (DN_Pool *pool, void const *data, DN_U64 size, uint8_t align);
#define DN_Pool_New(pool, T) (T *)DN_Pool_Alloc(pool, sizeof(T))
#define DN_Pool_NewArray(pool, T, count) (T *)DN_Pool_Alloc(pool, count * sizeof(T))
#define DN_Pool_NewCopy(arena, T, src) (T *)DN_Pool_Copy (arena, (src), sizeof(T), alignof(T))
#define DN_Pool_NewArrayCopy(arena, T, src, count) (T *)DN_Pool_Copy (arena, (src), sizeof(T) * (count), alignof(T))
// NOTE: DN_Debug //////////////////////////////////////////////////////////////////////////////////
#if defined(DN_LEAK_TRACKING) && !defined(DN_FREESTANDING)
DN_API void DN_Debug_TrackAlloc (void *ptr, DN_USize size, bool alloc_can_leak);
DN_API void DN_Debug_TrackDealloc(void *ptr);
DN_API void DN_Debug_DumpLeaks ();
#else
#define DN_Debug_TrackAlloc(ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
#define DN_Debug_TrackDealloc(ptr) do { (void)ptr; } while (0)
#define DN_Debug_DumpLeaks() do { } while (0)
#endif
#endif // !defined(DN_BASE_MEM_H)
+46
View File
@@ -0,0 +1,46 @@
#if !defined(DN_BASE_OS_H)
#define DN_BASE_OS_H
// NOTE: OS primitives that the OS layer can provide for the base layer but is optional.
struct DN_StackTraceFrame
{
DN_U64 address;
DN_U64 line_number;
DN_Str8 file_name;
DN_Str8 function_name;
};
struct DN_StackTraceRawFrame
{
void *process;
DN_U64 base_addr;
};
struct DN_StackTraceWalkResult
{
void *process; // [Internal] Windows handle to the process
DN_U64 *base_addr; // The addresses of the functions in the stack trace
DN_U16 size; // The number of `base_addr`'s stored from the walk
};
struct DN_StackTraceWalkResultIterator
{
DN_StackTraceRawFrame raw_frame;
DN_U16 index;
};
DN_API DN_Str8 DN_StackTrace_WalkStr8FromHeap (DN_U16 limit, DN_U16 skip);
DN_API DN_StackTraceWalkResult DN_StackTrace_Walk (struct DN_Arena *arena, DN_U16 limit);
DN_API bool DN_StackTrace_WalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk);
DN_API DN_Str8 DN_StackTrace_WalkResultToStr8 (struct DN_Arena *arena, DN_StackTraceWalkResult const *walk, DN_U16 skip);
DN_API DN_Str8 DN_StackTrace_WalkStr8 (struct DN_Arena *arena, DN_U16 limit, DN_U16 skip);
DN_API DN_Str8 DN_StackTrace_WalkStr8FromHeap (DN_U16 limit, DN_U16 skip);
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTrace_GetFrames (struct DN_Arena *arena, DN_U16 limit);
DN_API DN_StackTraceFrame DN_StackTrace_RawFrameToFrame (struct DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
DN_API void DN_StackTrace_Print (DN_U16 limit);
DN_API void DN_StackTrace_ReloadSymbols ();
#endif
File diff suppressed because it is too large Load Diff
+525
View File
@@ -0,0 +1,525 @@
#if !defined(DN_BASE_STRING_H)
#define DN_BASE_STRING_H
#if !defined(DN_STB_SPRINTF_HEADER_ONLY)
#define STB_SPRINTF_IMPLEMENTATION
#define STB_SPRINTF_STATIC
#endif
DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(4505) // Unused function warning
DN_GCC_WARNING_PUSH
DN_GCC_WARNING_DISABLE(-Wunused-function)
#include "../External/stb_sprintf.h"
DN_GCC_WARNING_POP
DN_MSVC_WARNING_POP
#define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
#define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
#define DN_VSPrintF(...) STB_SPRINTF_DECORATE(vsprintf)(__VA_ARGS__)
#define DN_VSNPrintF(...) STB_SPRINTF_DECORATE(vsnprintf)(__VA_ARGS__)
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\
// $$ __$$\\__$$ __|$$ __$$\ \_$$ _|$$$\ $$ |$$ __$$\
// $$ / \__| $$ | $$ | $$ | $$ | $$$$\ $$ |$$ / \__|
// \$$$$$$\ $$ | $$$$$$$ | $$ | $$ $$\$$ |$$ |$$$$\
// \____$$\ $$ | $$ __$$< $$ | $$ \$$$$ |$$ |\_$$ |
// $$\ $$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ |
// \$$$$$$ | $$ | $$ | $$ |$$$$$$\ $$ | \$$ |\$$$$$$ |
// \______/ \__| \__| \__|\______|\__| \__| \______/
//
// dn_base_string.h
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
// NOTE: DN_Str8 //////////////////////////////////////////////////////////////////////////////////
struct DN_Str8Link
{
DN_Str8 string; // The string
DN_Str8Link *next; // The next string in the linked list
DN_Str8Link *prev; // The prev string in the linked list
};
struct DN_Str8BinarySplitResult
{
DN_Str8 lhs;
DN_Str8 rhs;
};
struct DN_Str8FindResult
{
bool found; // True if string was found. If false, the subsequent fields below are not set.
DN_USize index; // Index in the buffer where the found string starts
DN_Str8 match; // Matching string in the buffer that was searched
DN_Str8 match_to_end_of_buffer; // Substring containing the found string to the end of the buffer
DN_Str8 after_match_to_end_of_buffer; // Substring starting after the found string to the end of the buffer
DN_Str8 start_to_before_match; // Substring from the start of the buffer up until the found string, not including it
};
enum DN_Str8IsAll
{
DN_Str8IsAll_Digits,
DN_Str8IsAll_Hex,
};
enum DN_Str8EqCase
{
DN_Str8EqCase_Sensitive,
DN_Str8EqCase_Insensitive,
};
enum DN_Str8FindFlag
{
DN_Str8FindFlag_Digit = 1 << 0, // 0-9
DN_Str8FindFlag_Whitespace = 1 << 1, // '\r', '\t', '\n', ' '
DN_Str8FindFlag_Alphabet = 1 << 2, // A-Z, a-z
DN_Str8FindFlag_Plus = 1 << 3, // +
DN_Str8FindFlag_Minus = 1 << 4, // -
DN_Str8FindFlag_AlphaNum = DN_Str8FindFlag_Alphabet | DN_Str8FindFlag_Digit,
};
enum DN_Str8SplitIncludeEmptyStrings
{
DN_Str8SplitIncludeEmptyStrings_No,
DN_Str8SplitIncludeEmptyStrings_Yes,
};
struct DN_Str8ToU64Result
{
bool success;
uint64_t value;
};
struct DN_Str8ToI64Result
{
bool success;
int64_t value;
};
struct DN_Str8DotTruncateResult
{
bool truncated;
DN_Str8 str8;
};
// NOTE: DN_FStr8 //////////////////////////////////////////////////////////////////////////////////
#if !defined(DN_NO_FSTR8)
template <DN_USize N>
struct DN_FStr8
{
char data[N + 1];
DN_USize size;
char *begin() { return data; }
char *end() { return data + size; }
char const *begin() const { return data; }
char const *end() const { return data + size; }
};
#endif // !defined(DN_NO_FSTR8)
struct DN_Str8Builder
{
DN_Arena *arena; // Allocator to use to back the string list
DN_Str8Link *head; // First string in the linked list of strings
DN_Str8Link *tail; // Last string in the linked list of strings
DN_USize string_size; // The size in bytes necessary to construct the current string
DN_USize count; // The number of links in the linked list of strings
};
enum DN_Str8BuilderAdd
{
DN_Str8BuilderAdd_Append,
DN_Str8BuilderAdd_Prepend,
};
// NOTE: DN_CStr8 //////////////////////////////////////////////////////////////////////////////////
template <DN_USize N> constexpr DN_USize DN_CStr8_ArrayUCount (char const (&literal)[N]) { (void)literal; return N - 1; }
template <DN_USize N> constexpr DN_USize DN_CStr8_ArrayICount (char const (&literal)[N]) { (void)literal; return N - 1; }
DN_API DN_USize DN_CStr8_FSize (DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_USize DN_CStr8_FVSize (DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_USize DN_CStr8_Size (char const *a);
DN_API DN_USize DN_CStr16_Size (wchar_t const *a);
// NOTE: DN_Str16 //////////////////////////////////////////////////////////////////////////////////
#define DN_STR16(string) DN_Str16{(wchar_t *)(string), sizeof(string)/sizeof(string[0]) - 1}
#define DN_Str16_HasData(string) ((string).data && (string).size)
#if defined(__cplusplus)
DN_API bool operator== (DN_Str16 const &lhs, DN_Str16 const &rhs);
DN_API bool operator!= (DN_Str16 const &lhs, DN_Str16 const &rhs);
#endif
// NOTE: DN_Str8 ///////////////////////////////////////////////////////////////////////////////////
#define DN_STR8(string) DN_Str8{(char *)(string), (sizeof(string) - 1)}
#define DN_STR_FMT(string) (int)((string).size), (string).data
#define DN_Str8_Init(data, size) DN_Str8{(char *)(data), (size_t)(size)}
DN_API DN_Str8 DN_Str8_InitCStr8 (char const *src);
#define DN_Str8_HasData(string) ((string).data && (string).size)
DN_API bool DN_Str8_IsAll (DN_Str8 string, DN_Str8IsAll is_all);
DN_API DN_Str8 DN_Str8_InitF (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8 DN_Str8_InitFV (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_Str8 DN_Str8_Alloc (DN_Arena *arena, DN_USize size, DN_ZeroMem zero_mem);
DN_API DN_Str8 DN_Str8_Copy (DN_Arena *arena, DN_Str8 string);
DN_API char * DN_Str8_End (DN_Str8 string);
DN_API DN_Str8 DN_Str8_Slice (DN_Str8 string, DN_USize offset, DN_USize size);
DN_API DN_Str8 DN_Str8_Advance (DN_Str8 string, DN_USize amount);
DN_API DN_Str8 DN_Str8_NextLine (DN_Str8 string);
DN_API DN_Str8BinarySplitResult DN_Str8_BinarySplitArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size);
DN_API DN_Str8BinarySplitResult DN_Str8_BinarySplit (DN_Str8 string, DN_Str8 find);
DN_API DN_Str8BinarySplitResult DN_Str8_BinarySplitLastArray (DN_Str8 string, DN_Str8 const *find, DN_USize find_size);
DN_API DN_Str8BinarySplitResult DN_Str8_BinarySplitLast (DN_Str8 string, DN_Str8 find);
DN_API DN_USize DN_Str8_Split (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitIncludeEmptyStrings mode);
DN_API DN_Slice<DN_Str8> DN_Str8_SplitAlloc (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
DN_API DN_Str8FindResult DN_Str8_FindStr8Array (DN_Str8 string, DN_Str8 const *find, DN_USize find_size, DN_Str8EqCase eq_case);
DN_API DN_Str8FindResult DN_Str8_FindStr8 (DN_Str8 string, DN_Str8 find, DN_Str8EqCase eq_case);
DN_API DN_Str8FindResult DN_Str8_Find (DN_Str8 string, uint32_t flags);
DN_API DN_Str8 DN_Str8_Segment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char);
DN_API DN_Str8 DN_Str8_ReverseSegment (DN_Arena *arena, DN_Str8 src, DN_USize segment_size, char segment_char);
DN_API bool DN_Str8_Eq (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
DN_API bool DN_Str8_EqInsensitive (DN_Str8 lhs, DN_Str8 rhs);
DN_API bool DN_Str8_StartsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
DN_API bool DN_Str8_StartsWithInsensitive (DN_Str8 string, DN_Str8 prefix);
DN_API bool DN_Str8_EndsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
DN_API bool DN_Str8_EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix);
DN_API bool DN_Str8_HasChar (DN_Str8 string, char ch);
DN_API DN_Str8 DN_Str8_TrimPrefix (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
DN_API DN_Str8 DN_Str8_TrimHexPrefix (DN_Str8 string);
DN_API DN_Str8 DN_Str8_TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
DN_API DN_Str8 DN_Str8_TrimAround (DN_Str8 string, DN_Str8 trim_string);
DN_API DN_Str8 DN_Str8_TrimHeadWhitespace (DN_Str8 string);
DN_API DN_Str8 DN_Str8_TrimTailWhitespace (DN_Str8 string);
DN_API DN_Str8 DN_Str8_TrimWhitespaceAround (DN_Str8 string);
DN_API DN_Str8 DN_Str8_TrimByteOrderMark (DN_Str8 string);
DN_API DN_Str8 DN_Str8_FileNameFromPath (DN_Str8 path);
DN_API DN_Str8 DN_Str8_FileNameNoExtension (DN_Str8 path);
DN_API DN_Str8 DN_Str8_FilePathNoExtension (DN_Str8 path);
DN_API DN_Str8 DN_Str8_FileExtension (DN_Str8 path);
DN_API DN_Str8 DN_Str8_FileDirectoryFromPath (DN_Str8 path);
DN_API DN_Str8ToU64Result DN_Str8_ToU64 (DN_Str8 string, char separator);
DN_API DN_Str8ToI64Result DN_Str8_ToI64 (DN_Str8 string, char separator);
DN_API DN_Str8 DN_Str8_AppendF (DN_Arena *arena, DN_Str8 string, char const *fmt, ...);
DN_API DN_Str8 DN_Str8_AppendFV (DN_Arena *arena, DN_Str8 string, char const *fmt, va_list args);
DN_API DN_Str8 DN_Str8_FillF (DN_Arena *arena, DN_USize count, char const *fmt, ...);
DN_API DN_Str8 DN_Str8_FillFV (DN_Arena *arena, DN_USize count, char const *fmt, va_list args);
DN_API void DN_Str8_Remove (DN_Str8 *string, DN_USize offset, DN_USize size);
DN_API DN_Str8DotTruncateResult DN_Str8_DotTruncateMiddle (DN_Arena *arena, DN_Str8 str8, uint32_t side_size, DN_Str8 truncator);
DN_API DN_Str8 DN_Str8_Lower (DN_Arena *arena, DN_Str8 string);
DN_API DN_Str8 DN_Str8_Upper (DN_Arena *arena, DN_Str8 string);
#if defined(__cplusplus)
DN_API bool operator== (DN_Str8 const &lhs, DN_Str8 const &rhs);
DN_API bool operator!= (DN_Str8 const &lhs, DN_Str8 const &rhs);
#endif
// NOTE: DN_Str8Builder ////////////////////////////////////////////////////////////////////////////
DN_API DN_Str8Builder DN_Str8Builder_Init (DN_Arena *arena);
DN_API DN_Str8Builder DN_Str8Builder_InitArrayRef (DN_Arena *arena, DN_Str8 const *strings, DN_USize size);
DN_API DN_Str8Builder DN_Str8Builder_InitArrayCopy (DN_Arena *arena, DN_Str8 const *strings, DN_USize size);
template <DN_USize N> DN_Str8Builder DN_Str8Builder_InitCArrayRef (DN_Arena *arena, DN_Str8 const (&array)[N]);
template <DN_USize N> DN_Str8Builder DN_Str8Builder_InitCArrayCopy (DN_Arena *arena, DN_Str8 const (&array)[N]);
DN_API bool DN_Str8Builder_AddArrayRef (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize size, DN_Str8BuilderAdd add);
DN_API bool DN_Str8Builder_AddArrayCopy (DN_Str8Builder *builder, DN_Str8 const *strings, DN_USize size, DN_Str8BuilderAdd add);
DN_API bool DN_Str8Builder_AddFV (DN_Str8Builder *builder, DN_Str8BuilderAdd add, DN_FMT_ATTRIB char const *fmt, va_list args);
#define DN_Str8Builder_AppendArrayRef(builder, strings, size) DN_Str8Builder_AddArrayRef(builder, strings, size, DN_Str8BuilderAdd_Append)
#define DN_Str8Builder_AppendArrayCopy(builder, strings, size) DN_Str8Builder_AddArrayCopy(builder, strings, size, DN_Str8BuilderAdd_Append)
#define DN_Str8Builder_AppendSliceRef(builder, slice) DN_Str8Builder_AddArrayRef(builder, slice.data, slice.size, DN_Str8BuilderAdd_Append)
#define DN_Str8Builder_AppendSliceCopy(builder, slice) DN_Str8Builder_AddArrayCopy(builder, slice.data, slice.size, DN_Str8BuilderAdd_Append)
DN_API bool DN_Str8Builder_AppendRef (DN_Str8Builder *builder, DN_Str8 string);
DN_API bool DN_Str8Builder_AppendCopy (DN_Str8Builder *builder, DN_Str8 string);
#define DN_Str8Builder_AppendFV(builder, fmt, args) DN_Str8Builder_AddFV(builder, DN_Str8BuilderAdd_Append, fmt, args)
DN_API bool DN_Str8Builder_AppendF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...);
DN_API bool DN_Str8Builder_AppendBytesRef (DN_Str8Builder *builder, void const *ptr, DN_USize size);
DN_API bool DN_Str8Builder_AppendBytesCopy (DN_Str8Builder *builder, void const *ptr, DN_USize size);
DN_API bool DN_Str8Builder_AppendBuilderRef (DN_Str8Builder *dest, DN_Str8Builder const *src);
DN_API bool DN_Str8Builder_AppendBuilderCopy (DN_Str8Builder *dest, DN_Str8Builder const *src);
#define DN_Str8Builder_PrependArrayRef(builder, strings, size) DN_Str8Builder_AddArrayRef(builder, strings, size, DN_Str8BuilderAdd_Prepend)
#define DN_Str8Builder_PrependArrayCopy(builder, strings, size) DN_Str8Builder_AddArrayCopy(builder, strings, size, DN_Str8BuilderAdd_Prepend)
#define DN_Str8Builder_PrependSliceRef(builder, slice) DN_Str8Builder_AddArrayRef(builder, slice.data, slice.size, DN_Str8BuilderAdd_Prepend)
#define DN_Str8Builder_PrependSliceCopy(builder, slice) DN_Str8Builder_AddArrayCopy(builder, slice.data, slice.size, DN_Str8BuilderAdd_Prepend)
DN_API bool DN_Str8Builder_PrependRef (DN_Str8Builder *builder, DN_Str8 string);
DN_API bool DN_Str8Builder_PrependCopy (DN_Str8Builder *builder, DN_Str8 string);
#define DN_Str8Builder_PrependFV(builder, fmt, args) DN_Str8Builder_AddFV(builder, DN_Str8BuilderAdd_Prepend, fmt, args)
DN_API bool DN_Str8Builder_PrependF (DN_Str8Builder *builder, DN_FMT_ATTRIB char const *fmt, ...);
DN_API bool DN_Str8Builder_Erase (DN_Str8Builder *builder, DN_Str8 string);
DN_API DN_Str8Builder DN_Str8Builder_Copy (DN_Arena *arena, DN_Str8Builder const *builder);
DN_API DN_Str8 DN_Str8Builder_Build (DN_Str8Builder const *builder, DN_Arena *arena);
DN_API DN_Str8 DN_Str8Builder_BuildDelimited (DN_Str8Builder const *builder, DN_Str8 delimiter, DN_Arena *arena);
DN_API DN_Slice<DN_Str8> DN_Str8Builder_BuildSlice (DN_Str8Builder const *builder, DN_Arena *arena);
// NOTE: DN_FStr8 //////////////////////////////////////////////////////////////////////////////////
#if !defined(DN_NO_FSTR8)
template <DN_USize N> DN_FStr8<N> DN_FStr8_InitF (DN_FMT_ATTRIB char const *fmt, ...);
template <DN_USize N> DN_FStr8<N> DN_FStr8_InitFV (char const *fmt, va_list args);
template <DN_USize N> DN_USize DN_FStr8_Max (DN_FStr8<N> const *string);
template <DN_USize N> void DN_FStr8_Clear (DN_FStr8<N> *string);
template <DN_USize N> bool DN_FStr8_AddFV (DN_FStr8<N> *string, DN_FMT_ATTRIB char const *fmt, va_list va);
template <DN_USize N> bool DN_FStr8_AddF (DN_FStr8<N> *string, DN_FMT_ATTRIB char const *fmt, ...);
template <DN_USize N> bool DN_FStr8_AddCStr8 (DN_FStr8<N> *string, char const *value, DN_USize size);
template <DN_USize N> bool DN_FStr8_Add (DN_FStr8<N> *string, DN_Str8 value);
template <DN_USize N> DN_Str8 DN_FStr8_ToStr8 (DN_FStr8<N> const *string);
template <DN_USize N> bool DN_FStr8_Eq (DN_FStr8<N> const *lhs, DN_FStr8<N> const *rhs, DN_Str8EqCase eq_case);
template <DN_USize N> bool DN_FStr8_EqStr8 (DN_FStr8<N> const *lhs, DN_Str8 rhs, DN_Str8EqCase eq_case);
template <DN_USize N> bool DN_FStr8_EqInsensitive (DN_FStr8<N> const *lhs, DN_FStr8<N> const *rhs);
template <DN_USize N> bool DN_FStr8_EqStr8Insensitive (DN_FStr8<N> const *lhs, DN_Str8 rhs);
template <DN_USize A, DN_USize B> bool DN_FStr8_EqFStr8 (DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs, DN_Str8EqCase eq_case);
template <DN_USize A, DN_USize B> bool DN_FStr8_EqFStr8Insensitive (DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs);
template <DN_USize N> bool operator== (DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs);
template <DN_USize N> bool operator!= (DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs);
template <DN_USize N> bool operator== (DN_FStr8<N> const &lhs, DN_Str8 const &rhs);
template <DN_USize N> bool operator!= (DN_FStr8<N> const &lhs, DN_Str8 const &rhs);
#endif // !defined(DN_NO_FSTR8)
// NOTE: DN_Char ///////////////////////////////////////////////////////////////////////////////////
struct DN_CharHexToU8
{
bool success;
uint8_t value;
};
DN_API bool DN_Char_IsAlphabet (char ch);
DN_API bool DN_Char_IsDigit (char ch);
DN_API bool DN_Char_IsAlphaNum (char ch);
DN_API bool DN_Char_IsWhitespace (char ch);
DN_API bool DN_Char_IsHex (char ch);
DN_API DN_CharHexToU8 DN_Char_HexToU8 (char ch);
DN_API char DN_Char_ToHex (char ch);
DN_API char DN_Char_ToHexUnchecked (char ch);
DN_API char DN_Char_ToLower (char ch);
DN_API char DN_Char_ToUpper (char ch);
// NOTE: DN_UTF ////////////////////////////////////////////////////////////////////////////////////
DN_API int DN_UTF8_EncodeCodepoint (uint8_t utf8[4], uint32_t codepoint);
DN_API int DN_UTF16_EncodeCodepoint (uint16_t utf16[2], uint32_t codepoint);
// NOTE: DN_Str8Builder ///////////////////////////////////////////////////////////////////////////
template <DN_USize N>
DN_Str8Builder DN_Str8Builder_InitCArrayRef(DN_Arena *arena, DN_Str8 const (&array)[N])
{
DN_Str8Builder result = DN_Str8Builder_InitArrayRef(arena, array, N);
return result;
}
template <DN_USize N>
DN_Str8Builder DN_Str8Builder_InitCArrayCopy(DN_Arena *arena, DN_Str8 const (&array)[N])
{
DN_Str8Builder result = DN_Str8Builder_InitArrayCopy(arena, array, N);
return result;
}
template <DN_USize N>
bool DN_Str8Builder_AddCArrayRef(DN_Str8Builder *builder, DN_Str8 const (&array)[N], DN_Str8BuilderAdd add)
{
bool result = DN_Str8Builder_AddArrayRef(builder, array, N, add);
return result;
}
template <DN_USize N>
bool DN_Str8Builder_AddCArrayCopy(DN_Str8Builder *builder, DN_Str8 const (&array)[N], DN_Str8BuilderAdd add)
{
bool result = DN_Str8Builder_AddArrayCopy(builder, array, N, add);
return result;
}
#if !defined(DN_NO_FSTR8)
// NOTE: DN_FStr8 //////////////////////////////////////////////////////////////////////////////////
template <DN_USize N>
DN_FStr8<N> DN_FStr8_InitF(DN_FMT_ATTRIB char const *fmt, ...)
{
DN_FStr8<N> result = {};
if (fmt) {
va_list args;
va_start(args, fmt);
DN_FStr8_AddFV(&result, fmt, args);
va_end(args);
}
return result;
}
template <DN_USize N>
DN_FStr8<N> DN_FStr8_InitFV(char const *fmt, va_list args)
{
DN_FStr8<N> result = {};
DN_FStr8_AddFV(&result, fmt, args);
return result;
}
template <DN_USize N>
DN_USize DN_FStr8_Max(DN_FStr8<N> const *)
{
DN_USize result = N;
return result;
}
template <DN_USize N>
void DN_FStr8_Clear(DN_FStr8<N> *string)
{
*string = {};
}
template <DN_USize N>
bool DN_FStr8_AddFV(DN_FStr8<N> *string, DN_FMT_ATTRIB char const *fmt, va_list args)
{
bool result = false;
if (!string || !fmt)
return result;
DN_USize require = DN_CStr8_FVSize(fmt, args) + 1 /*null_terminate*/;
DN_USize space = (N + 1) - string->size;
result = require <= space;
string->size += DN_VSNPrintF(string->data + string->size, DN_CAST(int) space, fmt, args);
// NOTE: snprintf returns the required size of the format string
// irrespective of if there's space or not.
string->size = DN_Min(string->size, N);
return result;
}
template <DN_USize N>
bool DN_FStr8_AddF(DN_FStr8<N> *string, DN_FMT_ATTRIB char const *fmt, ...)
{
bool result = false;
if (!string || !fmt)
return result;
va_list args;
va_start(args, fmt);
result = DN_FStr8_AddFV(string, fmt, args);
va_end(args);
return result;
}
template <DN_USize N>
bool DN_FStr8_AddCStr8(DN_FStr8<N> *string, char const *src, DN_USize size)
{
DN_Assert(string->size <= N);
bool result = false;
if (!string || !src || size == 0 || string->size >= N)
return result;
DN_USize space = N - string->size;
result = size <= space;
DN_Memcpy(string->data + string->size, src, DN_Min(space, size));
string->size = DN_Min(string->size + size, N);
string->data[string->size] = 0;
return result;
}
template <DN_USize N>
bool DN_FStr8_Add(DN_FStr8<N> *string, DN_Str8 src)
{
bool result = DN_FStr8_AddCStr8(string, src.data, src.size);
return result;
}
template <DN_USize N>
DN_Str8 DN_FStr8_ToStr8(DN_FStr8<N> const *string)
{
DN_Str8 result = {};
if (!string || string->size <= 0)
return result;
result.data = DN_CAST(char *) string->data;
result.size = string->size;
return result;
}
template <DN_USize N>
bool DN_FStr8_Eq(DN_FStr8<N> const *lhs, DN_FStr8<N> const *rhs, DN_Str8EqCase eq_case)
{
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, eq_case);
return result;
}
template <DN_USize N>
bool DN_FStr8_EqStr8(DN_FStr8<N> const *lhs, DN_Str8 rhs, DN_Str8EqCase eq_case)
{
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
bool result = DN_Str8_Eq(lhs_s8, rhs, eq_case);
return result;
}
template <DN_USize N>
bool DN_FStr8_EqInsensitive(DN_FStr8<N> const *lhs, DN_FStr8<N> const *rhs)
{
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, DN_Str8EqCase_Insensitive);
return result;
}
template <DN_USize N>
bool DN_FStr8_EqStr8Insensitive(DN_FStr8<N> const *lhs, DN_Str8 rhs)
{
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
bool result = DN_Str8_Eq(lhs_s8, rhs, DN_Str8EqCase_Insensitive);
return result;
}
template <DN_USize A, DN_USize B>
bool DN_FStr8_EqFStr8(DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs, DN_Str8EqCase eq_case)
{
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, eq_case);
return result;
}
template <DN_USize A, DN_USize B>
bool DN_FStr8_EqFStr8Insensitive(DN_FStr8<A> const *lhs, DN_FStr8<B> const *rhs)
{
DN_Str8 lhs_s8 = DN_FStr8_ToStr8(lhs);
DN_Str8 rhs_s8 = DN_FStr8_ToStr8(rhs);
bool result = DN_Str8_Eq(lhs_s8, rhs_s8, DN_Str8EqCase_Insensitive);
return result;
}
template <DN_USize N>
bool operator==(DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs)
{
bool result = DN_FStr8_Eq(&lhs, &rhs, DN_Str8EqCase_Sensitive);
return result;
}
template <DN_USize N>
bool operator!=(DN_FStr8<N> const &lhs, DN_FStr8<N> const &rhs)
{
bool result = !(lhs == rhs);
return result;
}
template <DN_USize N>
bool operator==(DN_FStr8<N> const &lhs, DN_Str8 const &rhs)
{
bool result = DN_Str8_Eq(DN_FStr8_ToStr8(&lhs), rhs, DN_Str8EqCase_Insensitive);
return result;
}
template <DN_USize N>
bool operator!=(DN_FStr8<N> const &lhs, DN_Str8 const &rhs)
{
bool result = !(lhs == rhs);
return result;
}
#endif // !defined(DN_NO_FSTR8)
#endif // !defined(DN_BASE_STRING_H)