diff --git a/Single-Header/dn_single_header.cpp b/Single-Header/dn_single_header.cpp index 59cc025..c188b6c 100644 --- a/Single-Header/dn_single_header.cpp +++ b/Single-Header/dn_single_header.cpp @@ -1,4 +1,4 @@ -// Generated by the DN single header generator 2026-06-18 18:20:05 +// Generated by the DN single header generator 2026-06-18 22:11:00 // DN: Single header generator commented out => #if defined(_CLANGD) // #define DN_H_WITH_OS 1 @@ -14,6 +14,10 @@ // #include "../dn.h" // #endif +#if DN_STR8_AVX512F + #include +#endif + enum DN_ArenaUAFCheckReportType_ { DN_ArenaUAFCheckReportType_AllocViolation, @@ -305,7 +309,7 @@ DN_API DN_CPUReport DN_CPUGetReport() // NOTE: DN_TicketMutex //////////////////////////////////////////////////////////////////////////// DN_API void DN_TicketMutex_Begin(DN_TicketMutex *mutex) { - unsigned int ticket = DN_AtomicAddU32(&mutex->ticket, 1); + DN_UInt ticket = DN_AtomicAddU32(&mutex->ticket, 1); DN_TicketMutex_BeginTicket(mutex, ticket); } @@ -368,163 +372,159 @@ DN_API bool DN_BitIsNotSet(DN_USize bits, DN_USize bits_to_check) return result; } -// NOTE: DN_Safe /////////////////////////////////////////////////////////////////////////////////// -DN_API DN_I64 DN_SafeAddI64(int64_t a, int64_t b) +DN_API DN_I64 DN_SafeAddI64(DN_I64 a, DN_I64 b) { - DN_I64 result = DN_CheckF(a <= INT64_MAX - b, "a=%zd, b=%zd", a, b) ? (a + b) : INT64_MAX; + DN_I64 result = a <= INT64_MAX - b ? (a + b) : INT64_MAX; return result; } -DN_API DN_I64 DN_SafeMulI64(int64_t a, int64_t b) +DN_API DN_I64 DN_SafeMulI64(DN_I64 a, DN_I64 b) { - DN_I64 result = DN_CheckF(a <= INT64_MAX / b, "a=%zd, b=%zd", a, b) ? (a * b) : INT64_MAX; + DN_I64 result = a <= INT64_MAX / b ? (a * b) : INT64_MAX; return result; } DN_API DN_U64 DN_SafeAddU64(DN_U64 a, DN_U64 b) { - DN_U64 result = DN_CheckF(a <= UINT64_MAX - b, "a=%zu, b=%zu", a, b) ? (a + b) : UINT64_MAX; + DN_U64 result = a <= UINT64_MAX - b ? (a + b) : UINT64_MAX; return result; } DN_API DN_U64 DN_SafeSubU64(DN_U64 a, DN_U64 b) { - DN_U64 result = DN_CheckF(a >= b, "a=%zu, b=%zu", a, b) ? (a - b) : 0; + DN_U64 result = a >= b ? (a - b) : 0; return result; } DN_API DN_U64 DN_SafeMulU64(DN_U64 a, DN_U64 b) { - DN_U64 result = DN_CheckF(a <= UINT64_MAX / b, "a=%zu, b=%zu", a, b) ? (a * b) : UINT64_MAX; + DN_U64 result = a <= UINT64_MAX / b ? (a * b) : UINT64_MAX; return result; } DN_API DN_U32 DN_SafeSubU32(DN_U32 a, DN_U32 b) { - DN_U32 result = DN_CheckF(a >= b, "a=%u, b=%u", a, b) ? (a - b) : 0; + DN_U32 result = 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). +// NOTE: 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; + int result = DN_Cast(uintmax_t) val <= INT_MAX ? DN_Cast(int) val : INT_MAX; return result; } -DN_API int8_t DN_SaturateCastUSizeToI8(DN_USize val) +DN_API DN_I8 DN_SaturateCastUSizeToI8(DN_USize val) { - int8_t result = DN_Check(DN_Cast(uintmax_t) val <= INT8_MAX) ? DN_Cast(int8_t) val : INT8_MAX; + DN_I8 result = DN_Cast(uintmax_t) val <= INT8_MAX ? DN_Cast(DN_I8) 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; + DN_I16 result = 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; + DN_I32 result = 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) +DN_API DN_I64 DN_SaturateCastUSizeToI64(DN_USize val) { - int64_t result = DN_Check(DN_Cast(uintmax_t) val <= INT64_MAX) ? DN_Cast(int64_t) val : INT64_MAX; + DN_I64 result = DN_Cast(uintmax_t) val <= INT64_MAX ? DN_Cast(DN_I64) val : INT64_MAX; return result; } -// NOTE: DN_SaturateCastUSizeToU* //////////////////////////////////////////////////////////// -// Both operands are unsigned and the lowest rank operand will be promoted to +// NOTE: 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; + DN_U8 result = 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; + DN_U16 result = 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; + DN_U32 result = 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; + DN_U64 result = DN_Cast(DN_U64) val <= UINT64_MAX ? DN_Cast(DN_U64) val : UINT64_MAX; return result; } -// NOTE: DN_SaturateCastU64To* /////////////////////////////////////////////////////////////// +// NOTE: DN_SaturateCastU64To* DN_API int DN_SaturateCastU64ToInt(DN_U64 val) { - int result = DN_Check(val <= INT_MAX) ? DN_Cast(int) val : INT_MAX; + int result = val <= INT_MAX ? DN_Cast(int) val : INT_MAX; return result; } -DN_API int8_t DN_SaturateCastU64ToI8(DN_U64 val) +DN_API DN_I8 DN_SaturateCastU64ToI8(DN_U64 val) { - int8_t result = DN_Check(val <= INT8_MAX) ? DN_Cast(int8_t) val : INT8_MAX; + DN_I8 result = val <= INT8_MAX ? DN_Cast(DN_I8) 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; + DN_I16 result = 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; + DN_I32 result = val <= INT32_MAX ? DN_Cast(DN_I32) val : INT32_MAX; return result; } -DN_API int64_t DN_SaturateCastU64ToI64(DN_U64 val) +DN_API DN_I64 DN_SaturateCastU64ToI64(DN_U64 val) { - int64_t result = DN_Check(val <= INT64_MAX) ? DN_Cast(int64_t) val : INT64_MAX; + DN_I64 result = val <= INT64_MAX ? DN_Cast(DN_I64) 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) +// NOTE: Both operands are unsigned and the lowest rank operand will be promoted to match the +// highest rank operand. +DN_API DN_UInt DN_SaturateCastU64ToUInt(DN_U64 val) { - unsigned int result = DN_Check(val <= UINT8_MAX) ? DN_Cast(unsigned int) val : UINT_MAX; + DN_UInt result = val <= UINT8_MAX ? DN_Cast(DN_UInt) 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; + DN_U8 result = 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; + DN_U16 result = 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; + DN_U32 result = 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. +// NOTE: 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); @@ -532,10 +532,10 @@ DN_API int DN_SaturateCastISizeToInt(DN_ISize val) return result; } -DN_API int8_t DN_SaturateCastISizeToI8(DN_ISize val) +DN_API DN_I8 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); + DN_I8 result = DN_Cast(DN_I8) DN_Clamp(val, INT8_MIN, INT8_MAX); return result; } @@ -553,23 +553,21 @@ DN_API DN_I32 DN_SaturateCastISizeToI32(DN_ISize val) return result; } -DN_API int64_t DN_SaturateCastISizeToI64(DN_ISize val) +DN_API DN_I64 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); + DN_Assert(DN_Cast(DN_I64) val >= INT64_MIN && DN_Cast(DN_I64) val <= INT64_MAX); + DN_I64 result = DN_Cast(DN_I64) DN_Clamp(DN_Cast(DN_I64) 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) +// NOTE: 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 DN_UInt 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; + DN_UInt result = 0; + if (val >= DN_Cast(DN_ISize)0) { + if (DN_Cast(uintmax_t) val <= UINT_MAX) + result = DN_Cast(DN_UInt) val; else result = UINT_MAX; } @@ -579,8 +577,8 @@ DN_API unsigned int DN_SaturateCastISizeToUInt(DN_ISize val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT8_MAX) result = DN_Cast(DN_U8) val; else result = UINT8_MAX; @@ -591,8 +589,8 @@ DN_API DN_U8 DN_SaturateCastISizeToU8(DN_ISize val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT16_MAX) result = DN_Cast(DN_U16) val; else result = UINT16_MAX; @@ -603,8 +601,8 @@ DN_API DN_U16 DN_SaturateCastISizeToU16(DN_ISize val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT32_MAX) result = DN_Cast(DN_U32) val; else result = UINT32_MAX; @@ -615,8 +613,8 @@ DN_API DN_U32 DN_SaturateCastISizeToU32(DN_ISize val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT64_MAX) result = DN_Cast(DN_U64) val; else result = UINT64_MAX; @@ -624,54 +622,49 @@ DN_API DN_U64 DN_SaturateCastISizeToU64(DN_ISize val) 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) +// NOTE: Both operands are signed so the lowest rank operand will be promoted to match the highest +// rank operand. +DN_API DN_ISize DN_SaturateCastI64ToISize(DN_I64 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); + DN_ISize result = DN_Cast(DN_I64) DN_Clamp(val, DN_ISIZE_MIN, DN_ISIZE_MAX); return result; } -DN_API int8_t DN_SaturateCastI64ToI8(int64_t val) +DN_API DN_I8 DN_SaturateCastI64ToI8(DN_I64 val) { - DN_Check(val >= INT8_MIN && val <= INT8_MAX); - int8_t result = DN_Cast(int8_t) DN_Clamp(val, INT8_MIN, INT8_MAX); + DN_I8 result = DN_Cast(DN_I8) DN_Clamp(val, INT8_MIN, INT8_MAX); return result; } -DN_API DN_I16 DN_SaturateCastI64ToI16(int64_t val) +DN_API DN_I16 DN_SaturateCastI64ToI16(DN_I64 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_API DN_I32 DN_SaturateCastI64ToI32(DN_I64 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) +DN_API DN_UInt DN_SaturateCastI64ToUInt(DN_I64 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; + DN_UInt result = 0; + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= UINT_MAX) + result = DN_Cast(DN_UInt) val; else result = UINT_MAX; } return result; } -DN_API DN_ISize DN_SaturateCastI64ToUSize(int64_t val) +DN_API DN_USize DN_SaturateCastI64ToUSize(DN_I64 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)) + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= DN_USIZE_MAX) result = DN_Cast(DN_USize) val; else result = DN_USIZE_MAX; @@ -679,11 +672,11 @@ DN_API DN_ISize DN_SaturateCastI64ToUSize(int64_t val) return result; } -DN_API DN_U8 DN_SaturateCastI64ToU8(int64_t val) +DN_API DN_U8 DN_SaturateCastI64ToU8(DN_I64 val) { DN_U8 result = 0; - if (DN_Check(val >= DN_Cast(int64_t) 0)) { - if (DN_Check(DN_Cast(uintmax_t) val <= UINT8_MAX)) + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= UINT8_MAX) result = DN_Cast(DN_U8) val; else result = UINT8_MAX; @@ -691,11 +684,11 @@ DN_API DN_U8 DN_SaturateCastI64ToU8(int64_t val) return result; } -DN_API DN_U16 DN_SaturateCastI64ToU16(int64_t val) +DN_API DN_U16 DN_SaturateCastI64ToU16(DN_I64 val) { DN_U16 result = 0; - if (DN_Check(val >= DN_Cast(int64_t) 0)) { - if (DN_Check(DN_Cast(uintmax_t) val <= UINT16_MAX)) + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= UINT16_MAX) result = DN_Cast(DN_U16) val; else result = UINT16_MAX; @@ -703,11 +696,11 @@ DN_API DN_U16 DN_SaturateCastI64ToU16(int64_t val) return result; } -DN_API DN_U32 DN_SaturateCastI64ToU32(int64_t val) +DN_API DN_U32 DN_SaturateCastI64ToU32(DN_I64 val) { DN_U32 result = 0; - if (DN_Check(val >= DN_Cast(int64_t) 0)) { - if (DN_Check(DN_Cast(uintmax_t) val <= UINT32_MAX)) + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= UINT32_MAX) result = DN_Cast(DN_U32) val; else result = UINT32_MAX; @@ -715,11 +708,11 @@ DN_API DN_U32 DN_SaturateCastI64ToU32(int64_t val) return result; } -DN_API DN_U64 DN_SaturateCastI64ToU64(int64_t val) +DN_API DN_U64 DN_SaturateCastI64ToU64(DN_I64 val) { DN_U64 result = 0; - if (DN_Check(val >= DN_Cast(int64_t) 0)) { - if (DN_Check(DN_Cast(uintmax_t) val <= UINT64_MAX)) + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= UINT64_MAX) result = DN_Cast(DN_U64) val; else result = UINT64_MAX; @@ -727,16 +720,14 @@ DN_API DN_U64 DN_SaturateCastI64ToU64(int64_t val) return result; } -DN_API int8_t DN_SaturateCastIntToI8(int val) +DN_API DN_I8 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); + DN_I8 result = DN_Cast(DN_I8) 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; } @@ -744,8 +735,8 @@ DN_API DN_I16 DN_SaturateCastIntToI16(int val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT8_MAX) result = DN_Cast(DN_U8) val; else result = UINT8_MAX; @@ -756,8 +747,8 @@ DN_API DN_U8 DN_SaturateCastIntToU8(int val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT16_MAX) result = DN_Cast(DN_U16) val; else result = UINT16_MAX; @@ -767,27 +758,25 @@ DN_API DN_U16 DN_SaturateCastIntToU16(int val) DN_API DN_U32 DN_SaturateCastIntToU32(int val) { - static_assert(sizeof(val) <= sizeof(DN_U32), "Sanity check to allow simplifying of casting"); + DN_StaticAssert(sizeof(val) <= sizeof(DN_U32) && "Sanity check to allow simplifying of casting"); DN_U32 result = 0; - if (DN_Check(val >= 0)) + if (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_StaticAssert(sizeof(val) <= sizeof(DN_U64) && "Sanity check to allow simplifying of casting"); DN_U64 result = 0; - if (DN_Check(val >= 0)) + if (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_StaticAssert(DN_IsPowerOfTwoAligned(DN_ASAN_POISON_GUARD_SIZE, DN_ASAN_POISON_ALIGNMENT) && + "ASAN poison guard size must be a power-of-two and aligned to ASAN's alignment" "requirement (8 bytes)"); DN_API void DN_ASanPoisonMemoryRegion(void const volatile *ptr, DN_USize size) { if (!ptr || !size) @@ -1006,14 +995,19 @@ DN_API bool DN_MemListCommitTo(DN_MemList *mem, DN_U64 pos) if (!mem || !mem->curr) return false; + // NOTE: Early out if the position to commit to is already committed DN_MemBlock *curr = mem->curr; if (pos <= curr->commit) return true; + // NOTE: Sanity check position is within the bounds of the memory block DN_U64 real_pos = pos; - if (!DN_Check(pos <= curr->reserve)) + if (pos > curr->reserve) { + DN_Assert(pos <= curr->reserve); real_pos = curr->reserve; + } + // NOTE: Do the commit DN_Assert(mem->funcs.virtual_page_size); DN_USize end_commit = DN_AlignUpPowerOfTwo(real_pos, mem->funcs.virtual_page_size); DN_USize commit_size = end_commit - curr->commit; @@ -1054,7 +1048,7 @@ DN_API bool DN_MemListGrow(DN_MemList *mem, DN_U64 reserve, DN_U64 commit) return result; } -DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, uint8_t align, DN_ZMem z_mem) +DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem z_mem) { if (!mem) return nullptr; @@ -1070,7 +1064,7 @@ DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, uint8_t align, DN_ZMe try_alloc_again: DN_MemBlock *curr = mem->curr; bool poison = DN_ArenaHasPoison_(mem->flags); - uint8_t real_align = poison ? DN_Max(align, DN_ASAN_POISON_ALIGNMENT) : align; + DN_U8 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; @@ -1122,7 +1116,7 @@ DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, uint8_t align, DN_ZMe return result; } -DN_API void *DN_MemListAllocContiguous(DN_MemList *mem, DN_U64 size, uint8_t align, DN_ZMem z_mem) +DN_API void *DN_MemListAllocContiguous(DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem z_mem) { DN_MemFlags prev_flags = mem->flags; mem->flags |= (DN_MemFlags_NoGrow | DN_MemFlags_NoPoison); @@ -1131,7 +1125,7 @@ DN_API void *DN_MemListAllocContiguous(DN_MemList *mem, DN_U64 size, uint8_t ali return memory; } -DN_API void *DN_MemListCopy(DN_MemList *mem, void const *data, DN_U64 size, uint8_t align) +DN_API void *DN_MemListCopy(DN_MemList *mem, void const *data, DN_U64 size, DN_U8 align) { if (!mem || !data || size == 0) return nullptr; @@ -1190,10 +1184,9 @@ DN_API void DN_MemListPopTo(DN_MemList *mem, DN_U64 init_used) DN_API void DN_MemListPop(DN_MemList *mem, DN_U64 amount) { DN_MemBlock *curr = mem->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_USize used_sum = curr->reserve_sum + curr->used; + amount = DN_Min(amount, used_sum); + DN_USize pop_to = used_sum - amount; DN_MemListPopTo(mem, pop_to); } @@ -1410,28 +1403,28 @@ DN_API void DN_ArenaTempEnd(DN_Arena *arena, DN_ArenaReset reset) #endif } -DN_API void *DN_ArenaAlloc(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem) +DN_API void *DN_ArenaAlloc(DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_mem) { DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListAlloc(arena->mem, size, align, z_mem); return result; } -DN_API void *DN_ArenaAllocContiguous(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem) +DN_API void *DN_ArenaAllocContiguous(DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_mem) { DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListAllocContiguous(arena->mem, size, align, z_mem); return result; } -DN_API void *DN_ArenaCopy(DN_Arena *arena, void const *data, DN_U64 size, uint8_t align) +DN_API void *DN_ArenaCopy(DN_Arena *arena, void const *data, DN_U64 size, DN_U8 align) { DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListCopy(arena->mem, data, size, align); return result; } -DN_API DN_Pool DN_PoolFromArena(DN_Arena *arena, uint8_t align) +DN_API DN_Pool DN_PoolFromArena(DN_Arena *arena, DN_U8 align) { DN_Pool result = {}; if (arena) { @@ -1454,7 +1447,7 @@ DN_API void *DN_PoolAlloc(DN_Pool *pool, DN_USize size) 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 const DN_USizeo_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. @@ -1462,14 +1455,16 @@ DN_API void *DN_PoolAlloc(DN_Pool *pool, DN_USize size) dist_to_next_msb -= DN_Cast(DN_USize)(!DN_IsPowerOfTwo(required_size)); DN_USize const register_size = sizeof(DN_USize) * 8; - DN_AssertF(register_size >= (dist_to_next_msb - size_to_slot_offset), "lhs=%zu, rhs=%zu", register_size, (dist_to_next_msb - size_to_slot_offset)); - slot_index = register_size - dist_to_next_msb - size_to_slot_offset; + DN_AssertF(register_size >= (dist_to_next_msb - DN_USizeo_slot_offset), "lhs=%zu, rhs=%zu", register_size, (dist_to_next_msb - DN_USizeo_slot_offset)); + slot_index = register_size - dist_to_next_msb - DN_USizeo_slot_offset; } - if (!DN_CheckF(slot_index < DN_PoolSlotSize_Count, "Chunk pool does not support the requested allocation size")) + if (slot_index >= DN_PoolSlotSize_Count) { + DN_AssertF(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_USize slot_size_in_bytes = 1ULL << (slot_index + DN_USizeo_slot_offset); DN_AssertF(required_size <= (slot_size_in_bytes << 0), "slot_index=%zu, lhs=%zu, rhs=%zu", slot_index, required_size, (slot_size_in_bytes << 0)); DN_AssertF(required_size >= (slot_size_in_bytes >> 1), "slot_index=%zu, lhs=%zu, rhs=%zu", slot_index, required_size, (slot_size_in_bytes >> 1)); @@ -1553,8 +1548,8 @@ static void DN_ErrSinkCheck_(DN_ErrSink const *err) // NOTE: Walk the list ensuring we eventually terminate at the sentinel (e.g. we have a // well formed doubly-linked-list terminated by a sentinel, or otherwise we will hit the // walk limit or dereference a null pointer and assert) - size_t WALK_LIMIT = 99'999; - size_t walk = 0; + DN_USize WALK_LIMIT = 99'999; + DN_USize walk = 0; for (DN_ErrSinkMsg *it = node->msg_sentinel->next; it != node->msg_sentinel; it = it->next, walk++) { DN_AssertF(it, "Encountered null pointer which should not happen in a sentinel DLL"); DN_Assert(walk < WALK_LIMIT); @@ -1580,7 +1575,7 @@ DN_API DN_ErrSink* DN_ErrSinkBegin_(DN_ErrSink *err, DN_ErrSinkMode mode, DN_Cal DN_SentinelDoublyLLInitArena(node->msg_sentinel, DN_ErrSinkMsg, err->arena); // NOTE: Handle allocation error - if (!DN_Check(node && node->msg_sentinel)) { + if (!node || !node->msg_sentinel) { DN_MemListPopTo(err->arena->mem, node->arena_pos); node->msg_sentinel = nullptr; err->stack_size--; @@ -1764,14 +1759,13 @@ DN_API void DN_ErrSinkAppendFV_(DN_ErrSink *err, DN_U32 error_code, DN_CallSite DN_AssertF(node, "Error sink must be begun by calling 'Begin' before using this function."); DN_ErrSinkMsg *msg = DN_ArenaNew(err->arena, DN_ErrSinkMsg, DN_ZMem_Yes); - if (DN_Check(msg)) { - msg->msg = DN_Str8FromFmtVArena(err->arena, fmt, args); - msg->error_code = error_code; - msg->call_site = call_site; - DN_SentinelDoublyLLPrepend(node->msg_sentinel, msg); - if (node->mode == DN_ErrSinkMode_ExitOnError) - DN_ErrSinkEndExitIfErrorF_(err, msg->call_site, error_code, "Fatal error %u", error_code); - } + DN_Assert(msg); + msg->msg = DN_Str8FromFmtVArena(err->arena, fmt, args); + msg->error_code = error_code; + msg->call_site = call_site; + DN_SentinelDoublyLLPrepend(node->msg_sentinel, msg); + if (node->mode == DN_ErrSinkMode_ExitOnError) + DN_ErrSinkEndExitIfErrorF_(err, msg->call_site, error_code, "Fatal error %u", error_code); } DN_API void DN_ErrSinkAppendF_(DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...) @@ -1998,7 +1992,7 @@ DN_API DN_ErrSink *DN_TCErrSink() return result; } -DN_API void *DN_PoolCopy(DN_Pool *pool, void const *data, DN_U64 size, uint8_t align) +DN_API void *DN_PoolCopy(DN_Pool *pool, void const *data, DN_U64 size, DN_U8 align) { if (!pool || !data || size == 0) return nullptr; @@ -2095,7 +2089,7 @@ DN_API DN_U64FromResult DN_U64FromStr8(DN_Str8 string, char separator) return result; result.value = DN_SafeMulU64(result.value, 10); - uint64_t digit = ch - '0'; + DN_U64 digit = ch - '0'; result.value = DN_SafeAddU64(result.value, digit); } @@ -2252,9 +2246,9 @@ DN_API DN_I64FromResult DN_I64FromStr8(DN_Str8 string, char separator) if (!DN_CharIsDigit(ch)) return result; - result.value = DN_SafeMulU64(result.value, 10); - uint64_t digit = ch - '0'; - result.value = DN_SafeAddU64(result.value, digit); + result.value = DN_SafeMulU64(result.value, 10); + DN_U64 digit = ch - '0'; + result.value = DN_SafeAddU64(result.value, digit); } if (negative) @@ -2872,7 +2866,7 @@ DN_API DN_Str8BSplitResult DN_Str8BSplitArray(DN_Str8 string, DN_Str8 const *fin return result; result.lhs = string; - for (size_t index = 0; !result.rhs.data && index < string.size; index++) { + for (DN_USize index = 0; !result.rhs.data && index < string.size; index++) { for (DN_USize find_index = 0; find_index < find_size; find_index++) { DN_Str8 find_item = find[find_index]; DN_Str8 string_slice = DN_Str8Subset(string, index, find_item.size); @@ -2902,7 +2896,7 @@ DN_API DN_Str8BSplitResult DN_Str8BSplitLastArray(DN_Str8 string, DN_Str8 const return result; result.lhs = string; - for (size_t index = string.size - 1; !result.rhs.data && index < string.size; index--) { + for (DN_USize index = string.size - 1; !result.rhs.data && index < string.size; index--) { for (DN_USize find_index = 0; find_index < find_size; find_index++) { DN_Str8 find_item = find[find_index]; DN_Str8 string_slice = DN_Str8Subset(string, index, find_item.size); @@ -2998,7 +2992,7 @@ DN_API DN_Str8FindResult DN_Str8FindStr8(DN_Str8 string, DN_Str8 find, DN_Str8Eq DN_API DN_Str8FindResult DN_Str8Find(DN_Str8 string, DN_Str8FindFlag flags) { DN_Str8FindResult result = {}; - for (size_t index = 0; !result.found && index < string.size; index++) { + for (DN_USize index = 0; !result.found && index < string.size; index++) { result.found |= ((flags & DN_Str8FindFlag_Digit) && DN_CharIsDigit(string.data[index])); result.found |= ((flags & DN_Str8FindFlag_Alphabet) && DN_CharIsAlphabet(string.data[index])); result.found |= ((flags & DN_Str8FindFlag_Whitespace) && DN_CharIsWhitespace(string.data[index])); @@ -3059,7 +3053,7 @@ DN_API DN_Str8 DN_Str8ReverseSegment(DN_Arena *arena, DN_Str8 src, DN_USize segm DN_MSVC_WARNING_PUSH DN_MSVC_WARNING_DISABLE(6293) // NOTE: Ill-defined loop - for (size_t src_index = src.size - 1; src_index < src.size; src_index--) { + for (DN_USize src_index = src.size - 1; src_index < src.size; src_index--) { DN_MSVC_WARNING_POP result.data[write_index--] = src.data[src_index]; if (++write_counter % segment_size == 0 && segment_counter < segments) { @@ -3636,6 +3630,276 @@ DN_API DN_Str8 DN_Str8Table(DN_Str8 const *rows, DN_USize num_rows, DN_USize num return result; } +#if DN_STR8_AVX512F +DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F(DN_Str8 string, DN_Str8 find) +{ + // NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html + DN_Str8FindResult result = {}; + if (string.size == 0 || find.size == 0 || find.size > string.size) + return result; + + __m512i const find_first_ch = _mm512_set1_epi8(find.data[0]); + __m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]); + + DN_USize const search_size = string.size - find.size; + DN_USize simd_iterations = search_size / sizeof(__m512i); + char const *ptr = string.data; + + while (simd_iterations--) { + __m512i find_first_ch_block = _mm512_loadu_si512(ptr); + __m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1); + + // NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit + // where matches are found. + __m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch); + + // NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one + // operation using the ternarylogic intrinsic. + // + // A = first_ch_matches (find_first_ch_block ^ find_first_ch) + // B = find_last_ch_block + // C = find_last_ch + // + // ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6 + // + // / A / B / C / B ^ C / A | (B ^ C) / + // | 0 | 0 | 0 | 0 | 0 | + // | 0 | 0 | 1 | 1 | 1 | + // | 0 | 1 | 0 | 1 | 1 | + // | 0 | 1 | 1 | 0 | 0 | + // | 1 | 0 | 0 | 0 | 1 | + // | 1 | 0 | 1 | 1 | 1 | + // | 1 | 1 | 0 | 1 | 1 | + // | 1 | 1 | 1 | 0 | 1 | + + __m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6); + + // NOTE: Matches were XOR-ed and are hence indicated as zero so we mask + // out which 32 bit elements in the vector had zero bytes. This uses a + // bit twiddling trick + // https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord + __mmask16 zero_byte_mask = {}; + { + const __m512i v01 = _mm512_set1_epi32(0x01010101u); + const __m512i v80 = _mm512_set1_epi32(0x80808080u); + const __m512i v1 = _mm512_sub_epi32(ch_matches, v01); + const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20); + zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1); + } + + while (zero_byte_mask) { + uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask); + char const *base_ptr = ptr + (4 * lsb_zero_pos); + + if (DN_Memcmp(base_ptr + 0, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data; + } else if (DN_Memcmp(base_ptr + 1, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 1; + } else if (DN_Memcmp(base_ptr + 2, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 2; + } else if (DN_Memcmp(base_ptr + 3, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 3; + } + + if (result.found) { + result.start_to_before_match = DN_Str8FromPtr(string.data, result.index); + result.match = DN_Str8FromPtr(string.data + result.index, find.size); + result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - result.index); + result.after_match_to_end_of_buffer = DN_Str8Advance(result.match_to_end_of_buffer, find.size); + return result; + } + + zero_byte_mask = DN_BitClearNextLSB(zero_byte_mask); + } + + ptr += sizeof(__m512i); + } + + for (DN_USize index = ptr - string.data; index < string.size; index++) { + DN_Str8 string_slice = DN_Str8Subset(string, index, find.size); + if (DN_Str8Eq(string_slice, find)) { + result.found = true; + result.index = index; + result.start_to_before_match = DN_Str8FromPtr(string.data, index); + result.match = DN_Str8FromPtr(string.data + index, find.size); + result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - index); + result.after_match_to_end_of_buffer = DN_Str8Advance(result.match_to_end_of_buffer, find.size); + return result; + } + } + + return result; +} + +DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F(DN_Str8 string, DN_Str8 find) +{ + // NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html + DN_Str8FindResult result = {}; + if (string.size == 0 || find.size == 0 || find.size > string.size) + return result; + + __m512i const find_first_ch = _mm512_set1_epi8(find.data[0]); + __m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]); + + DN_USize const search_size = string.size - find.size; + DN_USize simd_iterations = search_size / sizeof(__m512i); + char const *ptr = string.data + search_size + 1; + + while (simd_iterations--) { + ptr -= sizeof(__m512i); + __m512i find_first_ch_block = _mm512_loadu_si512(ptr); + __m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1); + + // NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit + // where matches are found. + __m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch); + + // NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one + // operation using the ternarylogic intrinsic. + // + // A = first_ch_matches (find_first_ch_block ^ find_first_ch) + // B = find_last_ch_block + // C = find_last_ch + // + // ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6 + // + // / A / B / C / B ^ C / A | (B ^ C) / + // | 0 | 0 | 0 | 0 | 0 | + // | 0 | 0 | 1 | 1 | 1 | + // | 0 | 1 | 0 | 1 | 1 | + // | 0 | 1 | 1 | 0 | 0 | + // | 1 | 0 | 0 | 0 | 1 | + // | 1 | 0 | 1 | 1 | 1 | + // | 1 | 1 | 0 | 1 | 1 | + // | 1 | 1 | 1 | 0 | 1 | + + __m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6); + + // NOTE: Matches were XOR-ed and are hence indicated as zero so we mask + // out which 32 bit elements in the vector had zero bytes. This uses a + // bit twiddling trick + // https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord + __mmask16 zero_byte_mask = {}; + { + const __m512i v01 = _mm512_set1_epi32(0x01010101u); + const __m512i v80 = _mm512_set1_epi32(0x80808080u); + const __m512i v1 = _mm512_sub_epi32(ch_matches, v01); + const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20); + zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1); + } + + while (zero_byte_mask) { + uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask); + char const *base_ptr = ptr + (4 * lsb_zero_pos); + + if (DN_Memcmp(base_ptr + 0, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data; + } else if (DN_Memcmp(base_ptr + 1, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 1; + } else if (DN_Memcmp(base_ptr + 2, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 2; + } else if (DN_Memcmp(base_ptr + 3, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 3; + } + + if (result.found) { + result.start_to_before_match = DN_Str8FromPtr(string.data, result.index); + result.match = DN_Str8FromPtr(string.data + result.index, find.size); + result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - result.index); + return result; + } + + zero_byte_mask = DN_BitClearNextLSB(zero_byte_mask); + } + } + + for (DN_USize index = ptr - string.data - 1; index < string.size; index--) { + DN_Str8 string_slice = DN_Str8Subset(string, index, find.size); + if (DN_Str8Eq(string_slice, find)) { + result.found = true; + result.index = index; + result.start_to_before_match = DN_Str8FromPtr(string.data, index); + result.match = DN_Str8FromPtr(string.data + index, find.size); + result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - index); + return result; + } + } + + return result; +} + +DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F(DN_Str8 string, DN_Str8 find) +{ + DN_Str8BSplitResult result = {}; + DN_Str8FindResult find_result = DN_Str8FindAVX512F(string, find); + if (find_result.found) { + result.lhs.data = string.data; + result.lhs.size = find_result.index; + result.rhs = DN_Str8Advance(find_result.match_to_end_of_buffer, find.size); + } else { + result.lhs = string; + } + + return result; +} + +DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F(DN_Str8 string, DN_Str8 find) +{ + DN_Str8BSplitResult result = {}; + DN_Str8FindResult find_result = DN_Str8FindLastAVX512F(string, find); + if (find_result.found) { + result.lhs.data = string.data; + result.lhs.size = find_result.index; + result.rhs = DN_Str8Advance(find_result.match_to_end_of_buffer, find.size); + } else { + result.lhs = string; + } + + return result; +} + +DN_API DN_USize DN_Str8SplitAVX512F(DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags flags) +{ + DN_USize result = 0; // The number of splits in the actual string. + if (string.size == 0 || delimiter.size == 0 || delimiter.size <= 0) + return result; + + DN_Str8BSplitResult split = {}; + DN_Str8 first = string; + do { + split = DN_Str8BSplitAVX512F(first, delimiter); + if (split.lhs.size || DN_BitIsNotSet(flags, DN_Str8SplitFlags_ExcludeEmptyStrings)) { + if (splits && result < splits_count) + splits[result] = split.lhs; + result++; + } + first = split.rhs; + } while (first.size); + + return result; +} + +DN_API DN_Str8Slice DN_Str8SplitAllocAVX512F(DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags flags) +{ + DN_Str8Slice result = {}; + DN_USize splits_required = DN_Str8SplitAVX512F(string, delimiter, /*splits*/ nullptr, /*count*/ 0, flags); + result.data = DN_ArenaNewArray(arena, DN_Str8, splits_required, DN_ZMem_No); + if (result.data) { + result.count = DN_Str8SplitAVX512F(string, delimiter, result.data, splits_required, flags); + DN_Assert(splits_required == result.count); + } + return result; +} +#endif // DN_STR8_AVX512F + DN_API DN_Str8 DN_Str8SliceRender(DN_Str8Slice slice, DN_Str8 separator, DN_Arena *arena) { DN_Str8 result = {}; @@ -4111,7 +4375,7 @@ DN_API int DN_UTF8Encode(DN_U8 utf8[4], DN_U32 codepoint) // ----------------------------------------+----------------------------+--------------------+ if (codepoint <= 0b0111'1111) { - utf8[0] = DN_Cast(uint8_t) codepoint; + utf8[0] = DN_Cast(DN_U8) codepoint; return 1; } @@ -5135,7 +5399,7 @@ DN_API DN_F32 DN_PCG32NextF32(DN_PCG32 *rng) DN_API DN_F64 DN_PCG32NextF64(DN_PCG32 *rng) { DN_U64 x = DN_PCG32Next64(rng); - return (DN_F64)(int64_t)(x >> 11) * 0x1.0p-53; + return (DN_F64)(DN_I64)(x >> 11) * 0x1.0p-53; } DN_API void DN_PCG32Advance(DN_PCG32 *rng, DN_U64 delta) @@ -6879,15 +7143,15 @@ DN_API DN_Str8x256 DN_M4ColumnMajorString(DN_M4 mat) return result; } -DN_API bool operator==(DN_M2x3 const &lhs, DN_M2x3 const &rhs) +DN_API bool DN_M2x3Eq(DN_M2x3 const *lhs, DN_M2x3 const *rhs) { - bool result = DN_Memcmp(lhs.e, rhs.e, sizeof(lhs.e[0]) * DN_ArrayCountU(lhs.e)) == 0; + bool result = DN_Memcmp(lhs->e, rhs->e, sizeof(lhs->e[0]) * DN_ArrayCountU(lhs->e)) == 0; return result; } -DN_API bool operator!=(DN_M2x3 const &lhs, DN_M2x3 const &rhs) +DN_API bool DN_M2x3NotEq(DN_M2x3 const *lhs, DN_M2x3 const *rhs) { - bool result = !(lhs == rhs); + bool result = !DN_M2x3Eq(lhs, rhs); return result; } @@ -7080,12 +7344,6 @@ DN_API DN_Rect DN_M2x3MulRect(DN_M2x3 m1, DN_Rect rect) return result; } -DN_API bool operator==(const DN_Rect &lhs, const DN_Rect &rhs) -{ - bool result = (lhs.pos == rhs.pos) && (lhs.size == rhs.size); - return result; -} - DN_API DN_V2F32 DN_RectCenter(DN_Rect rect) { DN_V2F32 result = rect.pos + (rect.size * .5f); @@ -7321,10 +7579,6 @@ DN_API DN_RaycastV2 DN_RaycastLineIntersectV2(DN_V2F32 origin_a, DN_V2F32 dir_a, } return result; } -// DN: Single header generator commented out => #include "Base/dn_base_containers.cpp" -// DN: Single header generator commented out => #if defined(_CLANGD) -// #include "../dn.h" -// #endif struct DN_ArrayFindEqMemcmpContext_ { @@ -7332,7 +7586,6 @@ struct DN_ArrayFindEqMemcmpContext_ void const *find; }; - DN_API void *DN_SliceAllocArena(void **data, DN_USize *slice_size_field, DN_USize size, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena) { void *result = *data; @@ -7677,16 +7930,19 @@ DN_API void DN_RingRead(DN_Ring *ring, void *dest, DN_U64 dest_size) ring->read_pos += dest_size; } +#if defined(__cplusplus) template DN_DSMap DN_DSMapInit(DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags) { + DN_AssertF(DN_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size); + DN_Assert(arena); + DN_DSMap result = {}; - if (!DN_CheckF(DN_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size)) - return result; if (size <= 0) return result; - if (!DN_Check(arena)) + if (!arena) return result; + result.arena = arena; result.pool = DN_PoolFromArena(arena, DN_POOL_DEFAULT_ALIGN); result.hash_to_slot = DN_ArenaNewArray(result.arena, DN_U32, size, DN_ZMem_Yes); @@ -8143,12 +8399,493 @@ DN_API bool operator==(DN_DSMapKey lhs, DN_DSMapKey rhs) bool result = DN_DSMapKeyEquals(lhs, rhs); return result; } -// DN: Single header generator commented out => #include "Base/dn_base_leak.cpp" -#define DN_BASE_LEAK_CPP +#endif // defined(__cplusplus) -// DN: Single header generator commented out => #if defined(_CLANGD) -// #include "../dn.h" -// #endif +DN_API void DN_BinPackU64(DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item) +{ + DN_U64 const VALUE_MASK = 0b0111'1111; + DN_U8 const CONTINUE_BIT = 0b1000'0000; + + if (mode == DN_BinPackMode_Serialise) { + DN_U64 it = *item; + do { + DN_U8 write_value = DN_Cast(DN_U8)(it & VALUE_MASK); + it >>= 7; + if (it) + write_value |= CONTINUE_BIT; + DN_Str8BuilderAppendBytesCopy(&pack->writer, &write_value, sizeof(write_value)); + } while (it); + } else { + *item = 0; + DN_USize bits_read = 0; + for (DN_U8 src = CONTINUE_BIT; (src & CONTINUE_BIT) && bits_read < 64; bits_read += 7) { + src = pack->read.data[pack->read_index++]; + DN_U8 masked_src = src & VALUE_MASK; + *item |= (DN_Cast(DN_U64) masked_src << bits_read); + } + } +} + +DN_API void DN_BinPackVarInt_(DN_BinPack *pack, DN_BinPackMode mode, void *item, DN_USize size) +{ + DN_U64 value = 0; + DN_AssertF(size <= sizeof(value), + "An item larger than 64 bits (%zu) is trying to be packed as a variable integer which is not supported", + size * 8); + + if (mode == DN_BinPackMode_Serialise) // Read `item` into U64 `value` + DN_Memcpy(&value, item, size); + + DN_BinPackU64(pack, mode, &value); + + if (mode == DN_BinPackMode_Deserialise) // Write U64 `value` into `item` + DN_Memcpy(item, &value, size); +} + +DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack) +{ + bool result = pack->read_index == pack->read.size; + return result; +} + +DN_API void DN_BinPackUSize(DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackU32(DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackU16(DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackU8(DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackI64(DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackI32(DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackI16(DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackI8(DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackF64(DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackF32(DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackV2(DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item) +{ + DN_BinPackF32(pack, mode, &item->x); + DN_BinPackF32(pack, mode, &item->y); +} + +DN_API void DN_BinPackV4(DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item) +{ + DN_BinPackF32(pack, mode, &item->x); + DN_BinPackF32(pack, mode, &item->y); + DN_BinPackF32(pack, mode, &item->z); + DN_BinPackF32(pack, mode, &item->w); +} + +DN_API void DN_BinPackBool(DN_BinPack *pack, DN_BinPackMode mode, bool *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackStr8FromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string) +{ + DN_BinPackVarInt_(pack, mode, &string->size, sizeof(string->size)); + if (mode == DN_BinPackMode_Serialise) { + DN_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size); + } else { + DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, string->size); + *string = DN_Str8FromStr8Arena(src, arena); + pack->read_index += src.size; + } +} + +DN_API void DN_BinPackStr8FromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string) +{ + DN_BinPackVarInt_(pack, mode, &string->size, sizeof(string->size)); + if (mode == DN_BinPackMode_Serialise) { + DN_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size); + } else { + DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, string->size); + *string = DN_Str8FromStr8Pool(src, pool); + pack->read_index += src.size; + } +} + +DN_API DN_Str8 DN_BinPackStr8FromBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max) +{ + DN_BinPackCBuffer(pack, mode, ptr, size, max); + DN_Str8 result = DN_Str8FromPtr(ptr, *size); + return result; +} + +DN_API void DN_BinPackBytesFromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size) +{ + DN_Str8 string = DN_Str8FromPtr(*ptr, *size); + DN_BinPackStr8FromArena(pack, arena, mode, &string); + *ptr = string.data; + *size = string.size; +} + +DN_API void DN_BinPackBytesFromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size) +{ + DN_Str8 string = DN_Str8FromPtr(*ptr, *size); + DN_BinPackStr8FromPool(pack, pool, mode, &string); + *ptr = string.data; + *size = string.size; +} + +DN_API void DN_BinPackCArray(DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size) +{ + DN_BinPackVarInt_(pack, mode, &size, sizeof(size)); + if (mode == DN_BinPackMode_Serialise) { + DN_Str8BuilderAppendBytesCopy(&pack->writer, ptr, size); + } else { + DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, size); + DN_Assert(src.size == size); + DN_Memcpy(ptr, src.data, DN_Min(src.size, size)); + pack->read_index += src.size; + } +} + +DN_API void DN_BinPackCBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max) +{ + if (mode == DN_BinPackMode_Serialise) { + DN_BinPackUSize(pack, mode, size); + DN_Str8BuilderAppendBytesCopy(&pack->writer, ptr, *size); + } else { + DN_U64 size_u64 = 0; + DN_BinPackU64(pack, mode, &size_u64); + DN_Assert(size_u64 < DN_USIZE_MAX); + DN_Assert(size_u64 <= max); + + *size = DN_Min(size_u64, max); + DN_Memcpy(ptr, pack->read.data + pack->read_index, *size); + pack->read_index += size_u64; + } +} + +DN_API DN_Str8 DN_BinPackBuild(DN_BinPack const *pack, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8FromStr8BuilderArena(&pack->writer, arena); + return result; +} + +DN_API DN_CSVTokeniser DN_CSVTokeniserInit(DN_Str8 string, char delimiter) +{ + DN_CSVTokeniser result = {}; + result.string = string; + result.delimiter = delimiter; + return result; +} + +DN_API bool DN_CSVTokeniserValid(DN_CSVTokeniser *tokeniser) +{ + bool result = tokeniser && !tokeniser->bad; + return result; +} + +static void DN_CSVTokeniserEatNewLines_(DN_CSVTokeniser *tokeniser) +{ + char const *end = tokeniser->string.data + tokeniser->string.size; + while (tokeniser->it[0] == '\n' || tokeniser->it[0] == '\r') + if (++tokeniser->it == end) + break; +} + +DN_API bool DN_CSVTokeniserNextRow(DN_CSVTokeniser *tokeniser) +{ + bool result = false; + if (DN_CSVTokeniserValid(tokeniser) && tokeniser->string.size) { + // NOTE: First time querying row iterator is nil, let tokeniser advance + if (tokeniser->it) { + // NOTE: Only advance the tokeniser if we're at the end of the line and + // there's more to tokenise. + char const *end = tokeniser->string.data + tokeniser->string.size; + if (tokeniser->it != end && tokeniser->end_of_line) { + tokeniser->end_of_line = false; + result = true; + } + } + } + + return result; +} + +DN_API DN_Str8 DN_CSVTokeniserNextField(DN_CSVTokeniser *tokeniser) +{ + DN_Str8 result = {}; + if (!DN_CSVTokeniserValid(tokeniser)) + return result; + + if (tokeniser->string.size == 0) { + tokeniser->bad = true; + return result; + } + + // NOTE: First time tokeniser is invoked with a string, set up initial state. + char const *string_end = tokeniser->string.data + tokeniser->string.size; + if (!tokeniser->it) { + tokeniser->it = tokeniser->string.data; + DN_CSVTokeniserEatNewLines_(tokeniser); // NOTE: Skip any leading new lines + } + + // NOTE: Tokeniser pointing at end, no more valid data to parse. + if (tokeniser->it == string_end) + return result; + + // NOTE: Scan forward until the next control character. + // 1. '"' Double quoted field, extract everything between the quotes. + // 2. tokeniser->delimiter End of the field, extract everything leading up to the delimiter. + // 3. '\n' Last field in record, extract everything leading up the the new line. + char const *begin = tokeniser->it; + while (tokeniser->it != string_end && (tokeniser->it[0] != '"' && + tokeniser->it[0] != tokeniser->delimiter && + tokeniser->it[0] != '\n')) + tokeniser->it++; + + bool quoted_field = (tokeniser->it != string_end) && tokeniser->it[0] == '"'; + if (quoted_field) { + begin = ++tokeniser->it; // Begin after the quote + + // NOTE: Scan forward until the next '"' which marks the end + // of the field unless it is escaped by another '"'. + find_next_quote: + while (tokeniser->it != string_end && tokeniser->it[0] != '"') + tokeniser->it++; + + // NOTE: If we encounter a '"' right after, the quotes were escaped + // and we need to skip to the next instance of a '"'. + if (tokeniser->it != string_end && tokeniser->it + 1 != string_end && tokeniser->it[1] == '"') { + tokeniser->it += 2; + goto find_next_quote; + } + } + + // NOTE: Mark the end of the field + char const *end = tokeniser->it; + tokeniser->end_of_line = tokeniser->it == string_end || end[0] == '\n'; + + // NOTE: In files with \r\n style new lines ensure that we don't include + // the \r byte in the CSV field we produce. + if (end != string_end && end[0] == '\n') { + DN_Assert((uintptr_t)(end - 1) > (uintptr_t)tokeniser->string.data && + "Internal error: The string iterator is pointing behind the start of the string we're reading"); + if (end[-1] == '\r') + end = end - 1; + } + + // NOTE: Quoted fields may have whitespace after the closing quote, we skip + // until we reach the field terminator. + if (quoted_field) + while (tokeniser->it != string_end && (tokeniser->it[0] != tokeniser->delimiter && tokeniser->it[0] != '\n')) + tokeniser->it++; + + // NOTE: Advance the tokeniser past the field terminator. + if (tokeniser->it != string_end) + tokeniser->it++; + + // NOTE: Generate the record + result.data = DN_Cast(char *) begin; + result.size = DN_Cast(int)(end - begin); + return result; +} + +DN_API DN_Str8 DN_CSVTokeniserNextColumn(DN_CSVTokeniser *tokeniser) +{ + DN_Str8 result = {}; + if (!DN_CSVTokeniserValid(tokeniser)) + return result; + + // NOTE: End of line, the user must explicitly advance to the next row + if (tokeniser->end_of_line) + return result; + + // NOTE: Advance tokeniser to the next field in the row + result = DN_CSVTokeniserNextField(tokeniser); + return result; +} + +DN_API void DN_CSVTokeniserSkipLine(DN_CSVTokeniser *tokeniser) +{ + while (DN_CSVTokeniserValid(tokeniser) && !tokeniser->end_of_line) + DN_CSVTokeniserNextColumn(tokeniser); + DN_CSVTokeniserNextRow(tokeniser); +} + +DN_API int DN_CSVTokeniserNextN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator) +{ + if (!DN_CSVTokeniserValid(tokeniser) || !fields || fields_size <= 0) + return 0; + + int result = 0; + for (; result < fields_size; result++) { + fields[result] = column_iterator ? DN_CSVTokeniserNextColumn(tokeniser) : DN_CSVTokeniserNextField(tokeniser); + if (!DN_CSVTokeniserValid(tokeniser) || !fields[result].data) + break; + } + + return result; +} + +DN_API int DN_CSVTokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size) +{ + int result = DN_CSVTokeniserNextN(tokeniser, fields, fields_size, true /*column_iterator*/); + return result; +} + +DN_API int DN_CSVTokeniserNextFieldN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size) +{ + int result = DN_CSVTokeniserNextN(tokeniser, fields, fields_size, false /*column_iterator*/); + return result; +} + +DN_API void DN_CSVTokeniserSkipLineN(DN_CSVTokeniser *tokeniser, int count) +{ + for (int i = 0; i < count && DN_CSVTokeniserValid(tokeniser); i++) + DN_CSVTokeniserSkipLine(tokeniser); +} + +DN_API void DN_CSVPackU64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value) +{ + if (serialise == DN_CSVSerialise_Read) { + DN_Str8 csv_value = DN_CSVTokeniserNextColumn(&pack->read_tokeniser); + DN_U64FromResult to_u64 = DN_U64FromStr8(csv_value, 0); + DN_Assert(to_u64.success); + *value = to_u64.value; + } else { + DN_Str8BuilderAppendF(&pack->write_builder, "%s%I64u", pack->write_column++ ? "," : "", *value); + } +} + +DN_API void DN_CSVPackI64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value) +{ + if (serialise == DN_CSVSerialise_Read) { + DN_Str8 csv_value = DN_CSVTokeniserNextColumn(&pack->read_tokeniser); + DN_I64FromResult to_i64 = DN_I64FromStr8(csv_value, 0); + DN_Assert(to_i64.success); + *value = to_i64.value; + } else { + DN_Str8BuilderAppendF(&pack->write_builder, "%s%I64d", pack->write_column++ ? "," : "", *value); + } +} + +DN_API void DN_CSVPackI32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value) +{ + DN_I64 u64 = *value; + DN_CSVPackI64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = DN_SaturateCastI64ToI32(u64); +} + +DN_API void DN_CSVPackI16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value) +{ + DN_I64 u64 = *value; + DN_CSVPackI64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = DN_SaturateCastI64ToI16(u64); +} + +DN_API void DN_CSVPackI8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value) +{ + DN_I64 u64 = *value; + DN_CSVPackI64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = DN_SaturateCastI64ToI8(u64); +} + +DN_API void DN_CSVPackU32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value) +{ + DN_U64 u64 = *value; + DN_CSVPackU64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = DN_SaturateCastU64ToU32(u64); +} + +DN_API void DN_CSVPackU16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value) +{ + DN_U64 u64 = *value; + DN_CSVPackU64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = DN_SaturateCastU64ToU16(u64); +} + +DN_API void DN_CSVPackBoolAsU64(DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value) +{ + DN_U64 u64 = *value; + DN_CSVPackU64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = u64 ? 1 : 0; +} + +DN_API void DN_CSVPackStr8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena) +{ + if (serialise == DN_CSVSerialise_Read) { + DN_Str8 csv_value = DN_CSVTokeniserNextColumn(&pack->read_tokeniser); + *str8 = DN_Str8FromStr8Arena(csv_value, arena); + } else { + DN_Str8BuilderAppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_Str8PrintFmt(*str8)); + } +} + +DN_API void DN_CSVPackBuffer(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, DN_USize *size) +{ + if (serialise == DN_CSVSerialise_Read) { + DN_Str8 csv_value = DN_CSVTokeniserNextColumn(&pack->read_tokeniser); + *size = DN_Min(*size, csv_value.size); + DN_Memcpy(dest, csv_value.data, *size); + } else { + DN_Str8BuilderAppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_Cast(int)(*size), DN_Cast(char *)dest); + } +} + +DN_API void DN_CSVPackBufferWithMax(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, DN_USize *size, DN_USize max) +{ + if (serialise == DN_CSVSerialise_Read) + *size = max; + DN_CSVPackBuffer(pack, serialise, dest, size); +} + +DN_API bool DN_CSVPackNewLine(DN_CSVPack *pack, DN_CSVSerialise serialise) +{ + bool result = true; + if (serialise == DN_CSVSerialise_Read) { + result = DN_CSVTokeniserNextRow(&pack->read_tokeniser); + } else { + pack->write_column = 0; + result = DN_Str8BuilderAppendRef(&pack->write_builder, DN_Str8Lit("\n")); + } + return result; +} DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, bool leak_permitted) { @@ -9169,27 +9906,122 @@ DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip(DN_OSThreadLane lane) return result; } -// NOTE: DN_OSHttp -DN_API void DN_OS_HttpRequestWait(DN_OSHttpResponse *response) +static DN_I32 DN_OS_AsyncThreadEntryPoint_(DN_OSThread *thread) { - if (response && response->on_complete_semaphore.handle != 0) - DN_OS_SemaphoreWait(&response->on_complete_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT); + DN_OS_ThreadSetNameFmt("%.*s", DN_Str8PrintFmt(thread->name)); + DN_OSAsyncCore *async = DN_Cast(DN_OSAsyncCore *) thread->user_context; + DN_Ring *ring = &async->ring; + for (;;) { + DN_OS_SemaphoreWait(&async->worker_sem, UINT32_MAX); + if (async->join_threads) + break; + + DN_OSAsyncTask task = {}; + for (DN_OS_MutexScope(&async->ring_mutex)) { + if (DN_RingHasData(ring, sizeof(task))) + DN_RingRead(ring, &task, sizeof(task)); + } + + if (task.work.func) { + DN_OS_ConditionVariableBroadcast(&async->ring_write_cv); // Resume any blocked ring write(s) + + DN_OSAsyncWorkArgs args = {}; + args.input = task.work.input; + args.thread = thread; + + DN_AtomicAddU32(&async->busy_threads, 1); + task.work.func(args); + DN_AtomicSubU32(&async->busy_threads, 1); + + if (task.completion_sem.handle != 0) + DN_OS_SemaphoreIncrement(&task.completion_sem, 1); + } + } + + return 0; } -DN_API DN_OSHttpResponse DN_OS_HttpRequest(DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers) +DN_API void DN_OS_AsyncInit(DN_OSAsyncCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size) { - // TODO(doyle): Revise the memory allocation and its lifetime - DN_OSHttpResponse result = {}; - DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); - result.scratch_arena = scratch.arena; + DN_Assert(async); + async->ring.size = base_size; + async->ring.base = base; + async->ring_mutex = DN_OS_MutexInit(); + async->ring_write_cv = DN_OS_ConditionVariableInit(); + async->worker_sem = DN_OS_SemaphoreInit(0); + async->thread_count = threads_size; + async->threads = threads; + for (DN_ForIndexU(index, async->thread_count)) { + DN_OSThread *thread = async->threads + index; + DN_OS_ThreadInit(thread, DN_OS_AsyncThreadEntryPoint_, /*lane=*/ nullptr, DN_TCInitArgsDefault(), async); + } +} + +DN_API void DN_OS_AsyncDeinit(DN_OSAsyncCore *async) +{ + DN_Assert(async); + DN_AtomicSetValue32(&async->join_threads, true); + DN_OS_SemaphoreIncrement(&async->worker_sem, async->thread_count); + for (DN_ForItSize(it, DN_OSThread, async->threads, async->thread_count)) + DN_OS_ThreadJoin(it.data, DN_TCDeinitArenas_Yes); +} + +static bool DN_OS_AsyncQueueTask_(DN_OSAsyncCore *async, DN_OSAsyncTask const *task, DN_U64 wait_time_ms) { + DN_U64 end_time_ms = DN_OS_DateUnixTimeMs() + wait_time_ms; + bool result = false; + for (DN_OS_MutexScope(&async->ring_mutex)) { + for (;;) { + if (DN_RingHasSpace(&async->ring, sizeof(*task))) { + DN_RingWriteStruct(&async->ring, task); + result = true; + break; + } + DN_OS_ConditionVariableWaitUntil(&async->ring_write_cv, &async->ring_mutex, end_time_ms); + if (DN_OS_DateUnixTimeMs() >= end_time_ms) + break; + } + } + + if (result) + DN_OS_SemaphoreIncrement(&async->worker_sem, 1); // Flag that a job is available - DN_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers); - DN_OS_HttpRequestWait(&result); - DN_TCScratchEnd(&scratch); return result; } -// NOTE: DN_OSPrint +DN_API bool DN_OS_AsyncQueueWork(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms) +{ + DN_OSAsyncTask task = {}; + task.work.func = func; + task.work.input = input; + bool result = DN_OS_AsyncQueueTask_(async, &task, wait_time_ms); + return result; +} + +DN_API DN_OSAsyncTask DN_OS_AsyncQueueTask(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms) +{ + DN_OSAsyncTask result = {}; + result.work.func = func; + result.work.input = input; + result.completion_sem = DN_OS_SemaphoreInit(0); + result.queued = DN_OS_AsyncQueueTask_(async, &result, wait_time_ms); + if (!result.queued) + DN_OS_SemaphoreDeinit(&result.completion_sem); + return result; +} + +DN_API bool DN_OS_AsyncWaitTask(DN_OSAsyncTask *task, DN_U32 timeout_ms) +{ + bool result = true; + if (!task->queued) + return result; + + DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&task->completion_sem, timeout_ms); + result = wait == DN_OSSemaphoreWaitResult_Success; + if (result) + DN_OS_SemaphoreDeinit(&task->completion_sem); + return result; +} + DN_API DN_LogStyle DN_OS_PrintStyleColour(uint8_t r, uint8_t g, uint8_t b, DN_LogBold bold) { DN_LogStyle result = {}; @@ -9363,173 +10195,6 @@ DN_API void DN_OS_PrintLnFVStyle(DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ DN_OS_Print(dest, DN_Str8Lit("\n")); } -// NOTE: DN_VArray -template -DN_VArray DN_OS_VArrayInitByteSize(DN_USize byte_size) -{ - DN_VArray result = {}; - result.data = DN_Cast(T *) DN_OS_MemReserve(byte_size, DN_MemCommit_No, DN_MemPage_ReadWrite); - if (result.data) - result.max = byte_size / sizeof(T); - return result; -} - -template -DN_VArray DN_OS_VArrayInit(DN_USize max) -{ - DN_VArray result = DN_OS_VArrayInitByteSize(max * sizeof(T)); - DN_Assert(result.max >= max); - return result; -} - -template -DN_VArray DN_OS_VArrayInitCArray(T const (&items)[N], DN_USize max) -{ - DN_USize real_max = DN_Max(N, max); - DN_VArray result = DN_OS_VArrayInit(real_max); - if (DN_OS_VArrayIsValid(&result)) - DN_OS_VArrayAddArray(&result, items, N); - return result; -} - -template -void DN_OS_VArrayDeinit(DN_VArray *array) -{ - DN_OS_MemRelease(array->data, array->max * sizeof(T)); - *array = {}; -} - -template -bool DN_OS_VArrayIsValid(DN_VArray const *array) -{ - bool result = array->data && array->size <= array->max; - return result; -} - -template -T *DN_OS_VArrayAddArray(DN_VArray *array, T const *items, DN_USize count) -{ - T *result = DN_OS_VArrayMakeArray(array, count, DN_ZMem_No); - if (result) - DN_Memcpy(result, items, count * sizeof(T)); - return result; -} - -template -T *DN_OS_VArrayAddCArray(DN_VArray *array, T const (&items)[N]) -{ - T *result = DN_OS_VArrayAddArray(array, items, N); - return result; -} - -template -T *DN_OS_VArrayAdd(DN_VArray *array, T const &item) -{ - T *result = DN_OS_VArrayAddArray(array, &item, 1); - return result; -} - -template -T *DN_OS_VArrayMakeArray(DN_VArray *array, DN_USize count, DN_ZMem z_mem) -{ - if (!DN_OS_VArrayIsValid(array)) - return nullptr; - - if (!DN_CheckF((array->size + count) < array->max, "Array is out of space (user requested +%zu items, array has %zu/%zu items)", count, array->size, array->max)) - return nullptr; - - if (!DN_OS_VArrayReserve(array, count)) - return nullptr; - - // TODO: Use placement new - T *result = array->data + array->size; - array->size += count; - if (z_mem == DN_ZMem_Yes) - DN_Memset(result, 0, count * sizeof(T)); - return result; -} - -template -T *DN_OS_VArrayMake(DN_VArray *array, DN_ZMem z_mem) -{ - T *result = DN_OS_VArrayMakeArray(array, 1, z_mem); - return result; -} - -template -T *DN_OS_VArrayInsertArray(DN_VArray *array, DN_USize index, T const *items, DN_USize count) -{ - T *result = nullptr; - if (!DN_OS_VArrayIsValid(array)) - return result; - if (DN_OS_VArrayReserve(array, array->size + count)) - result = DN_ArrayInsertArray(array->data, &array->size, array->max, index, items, count); - return result; -} - -template -T *DN_OS_VArrayInsertCArray(DN_VArray *array, DN_USize index, T const (&items)[N]) -{ - T *result = DN_OS_VArrayInsertArray(array, index, items, N); - return result; -} - -template -T *DN_OS_VArrayInsert(DN_VArray *array, DN_USize index, T const &item) -{ - T *result = DN_OS_VArrayInsertArray(array, index, &item, 1); - return result; -} - -template -T *DN_OS_VArrayPopFront(DN_VArray *array, DN_USize count) -{ - T *result = DN_Cast(T *)DN_ArrayPopFront(array->data, &array->size, sizeof(T), count); - return result; -} - -template -T *DN_OS_VArrayPopBack(DN_VArray *array, DN_USize count) -{ - T *result = DN_Cast(T *)DN_ArrayPopBack(array->data, &array->size, sizeof(T), count); - return result; -} - -template -DN_ArrayEraseResult DN_OS_VArrayEraseRange(DN_VArray *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase) -{ - DN_ArrayEraseResult result = {}; - if (!DN_OS_VArrayIsValid(array)) - return result; - result = DN_ArrayEraseRange(array->data, &array->size, sizeof(T), begin_index, count, erase); - return result; -} - -template -void DN_OS_VArrayClear(DN_VArray *array, DN_ZMem z_mem) -{ - if (array) { - if (z_mem == DN_ZMem_Yes) - DN_Memset(array->data, 0, array->size * sizeof(T)); - array->size = 0; - } -} - -template -bool DN_OS_VArrayReserve(DN_VArray *array, DN_USize count) -{ - if (!DN_OS_VArrayIsValid(array) || count == 0) - return false; - - DN_USize real_commit = (array->size + count) * sizeof(T); - DN_USize aligned_commit = DN_AlignUpPowerOfTwo(real_commit, DN_Get()->os.page_size); - if (array->commit >= aligned_commit) - return true; - bool result = DN_OS_MemCommit(array->data, aligned_commit, DN_MemPage_ReadWrite); - array->commit = aligned_commit; - return result; -} - DN_API DN_StackTrace DN_StackTraceFromAllocator(DN_Allocator allocator, DN_U16 limit) { DN_StackTrace result = {}; @@ -11218,157 +11883,6 @@ DN_API DN_OSPosixProcSelfStatus DN_OS_PosixProcSelfStatus() DN_OS_FileClose(&file); return result; } - -// NOTE: DN_OSHttp ///////////////////////////////////////////////////////////////////////////////// -#if 0 // TODO(doyle): Implement websockets for Windows and Emscripten -static EM_BOOL EMWebSocketOnOpenCallback(int type, const EmscriptenWebSocketOpenEvent *event, void *user_context) -{ - (void)user_context; - (void)type; - (void)event; - // EMSCRIPTEN_RESULT result = emscripten_websocket_send_utf8_text(event->socket, R"({"jsonrpc":"2.0","id":1,"method": "eth_subscribe","params":["newHeads"]})"); - // if (result) - // DN_LogInfoF("Failed to emscripten_websocket_send_utf8_text(): %d\n", result); - return EM_TRUE; -} - -static EM_BOOL EMWebSocketOnMsgCallback(int type, const EmscriptenWebSocketMessageEvent *event __attribute__((nonnull)), void *user_context) -{ - (void)type; - (void)user_context; - (void)event; - if (event->isText) { - DN_LogInfoF("Received: %.*s", event->numBytes, event->data); - } else { - DN_LogInfoF("Received: %d bytes", event->numBytes); - } - return EM_TRUE; -} - -static EM_BOOL EMWebSocketOnErrorCallback(int type, const EmscriptenWebSocketErrorEvent *event, void *user_context) -{ - (void)user_context; - (void)type; - (void)event; - return EM_TRUE; -} - -static EM_BOOL EMWebSocketOnCloseCallback(int type, const EmscriptenWebSocketCloseEvent *event, void *user_context) -{ - (void)user_context; - (void)type; - (void)event; - return EM_TRUE; -} -#endif - -#if defined(DN_PLATFORM_EMSCRIPTEN) -static void DN_OS_HttpRequestEMFetchOnSuccessCallback(emscripten_fetch_t *fetch) -{ - DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData; - if (!DN_Check(response)) - return; - - response->http_status = DN_Cast(DN_U32) fetch->status; - response->body = DN_Str8AllocArena(fetch->numBytes, DN_ZMem_No, response->arena); - if (response->body.data) - DN_Memcpy(response->body.data, fetch->data, fetch->numBytes); - - DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1); - DN_AtomicAddU32(&response->done, 1); -} - -static void DN_OS_HttpRequestEMFetchOnErrorCallback(emscripten_fetch_t *fetch) -{ - DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData; - if (!DN_Check(response)) - return; - - response->http_status = DN_Cast(DN_U32) fetch->status; - response->body = DN_Str8AllocArena(fetch->numBytes, DN_ZMem_No, response->arena); - if (response->body.size) - DN_Memcpy(response->body.data, fetch->data, fetch->numBytes); - - DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1); - DN_AtomicAddU32(&response->done, 1); -} -#endif - -DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response, - DN_Arena *arena, - DN_Str8 host, - DN_Str8 path, - DN_OSHttpRequestSecure secure, - DN_Str8 method, - DN_Str8 body, - DN_Str8 headers) -{ - if (!response || !arena) - return; - - response->arena = arena; - response->builder.arena = response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena; - - DN_Arena *scratch = &response->scratch_arena; - DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1); - DN_DEFER { DN_TCScratchEnd(&scratch_); }; - if (!scratch) - scratch = &scratch_.arena; - -#if defined(DN_PLATFORM_EMSCRIPTEN) - emscripten_fetch_attr_t fetch_attribs = {}; - emscripten_fetch_attr_init(&fetch_attribs); - - if (method.size >= sizeof(fetch_attribs.requestMethod)) { - response->error_msg = - DN_Str8FromFmtArena(arena, - "Request method in EM has a size limit of 31 characters, method was " - "'%.*s' which is %zu characters long", - DN_Str8PrintFmt(method), - method.size); - DN_CheckF(method.size < sizeof(fetch_attribs.requestMethod), - "%.*s", - DN_Str8PrintFmt(response->error_msg)); - response->error_code = DN_Cast(DN_U32) - 1; - DN_AtomicAddU32(&response->done, 1); - return; - } - - DN_Memcpy(fetch_attribs.requestMethod, method.data, method.size); - - fetch_attribs.requestData = body.data; - fetch_attribs.requestDataSize = body.size; - fetch_attribs.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; - fetch_attribs.onsuccess = DN_OS_HttpRequestEMFetchOnSuccessCallback; - fetch_attribs.onerror = DN_OS_HttpRequestEMFetchOnErrorCallback; - fetch_attribs.userData = response; - - DN_Str8 url = DN_Str8FromFmtArena(scratch, "%.*s%.*s", DN_Str8PrintFmt(host), DN_Str8PrintFmt(path)); - DN_LogInfoF("Initiating HTTP '%s' request to '%.*s' with payload '%.*s'", - fetch_attribs.requestMethod, - DN_Str8PrintFmt(url), - DN_Str8PrintFmt(body)); - response->on_complete_semaphore = DN_OS_SemaphoreInit(0); - response->em_handle = emscripten_fetch(&fetch_attribs, url.data); -#else // #elif defined(DN_OS_WIN32) - DN_InvalidCodePathF("Unimplemented function"); -#endif -} - -DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response) -{ -// NOTE: Cleanup -#if defined(DN_PLATFORM_EMSCRIPTEN) - if (response->em_handle) { - emscripten_fetch_close(response->em_handle); - response->em_handle = nullptr; - } -#endif // #elif defined(DN_OS_WIN32) - - DN_MemListDeinit(response->tmp_arena.mem); - DN_OS_SemaphoreDeinit(&response->on_complete_semaphore); - *response = {}; -} #elif defined(DN_PLATFORM_WIN32) // DN: Single header generator commented out => #include "OS/dn_os_w32.cpp" #define DN_OS_W32_CPP @@ -11768,15 +12282,9 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size if (!file || !file->handle || file->error || !buffer || size <= 0) return result; - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - if (!DN_Check(size <= (unsigned long)-1)) { - DN_Str8x32 buffer_size_str8 = DN_Str8x32FromByteCountU64Auto(size); - DN_ErrSinkAppendF( - err, - 1 /*error_code*/, - "Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO", - DN_Str8PrintFmt(buffer_size_str8)); - DN_TCScratchEnd(&scratch); + if (size > ULONG_MAX) { + DN_Str8x32 desc = DN_Str8x32FromByteCountU64Auto(size); + DN_ErrSinkAppendF(err, 1 /*error_code*/, "Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO", DN_Str8PrintFmt(desc)); return result; } @@ -11787,6 +12295,7 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size /*LPDWORD lpNumberOfByesRead*/ &bytes_read, /*LPOVERLAPPED lpOverlapped*/ nullptr); if (read_result == 0) { + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); DN_ErrSinkAppendF(err, win_error.code, "Failed to read data from file: (%u) %.*s", win_error.code, DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); @@ -11794,6 +12303,7 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size } if (bytes_read != size) { + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); DN_ErrSinkAppendF( err, @@ -11809,7 +12319,6 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size result.bytes_read = bytes_read; result.success = true; - DN_TCScratchEnd(&scratch); return result; } @@ -12105,7 +12614,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle, DN_ErrSinkAppendF(err, result.os_error_code, "Executed command failed to terminate: %.*s", DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; - } else if (DN_Check(exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0)) { + } else if (exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0) { // NOTE: Read stdout from process // If the pipes are full, the process will block. We periodically // flush the pipes to make sure this doesn't happen @@ -12714,204 +13223,6 @@ DN_API void DN_OS_W32ThreadSetName(DN_Str8 name) DN_TCScratchEnd(&scratch); } -void DN_OS_HttpRequestWin32Callback(HINTERNET session, DWORD *dwContext, DWORD dwInternetStatus, VOID *lpvStatusInformation, DWORD dwStatusInformationLength) -{ - (void)session; - (void)dwStatusInformationLength; - - DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) dwContext; - HINTERNET request = DN_Cast(HINTERNET) response->w32_request_handle; - DN_OSW32Error error = {}; - DWORD const READ_BUFFER_SIZE = DN_Megabytes(1); - - if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESOLVING_NAME) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_NAME_RESOLVED) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDING_REQUEST) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_SENT) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CREATED) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DETECTING_PROXY) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REDIRECT) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE) { - DWORD status = 0; - DWORD status_size = sizeof(status_size); - if (WinHttpQueryHeaders(request, - WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, - WINHTTP_HEADER_NAME_BY_INDEX, - &status, - &status_size, - WINHTTP_NO_HEADER_INDEX)) { - response->http_status = DN_Cast(uint16_t) status; - - // NOTE: You can normally call into WinHttpQueryDataAvailable which means the kernel - // will buffer the response into a single buffer and return us the full size of the - // request. - // - // or - // - // You may call WinHttpReadData directly to write the memory into our buffer directly. - // This is advantageous to avoid a copy from the kernel buffer into our buffer. If the - // end user application knows the typical payload size then they can optimise for this - // to prevent unnecessary allocation on the user side. - void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No); - if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr)) - error = DN_OS_W32LastError(&response->tmp_arena); - } else { - error = DN_OS_W32LastError(&response->tmp_arena); - } - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE) { - DWORD bytes_read = dwStatusInformationLength; - if (bytes_read) { - DN_Str8 prev_buffer = DN_Str8FromPtr(DN_Cast(char *) lpvStatusInformation, bytes_read); - DN_Str8BuilderAppendRef(&response->builder, prev_buffer); - - void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No); - if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr)) - error = DN_OS_W32LastError(&response->tmp_arena); - } - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR) { - WINHTTP_ASYNC_RESULT *async_result = DN_Cast(WINHTTP_ASYNC_RESULT *) lpvStatusInformation; - error = DN_OS_W32ErrorCodeToMsg(&response->tmp_arena, DN_Cast(DN_U32) async_result->dwError); - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE) { - if (!WinHttpReceiveResponse(request, 0)) - error = DN_OS_W32LastError(&response->tmp_arena); - } - - // NOTE: If the request handle is missing, then, the response has been freed. - // MSDN says that this callback can still be called after closing the handle - // and trigger the WINHTTP_CALLBACK_STATUS_REQUEST_ERROR. - if (request) { - bool read_complete = dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE && dwStatusInformationLength == 0; - if (read_complete) - response->body = DN_Str8FromStr8BuilderArena(&response->builder, response->arena); - - if (read_complete || dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR || error.code) { - DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1); - DN_AtomicAddU32(&response->done, 1); - } - - if (error.code) { - response->error_code = error.code; - response->error_msg = error.msg; - } - } -} - -DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response, - DN_Arena *arena, - DN_Str8 host, - DN_Str8 path, - DN_OSHttpRequestSecure secure, - DN_Str8 method, - DN_Str8 body, - DN_Str8 headers) -{ - if (!response || !arena) - return; - - response->arena = arena; - response->builder = DN_Str8BuilderFromArena(response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena); - - DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1); - if (!response->scratch_arena.mem) - response->scratch_arena = scratch_.arena; - - DN_OSW32Error error = {}; - DN_DEFER - { - response->error_msg = error.msg; - response->error_code = error.code; - if (error.code) { - // NOTE: 'Wait' handles failures gracefully, skipping the wait and - // cleans up the request - DN_OS_HttpRequestWait(response); - DN_AtomicAddU32(&response->done, 1); - } - DN_TCScratchEnd(&scratch_); - }; - - response->w32_request_session = WinHttpOpen(nullptr /*user agent*/, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC); - if (!response->w32_request_session) { - error = DN_OS_W32LastError(&response->tmp_arena); - return; - } - - DWORD callback_flags = WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE | - WINHTTP_CALLBACK_STATUS_READ_COMPLETE | - WINHTTP_CALLBACK_STATUS_REQUEST_ERROR | - WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE; - if (WinHttpSetStatusCallback(response->w32_request_session, - DN_Cast(WINHTTP_STATUS_CALLBACK) DN_OS_HttpRequestWin32Callback, - callback_flags, - DN_Cast(DWORD_PTR) nullptr /*dwReserved*/) == WINHTTP_INVALID_STATUS_CALLBACK) { - error = DN_OS_W32LastError(&response->tmp_arena); - return; - } - - DN_Str16 host16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, host); - response->w32_request_connection = WinHttpConnect(response->w32_request_session, host16.data, secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0 /*reserved*/); - if (!response->w32_request_connection) { - error = DN_OS_W32LastError(&response->tmp_arena); - return; - } - - DN_Str16 method16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, method); - DN_Str16 path16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, path); - response->w32_request_handle = WinHttpOpenRequest(response->w32_request_connection, - method16.data, - path16.data, - nullptr /*version*/, - nullptr /*referrer*/, - nullptr /*accept types*/, - secure ? WINHTTP_FLAG_SECURE : 0); - if (!response->w32_request_handle) { - error = DN_OS_W32LastError(&response->tmp_arena); - return; - } - - DN_Str16 headers16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, headers); - response->on_complete_semaphore = DN_OS_SemaphoreInit(0); - if (!WinHttpSendRequest(response->w32_request_handle, - headers16.data, - DN_Cast(DWORD) headers16.size, - body.data /*optional data*/, - DN_Cast(DWORD) body.size /*optional length*/, - DN_Cast(DWORD) body.size /*total content length*/, - DN_Cast(DWORD_PTR) response)) { - error = DN_OS_W32LastError(&response->tmp_arena); - return; - } -} - -DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response) -{ - // NOTE: Cleanup - // NOTE: These calls are synchronous even when the HTTP request is async. - WinHttpCloseHandle(response->w32_request_handle); - WinHttpCloseHandle(response->w32_request_connection); - WinHttpCloseHandle(response->w32_request_session); - - response->w32_request_session = nullptr; - response->w32_request_connection = nullptr; - response->w32_request_handle = nullptr; - DN_MemListDeinit(response->tmp_arena.mem); - DN_OS_SemaphoreDeinit(&response->on_complete_semaphore); - - *response = {}; -} - -// NOTE: DN_OS_W32 DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code) { DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; @@ -13011,7 +13322,8 @@ DN_API DN_Str16 DN_OS_W32Str8ToStr16(DN_Arena *arena, DN_Str8 src) return result; int chars_written = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, buffer, required_size); - if (DN_Check(chars_written == required_size)) { + DN_Assert(chars_written == required_size); + if (chars_written == required_size) { result.data = buffer; result.size = chars_written; result.data[result.size] = 0; @@ -13073,7 +13385,8 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8(DN_Arena *arena, DN_Str16 src) DN_Str8 buffer = DN_Str8AllocArena(required_size, DN_ZMem_No, &temp); if (buffer.size) { int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr); - if (DN_Check(chars_written == required_size)) { + DN_Assert(chars_written == required_size); + if (chars_written == required_size) { result = buffer; result.data[result.size] = 0; } @@ -13103,7 +13416,8 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap(DN_Str16 src) return result; int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr); - if (DN_Check(chars_written == required_size)) { + DN_Assert(chars_written == required_size); + if (chars_written == required_size) { result = buffer; result.data[result.size] = 0; } else { @@ -13470,291 +13784,6 @@ DN_API void DN_BeginFrame() #endif } -#if DN_H_WITH_HELPERS -// DN: Single header generator commented out => #include "Extra/dn_helpers.cpp" -#define DN_HELPERS_CPP - -// DN: Single header generator commented out => #if defined(_CLANGD) -// #include "dn_helpers.h" -// #endif - -DN_API DN_JSONBuilder DN_JSONBuilder_Init(DN_Arena *arena, int spaces_per_indent) -{ - DN_JSONBuilder result = {}; - result.spaces_per_indent = spaces_per_indent; - result.string_builder.arena = arena; - return result; -} - -DN_API DN_Str8 DN_JSONBuilder_Build(DN_JSONBuilder const *builder, DN_Arena *arena) -{ - DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder->string_builder, arena); - return result; -} - -DN_API void DN_JSONBuilder_KeyValue(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value) -{ - if (key.size == 0 && value.size == 0) - return; - - DN_JSONBuilderItem item = DN_JSONBuilderItem_KeyValue; - if (value.size >= 1) { - if (value.data[0] == '{' || value.data[0] == '[') - item = DN_JSONBuilderItem_OpenContainer; - else if (value.data[0] == '}' || value.data[0] == ']') - item = DN_JSONBuilderItem_CloseContainer; - } - - bool adding_to_container_with_items = - item != DN_JSONBuilderItem_CloseContainer && (builder->last_item == DN_JSONBuilderItem_KeyValue || - builder->last_item == DN_JSONBuilderItem_CloseContainer); - - uint8_t prefix_size = 0; - char prefix[2] = {0}; - if (adding_to_container_with_items) - prefix[prefix_size++] = ','; - - if (builder->last_item != DN_JSONBuilderItem_Empty) - prefix[prefix_size++] = '\n'; - - if (item == DN_JSONBuilderItem_CloseContainer) - builder->indent_level--; - - int spaces_per_indent = builder->spaces_per_indent ? builder->spaces_per_indent : 2; - int spaces = builder->indent_level * spaces_per_indent; - - if (key.size) - DN_Str8BuilderAppendF(&builder->string_builder, - "%.*s%*c\"%.*s\": %.*s", - prefix_size, - prefix, - spaces, - ' ', - DN_Str8PrintFmt(key), - DN_Str8PrintFmt(value)); - else if (spaces == 0) - DN_Str8BuilderAppendF(&builder->string_builder, "%.*s%.*s", prefix_size, prefix, DN_Str8PrintFmt(value)); - else - DN_Str8BuilderAppendF(&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DN_Str8PrintFmt(value)); - - if (item == DN_JSONBuilderItem_OpenContainer) - builder->indent_level++; - - builder->last_item = item; -} - -DN_API void DN_JSONBuilder_KeyValueFV(DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, va_list args) -{ - DN_TCScratch scratch = DN_TCScratchBeginArena(&builder->string_builder.arena, 1); - DN_Str8 value = DN_Str8FromFmtVArena(&scratch.arena, value_fmt, args); - DN_JSONBuilder_KeyValue(builder, key, value); - DN_TCScratchEnd(&scratch); -} - -DN_API void DN_JSONBuilder_KeyValueF(DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...) -{ - va_list args; - va_start(args, value_fmt); - DN_JSONBuilder_KeyValueFV(builder, key, value_fmt, args); - va_end(args); -} - -DN_API void DN_JSONBuilder_ObjectBeginNamed(DN_JSONBuilder *builder, DN_Str8 name) -{ - DN_JSONBuilder_KeyValue(builder, name, DN_Str8Lit("{")); -} - -DN_API void DN_JSONBuilder_ObjectEnd(DN_JSONBuilder *builder) -{ - DN_JSONBuilder_KeyValue(builder, DN_Str8Lit(""), DN_Str8Lit("}")); -} - -DN_API void DN_JSONBuilder_ArrayBeginNamed(DN_JSONBuilder *builder, DN_Str8 name) -{ - DN_JSONBuilder_KeyValue(builder, name, DN_Str8Lit("[")); -} - -DN_API void DN_JSONBuilder_ArrayEnd(DN_JSONBuilder *builder) -{ - DN_JSONBuilder_KeyValue(builder, DN_Str8Lit(""), DN_Str8Lit("]")); -} - -DN_API void DN_JSONBuilder_Str8Named(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value) -{ - DN_JSONBuilder_KeyValueF(builder, key, "\"%.*s\"", value.size, value.data); -} - -DN_API void DN_JSONBuilder_LiteralNamed(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value) -{ - DN_JSONBuilder_KeyValueF(builder, key, "%.*s", value.size, value.data); -} - -DN_API void DN_JSONBuilder_U64Named(DN_JSONBuilder *builder, DN_Str8 key, uint64_t value) -{ - DN_JSONBuilder_KeyValueF(builder, key, "%I64u", value); -} - -DN_API void DN_JSONBuilder_I64Named(DN_JSONBuilder *builder, DN_Str8 key, int64_t value) -{ - DN_JSONBuilder_KeyValueF(builder, key, "%I64d", value); -} - -DN_API void DN_JSONBuilder_F64Named(DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places) -{ - if (!builder) - return; - - if (decimal_places >= 16) - decimal_places = 16; - - // NOTE: Generate the format string for the float, depending on how many - // decimals places it wants. - char float_fmt[16]; - if (decimal_places > 0) { - // NOTE: Emit the format string "%.f" i.e. %.1f - DN_SNPrintF(float_fmt, sizeof(float_fmt), "%%.%df", decimal_places); - } else { - // NOTE: Emit the format string "%f" - DN_SNPrintF(float_fmt, sizeof(float_fmt), "%%f"); - } - DN_JSONBuilder_KeyValueF(builder, key, float_fmt, value); -} - -DN_API void DN_JSONBuilder_BoolNamed(DN_JSONBuilder *builder, DN_Str8 key, bool value) -{ - DN_Str8 value_string = value ? DN_Str8Lit("true") : DN_Str8Lit("false"); - DN_JSONBuilder_KeyValueF(builder, key, "%.*s", value_string.size, value_string.data); -} -#endif - -#if DN_H_WITH_ASYNC -// DN: Single header generator commented out => #include "Extra/dn_async.cpp" -#define DN_ASYNC_CPP - -// DN: Single header generator commented out => #if defined(_CLANGD) -// #define DN_H_WITH_OS 1 -// #include "../dn.h" -// #include "dn_async.h" -// #endif - -static DN_I32 DN_ASYNC_ThreadEntryPoint_(DN_OSThread *thread) -{ - DN_OS_ThreadSetNameFmt("%.*s", DN_Str8PrintFmt(thread->name)); - DN_ASYNCCore *async = DN_Cast(DN_ASYNCCore *) thread->user_context; - DN_Ring *ring = &async->ring; - for (;;) { - DN_OS_SemaphoreWait(&async->worker_sem, UINT32_MAX); - if (async->join_threads) - break; - - DN_ASYNCTask task = {}; - for (DN_OS_MutexScope(&async->ring_mutex)) { - if (DN_RingHasData(ring, sizeof(task))) - DN_RingRead(ring, &task, sizeof(task)); - } - - if (task.work.func) { - DN_OS_ConditionVariableBroadcast(&async->ring_write_cv); // Resume any blocked ring write(s) - - DN_ASYNCWorkArgs args = {}; - args.input = task.work.input; - args.thread = thread; - - DN_AtomicAddU32(&async->busy_threads, 1); - task.work.func(args); - DN_AtomicSubU32(&async->busy_threads, 1); - - if (task.completion_sem.handle != 0) - DN_OS_SemaphoreIncrement(&task.completion_sem, 1); - } - } - - return 0; -} - -DN_API void DN_ASYNC_Init(DN_ASYNCCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size) -{ - DN_Assert(async); - async->ring.size = base_size; - async->ring.base = base; - async->ring_mutex = DN_OS_MutexInit(); - async->ring_write_cv = DN_OS_ConditionVariableInit(); - async->worker_sem = DN_OS_SemaphoreInit(0); - async->thread_count = threads_size; - async->threads = threads; - for (DN_ForIndexU(index, async->thread_count)) { - DN_OSThread *thread = async->threads + index; - DN_OS_ThreadInit(thread, DN_ASYNC_ThreadEntryPoint_, /*lane=*/ nullptr, DN_TCInitArgsDefault(), async); - } -} - -DN_API void DN_ASYNC_Deinit(DN_ASYNCCore *async) -{ - DN_Assert(async); - DN_AtomicSetValue32(&async->join_threads, true); - DN_OS_SemaphoreIncrement(&async->worker_sem, async->thread_count); - for (DN_ForItSize(it, DN_OSThread, async->threads, async->thread_count)) - DN_OS_ThreadJoin(it.data, DN_TCDeinitArenas_Yes); -} - -static bool DN_ASYNC_QueueTask_(DN_ASYNCCore *async, DN_ASYNCTask const *task, DN_U64 wait_time_ms) { - DN_U64 end_time_ms = DN_OS_DateUnixTimeMs() + wait_time_ms; - bool result = false; - for (DN_OS_MutexScope(&async->ring_mutex)) { - for (;;) { - if (DN_RingHasSpace(&async->ring, sizeof(*task))) { - DN_RingWriteStruct(&async->ring, task); - result = true; - break; - } - DN_OS_ConditionVariableWaitUntil(&async->ring_write_cv, &async->ring_mutex, end_time_ms); - if (DN_OS_DateUnixTimeMs() >= end_time_ms) - break; - } - } - - if (result) - DN_OS_SemaphoreIncrement(&async->worker_sem, 1); // Flag that a job is available - - return result; -} - -DN_API bool DN_ASYNC_QueueWork(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms) -{ - DN_ASYNCTask task = {}; - task.work.func = func; - task.work.input = input; - bool result = DN_ASYNC_QueueTask_(async, &task, wait_time_ms); - return result; -} - -DN_API DN_ASYNCTask DN_ASYNC_QueueTask(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms) -{ - DN_ASYNCTask result = {}; - result.work.func = func; - result.work.input = input; - result.completion_sem = DN_OS_SemaphoreInit(0); - result.queued = DN_ASYNC_QueueTask_(async, &result, wait_time_ms); - if (!result.queued) - DN_OS_SemaphoreDeinit(&result.completion_sem); - return result; -} - -DN_API bool DN_ASYNC_WaitTask(DN_ASYNCTask *task, DN_U32 timeout_ms) -{ - bool result = true; - if (!task->queued) - return result; - - DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&task->completion_sem, timeout_ms); - result = wait == DN_OSSemaphoreWaitResult_Success; - if (result) - DN_OS_SemaphoreDeinit(&task->completion_sem); - return result; -} - -#endif - #if DN_H_WITH_NET // DN: Single header generator commented out => #include "Extra/dn_net.cpp" #define DN_NET_CURL_CPP @@ -15235,90 +15264,6 @@ static DN_UTCore DN_TST_BaseArray() return result; } -static DN_UTCore DN_TST_BaseVArray() -{ - DN_UTCore result = DN_UT_Init(); - DN_UT_LogF(&result, "DN_VArray\n"); - { - { - DN_VArray array = DN_OS_VArrayInitByteSize(DN_Kilobytes(64)); - DN_DEFER - { - DN_OS_VArrayDeinit(&array); - }; - - for (DN_UT_Test(&result, "Test adding an array of items to the array")) { - DN_U32 array_literal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - DN_OS_VArrayAddArray(&array, array_literal, DN_ArrayCountU(array_literal)); - DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal)); - DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0); - } - - for (DN_UT_Test(&result, "Test adding an array of items")) { - DN_U32 array_literal[] = {0, 1, 2, 3}; - DN_OS_VArrayAddArray(&array, array_literal, DN_ArrayCountU(array_literal)); - - DN_U32 expected_literal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3}; - DN_UT_Assert(&result, array.size == DN_ArrayCountU(expected_literal)); - DN_UT_Assert(&result, DN_Memcmp(array.data, expected_literal, DN_ArrayCountU(expected_literal) * sizeof(expected_literal[0])) == 0); - } - } - - for (DN_UT_Test(&result, "Array of unaligned objects are contiguously laid out in memory")) { - // NOTE: Since we allocate from a virtual memory block, each time - // we request memory from the block we can demand some alignment - // on the returned pointer from the memory block. If there's - // additional alignment done in that function then we can no - // longer access the items in the array contiguously leading to - // confusing memory "corruption" errors. - // - // This result makes sure that the unaligned objects are allocated - // from the memory block (and hence the array) contiguously - // when the size of the object is not aligned with the required - // alignment of the object. - DN_MSVC_WARNING_PUSH - DN_MSVC_WARNING_DISABLE(4324) // warning C4324: 'TestVArray::UnalignedObject': structure was padded due to alignment specifier - - struct alignas(8) UnalignedObject - { - char data[511]; - }; - - DN_MSVC_WARNING_POP - - DN_VArray array = DN_OS_VArrayInitByteSize(DN_Kilobytes(64)); - DN_DEFER - { - DN_OS_VArrayDeinit(&array); - }; - - // NOTE: Verify that the items returned from the data array are - // contiguous in memory. - UnalignedObject *make_item_a = DN_OS_VArrayMakeArray(&array, 1, DN_ZMem_Yes); - UnalignedObject *make_item_b = DN_OS_VArrayMakeArray(&array, 1, DN_ZMem_Yes); - DN_Memset(make_item_a->data, 'a', sizeof(make_item_a->data)); - DN_Memset(make_item_b->data, 'b', sizeof(make_item_b->data)); - DN_UT_Assert(&result, (uintptr_t)make_item_b == (uintptr_t)(make_item_a + 1)); - - // NOTE: Verify that accessing the items from the data array yield - // the same object. - DN_UT_Assert(&result, array.size == 2); - UnalignedObject *data_item_a = array.data + 0; - UnalignedObject *data_item_b = array.data + 1; - DN_UT_Assert(&result, (uintptr_t)data_item_b == (uintptr_t)(data_item_a + 1)); - DN_UT_Assert(&result, (uintptr_t)data_item_b == (uintptr_t)(make_item_a + 1)); - DN_UT_Assert(&result, (uintptr_t)data_item_b == (uintptr_t)make_item_b); - - for (DN_USize i = 0; i < sizeof(data_item_a->data); i++) - DN_UT_Assert(&result, data_item_a->data[i] == 'a'); - - for (DN_USize i = 0; i < sizeof(data_item_b->data); i++) - DN_UT_Assert(&result, data_item_b->data[i] == 'b'); - } - } - return result; -} - #if defined(DN_UNIT_TESTS_WITH_KECCAK) DN_GCC_WARNING_PUSH DN_GCC_WARNING_DISABLE(-Wunused-parameter) @@ -17075,7 +17020,6 @@ DN_TSTResult DN_TST_RunSuite(DN_TSTPrint print) DN_TST_BaseDSMap(), DN_TST_BaseIArray(), DN_TST_BaseArray(), - DN_TST_BaseVArray(), DN_TST_Keccak(), DN_TST_M4(), DN_TST_OS(), @@ -17111,1715 +17055,4 @@ DN_TSTResult DN_TST_RunSuite(DN_TSTPrint print) return result; } -#endif - -#if DN_CPP_WITH_DEMO -// DN: Single header generator commented out => #include "Extra/dn_demo.cpp" -// DN: Single header generator commented out => #if defined(_CLANGD) -// #include "../dn.h" -// #endif - -DN_MSVC_WARNING_PUSH -DN_MSVC_WARNING_DISABLE(4702) // unreachable code -void DN_Demo() -{ -// NOTE: Before using anything in the library, DN_Core_Init() must be -// called, for example: -#if 0 - DN_Core core = {}; - DN_Core_Init(&core, DN_CoreOnInit_Nil); -#endif - - // NOTE: DN_AtomicSetValue64 - // NOTE: DN_AtomicSetValue32 - // Atomically set the value into the target using an atomic compare and swap - // idiom. The return value of the function is the value that was last stored - // in the target. - { - uint64_t target = 8; - uint64_t value_to_set = 0xCAFE; - if (DN_AtomicSetValue64(&target, value_to_set) == 8) { - // Atomic swap was successful, e.g. the last value that this thread - // observed was '8' which is the value we initialised with e.g. no - // other thread has modified the value. - } - } - - // NOTE: DN_HexFromBytes - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - unsigned char bytes[2] = {0xFA, 0xCE}; - DN_Str8 hex = DN_HexFromPtrBytesArena(bytes, sizeof(bytes), &scratch.arena, DN_TrimLeadingZero_No); - DN_Assert(DN_Str8Eq(hex, DN_Str8Lit("face"))); // NOTE: Guaranteed to be null-terminated - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_BytesFromHex - { - unsigned char bytes[2]; - DN_USize bytes_written = DN_BytesFromHex(DN_Str8Lit("0xFACE"), bytes, sizeof(bytes)); - DN_Assert(bytes_written == 2); - DN_Assert(bytes[0] == 0xFA); - DN_Assert(bytes[1] == 0xCE); - } - - // NOTE: DN_Check - // - // Check the expression trapping in debug, whilst in release- trapping is - // removed and the expression is evaluated as if it were a normal 'if' branch. - // - // This allows handling of the condition gracefully when compiled out but - // traps to notify the developer in builds when it's compiled in. - { - bool flag = true; - if (DN_CheckF(flag, "Flag was false!")) { - /// This branch will execute! - } - } - - // NOTE: DN_CPUID - // Execute the 'CPUID' instruction which lets you query the capabilities of - // the current CPU. - - // NOTE: DN_DEFER - // - // A macro that expands to a C++ lambda that executes arbitrary code on - // scope exit. - { - int x = 0; - DN_DEFER - { - x = 3; - }; - x = 1; - // On scope exit, DN_DEFER object executes and assigns x = 3 - } - - // NOTE: DN_DSMap - // - // A hash table configured using the presets recommended by Demitri Spanos - // from the Handmade Network (HMN), - // - // - power of two capacity - // - grow by 2x on load >= 75% - // - open-addressing with linear probing - // - separate large values (esp. variable length values) into a separate table - // - use a well-known hash function: MurmurHash3 (or xxhash, city, spooky ...) - // - chain-repair on delete (rehash items in the probe chain after delete) - // - shrink by 1/2 on load < 25% (suggested by Martins Mmozeiko of HMN) - // - // Source: discord.com/channels/239737791225790464/600063880533770251/941835678424129597 - // - // This hash-table stores slots (values) separate from the hash mapping. - // Hashes are mapped to slots using the hash-to-slot array which is an array - // of slot indexes. This array intentionally only stores indexes to maximise - // usage of the cache line. Linear probing on collision will only cost a - // couple of cycles to fetch from L1 cache the next slot index to attempt. - // - // The slots array stores values contiguously, non-sorted allowing iteration - // of the map. On element erase, the last element is swapped into the - // deleted element causing the non-sorted property of this table. - // - // The 0th slot (DN_DS_MAP_SENTINEL_SLOT) in the slots array is reserved - // for a sentinel value, e.g. all zeros value. After map initialisation the - // 'occupied' value of the array will be set to 1 to exclude the sentinel - // from the capacity of the table. Skip the first value if you are iterating - // the hash table! - // - // This hash-table accept either a U64 or a buffer (ptr + len) as the key. - // In practice this covers a majority of use cases (with string, buffer and - // number keys). It also allows us to minimise our C++ templates to only - // require 1 variable which is the Value part of the hash-table simplifying - // interface complexity and cruft brought by C++. - // - // Keys are value-copied into the hash-table. If the key uses a pointer to a - // buffer, this buffer must be valid throughout the lifetime of the hash - // table! - { - // NOTE: DN_DSMapInit - // NOTE: DN_DSMapDeinit - // - // Initialise a hash table where the table size *must* be a - // power-of-two, otherwise an assert will be triggered. If - // initialisation fails (e.g. memory allocation failure) the table is - // returned zero-initialised where a call to 'IsValid' will return - // false. - // - // The map takes ownership of the arena. This means in practice that if the - // map needs to resize (e.g. because the load threshold of the table is - // exceeded), the arena associated with it will be released and the memory - // will be reallocated with the larger capacity and reassigned to the arena. - // - // In simple terms, when the map resizes it invalidates all memory that was - // previously allocated with the given arena! - // - // A 'Deinit' of the map will similarly deallocate the passed in arena (as - // the map takes ownership of the arena). - DN_Arena arena = DN_ArenaFromVMem(0, 0, DN_ArenaFlags_Nil); - DN_DSMap map = DN_DSMapInit(&arena, /*size*/ 1024, DN_DSMapFlags_Nil); // Size must be PoT! - DN_Assert(DN_DSMapIsValid(&map)); // Valid if no initialisation failure (e.g. mem alloc failure) - - // NOTE: DN_DSMapKeyCStringLit - // NOTE: DN_DSMapKeyU64 - // NOTE: DN_DSMapKeyU64NoHash - // NOTE: DN_DSMapKeyBuffer - // NOTE: DN_DSMapKeyStr8 - // NOTE: DN_DSMapKeyStr8Copy - // Create a hash-table key where: - // - // KeyCStringLit: Uses a Hash(cstring literal) - // KeyU64: Uses a Hash(U64) - // KeyU64NoHash: Uses a U64 (where it's truncated to 4 bytes) - // KeyBuffer: Uses a Hash(ptr+len) slice of bytes - // KeyStr8: Uses a Hash(string) - // KeyStr8Copy: Uses a Hash(string) that is copied first using the arena - // - // Buffer-based keys memory must persist throughout lifetime of the map. - // Keys are valued copied into the map, alternatively, copy the - // key/buffer before constructing the key. - // - // You *can't* use the map's arena to allocate keys because on resize it - // will deallocate then reallocate the entire arena. - // - // KeyU64NoHash may be useful if you have a source of data that is - // already sufficiently uniformly distributed already (e.g. using 8 - // bytes taken from a SHA256 hash as the key) and the first 4 bytes - // will be used verbatim. - DN_DSMapKey key = DN_DSMapKeyStr8(&map, DN_Str8Lit("Sample Key")); - - // NOTE: DN_DSMapFind - // NOTE: DN_DSMapMake - // NOTE: DN_DSMapSet - // - // Query or commit key-value pair to the table, where: - // - // Find: does a key-lookup on the table and returns the hash table slot's value - // Make: assigns the key to the table and returns the hash table slot's value - // Set: assigns the key-value to the table and returns the hash table slot's value - // - // A find query will set 'found' to false if it does not exist. - // - // For 'Make' and 'Set', 'found' can be set to 'true' if the item already - // existed in the map prior to the call. If it's the first time the - // key-value pair is being inserted 'found' will be set to 'false'. - // - // If by adding the key-value pair to the table puts the table over 75% load, - // the table will be grown to 2x the current the size before insertion - // completes. - { - DN_DSMapResult set_result = DN_DSMapSet(&map, key, 0xCAFE); - DN_Assert(!set_result.found); // First time we are setting the key-value pair, it wasn't previously in the table - DN_Assert(map.occupied == 2); // Sentinel + new element == 2 - } - - // Iterating elements in the array, note that index '0' is the sentinel - // slot! You typically don't care about it! - for (DN_USize index = 1; index < map.occupied; index++) { - DN_DSMapSlot *it = map.slots + index; - DN_DSMapKey it_key = it->key; - int *it_value = &it->value; - DN_Assert(*it_value == 0xCAFE); - - DN_Assert(DN_Str8Eq(DN_Str8FromPtr(it_key.buffer_data, it_key.buffer_size), DN_Str8Lit("Sample Key"))); - } - - // NOTE: DN_DSMapErase - // - // Remove the key-value pair from the table. If by erasing the key-value - // pair from the table puts the table under 25% load, the table will be - // shrunk by 1/2 the current size after erasing. The table will not shrink - // below the initial size that the table was initialised as. - { - bool erased = DN_DSMapErase(&map, key); - DN_Assert(erased); - DN_Assert(map.occupied == 1); // Sentinel element - } - - DN_DSMapDeinit(&map, DN_ZMem_Yes); // Deallocates the 'arena' for us! - } - -// NOTE: DN_DSMapHash -// -// Hash the input key using the custom hash function if it's set on the map, -// otherwise uses the default hashing function (32bit Murmur3). - -// NOTE: DN_DSMapHashToSlotIndex -// -// Calculate the index into the map's 'slots' array from the given hash. - -// NOTE: DN_DSMapResize -// -// Resize the table and move all elements to the new map, note that the new -// size must be a power of two. This function wil fail on memory allocation -// failure, or the requested size is smaller than the current number of -// elements in the map to resize. - -// NOTE: DN_ErrSink -// -// Error sinks are a way of accumulating errors from API calls related or -// unrelated into 1 unified error handling pattern. The implemenation of a -// sink requires 2 fundamental design constraints on the APIs supporting -// this pattern. -// -// 1. Pipelining of errors -// Errors emitted over the course of several API calls are accumulated -// into a sink which save the error code and message of the first error -// encountered and can be checked later. -// -// 2. Error proof APIs -// Functions that produce errors must return objects/handles that are -// marked to trigger no-ops used in subsequent functions dependent on it. -// -// Consider the following example demonstrating a conventional error -// handling approach (error values by return/sentinel values) and error -// handling using error-proof and pipelining. - -// (A) Conventional error checking patterns using return/sentinel values -#if 0 - DN_OSFile *file = DN_OS_FileOpen("/path/to/file", ...); - if (file) { - if (!DN_OS_FileWrite(file, "abc")) { - // Error handling! - } - Dnq_OS_FileClose(file); - } else { - // Error handling! - } -#endif - - // (B) Error handling using pipelining and and error proof APIs. APIs that - // produce errors take in the error sink as a parameter. - if (0) { - DN_ErrSink *error = DN_TCErrSinkBegin(DN_ErrSinkMode_Nil); - DN_OSFile file = DN_OS_FileOpen(DN_Str8Lit("/path/to/file"), DN_OSFileOpen_OpenIfExist, DN_OSFileAccess_ReadWrite, error); - DN_OS_FileWrite(&file, DN_Str8Lit("abc"), error); - DN_OS_FileClose(&file); - if (DN_ErrSinkEndLogErrorF(error, "Failed to write to file")) { - // Do error handling! - } - } - - // Pipeling and error-proof APIs lets you write sequence of instructions and - // defer error checking until it is convenient or necessary. Functions are - // *guaranteed* to return an object that is usable. There are no hidden - // exceptions to be thrown. Functions may opt to still return error values - // by way of return values thereby *not* precluding the ability to check - // every API call either. - // - // Ultimately, this error handling approach gives more flexibility on the - // manner in how errors are handled with less code. - // - // Error sinks can nest begin and end statements. This will open a new scope - // whereby the current captured error pushed onto a stack and the sink will - // be populated by the first error encountered in that scope. - - if (0) { - DN_ErrSink *error = DN_TCErrSinkBegin(DN_ErrSinkMode_Nil); - DN_OSFile file = DN_OS_FileOpen(DN_Str8Lit("/path/to/file"), DN_OSFileOpen_OpenIfExist, DN_OSFileAccess_ReadWrite, error); - DN_OS_FileWrite(&file, DN_Str8Lit("abc"), error); - DN_OS_FileClose(&file); - - { - // NOTE: My error sinks are thread-local, so the returned 'error' is - // the same as the 'error' value above. - DN_TCErrSinkBegin(DN_ErrSinkMode_Nil); - DN_OS_FileWriteAll(DN_Str8Lit("/path/to/another/file"), DN_Str8Lit("123"), error); - DN_ErrSinkEndLogErrorF(error, "Failed to write to another file"); - } - - if (DN_ErrSinkEndLogErrorF(error, "Failed to write to file")) { - // Do error handling! - } - } - - // NOTE: DN_JSONBuilder_Build - // - // Convert the internal JSON buffer in the builder into a string. - - // NOTE: DN_JSONBuilder_KeyValue, DN_JSONBuilder_KeyValueF - // - // Add a JSON key value pair untyped. The value is emitted directly without - // checking the contents of value. - // - // All other functions internally call into this function which is the main - // workhorse of the builder. - - // NOTE: DN_JSON_Builder_ObjectEnd - // - // End a JSON object in the builder, generates internally a '}' string - - // NOTE: DN_JSON_Builder_ArrayEnd - // - // End a JSON array in the builder, generates internally a ']' string - - // NOTE: DN_JSONBuilder_LiteralNamed - // - // Add a named JSON key-value object whose value is directly written to - // the following '"": ' (e.g. useful for emitting the 'null' - // value) - - // NOTE: DN_JSONBuilder_U64 - // NOTE: DN_JSONBuilder_U64Named - // NOTE: DN_JSONBuilder_I64 - // NOTE: DN_JSONBuilder_I64Named - // NOTE: DN_JSONBuilder_F64 - // NOTE: DN_JSONBuilder_F64Named - // NOTE: DN_JSONBuilder_Bool - // NOTE: DN_JSONBuilder_BoolNamed - // - // Add the named JSON data type as a key-value object. The named variants - // generates internally the key-value pair, e.g. - // - // "": - // - // And the non-named version emit just the 'value' portion - - // NOTE: DN_LOGProc - // - // Function prototype of the logging interface exposed by this library. Logs - // emitted using the DN_LOG_* family of functions are routed through this - // routine. - - // NOTE: DN_FNV1A -#if 0 - { - // Using the default hash as defined by DN_FNV1A32_SEED and - // DN_FNV1A64_SEED for 32/64bit hashes respectively - uint32_t buffer1 = 0xCAFE0000; - uint32_t buffer2 = 0xDEAD0000; - { - uint64_t hash = DN_FNV1A64_Hash(&buffer1, sizeof(buffer1)); - hash = DN_FNV1A64_Iterate(&buffer2, sizeof(buffer2), hash); // Chained hashing - (void)hash; - } - - // You can use a custom seed by skipping the 'Hash' call and instead - // calling 'Iterate' immediately. - { - uint64_t custom_seed = 0xABCDEF12; - uint64_t hash = DN_FNV1A64_Iterate(&buffer1, sizeof(buffer1), custom_seed); - hash = DN_FNV1A64_Iterate(&buffer2, sizeof(buffer2), hash); - (void)hash; - } - } -#endif - - // NOTE: DN_MurmurHash3 - // MurmurHash3 was written by Austin Appleby, and is placed in the public - // domain. The author (Austin Appleby) hereby disclaims copyright to this source - // code. - // - // Note - The x86 and x64 versions do _not_ produce the same results, as the - // algorithms are optimized for their respective platforms. You can still - // compile and run any of them on any platform, but your performance with the - // non-native version will be less than optimal. - - // NOTE: DN_OS_DateUnixTime - // - // Produce the time elapsed since the unix epoch - { - uint64_t now = DN_OS_DateUnixTimeS(); - (void)now; - } - - // NOTE: DN_OS_DirIterate - // - // Iterate the files within the passed in folder - for (DN_OSDirIterator it = {}; DN_OS_PathIterateDir(DN_Str8Lit("."), &it);) { - // printf("%.*s\n", DN_Str8PrintFmt(it.file_name)); - } - - // NOTE: DN_OS_FileDelete - // - // This function can only delete files and it can *only* delete directories - // if it is empty otherwise this function fails. - - // NOTE: DN_OS_WriteAllSafe - // Writes the file at the path first by appending '.tmp' to the 'path' to - // write to. If the temporary file is written successfully then the file is - // copied into 'path', for example: - // - // path: C:/Home/my.txt - // tmp_path: C:/Home/my.txt.tmp - // - // If 'tmp_path' is written to successfuly, the file will be copied over into - // 'path'. - if (0) { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_ErrSink *error = DN_TCErrSinkBegin(DN_ErrSinkMode_Nil); - DN_OS_FileWriteAllSafe(/*path*/ DN_Str8Lit("C:/Home/my.txt"), /*buffer*/ DN_Str8Lit("Hello world"), error); - DN_ErrSinkEndLogErrorF(error, ""); - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_OS_EstimateTSCPerSecond - // - // Estimate how many timestamp count's (TSC) there are per second. TSC - // is evaluated by calling __rdtsc() or the equivalent on the platform. This - // value can be used to convert TSC durations into seconds. - // - // The 'duration_ms_to_gauge_tsc_frequency' parameter specifies how many - // milliseconds to spend measuring the TSC rate of the current machine. - // 100ms is sufficient to produce a fairly accurate result with minimal - // blocking in applications if calculated on startup.. - // - // This may return 0 if querying the CPU timestamp counter is not supported - // on the platform (e.g. __rdtsc() or __builtin_readcyclecounter() returns 0). - - // NOTE: DN_OS_EXEDir - // - // Retrieve the executable directory without the trailing '/' or ('\' for - // windows). If this fails an empty string is returned. - - // NOTE: DN_OS_PerfCounterFrequency - // - // Get the number of ticks in the performance counter per second for the - // operating system you're running on. This value can be used to calculate - // duration from OS performance counter ticks. - - // NOTE: DN_OS_Path* - // Construct paths ensuring the native OS path separators are used in the - // string. In 99% of cases you can use 'PathConvertF' which converts the - // given path in one shot ensuring native path separators in the string. - // - // path: C:\Home/My/Folder - // converted: C:/Home/My/Folder (On Unix) - // C:\Home\My\Folder (On Windows) - // - // If you need to construct a path dynamically you can use the builder-esque - // interface to build a path's step-by-step using the 'OSPath' data structure. - // With this API you can append paths piece-meal to build the path after all - // pieces are appended. - // - // You may append a singular or nested path to the builder. In the builder, - // the string is scanned and separated into path separated chunks and stored - // in the builder, e.g. these are all valid to pass into 'PathAdd', - // 'PathAddRef' ... e.t.c - // - // "path/to/your/desired/folder" is valid - // "path" is valid - // "path/to\your/desired\folder" is valid - // - // 'PathPop' removes the last appended path from the current path stored in - // the 'OSPath': - // - // path: path/to/your/desired/folder - // popped_path: path/to/your/desired - - // NOTE: DN_OS_SecureRNGBytes - // - // Generate cryptographically secure bytes - -#if 0 - // NOTE: DN_PCG32 - // - // Random number generator of the PCG family. Implementation taken from - // Martins Mmozeiko from Handmade Network. - // https://gist.github.com/mmozeiko/1561361cd4105749f80bb0b9223e9db8 - { - DN_PCG32 rng = DN_PCG32_Init(0xb917'a66c'1d9b'3bd8); - - // NOTE: DN_PCG32_Range - // - // Generate a value in the [low, high) interval - uint32_t u32_value = DN_PCG32_Range(&rng, 32, 64); - DN_Assert(u32_value >= 32 && u32_value < 64); - - // NOTE: DN_PCG32_NextF32 - // NOTE: DN_PCG32_NextF64 - // - // Generate a float/double in the [0, 1) interval - DN_F64 f64_value = DN_PCG32_NextF64(&rng); - DN_Assert(f64_value >= 0.f && f64_value < 1.f); - - // NOTE: DN_PCG32_Advance - // - // Step the random number generator by 'delta' steps - DN_PCG32_Advance(&rng, /*delta*/ 5); - } -#endif - - // NOTE: DN_Profiler - // - // A profiler based off Casey Muratori's Computer Enhance course, Performance - // Aware Programming. This profiler measures function elapsed time using the - // CPU's time stamp counter (e.g. rdtsc) providing a rough cycle count - // that can be converted into a duration. - #if defined(DN_OS_CPP) - { - enum DemoZone - { - DemoZone_MainLoop, - DemoZone_Count - }; - - #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_ProfilerTSCNowFunc *tsc_now = DN_OS_PerfCounterNow; - DN_U64 tsc_frequency = DN_OS_PerfCounterFrequency(); - #else - DN_ProfilerTSCNowFunc *tsc_now = nullptr; - DN_U64 tsc_frequency = DN_OS_EstimateTSCPerSecond(100); - #endif - - DN_ProfilerAnchor anchors[4] = {}; - DN_USize anchors_count = DN_ArrayCountU(anchors); - DN_USize anchors_per_frame = anchors_count / 2; - DN_Profiler profiler = DN_ProfilerInit(anchors, anchors_count, anchors_per_frame, tsc_now, tsc_frequency); - - for (DN_USize it = 0; it < 1; it++) { - DN_ProfilerNewFrame(&profiler); - DN_ProfilerZone zone = DN_ProfilerBeginZone(&profiler, DN_Str8Lit("Main Loop"), DemoZone_MainLoop); - DN_OS_SleepMs(100); - DN_ProfilerEndZone(zone); - DN_ProfilerFmtToStdout(&profiler); - } - } - #endif - - // NOTE: DN_Raycast_LineIntersectV2 - // Calculate the intersection point of 2 rays returning a `t` value - // which is how much along the direction of the 'ray' did the intersection - // occur. - // - // The arguments passed in do not need to be normalised for the function to - // work. - - // NOTE: DN_Safe_* - // - // Performs the arithmetic operation and uses DN_Check on the operation to - // check if it overflows. If it overflows the MAX value of the integer is - // returned in add and multiply operations, and, the minimum is returned in - // subtraction and division. - - // NOTE: DN_SaturateCast* - // - // Truncate the passed in value to the return type clamping the resulting - // value to the max value of the desired data type. It DN_Check's the - // truncation. - // - // The following sentinel values are returned when saturated, - // USize -> Int: INT_MAX - // USize -> I8: INT8_MAX - // USize -> I16: INT16_MAX - // USize -> I32: INT32_MAX - // USize -> I64: INT64_MAX - // - // U64 -> UInt: UINT_MAX - // U64 -> U8: UINT8_MAX - // U64 -> U16: UINT16_MAX - // U64 -> U32: UINT32_MAX - // - // USize -> U8: UINT8_MAX - // USize -> U16: UINT16_MAX - // USize -> U32: UINT32_MAX - // USize -> U64: UINT64_MAX - // - // ISize -> Int: INT_MIN or INT_MAX - // ISize -> I8: INT8_MIN or INT8_MAX - // ISize -> I16: INT16_MIN or INT16_MAX - // ISize -> I32: INT32_MIN or INT32_MAX - // ISize -> I64: INT64_MIN or INT64_MAX - // - // ISize -> UInt: 0 or UINT_MAX - // ISize -> U8: 0 or UINT8_MAX - // ISize -> U16: 0 or UINT16_MAX - // ISize -> U32: 0 or UINT32_MAX - // ISize -> U64: 0 or UINT64_MAX - // - // I64 -> ISize: DN_ISIZE_MIN or DN_ISIZE_MAX - // I64 -> I8: INT8_MIN or INT8_MAX - // I64 -> I16: INT16_MIN or INT16_MAX - // I64 -> I32: INT32_MIN or INT32_MAX - // - // Int -> I8: INT8_MIN or INT8_MAX - // Int -> I16: INT16_MIN or INT16_MAX - // Int -> U8: 0 or UINT8_MAX - // Int -> U16: 0 or UINT16_MAX - // Int -> U32: 0 or UINT32_MAX - // Int -> U64: 0 or UINT64_MAX - - // NOTE: DN_StackTrace - // Emit stack traces at the calling site that these functions are invoked - // from. - // - // For some applications, it may be viable to generate raw stack traces and - // store just the base addresses of the call stack from the 'Walk' - // functions. This reduces the memory overhead and required to hold onto - // stack traces and resolve the addresses on-demand when required. - // - // However if your application is loading and/or unloading shared libraries, - // on Windows it may be impossible for the application to resolve raw base - // addresses if they become invalid over time. In these applications you - // must convert the raw stack traces before the unloading occurs, and when - // loading new shared libraries, 'ReloadSymbols' must be called to ensure - // the debug APIs are aware of how to resolve the new addresses imported - // into the address space. - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - - // NOTE: DN_StackTraceFromArena - // - // Generate a stack trace as a series of addresses to the base of the - // functions on the call-stack at the current instruction pointer. The - // addresses are stored in order from the current executing function - // first to the most ancestor function last in the walk. - DN_StackTrace walk = DN_StackTraceFromArena(&scratch.arena, /*depth limit*/ 128); - - // Loop over the addresses produced in the stack trace - for (DN_StackTraceIterator it = {}; DN_StackTraceIterate(&it, &walk);) { - // NOTE: DN_StackTraceRawFrameToFrame - // - // Converts the base address into a human readable stack trace - // entry (e.g. address, line number, file and function name). - DN_StackTraceFrame frame = DN_StackTraceRawFrameToFrame(&scratch.arena, it.raw_frame); - - // You may then print out the frame like so - if (0) - printf("%.*s(%" PRIu64 "): %.*s\n", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name)); - } - - // If you load new shared-libraries into the address space it maybe - // necessary to call into 'ReloadSymbols' to ensure that the OS is able - // to resolve the new addresses. - DN_StackTraceReloadSymbols(); - - // NOTE: DN_OS_StackTraceGetFrames - // - // Helper function to create a stack trace and automatically convert the - // raw frames into human readable frames. This function effectively - // calls 'Walk' followed by 'RawFrameToFrame'. - DN_StackTraceFrameSlice frames = DN_StackTraceGetFrames(&scratch.arena, /*depth limit*/ 128); - (void)frames; - - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_Str8FromArena - // - // Allocates a string with the requested 'size'. An additional byte is - // always requested from the allocator to null-terminate the buffer. This - // allows the string to be used with C-style string APIs. - // - // The returned string's 'size' member variable does *not* include this - // additional null-terminating byte. - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_Str8 string = DN_Str8AllocArena(/*size*/ 1, DN_ZMem_Yes, &scratch.arena); - DN_Assert(string.size == 1); - DN_Assert(string.data[string.size] == 0); // It is null-terminated! - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_Str8BSplit - // - // Splits a string into 2 substrings occuring prior and after the first - // occurence of the delimiter. Neither strings include the matched - // delimiter. If no delimiter is found, the 'rhs' of the split will be - // empty. - { - DN_Str8BSplitResult dot_split = DN_Str8BSplit(/*string*/ DN_Str8Lit("abc.def.ghi"), /*delimiter*/ DN_Str8Lit(".")); - DN_Str8BSplitResult slash_split = DN_Str8BSplit(/*string*/ DN_Str8Lit("abc.def.ghi"), /*delimiter*/ DN_Str8Lit("/")); - DN_Assert(DN_Str8Eq(dot_split.lhs, DN_Str8Lit("abc")) && DN_Str8Eq(dot_split.rhs, DN_Str8Lit("def.ghi"))); - DN_Assert(DN_Str8Eq(slash_split.lhs, DN_Str8Lit("abc.def.ghi")) && DN_Str8Eq(slash_split.rhs, DN_Str8Lit(""))); - - // Loop that walks the string and produces ("abc", "def", "ghi") - for (DN_Str8 it = DN_Str8Lit("abc.def.ghi"); it.size;) { - DN_Str8BSplitResult split = DN_Str8BSplit(it, DN_Str8Lit(".")); - DN_Str8 chunk = split.lhs; // "abc", "def", ... - it = split.rhs; - (void)chunk; - } - } - - // NOTE: DN_Str8FileNameFromPath - // - // Takes a slice to the file name from a file path. The file name is - // evaluated by searching from the end of the string backwards to the first - // occurring path separator '/' or '\'. If no path separator is found, the - // original string is returned. This function preserves the file extension - // if there were any. - { - { - DN_Str8 string = DN_Str8FileNameFromPath(DN_Str8Lit("C:/Folder/item.txt")); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("item.txt"))); - } - { - // TODO(doyle): Intuitively this seems incorrect. Empty string instead? - DN_Str8 string = DN_Str8FileNameFromPath(DN_Str8Lit("C:/Folder/")); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("C:/Folder"))); - } - { - DN_Str8 string = DN_Str8FileNameFromPath(DN_Str8Lit("C:/Folder")); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("Folder"))); - } - } - - // NOTE: DN_Str8FilePathNoExtension - // - // This function preserves the original string if no extension was found. - // An extension is defined as the substring after the last '.' encountered - // in the string. - { - DN_Str8 string = DN_Str8FilePathNoExtension(DN_Str8Lit("C:/Folder/item.txt.bak")); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("C:/Folder/item.txt"))); - } - - // NOTE: DN_Str8FileNameNoExtension - // - // This function is the same as calling 'FileNameFromPath' followed by - // 'FilePathNoExtension' - { - DN_Str8 string = DN_Str8FileNameNoExtension(DN_Str8Lit("C:/Folder/item.txt.bak")); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("item.txt"))); - } - - // NOTE: DN_Str8Replace - // NOTE: DN_Str8ReplaceInsensitive - // - // Replace any matching substring 'find' with 'replace' in the passed in - // 'string'. The 'start_index' may be specified to offset which index the - // string will start doing replacements from. - // - // String replacements are not done inline and the returned string will - // always be a newly allocated copy, irrespective of if any replacements - // were done or not. - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_Str8 string = DN_Str8Replace(/*string*/ DN_Str8Lit("Foo Foo Bar"), - /*find*/ DN_Str8Lit("Foo"), - /*replace*/ DN_Str8Lit("Moo"), - /*start_index*/ 1, - /*arena*/ &scratch.arena, - /*eq_case*/ DN_Str8EqCase_Sensitive); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("Foo Moo Bar"))); - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_Str8Segment - // - // Add a delimiting 'segment_char' every 'segment_size' number of characters - // in the string. - // - // Reverse segment delimits the string counting 'segment_size' from the back - // of the string. - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_Str8 string = DN_Str8Segment(&scratch.arena, /*string*/ DN_Str8Lit("123456789"), /*segment_size*/ 3, /*segment_char*/ ','); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("123,456,789"))); - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_Str8Split - { - // Splits the string at each delimiter into substrings occuring prior and - // after until the next delimiter. - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - { - DN_Str8SplitResult splits = DN_Str8SplitArena(/*string*/ DN_Str8Lit("192.168.8.1"), - /*delimiter*/ DN_Str8Lit("."), - DN_Str8SplitFlags_ExcludeEmptyStrings, - &scratch.arena); - DN_Assert(splits.count == 4); - DN_Assert(DN_Str8Eq(splits.data[0], DN_Str8Lit("192")) && - DN_Str8Eq(splits.data[1], DN_Str8Lit("168")) && - DN_Str8Eq(splits.data[2], DN_Str8Lit("8")) && - DN_Str8Eq(splits.data[3], DN_Str8Lit("1"))); - } - - // You can include empty strings that occur when splitting by setting - // the split mode to include empty strings. - { - DN_Str8SplitResult splits = DN_Str8SplitArena(/*string*/ DN_Str8Lit("a--b"), - /*delimiter*/ DN_Str8Lit("-"), - DN_Str8SplitFlags_Nil, - &scratch.arena); - DN_Assert(splits.count == 3); - DN_Assert(DN_Str8Eq(splits.data[0], DN_Str8Lit("a")) && - DN_Str8Eq(splits.data[1], DN_Str8Lit("")) && - DN_Str8Eq(splits.data[2], DN_Str8Lit("b"))); - } - - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_I64FromStr8, DN_U64FromStr8 - // - // Convert a number represented as a string to a signed 64 bit number. - // - // The 'separator' is an optional digit separator for example, if - // 'separator' is set to ',' then '1,234' will successfully be parsed to - // '1234'. If no separator is desired, you may pass in '0' in which - // '1,234' will *not* be succesfully parsed. - // - // Real numbers are truncated. Both '-' and '+' prefixed strings are permitted, - // i.e. "+1234" -> 1234 and "-1234" -> -1234. Strings must consist entirely of - // digits, the seperator or the permitted prefixes as previously mentioned - // otherwise this function will return false, i.e. "1234 dog" will cause the - // function to return false, however, the output is greedily converted and - // will be evaluated to "1234". - // - // 'ToU64' only '+' prefix is permitted - // 'ToI64' either '+' or '-' prefix is permitted - { - { - DN_I64FromResult result = DN_I64FromStr8(DN_Str8Lit("-1,234"), /*separator*/ ','); - DN_Assert(result.success && result.value == -1234); - } - { - DN_I64FromResult result = DN_I64FromStr8(DN_Str8Lit("-1,234"), /*separator*/ 0); - DN_Assert(!result.success && result.value == 1); // 1 because it's a greedy conversion - } - } - - // NOTE: DN_Str8TrimByteOrderMark - // - // Removes a leading UTF8, UTF16 BE/LE, UTF32 BE/LE byte order mark from the - // string if it's present. - - // NOTE: DN_Str8PrintFmt - // - // Unpacks a string struct that has the fields {.data, .size} for printing a - // pointer and length style string using the printf format specifier "%.*s" - // - // printf("%.*s\n", DN_Str8PrintFmt(DN_Str8Lit("Hello world"))); - - // NOTE: DN_Str8BuilderAppendF - // NOTE: DN_Str8BuilderAppendFV - // NOTE: DN_Str8BuilderAppendRef - // NOTE: DN_Str8BuilderAppendCopy - // - // - Appends a string to the string builder as follows - // - // AppendRef: Stores the string slice by value - // AppendCopy: Stores the string slice by copy (with builder's arena) - // AppendF/V: Constructs a format string and calls 'AppendRef' - - // NOTE: DN_Str8BuilderBuild - // NOTE: DN_Str8BuilderBuildCRT - // - // Constructs the final string by merging all the appended strings into - // one merged string. - // - // The CRT variant calls into 'malloc' and the string *must* be released - // using 'free'. - - // NOTE: DN_Str8BuilderBuildSlice - // - // Constructs the final string into an array of strings (e.g. a slice) - - // NOTE: DN_TicketMutex - // - // A mutex implemented using an atomic compare and swap on tickets handed - // out for each critical section. - // - // This mutex serves ticket in order and will block all other threads until - // the tickets are returned in order. The thread with the oldest ticket that - // has not been returned has right of way to execute, all other threads will - // be blocked in an atomic compare and swap loop. block execution by going - // into an atomic - // - // When a thread is blocked by this mutex, a spinlock intrinsic '_mm_pause' is - // used to yield the CPU and reduce spinlock on the thread. This mutex is not - // ideal for long blocking operations. This mutex does not issue any syscalls - // and relies entirely on atomic instructions. - { - DN_TicketMutex mutex = {}; - DN_TicketMutex_Begin(&mutex); // Simple procedural mutual exclusion lock - DN_TicketMutex_End(&mutex); - - // NOTE: DN_TicketMutex_MakeTicket - // - // Request the next available ticket for locking from the mutex. - DN_UInt ticket = DN_TicketMutex_MakeTicket(&mutex); - - if (DN_TicketMutex_CanLock(&mutex, ticket)) { - // NOTE: DN_TicketMutex_BeginTicket - // - // Locks the mutex using the given ticket if possible. If it's not - // the next ticket to be locked the executing thread will block - // until the mutex can lock the ticket, i.e. All prior tickets are - // returned, in sequence, to the mutex. - DN_TicketMutex_BeginTicket(&mutex, ticket); - DN_TicketMutex_End(&mutex); - } - } - - // NOTE: DN_ThreadContext - // - // Each thread is assigned in their thread-local storage (TLS) scratch and - // permanent arena allocators. These can be used for allocations with a - // lifetime scoped to the lexical scope or for storing data permanently - // using the arena paradigm. - // - // TLS in this implementation is implemented using the `thread_local` C/C++ - // keyword. - // - // 99% of the time you will want DN_OS_TLSTMem...) which returns you a - // temporary arena for function lifetime allocations. On scope exit, the - // arena is cleared out. - // - // This library's paradigm revolves heavily around arenas including scratch - // arenas into child functions for temporary calculations. If an arena is - // passed into a function, this poses a problem sometimes known as - // 'arena aliasing'. - // - // If an arena aliases another arena (e.g. the arena passed in) is the same - // as the scratch arena requested in the function, we risk the scratch arena - // on scope exit deallocating memory belonging to the caller. - // - // To avoid this we the 'DN_OS_TLSTMem...)' API takes in a list of arenas - // to ensure that we provide a scratch arena that *won't* alias with the - // caller's arena. If arena aliasing occurs, with ASAN on, generally - // the library will trap and report use-after-poison once violated. - { - DN_TCScratch scratch_a = DN_TCScratchBeginArena(nullptr, 0); - - // Now imagine we call a function where we pass scratch_a.arena down - // into it .. If we call scratch again, we need to pass in the arena - // to prevent aliasing. - DN_Arena *conflicts[] = {&scratch_a.arena}; - DN_TCScratch scratch_b = DN_TCScratchBeginArena(conflicts, DN_ArrayCountU(conflicts)); - DN_Assert(scratch_a.arena.mem != scratch_b.arena.mem); - - DN_TCScratchEnd(&scratch_b); - DN_TCScratchEnd(&scratch_a); - } - - // @proc DN_Thread_Getscratch - // @desc Retrieve the per-thread temporary arena allocator that is reset on scope - // exit. - - // The scratch arena must be deconflicted with any existing arenas in the - // function to avoid trampling over each other's memory. Consider the situation - // where the scratch arena is passed into the function. Inside the function, if - // the same arena is reused then, if both arenas allocate, when the inner arena - // is reset, this will undo the passed in arena's allocations in the function. - - // @param[in] conflict_arena A pointer to the arena currently being used in the - // function - - // NOTE: DN_Str8x32FromFmt - { - DN_Str8x32 string = DN_Str8x32FromFmt("%d", 123123); - if (0) // Prints "123123" - printf("%.*s", DN_Str8PrintFmt(string)); - } - - // NOTE: DN_CVT_AgeFromU64 - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_Str8x128 string = DN_AgeStr8FromSecF64(DN_SecFromHours(2) + DN_SecFromMins(30), DN_AgeUnit_All); - DN_Assert(DN_Str8Eq(DN_Str8FromStruct(&string), DN_Str8Lit("2h 30m"))); - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_VArray - // - // An array that is backed by virtual memory by reserving addressing space - // and comitting pages as items are allocated in the array. This array never - // reallocs, instead you should reserve the upper bound of the memory you - // will possibly ever need (e.g. 16GB) and let the array commit physical - // pages on demand. - // - // On 64 bit operating systems you are given 48 bits of addressable space - // giving you 256 TB of reservable memory. This gives you practically - // an unlimited array capacity that avoids reallocs and only consumes memory - // that is actually occupied by the array. - // - // Each page that is committed into the array will be at page/allocation - // granularity which are always cache aligned. This array essentially retains - // all the benefits of normal arrays, - // - // - contiguous memory - // - O(1) random access - // - O(N) iterate - // - // In addition to no realloc on expansion or shrinking. - // - { - // NOTE: DN_OS_VArrayInit - // NOTE: DN_OS_VArrayInitByteSize - // - // Initialise an array with the requested byte size or item capacity - // respectively. The returned array may have a higher capacity than the - // requested amount since requested memory from the OS may have a certain - // alignment requirement (e.g. on Windows reserve/commit are 64k/4k - // aligned). - DN_VArray array = DN_OS_VArrayInit(1024); - DN_Assert(array.size == 0 && array.max >= 1024); - - // NOTE: DN_OS_VArrayMake - // NOTE: DN_OS_VArrayAdd - // NOTE: DN_OS_VArrayMakeArray - // NOTE: DN_OS_VArrayAddArray - // - // Allocate items from the array where: - // - // Make: creates a zero-init item from the array - // Add: creates a zero-init item and memcpy passed in data into the item - // - // If the array has run out of capacity or was never initialised, a null - // pointer is returned. - int *item = DN_OS_VArrayAdd(&array, 0xCAFE); - DN_Assert(*item == 0xCAFE && array.size == 1); - - // NOTE: DN_OS_VArrayAddCArray - DN_OS_VArrayAddCArray(&array, {1, 2, 3}); - DN_Assert(array.size == 4); - -// TODO(doyle): There's a bug here with the negative erase! -// Loop over the array items and erase 1 item. -#if 0 - for (DN_USize index = 0; index < array.size; index++) { - if (index != 1) - continue; - - // NOTE: DN_OS_VArrayEraseRange - // - // Erase the next 'count' items at 'begin_index' in the array. - // 'count' can be positive or negative which dictates the if we - // erase forward from the 'begin_index' or in reverse. - // - // This operation will invalidate all pointers to the array! - // - // A stable erase will shift all elements after the erase ranged - // into the range preserving the order of prior elements. Unstable - // erase will move the tail elements into the range being erased. - // - // Erase range returns a result that contains the next iterator - // index that can be used to update the your for loop index if you - // are trying to iterate over the array. - - // TODO(doyle): There's a bug here! This doesn't work. - // Erase index 0 with the negative count! - DN_ArrayEraseResult erase_result = DN_OS_VArrayEraseRange(&array, - /*begin_index*/ index, - /*count*/ -1, - /*erase*/ DN_ArrayErase_Stable); - DN_Assert(erase_result.items_erased == 1); - - // Use the index returned to continue linearly iterating the array - index = erase_result.it_index; - DN_Assert(array.data[index + 1] == 2); // Next loop iteration will process item '2' - } - - DN_Assert(array.size == 3 && - array.data[0] == 1 && - array.data[1] == 2 && - array.data[2] == 3); -#endif - - // NOTE: DN_OS_VArrayReserve - // - // Ensure that the requested number of items are backed by physical pages - // from the OS. Calling this pre-emptively will minimise syscalls into the - // kernel to request memory. The requested items will be rounded up to the - // in bytes to the allocation granularity of OS allocation APIs hence the - // reserved space may be greater than the requested amount (e.g. this is 4k - // on Windows). - DN_OS_VArrayReserve(&array, /*count*/ 8); - - DN_OS_VArrayDeinit(&array); - } - - // NOTE: DN_W32_LastError - // NOTE: DN_W32_ErrorCodeToMsg - #if defined(DN_PLATFORM_WIN32) - if (0) { - // Generate the error string for the last Win32 API called that return - // an error value. - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_OSW32Error get_last_error = DN_OS_W32LastError(&scratch.arena); - printf("Error (%lu): %.*s", get_last_error.code, DN_Str8PrintFmt(get_last_error.msg)); - - // Alternatively, pass in the error code directly - DN_OSW32Error error_msg_for_code = DN_OS_W32ErrorCodeToMsg(&scratch.arena, /*error_code*/ 0); - printf("Error (%lu): %.*s", error_msg_for_code.code, DN_Str8PrintFmt(error_msg_for_code.msg)); - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_W32_MakeProcessDPIAware - // - // Call once at application start-up to ensure that the application is DPI - // aware on Windows and ensure that application UI is scaled up - // appropriately for the monitor. - - // NOTE: DN_W32_Str8ToStr16 - // NOTE: DN_W32_Str8ToStr16Buffer - // NOTE: DN_W32_Str16ToStr8 - // NOTE: DN_W32_Str16ToStr8Buffer - // - // Convert a UTF8 <-> UTF16 string. - // - // The exact size buffer required for this function can be determined by - // calling this function with the 'dest' set to null and 'dest_size' set to - // 0, the return size is the size required for conversion not-including - // space for the null-terminator. This function *always* null-terminates the - // input buffer. - // - // Returns the number of u8's (for UTF16->8) OR u16's (for UTF8->16) - // written/required for conversion. 0 if there was a conversion error and can be - // queried using 'DN_W32_LastError' - #endif -} -DN_MSVC_WARNING_POP -#endif - -#define DN_BIN_PACK_CPP - -// DN: Single header generator commented out => #if defined(_CLANGD) -// #include "dn_bin_pack.h" -// #endif - -DN_API void DN_BinPackU64(DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item) -{ - DN_U64 const VALUE_MASK = 0b0111'1111; - DN_U8 const CONTINUE_BIT = 0b1000'0000; - - if (mode == DN_BinPackMode_Serialise) { - DN_U64 it = *item; - do { - DN_U8 write_value = DN_Cast(DN_U8)(it & VALUE_MASK); - it >>= 7; - if (it) - write_value |= CONTINUE_BIT; - DN_Str8BuilderAppendBytesCopy(&pack->writer, &write_value, sizeof(write_value)); - } while (it); - } else { - *item = 0; - DN_USize bits_read = 0; - for (DN_U8 src = CONTINUE_BIT; (src & CONTINUE_BIT) && bits_read < 64; bits_read += 7) { - src = pack->read.data[pack->read_index++]; - DN_U8 masked_src = src & VALUE_MASK; - *item |= (DN_Cast(DN_U64) masked_src << bits_read); - } - } -} - -DN_API void DN_BinPackVarInt_(DN_BinPack *pack, DN_BinPackMode mode, void *item, DN_USize size) -{ - DN_U64 value = 0; - DN_AssertF(size <= sizeof(value), - "An item larger than 64 bits (%zu) is trying to be packed as a variable integer which is not supported", - size * 8); - - if (mode == DN_BinPackMode_Serialise) // Read `item` into U64 `value` - DN_Memcpy(&value, item, size); - - DN_BinPackU64(pack, mode, &value); - - if (mode == DN_BinPackMode_Deserialise) // Write U64 `value` into `item` - DN_Memcpy(item, &value, size); -} - -DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack) -{ - bool result = pack->read_index == pack->read.size; - return result; -} - -DN_API void DN_BinPackUSize(DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackU32(DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackU16(DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackU8(DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackI64(DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackI32(DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackI16(DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackI8(DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackF64(DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackF32(DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -#if defined(DN_MATH_H) -DN_API void DN_BinPackV2(DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item) -{ - DN_BinPackF32(pack, mode, &item->x); - DN_BinPackF32(pack, mode, &item->y); -} - -DN_API void DN_BinPackV4(DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item) -{ - DN_BinPackF32(pack, mode, &item->x); - DN_BinPackF32(pack, mode, &item->y); - DN_BinPackF32(pack, mode, &item->z); - DN_BinPackF32(pack, mode, &item->w); -} -#endif - -DN_API void DN_BinPackBool(DN_BinPack *pack, DN_BinPackMode mode, bool *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackStr8FromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string) -{ - DN_BinPackVarInt_(pack, mode, &string->size, sizeof(string->size)); - if (mode == DN_BinPackMode_Serialise) { - DN_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size); - } else { - DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, string->size); - *string = DN_Str8FromStr8Arena(src, arena); - pack->read_index += src.size; - } -} - -DN_API void DN_BinPackStr8FromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string) -{ - DN_BinPackVarInt_(pack, mode, &string->size, sizeof(string->size)); - if (mode == DN_BinPackMode_Serialise) { - DN_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size); - } else { - DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, string->size); - *string = DN_Str8FromStr8Pool(src, pool); - pack->read_index += src.size; - } -} - -DN_API DN_Str8 DN_BinPackStr8FromBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max) -{ - DN_BinPackCBuffer(pack, mode, ptr, size, max); - DN_Str8 result = DN_Str8FromPtr(ptr, *size); - return result; -} - -DN_API void DN_BinPackBytesFromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size) -{ - DN_Str8 string = DN_Str8FromPtr(*ptr, *size); - DN_BinPackStr8FromArena(pack, arena, mode, &string); - *ptr = string.data; - *size = string.size; -} - -DN_API void DN_BinPackBytesFromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size) -{ - DN_Str8 string = DN_Str8FromPtr(*ptr, *size); - DN_BinPackStr8FromPool(pack, pool, mode, &string); - *ptr = string.data; - *size = string.size; -} - -DN_API void DN_BinPackCArray(DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size) -{ - DN_BinPackVarInt_(pack, mode, &size, sizeof(size)); - if (mode == DN_BinPackMode_Serialise) { - DN_Str8BuilderAppendBytesCopy(&pack->writer, ptr, size); - } else { - DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, size); - DN_Assert(src.size == size); - DN_Memcpy(ptr, src.data, DN_Min(src.size, size)); - pack->read_index += src.size; - } -} - -DN_API void DN_BinPackCBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max) -{ - if (mode == DN_BinPackMode_Serialise) { - DN_BinPackUSize(pack, mode, size); - DN_Str8BuilderAppendBytesCopy(&pack->writer, ptr, *size); - } else { - DN_U64 size_u64 = 0; - DN_BinPackU64(pack, mode, &size_u64); - DN_Assert(size_u64 < DN_USIZE_MAX); - DN_Assert(size_u64 <= max); - - *size = DN_Min(size_u64, max); - DN_Memcpy(ptr, pack->read.data + pack->read_index, *size); - pack->read_index += size_u64; - } -} - -DN_API DN_Str8 DN_BinPackBuild(DN_BinPack const *pack, DN_Arena *arena) -{ - DN_Str8 result = DN_Str8FromStr8BuilderArena(&pack->writer, arena); - return result; -} -#define DN_CSV_CPP - -// DN: Single header generator commented out => #include "dn_csv.h" -#if !defined(DN_CSV_H) -#define DN_CSV_H - -// NOTE: Data structures to create and parse CSV files, supports Python style escaped quotes (e.g. -// Using "" to escape quotes inside a quoted string). -// -// API -// DN_CSV_TokeniserNextN: Reads the next N consecutive fields from the parser. If `column_iterator` -// is `false` then the read of the N consecutive fields does not proceed past the end of the -// current CSV row. If `true` then it reads the next N fields even if reading would progress onto -// the next row. - - // DN: Single header generator commented out => #if defined(_CLANGD) -// #include "../dn.h" -// #endif - -enum DN_CSVSerialise -{ - DN_CSVSerialise_Read, - DN_CSVSerialise_Write, -}; - -struct DN_CSVTokeniser -{ - bool bad; - DN_Str8 string; - char delimiter; - char const *it; - bool end_of_line; -}; - -struct DN_CSVPack -{ - DN_Str8Builder write_builder; - DN_USize write_column; - DN_CSVTokeniser read_tokeniser; -}; - -DN_CSVTokeniser DN_CSV_TokeniserInit (DN_Str8 string, char delimiter); -bool DN_CSV_TokeniserValid (DN_CSVTokeniser *tokeniser); -bool DN_CSV_TokeniserNextRow (DN_CSVTokeniser *tokeniser); -DN_Str8 DN_CSV_TokeniserNextField (DN_CSVTokeniser *tokeniser); -DN_Str8 DN_CSV_TokeniserNextColumn (DN_CSVTokeniser *tokeniser); -void DN_CSV_TokeniserSkipLine (DN_CSVTokeniser *tokeniser); -int DN_CSV_TokeniserNextN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator); -int DN_CSV_TokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size); -int DN_CSV_TokeniserNextFieldN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size); -void DN_CSV_TokeniserSkipLineN (DN_CSVTokeniser *tokeniser, int count); -void DN_CSV_PackU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value); -void DN_CSV_PackI64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value); -void DN_CSV_PackI32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value); -void DN_CSV_PackI16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value); -void DN_CSV_PackI8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value); -void DN_CSV_PackU32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value); -void DN_CSV_PackU16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value); -void DN_CSV_PackBoolAsU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value); -void DN_CSV_PackStr8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena); -void DN_CSV_PackBuffer (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size); -void DN_CSV_PackBufferWithMax (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max); -bool DN_CSV_PackNewLine (DN_CSVPack *pack, DN_CSVSerialise serialise); - -#endif // !defined(DN_CSV_H) - -DN_CSVTokeniser DN_CSV_TokeniserInit(DN_Str8 string, char delimiter) -{ - DN_CSVTokeniser result = {}; - result.string = string; - result.delimiter = delimiter; - return result; -} - -bool DN_CSV_TokeniserValid(DN_CSVTokeniser *tokeniser) -{ - bool result = tokeniser && !tokeniser->bad; - return result; -} - -static void DN_CSV_TokeniserEatNewLines_(DN_CSVTokeniser *tokeniser) -{ - char const *end = tokeniser->string.data + tokeniser->string.size; - while (tokeniser->it[0] == '\n' || tokeniser->it[0] == '\r') - if (++tokeniser->it == end) - break; -} - -bool DN_CSV_TokeniserNextRow(DN_CSVTokeniser *tokeniser) -{ - bool result = false; - if (DN_CSV_TokeniserValid(tokeniser) && tokeniser->string.size) { - // NOTE: First time querying row iterator is nil, let tokeniser advance - if (tokeniser->it) { - // NOTE: Only advance the tokeniser if we're at the end of the line and - // there's more to tokenise. - char const *end = tokeniser->string.data + tokeniser->string.size; - if (tokeniser->it != end && tokeniser->end_of_line) { - tokeniser->end_of_line = false; - result = true; - } - } - } - - return result; -} - -DN_Str8 DN_CSV_TokeniserNextField(DN_CSVTokeniser *tokeniser) -{ - DN_Str8 result = {}; - if (!DN_CSV_TokeniserValid(tokeniser)) - return result; - - if (tokeniser->string.size == 0) { - tokeniser->bad = true; - return result; - } - - // NOTE: First time tokeniser is invoked with a string, set up initial state. - char const *string_end = tokeniser->string.data + tokeniser->string.size; - if (!tokeniser->it) { - tokeniser->it = tokeniser->string.data; - DN_CSV_TokeniserEatNewLines_(tokeniser); // NOTE: Skip any leading new lines - } - - // NOTE: Tokeniser pointing at end, no more valid data to parse. - if (tokeniser->it == string_end) - return result; - - // NOTE: Scan forward until the next control character. - // 1. '"' Double quoted field, extract everything between the quotes. - // 2. tokeniser->delimiter End of the field, extract everything leading up to the delimiter. - // 3. '\n' Last field in record, extract everything leading up the the new line. - char const *begin = tokeniser->it; - while (tokeniser->it != string_end && (tokeniser->it[0] != '"' && - tokeniser->it[0] != tokeniser->delimiter && - tokeniser->it[0] != '\n')) - tokeniser->it++; - - bool quoted_field = (tokeniser->it != string_end) && tokeniser->it[0] == '"'; - if (quoted_field) { - begin = ++tokeniser->it; // Begin after the quote - - // NOTE: Scan forward until the next '"' which marks the end - // of the field unless it is escaped by another '"'. - find_next_quote: - while (tokeniser->it != string_end && tokeniser->it[0] != '"') - tokeniser->it++; - - // NOTE: If we encounter a '"' right after, the quotes were escaped - // and we need to skip to the next instance of a '"'. - if (tokeniser->it != string_end && tokeniser->it + 1 != string_end && tokeniser->it[1] == '"') { - tokeniser->it += 2; - goto find_next_quote; - } - } - - // NOTE: Mark the end of the field - char const *end = tokeniser->it; - tokeniser->end_of_line = tokeniser->it == string_end || end[0] == '\n'; - - // NOTE: In files with \r\n style new lines ensure that we don't include - // the \r byte in the CSV field we produce. - if (end != string_end && end[0] == '\n') { - DN_Assert((uintptr_t)(end - 1) > (uintptr_t)tokeniser->string.data && - "Internal error: The string iterator is pointing behind the start of the string we're reading"); - if (end[-1] == '\r') - end = end - 1; - } - - // NOTE: Quoted fields may have whitespace after the closing quote, we skip - // until we reach the field terminator. - if (quoted_field) - while (tokeniser->it != string_end && (tokeniser->it[0] != tokeniser->delimiter && tokeniser->it[0] != '\n')) - tokeniser->it++; - - // NOTE: Advance the tokeniser past the field terminator. - if (tokeniser->it != string_end) - tokeniser->it++; - - // NOTE: Generate the record - result.data = DN_Cast(char *) begin; - result.size = DN_Cast(int)(end - begin); - return result; -} - -DN_Str8 DN_CSV_TokeniserNextColumn(DN_CSVTokeniser *tokeniser) -{ - DN_Str8 result = {}; - if (!DN_CSV_TokeniserValid(tokeniser)) - return result; - - // NOTE: End of line, the user must explicitly advance to the next row - if (tokeniser->end_of_line) - return result; - - // NOTE: Advance tokeniser to the next field in the row - result = DN_CSV_TokeniserNextField(tokeniser); - return result; -} - -void DN_CSV_TokeniserSkipLine(DN_CSVTokeniser *tokeniser) -{ - while (DN_CSV_TokeniserValid(tokeniser) && !tokeniser->end_of_line) - DN_CSV_TokeniserNextColumn(tokeniser); - DN_CSV_TokeniserNextRow(tokeniser); -} - -int DN_CSV_TokeniserNextN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator) -{ - if (!DN_CSV_TokeniserValid(tokeniser) || !fields || fields_size <= 0) - return 0; - - int result = 0; - for (; result < fields_size; result++) { - fields[result] = column_iterator ? DN_CSV_TokeniserNextColumn(tokeniser) : DN_CSV_TokeniserNextField(tokeniser); - if (!DN_CSV_TokeniserValid(tokeniser) || !fields[result].data) - break; - } - - return result; -} - -int DN_CSV_TokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size) -{ - int result = DN_CSV_TokeniserNextN(tokeniser, fields, fields_size, true /*column_iterator*/); - return result; -} - -int DN_CSV_TokeniserNextFieldN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size) -{ - int result = DN_CSV_TokeniserNextN(tokeniser, fields, fields_size, false /*column_iterator*/); - return result; -} - -void DN_CSV_TokeniserSkipLineN(DN_CSVTokeniser *tokeniser, int count) -{ - for (int i = 0; i < count && DN_CSV_TokeniserValid(tokeniser); i++) - DN_CSV_TokeniserSkipLine(tokeniser); -} - -void DN_CSV_PackU64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value) -{ - if (serialise == DN_CSVSerialise_Read) { - DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser); - DN_U64FromResult to_u64 = DN_U64FromStr8(csv_value, 0); - DN_Assert(to_u64.success); - *value = to_u64.value; - } else { - DN_Str8BuilderAppendF(&pack->write_builder, "%s%" PRIu64, pack->write_column++ ? "," : "", *value); - } -} - -void DN_CSV_PackI64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value) -{ - if (serialise == DN_CSVSerialise_Read) { - DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser); - DN_I64FromResult to_i64 = DN_I64FromStr8(csv_value, 0); - DN_Assert(to_i64.success); - *value = to_i64.value; - } else { - DN_Str8BuilderAppendF(&pack->write_builder, "%s%" PRIu64, pack->write_column++ ? "," : "", *value); - } -} - -void DN_CSV_PackI32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value) -{ - DN_I64 u64 = *value; - DN_CSV_PackI64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = DN_SaturateCastI64ToI32(u64); -} - -void DN_CSV_PackI16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value) -{ - DN_I64 u64 = *value; - DN_CSV_PackI64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = DN_SaturateCastI64ToI16(u64); -} - -void DN_CSV_PackI8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value) -{ - DN_I64 u64 = *value; - DN_CSV_PackI64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = DN_SaturateCastI64ToI8(u64); -} - -void DN_CSV_PackU32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value) -{ - DN_U64 u64 = *value; - DN_CSV_PackU64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = DN_SaturateCastU64ToU32(u64); -} - -void DN_CSV_PackU16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value) -{ - DN_U64 u64 = *value; - DN_CSV_PackU64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = DN_SaturateCastU64ToU16(u64); -} - -void DN_CSV_PackBoolAsU64(DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value) -{ - DN_U64 u64 = *value; - DN_CSV_PackU64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = u64 ? 1 : 0; -} - -void DN_CSV_PackStr8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena) -{ - if (serialise == DN_CSVSerialise_Read) { - DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser); - *str8 = DN_Str8FromStr8Arena(csv_value, arena); - } else { - DN_Str8BuilderAppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_Str8PrintFmt(*str8)); - } -} - -void DN_CSV_PackBuffer(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size) -{ - if (serialise == DN_CSVSerialise_Read) { - DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser); - *size = DN_Min(*size, csv_value.size); - DN_Memcpy(dest, csv_value.data, *size); - } else { - DN_Str8BuilderAppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_Cast(int)(*size), dest); - } -} - -void DN_CSV_PackBufferWithMax(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max) -{ - if (serialise == DN_CSVSerialise_Read) - *size = max; - DN_CSV_PackBuffer(pack, serialise, dest, size); -} - -bool DN_CSV_PackNewLine(DN_CSVPack *pack, DN_CSVSerialise serialise) -{ - bool result = true; - if (serialise == DN_CSVSerialise_Read) { - result = DN_CSV_TokeniserNextRow(&pack->read_tokeniser); - } else { - pack->write_column = 0; - result = DN_Str8BuilderAppendRef(&pack->write_builder, DN_Str8Lit("\n")); - } - return result; -} \ No newline at end of file +#endif \ No newline at end of file diff --git a/Single-Header/dn_single_header.h b/Single-Header/dn_single_header.h index 596ee7f..a8258e2 100644 --- a/Single-Header/dn_single_header.h +++ b/Single-Header/dn_single_header.h @@ -1,4 +1,4 @@ -// Generated by the DN single header generator 2026-06-18 18:20:05 +// Generated by the DN single header generator 2026-06-18 22:11:00 #if !defined(DN_H) #define DN_H @@ -143,17 +143,23 @@ // Tracing incurs an additional much heavier performance penalty than the UAF guard due to // the stacktrace that is stored per region to report to the user when a UAF guard violation // occurs. +// +// Str8 AVX512F variants +// We have some AVX512 string functions that can be enabled by defining the following +// +// #define DN_STR8_AVX512F 1 // DN: Single header generator commented out => #include "Base/dn_base.h" #if !defined(DN_BASE_H) #define DN_BASE_H // DN: Single header generator commented out => #if defined(_CLANGD) +// #define DN_STR8_AVX512F 1 // #include "../dn.h" // #endif // NOTE: Compiler identification -// Warning! Order is important here, clang-cl on Windows defines _MSC_VER +// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER #if defined(_MSC_VER) #if defined(__clang__) #define DN_COMPILER_CLANG_CL @@ -168,7 +174,8 @@ #endif // NOTE: __has_feature -// MSVC for example does not support the feature detection macro for instance so we compile it out +// NOTE: MSVC for example does not support the feature detection macro for instance so we compile it +// out #if defined(__has_feature) #define DN_HAS_FEATURE(expr) __has_feature(expr) #else @@ -176,7 +183,7 @@ #endif // NOTE: __has_builtin -// MSVC for example does not support the feature detection macro for instance so we compile it out +// NOTE: MSVC for example does not support the feature detection macro for instance so we compile it out #if defined(__has_builtin) #define DN_HAS_BUILTIN(expr) __has_builtin(expr) #else @@ -391,15 +398,6 @@ typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \ DN_GCC_WARNING_POP -#define DN_Check(expr) DN_CheckF(expr, "") -#if defined(DN_NO_CHECK_BREAK) - #define DN_CheckF(expr, fmt, ...) \ - ((expr) ? true : (DN_LogWarningF(fmt, ##__VA_ARGS__), false)) -#else - #define DN_CheckF(expr, fmt, ...) \ - ((expr) ? true : (DN_LogErrorF(fmt, ##__VA_ARGS__), DN_StackTracePrint(128 /*limit*/), DN_DebugBreak, false)) -#endif - #if defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) #define DN_64_BIT #else @@ -3835,7 +3833,7 @@ DN_API DN_I16 DN_SaturateCastI64ToI16 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_USize DN_SaturateCastI64ToUSize (DN_I64 val); DN_API DN_U8 DN_SaturateCastI64ToU8 (DN_I64 val); DN_API DN_U16 DN_SaturateCastI64ToU16 (DN_I64 val); DN_API DN_U32 DN_SaturateCastI64ToU32 (DN_I64 val); @@ -4044,12 +4042,14 @@ DN_API DN_USize DN_CStr16Size #define DN_Str8Lit(c_str) DN_Literal(DN_Str8){(char *)(c_str), sizeof(c_str) - 1} #define DN_Str8PrintFmt(string) (int)((string).size), (string).data + #define DN_Str8FromPtr(data, size) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(size)} #define DN_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->size) #define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array)) DN_API DN_Str8 DN_Str8AllocAllocator (DN_USize size, DN_ZMem z_mem, DN_Allocator allocator); DN_API DN_Str8 DN_Str8AllocArena (DN_USize size, DN_ZMem z_mem, DN_Arena *arena); DN_API DN_Str8 DN_Str8AllocPool (DN_USize size, DN_Pool *pool); + DN_API DN_Str8 DN_Str8FromCStr8 (char const *src); DN_API DN_Str8 DN_Str8FromCStr8Arena (char const *src, DN_Arena *arena); DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize size, DN_Arena *arena); @@ -4063,6 +4063,7 @@ DN_API DN_Str8 DN_Str8FromFmtAllocator DN_API DN_Str8 DN_Str8FromFmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8 DN_Str8FromFmtVPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_Str8 DN_Str8FromFmtPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); + DN_API DN_Str8x16 DN_Str8x16FromFmt (DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...); @@ -4091,6 +4092,7 @@ DN_API void DN_Str8x512AppendFmt DN_API void DN_Str8x512AppendFmtV (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, va_list args); DN_API void DN_Str8x1024AppendFmt (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, ...); DN_API void DN_Str8x1024AppendFmtV (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, va_list args); + DN_API DN_Str8x32 DN_Str8x32FromU64 (DN_U64 val, char separator); DN_API bool DN_Str8IsAll (DN_Str8 string, DN_Str8IsAllType is_all); DN_API char * DN_Str8End (DN_Str8 string); @@ -4115,6 +4117,7 @@ DN_API bool DN_Str8StartsWithInsensitive DN_API bool DN_Str8EndsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive); DN_API bool DN_Str8EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix); DN_API bool DN_Str8HasChar (DN_Str8 string, char ch); + DN_API DN_Str8 DN_Str8TrimPrefix (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive); DN_API DN_Str8 DN_Str8TrimHexPrefix (DN_Str8 string); DN_API DN_Str8 DN_Str8TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive); @@ -4123,6 +4126,7 @@ DN_API DN_Str8 DN_Str8TrimHeadWhitespace DN_API DN_Str8 DN_Str8TrimTailWhitespace (DN_Str8 string); DN_API DN_Str8 DN_Str8TrimWhitespaceAround (DN_Str8 string); DN_API DN_Str8 DN_Str8TrimByteOrderMark (DN_Str8 string); + DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path); DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path); DN_API DN_Str8 DN_Str8FilePathNoExtension (DN_Str8 path); @@ -4146,6 +4150,15 @@ DN_API DN_Str8 DN_Str8LineBreakAllocator DN_API DN_Str8 DN_Str8LineBreakArena (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena); DN_API DN_Str8 DN_Str8Table (DN_Str8 const* rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena); +#if DN_STR8_AVX512F +DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_USize DN_Str8SplitAVX512F (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags flags); +DN_API DN_Str8Slice DN_Str8SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags flags); +#endif + DN_API DN_Str8 DN_Str8SliceRender (DN_Str8Slice array, DN_Str8 separator, DN_Arena *arena); DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena); DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); @@ -4627,8 +4640,8 @@ DN_API DN_M4 DN_M4MulF DN_API DN_M4 DN_M4DivF (DN_M4 lhs, DN_F32 rhs); DN_API DN_Str8x256 DN_M4ColumnMajorString (DN_M4 mat); -DN_API bool operator== (DN_M2x3 const &lhs, DN_M2x3 const &rhs); -DN_API bool operator!= (DN_M2x3 const &lhs, DN_M2x3 const &rhs); +DN_API bool DN_M2x3Eq (DN_M2x3 const *lhs, DN_M2x3 const *rhs); +DN_API bool DN_M2x3NotEq (DN_M2x3 const *lhs, DN_M2x3 const *rhs); DN_API DN_M2x3 DN_M2x3Identity (); DN_API DN_M2x3 DN_M2x3Translate (DN_V2F32 offset); DN_API DN_V2F32 DN_M2x3ScaleGet (DN_M2x3 m2x3); @@ -4648,7 +4661,6 @@ DN_API DN_Rect DN_M2x3MulRect #define DN_RectFrom4N(x, y, w, h) DN_Literal(DN_Rect){DN_Literal(DN_V2F32){{x, y}}, DN_Literal(DN_V2F32){{w, h}}} #define DN_RectZero DN_RectFrom4N(0, 0, 0, 0) -DN_API bool operator== (const DN_Rect& lhs, const DN_Rect& rhs); DN_API DN_V2F32 DN_RectCenter (DN_Rect rect); DN_API bool DN_RectContainsPoint (DN_Rect rect, DN_V2F32 p); DN_API bool DN_RectContainsRect (DN_Rect a, DN_Rect b); @@ -5115,9 +5127,10 @@ DN_API void DN_RingWrite (DN_Rin DN_API void DN_RingRead (DN_Ring *ring, void *dest, DN_U64 dest_size); #define DN_RingReadStruct(ring, dest) DN_RingRead((ring), (dest), sizeof(*(dest))) +// TODO: Replace with a C-style hash table +#if defined(__cplusplus) DN_U32 const DN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49; DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0; - template DN_DSMap DN_DSMapInit (DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags); template void DN_DSMapDeinit (DN_DSMap *map, DN_ZMem z_mem); template bool DN_DSMapIsValid (DN_DSMap const *map); @@ -5144,7 +5157,240 @@ template DN_DSMapKey DN_DSMapKeyStr8 (DN_DSMap DN_API DN_DSMapKey DN_DSMapKeyU64NoHash (DN_U64 u64); DN_API bool DN_DSMapKeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs); DN_API bool operator== (DN_DSMapKey lhs, DN_DSMapKey rhs); +#endif +enum DN_BinPackMode +{ + DN_BinPackMode_Serialise, + DN_BinPackMode_Deserialise, +}; + +struct DN_BinPack +{ + DN_Str8Builder writer; + DN_Str8 read; + DN_USize read_index; +}; + +DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack); +DN_API void DN_BinPackUSize (DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item); +DN_API void DN_BinPackU64 (DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item); +DN_API void DN_BinPackU32 (DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item); +DN_API void DN_BinPackU16 (DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item); +DN_API void DN_BinPackU8 (DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item); +DN_API void DN_BinPackI64 (DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item); +DN_API void DN_BinPackI32 (DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item); +DN_API void DN_BinPackI16 (DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item); +DN_API void DN_BinPackI8 (DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item); +DN_API void DN_BinPackF64 (DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item); +DN_API void DN_BinPackF32 (DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item); +DN_API void DN_BinPackV2 (DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item); +DN_API void DN_BinPackV4 (DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item); +DN_API void DN_BinPackBool (DN_BinPack *pack, DN_BinPackMode mode, bool *item); +DN_API void DN_BinPackStr8FromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string); +DN_API void DN_BinPackStr8FromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string); +DN_API DN_Str8 DN_BinPackStr8FromBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max); +DN_API void DN_BinPackBytesFromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size); +DN_API void DN_BinPackBytesFromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size); +DN_API void DN_BinPackCArray (DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size); +DN_API void DN_BinPackCBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max); +DN_API DN_Str8 DN_BinPackBuild (DN_BinPack const *pack, DN_Arena *arena); + +enum DN_CSVSerialise +{ + DN_CSVSerialise_Read, + DN_CSVSerialise_Write, +}; + +struct DN_CSVTokeniser +{ + bool bad; + DN_Str8 string; + char delimiter; + char const *it; + bool end_of_line; +}; + +struct DN_CSVPack +{ + DN_Str8Builder write_builder; + DN_USize write_column; + DN_CSVTokeniser read_tokeniser; +}; + +// NOTE: Data structures to create and parse CSV files, supports Python style escaped quotes (e.g. +// Using "" to escape quotes inside a quoted string). +// +// API +// DN_CSVTokeniserNextN: Reads the next N consecutive fields from the parser. If `column_iterator` +// is `false` then the read of the N consecutive fields does not proceed past the end of the +// current CSV row. If `true` then it reads the next N fields even if reading would progress onto +// the next row. +DN_API DN_CSVTokeniser DN_CSVTokeniserInit (DN_Str8 string, char delimiter); +DN_API bool DN_CSVTokeniserValid (DN_CSVTokeniser *tokeniser); +DN_API bool DN_CSVTokeniserNextRow (DN_CSVTokeniser *tokeniser); +DN_API DN_Str8 DN_CSVTokeniserNextField (DN_CSVTokeniser *tokeniser); +DN_API DN_Str8 DN_CSVTokeniserNextColumn (DN_CSVTokeniser *tokeniser); +DN_API void DN_CSVTokeniserSkipLine (DN_CSVTokeniser *tokeniser); +DN_API int DN_CSVTokeniserNextN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator); +DN_API int DN_CSVTokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size); +DN_API int DN_CSVTokeniserNextFieldN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size); +DN_API void DN_CSVTokeniserSkipLineN (DN_CSVTokeniser *tokeniser, int count); +DN_API void DN_CSVPackU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value); +DN_API void DN_CSVPackI64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value); +DN_API void DN_CSVPackI32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value); +DN_API void DN_CSVPackI16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value); +DN_API void DN_CSVPackI8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value); +DN_API void DN_CSVPackU32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value); +DN_API void DN_CSVPackU16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value); +DN_API void DN_CSVPackBoolAsU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value); +DN_API void DN_CSVPackStr8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena); +DN_API void DN_CSVPackBuffer (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size); +DN_API void DN_CSVPackBufferWithMax (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max); +DN_API bool DN_CSVPackNewLine (DN_CSVPack *pack, DN_CSVSerialise serialise); + +// TODO: Replace with a C implementation +template +using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs); + +template +bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); + +enum DN_BinarySearchType +{ + // Index of the match. If no match is found, found is set to false and the + // index is set to the index where the match should be inserted/exist, if + // it were in the array + DN_BinarySearchType_Match, + + // Index of the first element in the array that is `element >= find`. If no such + // item is found or the array is empty, then, the index is set to the array + // size and found is set to `false`. + // + // For example: + // int array[] = {0, 1, 2, 3, 4, 5}; + // DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_LowerBound); + // printf("%zu\n", result.index); // Prints index '4' + + DN_BinarySearchType_LowerBound, + + // Index of the first element in the array that is `element > find`. If no such + // item is found or the array is empty, then, the index is set to the array + // size and found is set to `false`. + // + // For example: + // int array[] = {0, 1, 2, 3, 4, 5}; + // DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_UpperBound); + // printf("%zu\n", result.index); // Prints index '5' + + DN_BinarySearchType_UpperBound, +}; + +struct DN_BinarySearchResult +{ + bool found; + DN_USize index; +}; + +template bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); +template DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc less_than = DN_BinarySearch_DefaultLessThan); + +template +bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs) +{ + bool result = lhs < rhs; + return result; +} + +template +DN_BinarySearchResult DN_BinarySearch(T const *array, + DN_USize array_size, + T const &find, + DN_BinarySearchType type, + DN_BinarySearchLessThanProc less_than) +{ + DN_BinarySearchResult result = {}; + if (!array || array_size <= 0 || !less_than) + return result; + + T const *end = array + array_size; + T const *first = array; + T const *last = end; + while (first != last) { + DN_USize count = last - first; + T const *it = first + (count / 2); + + bool advance_first = false; + if (type == DN_BinarySearchType_UpperBound) + advance_first = !less_than(find, it[0]); + else + advance_first = less_than(it[0], find); + + if (advance_first) + first = it + 1; + else + last = it; + } + + switch (type) { + case DN_BinarySearchType_Match: { + result.found = first != end && !less_than(find, *first); + } break; + + case DN_BinarySearchType_LowerBound: /*FALLTHRU*/ + case DN_BinarySearchType_UpperBound: { + result.found = first != end; + } break; + } + + result.index = first - array; + return result; +} + +enum DN_LeakAllocFlag +{ + DN_LeakAllocFlag_Freed = 1 << 0, + DN_LeakAllocFlag_LeakPermitted = 1 << 1, +}; + +struct DN_LeakAlloc +{ + void *ptr; // 8 Pointer to the allocation being tracked + DN_USize size; // 16 Size of the allocation + DN_USize freed_size; // 24 Store the size of the allocation when it is freed + DN_Str8 stack_trace; // 40 Stack trace at the point of allocation + DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed + DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag` +}; + +// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get +// expensive. Enforce that there is no unexpected padding. +DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively + +struct DN_LeakTracker +{ + DN_DSMap alloc_table; + DN_TicketMutex alloc_table_mutex; + DN_MemList alloc_table_mem; + DN_Arena alloc_table_arena; + DN_U64 alloc_table_bytes_allocated_for_stack_traces; +}; + +DN_API void DN_LeakTrackAlloc_ (DN_LeakTracker *leak, void *ptr, DN_USize size, bool alloc_can_leak); +DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr); +DN_API void DN_LeakDump_ (DN_LeakTracker *leak); + +#if defined(DN_LEAK_TRACKING) +#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) DN_LeakTrackAlloc_(leak, ptr, size, alloc_can_leak) +#define DN_LeakTrackDealloc(leak, ptr) DN_LeakTrackDealloc_(leak, ptr) +#define DN_LeakDump(leak) DN_LeakDump_(leak) +#else +#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0) +#define DN_LeakTrackDealloc(leak, ptr) do { (void)ptr; } while (0) +#define DN_LeakDump(leak) do { } while (0) +#endif + +// NOTE: Template implementations #if defined(__cplusplus) template T *DN_TMemCopyObj(T *dest, T const *src, DN_USize count) { @@ -5280,57 +5526,6 @@ bool DN_TArrayGrowIfNeededFromArena(T **data, DN_USize size, DN_USize *max, DN_A } #endif // defined(__cplusplus) #endif // !defined(DN_BASE_H) -// DN: Single header generator commented out => #include "Base/dn_base_leak.h" -#if !defined(DN_BASE_LEAK_H) -#define DN_BASE_LEAK_H - -// DN: Single header generator commented out => #if defined(_CLANGD) -// #include "../dn.h" -// #endif - -enum DN_LeakAllocFlag -{ - DN_LeakAllocFlag_Freed = 1 << 0, - DN_LeakAllocFlag_LeakPermitted = 1 << 1, -}; - -struct DN_LeakAlloc -{ - void *ptr; // 8 Pointer to the allocation being tracked - DN_USize size; // 16 Size of the allocation - DN_USize freed_size; // 24 Store the size of the allocation when it is freed - DN_Str8 stack_trace; // 40 Stack trace at the point of allocation - DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed - DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag` -}; - -// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get -// expensive. Enforce that there is no unexpected padding. -DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively - -struct DN_LeakTracker -{ - DN_DSMap alloc_table; - DN_TicketMutex alloc_table_mutex; - DN_MemList alloc_table_mem; - DN_Arena alloc_table_arena; - DN_U64 alloc_table_bytes_allocated_for_stack_traces; -}; - -DN_API void DN_LeakTrackAlloc_ (DN_LeakTracker *leak, void *ptr, DN_USize size, bool alloc_can_leak); -DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr); -DN_API void DN_LeakDump_ (DN_LeakTracker *leak); - -#if defined(DN_LEAK_TRACKING) -#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) DN_LeakTrackAlloc_(leak, ptr, size, alloc_can_leak) -#define DN_LeakTrackDealloc(leak, ptr) DN_LeakTrackDealloc_(leak, ptr) -#define DN_LeakDump(leak) DN_LeakDump_(leak) -#else -#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0) -#define DN_LeakTrackDealloc(leak, ptr) do { (void)ptr; } while (0) -#define DN_LeakDump(leak) do { } while (0) -#endif -#endif // DN_BASE_LEAK_H #if DN_H_WITH_OS #if defined(DN_PLATFORM_WIN32) @@ -6884,10 +7079,6 @@ DN_API DN_OSPosixProcSelfStatus DN_OS_PosixProcSelfStatus(); #endif #endif -#if defined(DN_PLATFORM_EMSCRIPTEN) - #include // emscripten_fetch (for DN_OSHttpResponse) -#endif - extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count]; struct DN_OSTimer /// Record time between two time-points using the OS's performance counter. @@ -7082,42 +7273,6 @@ struct DN_OSThread DN_TCInitArgs tc_init_args; }; -// NOTE: DN_OSHttp -enum DN_OSHttpRequestSecure -{ - DN_OSHttpRequestSecure_No, - DN_OSHttpRequestSecure_Yes, -}; - -struct DN_OSHttpResponse -{ - // NOTE: Response data - DN_U32 error_code; - DN_Str8 error_msg; - DN_U16 http_status; - DN_Str8 body; - DN_B32 done; - - // NOTE: Book-keeping - DN_Arena *arena; // Allocates memory for the response - - // NOTE: Async book-keeping - // Synchronous HTTP response uses the TLS scratch arena whereas async - // calls use their own dedicated arena. - DN_Arena tmp_arena; - DN_Arena scratch_arena; - DN_Str8Builder builder; - DN_OSSemaphore on_complete_semaphore; - - #if defined(DN_PLATFORM_EMSCRIPTEN) - emscripten_fetch_t *em_handle; - #elif defined(DN_PLATFORM_WIN32) - HINTERNET w32_request_session; - HINTERNET w32_request_connection; - HINTERNET w32_request_handle; - #endif -}; - struct DN_OSCore { DN_CPUReport cpu_report; @@ -7362,10 +7517,52 @@ DN_API DN_OSThreadLane* DN_OS_TCThreadLane () DN_API void DN_OS_TCThreadLaneSync (void **ptr_to_share); DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip (DN_OSThreadLane lane); -DN_API void DN_OS_HttpRequestAsync (DN_OSHttpResponse *response, DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers); -DN_API void DN_OS_HttpRequestWait (DN_OSHttpResponse *response); -DN_API void DN_OS_HttpRequestFree (DN_OSHttpResponse *response); -DN_API DN_OSHttpResponse DN_OS_HttpRequest (DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers); +enum DN_OSAsyncPriority +{ + DN_OSAsyncPriority_Low, + DN_OSAsyncPriority_High, + DN_OSAsyncPriority_Count, +}; + +struct DN_OSAsyncCore +{ + DN_OSMutex ring_mutex; + DN_OSConditionVariable ring_write_cv; + DN_OSSemaphore worker_sem; + DN_Ring ring; + DN_OSThread *threads; + DN_U32 thread_count; + DN_U32 busy_threads; + DN_U32 join_threads; +}; + +struct DN_OSAsyncWorkArgs +{ + DN_OSThread *thread; + void *input; +}; + +typedef void(DN_OSAsyncWorkFunc)(DN_OSAsyncWorkArgs work_args); + +struct DN_OSAsyncWork +{ + DN_OSAsyncWorkFunc *func; + void *input; + void *output; +}; + +struct DN_OSAsyncTask +{ + bool queued; + DN_OSAsyncWork work; + DN_OSSemaphore completion_sem; +}; + +DN_API void DN_OS_AsyncInit (DN_OSAsyncCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size); +DN_API void DN_OS_AsyncDeinit (DN_OSAsyncCore *async); +DN_API bool DN_OS_AsyncQueueWork(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms); +DN_API DN_OSAsyncTask DN_OS_AsyncQueueTask(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms); +DN_API bool DN_OS_AsyncWaitTask (DN_OSAsyncTask *task, DN_U32 timeout_ms); // NOTE: DN_OSPrint enum DN_OSPrintDest @@ -7423,48 +7620,6 @@ DN_API void DN_OS_PrintLnFV (DN_OSPrintDest dest, DN_FMT_ATTRIB char DN_API void DN_OS_PrintLnStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_Str8 string); DN_API void DN_OS_PrintLnFStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, ...); DN_API void DN_OS_PrintLnFVStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, va_list args); - -// NOTE: DN_VArray -// TODO(doyle): Add an API for shrinking the array by decomitting pages back to the OS. -template struct DN_VArray -{ - T *data; // Pointer to the start of the array items in the block of memory - DN_USize size; // Number of items currently in the array - DN_USize max; // Maximum number of items this array can store - DN_USize commit; // Bytes committed - - T *begin() { return data; } - T *end () { return data + size; } - T const *begin() const { return data; } - T const *end () const { return data + size; } -}; - -template DN_VArray DN_OS_VArrayInitByteSize (DN_USize byte_size); -template DN_VArray DN_OS_VArrayInit (DN_USize max); -template DN_VArray DN_OS_VArrayInitCArray (T const (&items)[N], DN_USize max); -template void DN_OS_VArrayDeinit (DN_VArray *array); -template bool DN_OS_VArrayIsValid (DN_VArray const *array); -template bool DN_OS_VArrayReserve (DN_VArray *array, DN_USize count); -template T * DN_OS_VArrayAddArray (DN_VArray *array, T const *items, DN_USize count); -template T * DN_OS_VArrayAddCArray (DN_VArray *array, T const (&items)[N]); -template T * DN_OS_VArrayAdd (DN_VArray *array, T const &item); -#define DN_OS_VArrayAddArrayAssert(...) DN_HardAssert(DN_OS_VArrayAddArray(__VA_ARGS__)) -#define DN_OS_VArrayAddCArrayAssert(...) DN_HardAssert(DN_OS_VArrayAddCArray(__VA_ARGS__)) -#define DN_OS_VArrayAddAssert(...) DN_HardAssert(DN_OS_VArrayAdd(__VA_ARGS__)) -template T * DN_OS_VArrayMakeArray (DN_VArray *array, DN_USize count, DN_ZMem z_mem); -template T * DN_OS_VArrayMake (DN_VArray *array, DN_ZMem z_mem); -#define DN_OS_VArrayMakeArrayAssert(...) DN_HardAssert(DN_OS_VArrayMakeArray(__VA_ARGS__)) -#define DN_OS_VArrayMakeAssert(...) DN_HardAssert(DN_OS_VArrayMake(__VA_ARGS__)) -template T * DN_OS_VArrayInsertArray (DN_VArray *array, DN_USize index, T const *items, DN_USize count); -template T * DN_OS_VArrayInsertCArray (DN_VArray *array, DN_USize index, T const (&items)[N]); -template T * DN_OS_VArrayInsert (DN_VArray *array, DN_USize index, T const &item); -#define DN_OS_VArrayInsertArrayAssert(...) DN_HardAssert(DN_OS_VArrayInsertArray(__VA_ARGS__)) -#define DN_OS_VArrayInsertCArrayAssert(...) DN_HardAssert(DN_OS_VArrayInsertCArray(__VA_ARGS__)) -#define DN_OS_VArrayInsertAssert(...) DN_HardAssert(DN_OS_VArrayInsert(__VA_ARGS__)) -template T DN_OS_VArrayPopFront (DN_VArray *array, DN_USize count); -template T DN_OS_VArrayPopBack (DN_VArray *array, DN_USize count); -template DN_ArrayEraseResult DN_OS_VArrayEraseRange (DN_VArray *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase); -template void DN_OS_VArrayClear (DN_VArray *array, DN_ZMem z_mem); #endif // !defined(DN_OS_H) #endif @@ -7502,255 +7657,6 @@ DN_API void DN_Set (DN_Core *dn); DN_API DN_Core *DN_Get (); DN_API void DN_BeginFrame(); -#if DN_H_WITH_HELPERS -// DN: Single header generator commented out => #include "Extra/dn_helpers.h" -#if !defined(DN_HELPERS_H) -#define DN_HELPERS_H - -// DN: Single header generator commented out => #if defined(_CLANGD) -// #include "../dn.h" -// #endif - -/* -//////////////////////////////////////////////////////////////////////////////////////////////////// -// -// $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\ -// $$ | $$ |$$ _____|$$ | $$ __$$\ $$ _____|$$ __$$\ $$ __$$\ -// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ |$$ / \__| -// $$$$$$$$ |$$$$$\ $$ | $$$$$$$ |$$$$$\ $$$$$$$ |\$$$$$$\ -// $$ __$$ |$$ __| $$ | $$ ____/ $$ __| $$ __$$< \____$$\ -// $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |$$\ $$ | -// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$ | $$$$$$$$\ $$ | $$ |\$$$$$$ | -// \__| \__|\________|\________|\__| \________|\__| \__| \______/ -// -// dn_helpers.h -- Helper functions/data structures -// -//////////////////////////////////////////////////////////////////////////////////////////////////// -*/ - -#if !defined(DN_NO_JSON_BUILDER) -enum DN_JSONBuilderItem -{ - DN_JSONBuilderItem_Empty, - DN_JSONBuilderItem_OpenContainer, - DN_JSONBuilderItem_CloseContainer, - DN_JSONBuilderItem_KeyValue, -}; - -struct DN_JSONBuilder -{ - bool use_stdout; // When set, ignore the string builder and dump immediately to stdout - DN_Str8Builder string_builder; // (Internal) - int indent_level; // (Internal) - int spaces_per_indent; // The number of spaces per indent level - DN_JSONBuilderItem last_item; -}; -#endif // !defined(DN_NO_JSON_BUIDLER) - -template -using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs); - -template -bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); - -enum DN_BinarySearchType -{ - // Index of the match. If no match is found, found is set to false and the - // index is set to the index where the match should be inserted/exist, if - // it were in the array - DN_BinarySearchType_Match, - - // Index of the first element in the array that is `element >= find`. If no such - // item is found or the array is empty, then, the index is set to the array - // size and found is set to `false`. - // - // For example: - // int array[] = {0, 1, 2, 3, 4, 5}; - // DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_LowerBound); - // printf("%zu\n", result.index); // Prints index '4' - - DN_BinarySearchType_LowerBound, - - // Index of the first element in the array that is `element > find`. If no such - // item is found or the array is empty, then, the index is set to the array - // size and found is set to `false`. - // - // For example: - // int array[] = {0, 1, 2, 3, 4, 5}; - // DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_UpperBound); - // printf("%zu\n", result.index); // Prints index '5' - - DN_BinarySearchType_UpperBound, -}; - -struct DN_BinarySearchResult -{ - bool found; - DN_USize index; -}; - -#if !defined(DN_NO_JSON_BUILDER) -// NOTE: DN_JSONBuilder -#define DN_JSONBuilder_Object(builder) \ - DN_DeferLoop(DN_JSONBuilder_ObjectBegin(builder), \ - DN_JSONBuilder_ObjectEnd(builder)) - -#define DN_JSONBuilder_ObjectNamed(builder, name) \ - DN_DeferLoop(DN_JSONBuilder_ObjectBeginNamed(builder, name), \ - DN_JSONBuilder_ObjectEnd(builder)) - -#define DN_JSONBuilder_Array(builder) \ - DN_DeferLoop(DN_JSONBuilder_ArrayBegin(builder), \ - DN_JSONBuilder_ArrayEnd(builder)) - -#define DN_JSONBuilder_ArrayNamed(builder, name) \ - DN_DeferLoop(DN_JSONBuilder_ArrayBeginNamed(builder, name), \ - DN_JSONBuilder_ArrayEnd(builder)) - -DN_API DN_JSONBuilder DN_JSONBuilder_Init (DN_Arena *arena, int spaces_per_indent); -DN_API DN_Str8 DN_JSONBuilder_Build (DN_JSONBuilder const *builder, DN_Arena *arena); -DN_API void DN_JSONBuilder_KeyValue (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value); -DN_API void DN_JSONBuilder_KeyValueF (DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...); -DN_API void DN_JSONBuilder_ObjectBeginNamed (DN_JSONBuilder *builder, DN_Str8 name); -DN_API void DN_JSONBuilder_ObjectEnd (DN_JSONBuilder *builder); -DN_API void DN_JSONBuilder_ArrayBeginNamed (DN_JSONBuilder *builder, DN_Str8 name); -DN_API void DN_JSONBuilder_ArrayEnd (DN_JSONBuilder *builder); -DN_API void DN_JSONBuilder_Str8Named (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value); -DN_API void DN_JSONBuilder_LiteralNamed (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value); -DN_API void DN_JSONBuilder_U64Named (DN_JSONBuilder *builder, DN_Str8 key, uint64_t value); -DN_API void DN_JSONBuilder_I64Named (DN_JSONBuilder *builder, DN_Str8 key, int64_t value); -DN_API void DN_JSONBuilder_F64Named (DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places); -DN_API void DN_JSONBuilder_BoolNamed (DN_JSONBuilder *builder, DN_Str8 key, bool value); - -#define DN_JSONBuilder_ObjectBegin(builder) DN_JSONBuilder_ObjectBeginNamed(builder, DN_Str8Lit("")) -#define DN_JSONBuilder_ArrayBegin(builder) DN_JSONBuilder_ArrayBeginNamed(builder, DN_Str8Lit("")) -#define DN_JSONBuilder_Str8(builder, value) DN_JSONBuilder_Str8Named(builder, DN_Str8Lit(""), value) -#define DN_JSONBuilder_Literal(builder, value) DN_JSONBuilder_LiteralNamed(builder, DN_Str8Lit(""), value) -#define DN_JSONBuilder_U64(builder, value) DN_JSONBuilder_U64Named(builder, DN_Str8Lit(""), value) -#define DN_JSONBuilder_I64(builder, value) DN_JSONBuilder_I64Named(builder, DN_Str8Lit(""), value) -#define DN_JSONBuilder_F64(builder, value) DN_JSONBuilder_F64Named(builder, DN_Str8Lit(""), value) -#define DN_JSONBuilder_Bool(builder, value) DN_JSONBuilder_BoolNamed(builder, DN_Str8Lit(""), value) -#endif // !defined(DN_NO_JSON_BUILDER) - -// NOTE: DN_BinarySearch -template bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); -template DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc less_than = DN_BinarySearch_DefaultLessThan); - -// NOTE: DN_BinarySearch -template -bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs) -{ - bool result = lhs < rhs; - return result; -} - -template -DN_BinarySearchResult DN_BinarySearch(T const *array, - DN_USize array_size, - T const &find, - DN_BinarySearchType type, - DN_BinarySearchLessThanProc less_than) -{ - DN_BinarySearchResult result = {}; - if (!array || array_size <= 0 || !less_than) - return result; - - T const *end = array + array_size; - T const *first = array; - T const *last = end; - while (first != last) { - DN_USize count = last - first; - T const *it = first + (count / 2); - - bool advance_first = false; - if (type == DN_BinarySearchType_UpperBound) - advance_first = !less_than(find, it[0]); - else - advance_first = less_than(it[0], find); - - if (advance_first) - first = it + 1; - else - last = it; - } - - switch (type) { - case DN_BinarySearchType_Match: { - result.found = first != end && !less_than(find, *first); - } break; - - case DN_BinarySearchType_LowerBound: /*FALLTHRU*/ - case DN_BinarySearchType_UpperBound: { - result.found = first != end; - } break; - } - - result.index = first - array; - return result; -} -#endif // !defined(DN_HELPERS_H) -#endif - -#if DN_H_WITH_ASYNC -// DN: Single header generator commented out => #include "Extra/dn_async.h" -#if !defined(DN_ASYNC_H) -#define DN_ASYNC_H - -// DN: Single header generator commented out => #if defined(_CLANGD) -// #define DN_H_WITH_OS 1 -// #include "../dn.h" -// #endif - -enum DN_ASYNCPriority -{ - DN_ASYNCPriority_Low, - DN_ASYNCPriority_High, - DN_ASYNCPriority_Count, -}; - -struct DN_ASYNCCore -{ - DN_OSMutex ring_mutex; - DN_OSConditionVariable ring_write_cv; - DN_OSSemaphore worker_sem; - DN_Ring ring; - DN_OSThread *threads; - DN_U32 thread_count; - DN_U32 busy_threads; - DN_U32 join_threads; -}; - -struct DN_ASYNCWorkArgs -{ - DN_OSThread *thread; - void *input; -}; - -typedef void(DN_ASYNCWorkFunc)(DN_ASYNCWorkArgs work_args); - -struct DN_ASYNCWork -{ - DN_ASYNCWorkFunc *func; - void *input; - void *output; -}; - -struct DN_ASYNCTask -{ - bool queued; - DN_ASYNCWork work; - DN_OSSemaphore completion_sem; -}; - -DN_API void DN_ASYNC_Init (DN_ASYNCCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size); -DN_API void DN_ASYNC_Deinit (DN_ASYNCCore *async); -DN_API bool DN_ASYNC_QueueWork(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms); -DN_API DN_ASYNCTask DN_ASYNC_QueueTask(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms); -DN_API void DN_ASYNC_WaitTask (DN_OSSemaphore *sem, DN_U32 timeout_ms); - -#endif // DN_ASYNC_H -#endif - #if DN_H_WITH_NET // DN: Single header generator commented out => #include "Extra/dn_net.h" #if !defined(DN_NET_H) @@ -7888,113 +7794,4 @@ void DN_NET_EndFinishedRequest (DN_NETRequest *request); #endif // DN_NET_H #endif -#endif // !defined(DN_H) -#if !defined(DN_BIN_PACK_H) -#define DN_BIN_PACK_H - -// DN: Single header generator commented out => #if defined(_CLANGD) -// #include "../dn.h" -// #endif - -enum DN_BinPackMode -{ - DN_BinPackMode_Serialise, - DN_BinPackMode_Deserialise, -}; - -struct DN_BinPack -{ - DN_Str8Builder writer; - DN_Str8 read; - DN_USize read_index; -}; - -DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack); -DN_API void DN_BinPackUSize (DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item); -DN_API void DN_BinPackU64 (DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item); -DN_API void DN_BinPackU32 (DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item); -DN_API void DN_BinPackU16 (DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item); -DN_API void DN_BinPackU8 (DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item); -DN_API void DN_BinPackI64 (DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item); -DN_API void DN_BinPackI32 (DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item); -DN_API void DN_BinPackI16 (DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item); -DN_API void DN_BinPackI8 (DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item); -DN_API void DN_BinPackF64 (DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item); -DN_API void DN_BinPackF32 (DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item); -#if defined (DN_MATH_H) -DN_API void DN_BinPackV2 (DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item); -DN_API void DN_BinPackV4 (DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item); -#endif -DN_API void DN_BinPackBool (DN_BinPack *pack, DN_BinPackMode mode, bool *item); -DN_API void DN_BinPackStr8FromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string); -DN_API void DN_BinPackStr8FromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string); -DN_API DN_Str8 DN_BinPackStr8FromBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max); -DN_API void DN_BinPackBytesFromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size); -DN_API void DN_BinPackBytesFromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size); -DN_API void DN_BinPackCArray (DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size); -DN_API void DN_BinPackCBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max); -DN_API DN_Str8 DN_BinPackBuild (DN_BinPack const *pack, DN_Arena *arena); - -#endif // !defined(DN_BIN_PACK_H) -#if !defined(DN_CSV_H) -#define DN_CSV_H - -// NOTE: Data structures to create and parse CSV files, supports Python style escaped quotes (e.g. -// Using "" to escape quotes inside a quoted string). -// -// API -// DN_CSV_TokeniserNextN: Reads the next N consecutive fields from the parser. If `column_iterator` -// is `false` then the read of the N consecutive fields does not proceed past the end of the -// current CSV row. If `true` then it reads the next N fields even if reading would progress onto -// the next row. - - // DN: Single header generator commented out => #if defined(_CLANGD) -// #include "../dn.h" -// #endif - -enum DN_CSVSerialise -{ - DN_CSVSerialise_Read, - DN_CSVSerialise_Write, -}; - -struct DN_CSVTokeniser -{ - bool bad; - DN_Str8 string; - char delimiter; - char const *it; - bool end_of_line; -}; - -struct DN_CSVPack -{ - DN_Str8Builder write_builder; - DN_USize write_column; - DN_CSVTokeniser read_tokeniser; -}; - -DN_CSVTokeniser DN_CSV_TokeniserInit (DN_Str8 string, char delimiter); -bool DN_CSV_TokeniserValid (DN_CSVTokeniser *tokeniser); -bool DN_CSV_TokeniserNextRow (DN_CSVTokeniser *tokeniser); -DN_Str8 DN_CSV_TokeniserNextField (DN_CSVTokeniser *tokeniser); -DN_Str8 DN_CSV_TokeniserNextColumn (DN_CSVTokeniser *tokeniser); -void DN_CSV_TokeniserSkipLine (DN_CSVTokeniser *tokeniser); -int DN_CSV_TokeniserNextN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator); -int DN_CSV_TokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size); -int DN_CSV_TokeniserNextFieldN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size); -void DN_CSV_TokeniserSkipLineN (DN_CSVTokeniser *tokeniser, int count); -void DN_CSV_PackU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value); -void DN_CSV_PackI64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value); -void DN_CSV_PackI32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value); -void DN_CSV_PackI16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value); -void DN_CSV_PackI8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value); -void DN_CSV_PackU32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value); -void DN_CSV_PackU16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value); -void DN_CSV_PackBoolAsU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value); -void DN_CSV_PackStr8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena); -void DN_CSV_PackBuffer (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size); -void DN_CSV_PackBufferWithMax (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max); -bool DN_CSV_PackNewLine (DN_CSVPack *pack, DN_CSVSerialise serialise); - -#endif // !defined(DN_CSV_H) \ No newline at end of file +#endif // !defined(DN_H) \ No newline at end of file diff --git a/Source/Base/dn_base.cpp b/Source/Base/dn_base.cpp index b6dfbe9..1a9cbc4 100644 --- a/Source/Base/dn_base.cpp +++ b/Source/Base/dn_base.cpp @@ -6,6 +6,10 @@ #include "../dn.h" #endif +#if DN_STR8_AVX512F + #include +#endif + enum DN_ArenaUAFCheckReportType_ { DN_ArenaUAFCheckReportType_AllocViolation, @@ -297,7 +301,7 @@ DN_API DN_CPUReport DN_CPUGetReport() // NOTE: DN_TicketMutex //////////////////////////////////////////////////////////////////////////// DN_API void DN_TicketMutex_Begin(DN_TicketMutex *mutex) { - unsigned int ticket = DN_AtomicAddU32(&mutex->ticket, 1); + DN_UInt ticket = DN_AtomicAddU32(&mutex->ticket, 1); DN_TicketMutex_BeginTicket(mutex, ticket); } @@ -360,163 +364,159 @@ DN_API bool DN_BitIsNotSet(DN_USize bits, DN_USize bits_to_check) return result; } -// NOTE: DN_Safe /////////////////////////////////////////////////////////////////////////////////// -DN_API DN_I64 DN_SafeAddI64(int64_t a, int64_t b) +DN_API DN_I64 DN_SafeAddI64(DN_I64 a, DN_I64 b) { - DN_I64 result = DN_CheckF(a <= INT64_MAX - b, "a=%zd, b=%zd", a, b) ? (a + b) : INT64_MAX; + DN_I64 result = a <= INT64_MAX - b ? (a + b) : INT64_MAX; return result; } -DN_API DN_I64 DN_SafeMulI64(int64_t a, int64_t b) +DN_API DN_I64 DN_SafeMulI64(DN_I64 a, DN_I64 b) { - DN_I64 result = DN_CheckF(a <= INT64_MAX / b, "a=%zd, b=%zd", a, b) ? (a * b) : INT64_MAX; + DN_I64 result = a <= INT64_MAX / b ? (a * b) : INT64_MAX; return result; } DN_API DN_U64 DN_SafeAddU64(DN_U64 a, DN_U64 b) { - DN_U64 result = DN_CheckF(a <= UINT64_MAX - b, "a=%zu, b=%zu", a, b) ? (a + b) : UINT64_MAX; + DN_U64 result = a <= UINT64_MAX - b ? (a + b) : UINT64_MAX; return result; } DN_API DN_U64 DN_SafeSubU64(DN_U64 a, DN_U64 b) { - DN_U64 result = DN_CheckF(a >= b, "a=%zu, b=%zu", a, b) ? (a - b) : 0; + DN_U64 result = a >= b ? (a - b) : 0; return result; } DN_API DN_U64 DN_SafeMulU64(DN_U64 a, DN_U64 b) { - DN_U64 result = DN_CheckF(a <= UINT64_MAX / b, "a=%zu, b=%zu", a, b) ? (a * b) : UINT64_MAX; + DN_U64 result = a <= UINT64_MAX / b ? (a * b) : UINT64_MAX; return result; } DN_API DN_U32 DN_SafeSubU32(DN_U32 a, DN_U32 b) { - DN_U32 result = DN_CheckF(a >= b, "a=%u, b=%u", a, b) ? (a - b) : 0; + DN_U32 result = 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). +// NOTE: 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; + int result = DN_Cast(uintmax_t) val <= INT_MAX ? DN_Cast(int) val : INT_MAX; return result; } -DN_API int8_t DN_SaturateCastUSizeToI8(DN_USize val) +DN_API DN_I8 DN_SaturateCastUSizeToI8(DN_USize val) { - int8_t result = DN_Check(DN_Cast(uintmax_t) val <= INT8_MAX) ? DN_Cast(int8_t) val : INT8_MAX; + DN_I8 result = DN_Cast(uintmax_t) val <= INT8_MAX ? DN_Cast(DN_I8) 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; + DN_I16 result = 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; + DN_I32 result = 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) +DN_API DN_I64 DN_SaturateCastUSizeToI64(DN_USize val) { - int64_t result = DN_Check(DN_Cast(uintmax_t) val <= INT64_MAX) ? DN_Cast(int64_t) val : INT64_MAX; + DN_I64 result = DN_Cast(uintmax_t) val <= INT64_MAX ? DN_Cast(DN_I64) val : INT64_MAX; return result; } -// NOTE: DN_SaturateCastUSizeToU* //////////////////////////////////////////////////////////// -// Both operands are unsigned and the lowest rank operand will be promoted to +// NOTE: 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; + DN_U8 result = 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; + DN_U16 result = 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; + DN_U32 result = 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; + DN_U64 result = DN_Cast(DN_U64) val <= UINT64_MAX ? DN_Cast(DN_U64) val : UINT64_MAX; return result; } -// NOTE: DN_SaturateCastU64To* /////////////////////////////////////////////////////////////// +// NOTE: DN_SaturateCastU64To* DN_API int DN_SaturateCastU64ToInt(DN_U64 val) { - int result = DN_Check(val <= INT_MAX) ? DN_Cast(int) val : INT_MAX; + int result = val <= INT_MAX ? DN_Cast(int) val : INT_MAX; return result; } -DN_API int8_t DN_SaturateCastU64ToI8(DN_U64 val) +DN_API DN_I8 DN_SaturateCastU64ToI8(DN_U64 val) { - int8_t result = DN_Check(val <= INT8_MAX) ? DN_Cast(int8_t) val : INT8_MAX; + DN_I8 result = val <= INT8_MAX ? DN_Cast(DN_I8) 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; + DN_I16 result = 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; + DN_I32 result = val <= INT32_MAX ? DN_Cast(DN_I32) val : INT32_MAX; return result; } -DN_API int64_t DN_SaturateCastU64ToI64(DN_U64 val) +DN_API DN_I64 DN_SaturateCastU64ToI64(DN_U64 val) { - int64_t result = DN_Check(val <= INT64_MAX) ? DN_Cast(int64_t) val : INT64_MAX; + DN_I64 result = val <= INT64_MAX ? DN_Cast(DN_I64) 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) +// NOTE: Both operands are unsigned and the lowest rank operand will be promoted to match the +// highest rank operand. +DN_API DN_UInt DN_SaturateCastU64ToUInt(DN_U64 val) { - unsigned int result = DN_Check(val <= UINT8_MAX) ? DN_Cast(unsigned int) val : UINT_MAX; + DN_UInt result = val <= UINT8_MAX ? DN_Cast(DN_UInt) 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; + DN_U8 result = 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; + DN_U16 result = 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; + DN_U32 result = 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. +// NOTE: 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); @@ -524,10 +524,10 @@ DN_API int DN_SaturateCastISizeToInt(DN_ISize val) return result; } -DN_API int8_t DN_SaturateCastISizeToI8(DN_ISize val) +DN_API DN_I8 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); + DN_I8 result = DN_Cast(DN_I8) DN_Clamp(val, INT8_MIN, INT8_MAX); return result; } @@ -545,23 +545,21 @@ DN_API DN_I32 DN_SaturateCastISizeToI32(DN_ISize val) return result; } -DN_API int64_t DN_SaturateCastISizeToI64(DN_ISize val) +DN_API DN_I64 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); + DN_Assert(DN_Cast(DN_I64) val >= INT64_MIN && DN_Cast(DN_I64) val <= INT64_MAX); + DN_I64 result = DN_Cast(DN_I64) DN_Clamp(DN_Cast(DN_I64) 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) +// NOTE: 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 DN_UInt 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; + DN_UInt result = 0; + if (val >= DN_Cast(DN_ISize)0) { + if (DN_Cast(uintmax_t) val <= UINT_MAX) + result = DN_Cast(DN_UInt) val; else result = UINT_MAX; } @@ -571,8 +569,8 @@ DN_API unsigned int DN_SaturateCastISizeToUInt(DN_ISize val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT8_MAX) result = DN_Cast(DN_U8) val; else result = UINT8_MAX; @@ -583,8 +581,8 @@ DN_API DN_U8 DN_SaturateCastISizeToU8(DN_ISize val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT16_MAX) result = DN_Cast(DN_U16) val; else result = UINT16_MAX; @@ -595,8 +593,8 @@ DN_API DN_U16 DN_SaturateCastISizeToU16(DN_ISize val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT32_MAX) result = DN_Cast(DN_U32) val; else result = UINT32_MAX; @@ -607,8 +605,8 @@ DN_API DN_U32 DN_SaturateCastISizeToU32(DN_ISize val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT64_MAX) result = DN_Cast(DN_U64) val; else result = UINT64_MAX; @@ -616,54 +614,49 @@ DN_API DN_U64 DN_SaturateCastISizeToU64(DN_ISize val) 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) +// NOTE: Both operands are signed so the lowest rank operand will be promoted to match the highest +// rank operand. +DN_API DN_ISize DN_SaturateCastI64ToISize(DN_I64 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); + DN_ISize result = DN_Cast(DN_I64) DN_Clamp(val, DN_ISIZE_MIN, DN_ISIZE_MAX); return result; } -DN_API int8_t DN_SaturateCastI64ToI8(int64_t val) +DN_API DN_I8 DN_SaturateCastI64ToI8(DN_I64 val) { - DN_Check(val >= INT8_MIN && val <= INT8_MAX); - int8_t result = DN_Cast(int8_t) DN_Clamp(val, INT8_MIN, INT8_MAX); + DN_I8 result = DN_Cast(DN_I8) DN_Clamp(val, INT8_MIN, INT8_MAX); return result; } -DN_API DN_I16 DN_SaturateCastI64ToI16(int64_t val) +DN_API DN_I16 DN_SaturateCastI64ToI16(DN_I64 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_API DN_I32 DN_SaturateCastI64ToI32(DN_I64 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) +DN_API DN_UInt DN_SaturateCastI64ToUInt(DN_I64 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; + DN_UInt result = 0; + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= UINT_MAX) + result = DN_Cast(DN_UInt) val; else result = UINT_MAX; } return result; } -DN_API DN_ISize DN_SaturateCastI64ToUSize(int64_t val) +DN_API DN_USize DN_SaturateCastI64ToUSize(DN_I64 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)) + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= DN_USIZE_MAX) result = DN_Cast(DN_USize) val; else result = DN_USIZE_MAX; @@ -671,11 +664,11 @@ DN_API DN_ISize DN_SaturateCastI64ToUSize(int64_t val) return result; } -DN_API DN_U8 DN_SaturateCastI64ToU8(int64_t val) +DN_API DN_U8 DN_SaturateCastI64ToU8(DN_I64 val) { DN_U8 result = 0; - if (DN_Check(val >= DN_Cast(int64_t) 0)) { - if (DN_Check(DN_Cast(uintmax_t) val <= UINT8_MAX)) + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= UINT8_MAX) result = DN_Cast(DN_U8) val; else result = UINT8_MAX; @@ -683,11 +676,11 @@ DN_API DN_U8 DN_SaturateCastI64ToU8(int64_t val) return result; } -DN_API DN_U16 DN_SaturateCastI64ToU16(int64_t val) +DN_API DN_U16 DN_SaturateCastI64ToU16(DN_I64 val) { DN_U16 result = 0; - if (DN_Check(val >= DN_Cast(int64_t) 0)) { - if (DN_Check(DN_Cast(uintmax_t) val <= UINT16_MAX)) + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= UINT16_MAX) result = DN_Cast(DN_U16) val; else result = UINT16_MAX; @@ -695,11 +688,11 @@ DN_API DN_U16 DN_SaturateCastI64ToU16(int64_t val) return result; } -DN_API DN_U32 DN_SaturateCastI64ToU32(int64_t val) +DN_API DN_U32 DN_SaturateCastI64ToU32(DN_I64 val) { DN_U32 result = 0; - if (DN_Check(val >= DN_Cast(int64_t) 0)) { - if (DN_Check(DN_Cast(uintmax_t) val <= UINT32_MAX)) + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= UINT32_MAX) result = DN_Cast(DN_U32) val; else result = UINT32_MAX; @@ -707,11 +700,11 @@ DN_API DN_U32 DN_SaturateCastI64ToU32(int64_t val) return result; } -DN_API DN_U64 DN_SaturateCastI64ToU64(int64_t val) +DN_API DN_U64 DN_SaturateCastI64ToU64(DN_I64 val) { DN_U64 result = 0; - if (DN_Check(val >= DN_Cast(int64_t) 0)) { - if (DN_Check(DN_Cast(uintmax_t) val <= UINT64_MAX)) + if (val >= DN_Cast(DN_I64) 0) { + if (DN_Cast(uintmax_t) val <= UINT64_MAX) result = DN_Cast(DN_U64) val; else result = UINT64_MAX; @@ -719,16 +712,14 @@ DN_API DN_U64 DN_SaturateCastI64ToU64(int64_t val) return result; } -DN_API int8_t DN_SaturateCastIntToI8(int val) +DN_API DN_I8 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); + DN_I8 result = DN_Cast(DN_I8) 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; } @@ -736,8 +727,8 @@ DN_API DN_I16 DN_SaturateCastIntToI16(int val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT8_MAX) result = DN_Cast(DN_U8) val; else result = UINT8_MAX; @@ -748,8 +739,8 @@ DN_API DN_U8 DN_SaturateCastIntToU8(int val) 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)) + if (val >= DN_Cast(DN_ISize) 0) { + if (DN_Cast(uintmax_t) val <= UINT16_MAX) result = DN_Cast(DN_U16) val; else result = UINT16_MAX; @@ -759,27 +750,25 @@ DN_API DN_U16 DN_SaturateCastIntToU16(int val) DN_API DN_U32 DN_SaturateCastIntToU32(int val) { - static_assert(sizeof(val) <= sizeof(DN_U32), "Sanity check to allow simplifying of casting"); + DN_StaticAssert(sizeof(val) <= sizeof(DN_U32) && "Sanity check to allow simplifying of casting"); DN_U32 result = 0; - if (DN_Check(val >= 0)) + if (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_StaticAssert(sizeof(val) <= sizeof(DN_U64) && "Sanity check to allow simplifying of casting"); DN_U64 result = 0; - if (DN_Check(val >= 0)) + if (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_StaticAssert(DN_IsPowerOfTwoAligned(DN_ASAN_POISON_GUARD_SIZE, DN_ASAN_POISON_ALIGNMENT) && + "ASAN poison guard size must be a power-of-two and aligned to ASAN's alignment" "requirement (8 bytes)"); DN_API void DN_ASanPoisonMemoryRegion(void const volatile *ptr, DN_USize size) { if (!ptr || !size) @@ -998,14 +987,19 @@ DN_API bool DN_MemListCommitTo(DN_MemList *mem, DN_U64 pos) if (!mem || !mem->curr) return false; + // NOTE: Early out if the position to commit to is already committed DN_MemBlock *curr = mem->curr; if (pos <= curr->commit) return true; + // NOTE: Sanity check position is within the bounds of the memory block DN_U64 real_pos = pos; - if (!DN_Check(pos <= curr->reserve)) + if (pos > curr->reserve) { + DN_Assert(pos <= curr->reserve); real_pos = curr->reserve; + } + // NOTE: Do the commit DN_Assert(mem->funcs.virtual_page_size); DN_USize end_commit = DN_AlignUpPowerOfTwo(real_pos, mem->funcs.virtual_page_size); DN_USize commit_size = end_commit - curr->commit; @@ -1046,7 +1040,7 @@ DN_API bool DN_MemListGrow(DN_MemList *mem, DN_U64 reserve, DN_U64 commit) return result; } -DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, uint8_t align, DN_ZMem z_mem) +DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem z_mem) { if (!mem) return nullptr; @@ -1062,7 +1056,7 @@ DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, uint8_t align, DN_ZMe try_alloc_again: DN_MemBlock *curr = mem->curr; bool poison = DN_ArenaHasPoison_(mem->flags); - uint8_t real_align = poison ? DN_Max(align, DN_ASAN_POISON_ALIGNMENT) : align; + DN_U8 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; @@ -1114,7 +1108,7 @@ DN_API void *DN_MemListAlloc(DN_MemList *mem, DN_U64 size, uint8_t align, DN_ZMe return result; } -DN_API void *DN_MemListAllocContiguous(DN_MemList *mem, DN_U64 size, uint8_t align, DN_ZMem z_mem) +DN_API void *DN_MemListAllocContiguous(DN_MemList *mem, DN_U64 size, DN_U8 align, DN_ZMem z_mem) { DN_MemFlags prev_flags = mem->flags; mem->flags |= (DN_MemFlags_NoGrow | DN_MemFlags_NoPoison); @@ -1123,7 +1117,7 @@ DN_API void *DN_MemListAllocContiguous(DN_MemList *mem, DN_U64 size, uint8_t ali return memory; } -DN_API void *DN_MemListCopy(DN_MemList *mem, void const *data, DN_U64 size, uint8_t align) +DN_API void *DN_MemListCopy(DN_MemList *mem, void const *data, DN_U64 size, DN_U8 align) { if (!mem || !data || size == 0) return nullptr; @@ -1182,10 +1176,9 @@ DN_API void DN_MemListPopTo(DN_MemList *mem, DN_U64 init_used) DN_API void DN_MemListPop(DN_MemList *mem, DN_U64 amount) { DN_MemBlock *curr = mem->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_USize used_sum = curr->reserve_sum + curr->used; + amount = DN_Min(amount, used_sum); + DN_USize pop_to = used_sum - amount; DN_MemListPopTo(mem, pop_to); } @@ -1402,28 +1395,28 @@ DN_API void DN_ArenaTempEnd(DN_Arena *arena, DN_ArenaReset reset) #endif } -DN_API void *DN_ArenaAlloc(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem) +DN_API void *DN_ArenaAlloc(DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_mem) { DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListAlloc(arena->mem, size, align, z_mem); return result; } -DN_API void *DN_ArenaAllocContiguous(DN_Arena *arena, DN_U64 size, uint8_t align, DN_ZMem z_mem) +DN_API void *DN_ArenaAllocContiguous(DN_Arena *arena, DN_U64 size, DN_U8 align, DN_ZMem z_mem) { DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListAllocContiguous(arena->mem, size, align, z_mem); return result; } -DN_API void *DN_ArenaCopy(DN_Arena *arena, void const *data, DN_U64 size, uint8_t align) +DN_API void *DN_ArenaCopy(DN_Arena *arena, void const *data, DN_U64 size, DN_U8 align) { DN_ArenaUAFCheck_(arena, DN_ArenaUAFCheckReportType_AllocViolation); void *result = DN_MemListCopy(arena->mem, data, size, align); return result; } -DN_API DN_Pool DN_PoolFromArena(DN_Arena *arena, uint8_t align) +DN_API DN_Pool DN_PoolFromArena(DN_Arena *arena, DN_U8 align) { DN_Pool result = {}; if (arena) { @@ -1446,7 +1439,7 @@ DN_API void *DN_PoolAlloc(DN_Pool *pool, DN_USize size) 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 const DN_USizeo_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. @@ -1454,14 +1447,16 @@ DN_API void *DN_PoolAlloc(DN_Pool *pool, DN_USize size) dist_to_next_msb -= DN_Cast(DN_USize)(!DN_IsPowerOfTwo(required_size)); DN_USize const register_size = sizeof(DN_USize) * 8; - DN_AssertF(register_size >= (dist_to_next_msb - size_to_slot_offset), "lhs=%zu, rhs=%zu", register_size, (dist_to_next_msb - size_to_slot_offset)); - slot_index = register_size - dist_to_next_msb - size_to_slot_offset; + DN_AssertF(register_size >= (dist_to_next_msb - DN_USizeo_slot_offset), "lhs=%zu, rhs=%zu", register_size, (dist_to_next_msb - DN_USizeo_slot_offset)); + slot_index = register_size - dist_to_next_msb - DN_USizeo_slot_offset; } - if (!DN_CheckF(slot_index < DN_PoolSlotSize_Count, "Chunk pool does not support the requested allocation size")) + if (slot_index >= DN_PoolSlotSize_Count) { + DN_AssertF(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_USize slot_size_in_bytes = 1ULL << (slot_index + DN_USizeo_slot_offset); DN_AssertF(required_size <= (slot_size_in_bytes << 0), "slot_index=%zu, lhs=%zu, rhs=%zu", slot_index, required_size, (slot_size_in_bytes << 0)); DN_AssertF(required_size >= (slot_size_in_bytes >> 1), "slot_index=%zu, lhs=%zu, rhs=%zu", slot_index, required_size, (slot_size_in_bytes >> 1)); @@ -1545,8 +1540,8 @@ static void DN_ErrSinkCheck_(DN_ErrSink const *err) // NOTE: Walk the list ensuring we eventually terminate at the sentinel (e.g. we have a // well formed doubly-linked-list terminated by a sentinel, or otherwise we will hit the // walk limit or dereference a null pointer and assert) - size_t WALK_LIMIT = 99'999; - size_t walk = 0; + DN_USize WALK_LIMIT = 99'999; + DN_USize walk = 0; for (DN_ErrSinkMsg *it = node->msg_sentinel->next; it != node->msg_sentinel; it = it->next, walk++) { DN_AssertF(it, "Encountered null pointer which should not happen in a sentinel DLL"); DN_Assert(walk < WALK_LIMIT); @@ -1572,7 +1567,7 @@ DN_API DN_ErrSink* DN_ErrSinkBegin_(DN_ErrSink *err, DN_ErrSinkMode mode, DN_Cal DN_SentinelDoublyLLInitArena(node->msg_sentinel, DN_ErrSinkMsg, err->arena); // NOTE: Handle allocation error - if (!DN_Check(node && node->msg_sentinel)) { + if (!node || !node->msg_sentinel) { DN_MemListPopTo(err->arena->mem, node->arena_pos); node->msg_sentinel = nullptr; err->stack_size--; @@ -1756,14 +1751,13 @@ DN_API void DN_ErrSinkAppendFV_(DN_ErrSink *err, DN_U32 error_code, DN_CallSite DN_AssertF(node, "Error sink must be begun by calling 'Begin' before using this function."); DN_ErrSinkMsg *msg = DN_ArenaNew(err->arena, DN_ErrSinkMsg, DN_ZMem_Yes); - if (DN_Check(msg)) { - msg->msg = DN_Str8FromFmtVArena(err->arena, fmt, args); - msg->error_code = error_code; - msg->call_site = call_site; - DN_SentinelDoublyLLPrepend(node->msg_sentinel, msg); - if (node->mode == DN_ErrSinkMode_ExitOnError) - DN_ErrSinkEndExitIfErrorF_(err, msg->call_site, error_code, "Fatal error %u", error_code); - } + DN_Assert(msg); + msg->msg = DN_Str8FromFmtVArena(err->arena, fmt, args); + msg->error_code = error_code; + msg->call_site = call_site; + DN_SentinelDoublyLLPrepend(node->msg_sentinel, msg); + if (node->mode == DN_ErrSinkMode_ExitOnError) + DN_ErrSinkEndExitIfErrorF_(err, msg->call_site, error_code, "Fatal error %u", error_code); } DN_API void DN_ErrSinkAppendF_(DN_ErrSink *err, DN_U32 error_code, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, ...) @@ -1990,7 +1984,7 @@ DN_API DN_ErrSink *DN_TCErrSink() return result; } -DN_API void *DN_PoolCopy(DN_Pool *pool, void const *data, DN_U64 size, uint8_t align) +DN_API void *DN_PoolCopy(DN_Pool *pool, void const *data, DN_U64 size, DN_U8 align) { if (!pool || !data || size == 0) return nullptr; @@ -2087,7 +2081,7 @@ DN_API DN_U64FromResult DN_U64FromStr8(DN_Str8 string, char separator) return result; result.value = DN_SafeMulU64(result.value, 10); - uint64_t digit = ch - '0'; + DN_U64 digit = ch - '0'; result.value = DN_SafeAddU64(result.value, digit); } @@ -2244,9 +2238,9 @@ DN_API DN_I64FromResult DN_I64FromStr8(DN_Str8 string, char separator) if (!DN_CharIsDigit(ch)) return result; - result.value = DN_SafeMulU64(result.value, 10); - uint64_t digit = ch - '0'; - result.value = DN_SafeAddU64(result.value, digit); + result.value = DN_SafeMulU64(result.value, 10); + DN_U64 digit = ch - '0'; + result.value = DN_SafeAddU64(result.value, digit); } if (negative) @@ -2864,7 +2858,7 @@ DN_API DN_Str8BSplitResult DN_Str8BSplitArray(DN_Str8 string, DN_Str8 const *fin return result; result.lhs = string; - for (size_t index = 0; !result.rhs.data && index < string.size; index++) { + for (DN_USize index = 0; !result.rhs.data && index < string.size; index++) { for (DN_USize find_index = 0; find_index < find_size; find_index++) { DN_Str8 find_item = find[find_index]; DN_Str8 string_slice = DN_Str8Subset(string, index, find_item.size); @@ -2894,7 +2888,7 @@ DN_API DN_Str8BSplitResult DN_Str8BSplitLastArray(DN_Str8 string, DN_Str8 const return result; result.lhs = string; - for (size_t index = string.size - 1; !result.rhs.data && index < string.size; index--) { + for (DN_USize index = string.size - 1; !result.rhs.data && index < string.size; index--) { for (DN_USize find_index = 0; find_index < find_size; find_index++) { DN_Str8 find_item = find[find_index]; DN_Str8 string_slice = DN_Str8Subset(string, index, find_item.size); @@ -2990,7 +2984,7 @@ DN_API DN_Str8FindResult DN_Str8FindStr8(DN_Str8 string, DN_Str8 find, DN_Str8Eq DN_API DN_Str8FindResult DN_Str8Find(DN_Str8 string, DN_Str8FindFlag flags) { DN_Str8FindResult result = {}; - for (size_t index = 0; !result.found && index < string.size; index++) { + for (DN_USize index = 0; !result.found && index < string.size; index++) { result.found |= ((flags & DN_Str8FindFlag_Digit) && DN_CharIsDigit(string.data[index])); result.found |= ((flags & DN_Str8FindFlag_Alphabet) && DN_CharIsAlphabet(string.data[index])); result.found |= ((flags & DN_Str8FindFlag_Whitespace) && DN_CharIsWhitespace(string.data[index])); @@ -3051,7 +3045,7 @@ DN_API DN_Str8 DN_Str8ReverseSegment(DN_Arena *arena, DN_Str8 src, DN_USize segm DN_MSVC_WARNING_PUSH DN_MSVC_WARNING_DISABLE(6293) // NOTE: Ill-defined loop - for (size_t src_index = src.size - 1; src_index < src.size; src_index--) { + for (DN_USize src_index = src.size - 1; src_index < src.size; src_index--) { DN_MSVC_WARNING_POP result.data[write_index--] = src.data[src_index]; if (++write_counter % segment_size == 0 && segment_counter < segments) { @@ -3628,6 +3622,276 @@ DN_API DN_Str8 DN_Str8Table(DN_Str8 const *rows, DN_USize num_rows, DN_USize num return result; } +#if DN_STR8_AVX512F +DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F(DN_Str8 string, DN_Str8 find) +{ + // NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html + DN_Str8FindResult result = {}; + if (string.size == 0 || find.size == 0 || find.size > string.size) + return result; + + __m512i const find_first_ch = _mm512_set1_epi8(find.data[0]); + __m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]); + + DN_USize const search_size = string.size - find.size; + DN_USize simd_iterations = search_size / sizeof(__m512i); + char const *ptr = string.data; + + while (simd_iterations--) { + __m512i find_first_ch_block = _mm512_loadu_si512(ptr); + __m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1); + + // NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit + // where matches are found. + __m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch); + + // NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one + // operation using the ternarylogic intrinsic. + // + // A = first_ch_matches (find_first_ch_block ^ find_first_ch) + // B = find_last_ch_block + // C = find_last_ch + // + // ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6 + // + // / A / B / C / B ^ C / A | (B ^ C) / + // | 0 | 0 | 0 | 0 | 0 | + // | 0 | 0 | 1 | 1 | 1 | + // | 0 | 1 | 0 | 1 | 1 | + // | 0 | 1 | 1 | 0 | 0 | + // | 1 | 0 | 0 | 0 | 1 | + // | 1 | 0 | 1 | 1 | 1 | + // | 1 | 1 | 0 | 1 | 1 | + // | 1 | 1 | 1 | 0 | 1 | + + __m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6); + + // NOTE: Matches were XOR-ed and are hence indicated as zero so we mask + // out which 32 bit elements in the vector had zero bytes. This uses a + // bit twiddling trick + // https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord + __mmask16 zero_byte_mask = {}; + { + const __m512i v01 = _mm512_set1_epi32(0x01010101u); + const __m512i v80 = _mm512_set1_epi32(0x80808080u); + const __m512i v1 = _mm512_sub_epi32(ch_matches, v01); + const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20); + zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1); + } + + while (zero_byte_mask) { + uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask); + char const *base_ptr = ptr + (4 * lsb_zero_pos); + + if (DN_Memcmp(base_ptr + 0, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data; + } else if (DN_Memcmp(base_ptr + 1, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 1; + } else if (DN_Memcmp(base_ptr + 2, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 2; + } else if (DN_Memcmp(base_ptr + 3, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 3; + } + + if (result.found) { + result.start_to_before_match = DN_Str8FromPtr(string.data, result.index); + result.match = DN_Str8FromPtr(string.data + result.index, find.size); + result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - result.index); + result.after_match_to_end_of_buffer = DN_Str8Advance(result.match_to_end_of_buffer, find.size); + return result; + } + + zero_byte_mask = DN_BitClearNextLSB(zero_byte_mask); + } + + ptr += sizeof(__m512i); + } + + for (DN_USize index = ptr - string.data; index < string.size; index++) { + DN_Str8 string_slice = DN_Str8Subset(string, index, find.size); + if (DN_Str8Eq(string_slice, find)) { + result.found = true; + result.index = index; + result.start_to_before_match = DN_Str8FromPtr(string.data, index); + result.match = DN_Str8FromPtr(string.data + index, find.size); + result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - index); + result.after_match_to_end_of_buffer = DN_Str8Advance(result.match_to_end_of_buffer, find.size); + return result; + } + } + + return result; +} + +DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F(DN_Str8 string, DN_Str8 find) +{ + // NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html + DN_Str8FindResult result = {}; + if (string.size == 0 || find.size == 0 || find.size > string.size) + return result; + + __m512i const find_first_ch = _mm512_set1_epi8(find.data[0]); + __m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]); + + DN_USize const search_size = string.size - find.size; + DN_USize simd_iterations = search_size / sizeof(__m512i); + char const *ptr = string.data + search_size + 1; + + while (simd_iterations--) { + ptr -= sizeof(__m512i); + __m512i find_first_ch_block = _mm512_loadu_si512(ptr); + __m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1); + + // NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit + // where matches are found. + __m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch); + + // NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one + // operation using the ternarylogic intrinsic. + // + // A = first_ch_matches (find_first_ch_block ^ find_first_ch) + // B = find_last_ch_block + // C = find_last_ch + // + // ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6 + // + // / A / B / C / B ^ C / A | (B ^ C) / + // | 0 | 0 | 0 | 0 | 0 | + // | 0 | 0 | 1 | 1 | 1 | + // | 0 | 1 | 0 | 1 | 1 | + // | 0 | 1 | 1 | 0 | 0 | + // | 1 | 0 | 0 | 0 | 1 | + // | 1 | 0 | 1 | 1 | 1 | + // | 1 | 1 | 0 | 1 | 1 | + // | 1 | 1 | 1 | 0 | 1 | + + __m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6); + + // NOTE: Matches were XOR-ed and are hence indicated as zero so we mask + // out which 32 bit elements in the vector had zero bytes. This uses a + // bit twiddling trick + // https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord + __mmask16 zero_byte_mask = {}; + { + const __m512i v01 = _mm512_set1_epi32(0x01010101u); + const __m512i v80 = _mm512_set1_epi32(0x80808080u); + const __m512i v1 = _mm512_sub_epi32(ch_matches, v01); + const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20); + zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1); + } + + while (zero_byte_mask) { + uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask); + char const *base_ptr = ptr + (4 * lsb_zero_pos); + + if (DN_Memcmp(base_ptr + 0, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data; + } else if (DN_Memcmp(base_ptr + 1, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 1; + } else if (DN_Memcmp(base_ptr + 2, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 2; + } else if (DN_Memcmp(base_ptr + 3, find.data, find.size) == 0) { + result.found = true; + result.index = base_ptr - string.data + 3; + } + + if (result.found) { + result.start_to_before_match = DN_Str8FromPtr(string.data, result.index); + result.match = DN_Str8FromPtr(string.data + result.index, find.size); + result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - result.index); + return result; + } + + zero_byte_mask = DN_BitClearNextLSB(zero_byte_mask); + } + } + + for (DN_USize index = ptr - string.data - 1; index < string.size; index--) { + DN_Str8 string_slice = DN_Str8Subset(string, index, find.size); + if (DN_Str8Eq(string_slice, find)) { + result.found = true; + result.index = index; + result.start_to_before_match = DN_Str8FromPtr(string.data, index); + result.match = DN_Str8FromPtr(string.data + index, find.size); + result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - index); + return result; + } + } + + return result; +} + +DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F(DN_Str8 string, DN_Str8 find) +{ + DN_Str8BSplitResult result = {}; + DN_Str8FindResult find_result = DN_Str8FindAVX512F(string, find); + if (find_result.found) { + result.lhs.data = string.data; + result.lhs.size = find_result.index; + result.rhs = DN_Str8Advance(find_result.match_to_end_of_buffer, find.size); + } else { + result.lhs = string; + } + + return result; +} + +DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F(DN_Str8 string, DN_Str8 find) +{ + DN_Str8BSplitResult result = {}; + DN_Str8FindResult find_result = DN_Str8FindLastAVX512F(string, find); + if (find_result.found) { + result.lhs.data = string.data; + result.lhs.size = find_result.index; + result.rhs = DN_Str8Advance(find_result.match_to_end_of_buffer, find.size); + } else { + result.lhs = string; + } + + return result; +} + +DN_API DN_USize DN_Str8SplitAVX512F(DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags flags) +{ + DN_USize result = 0; // The number of splits in the actual string. + if (string.size == 0 || delimiter.size == 0 || delimiter.size <= 0) + return result; + + DN_Str8BSplitResult split = {}; + DN_Str8 first = string; + do { + split = DN_Str8BSplitAVX512F(first, delimiter); + if (split.lhs.size || DN_BitIsNotSet(flags, DN_Str8SplitFlags_ExcludeEmptyStrings)) { + if (splits && result < splits_count) + splits[result] = split.lhs; + result++; + } + first = split.rhs; + } while (first.size); + + return result; +} + +DN_API DN_Str8Slice DN_Str8SplitAllocAVX512F(DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags flags) +{ + DN_Str8Slice result = {}; + DN_USize splits_required = DN_Str8SplitAVX512F(string, delimiter, /*splits*/ nullptr, /*count*/ 0, flags); + result.data = DN_ArenaNewArray(arena, DN_Str8, splits_required, DN_ZMem_No); + if (result.data) { + result.count = DN_Str8SplitAVX512F(string, delimiter, result.data, splits_required, flags); + DN_Assert(splits_required == result.count); + } + return result; +} +#endif // DN_STR8_AVX512F + DN_API DN_Str8 DN_Str8SliceRender(DN_Str8Slice slice, DN_Str8 separator, DN_Arena *arena) { DN_Str8 result = {}; @@ -4103,7 +4367,7 @@ DN_API int DN_UTF8Encode(DN_U8 utf8[4], DN_U32 codepoint) // ----------------------------------------+----------------------------+--------------------+ if (codepoint <= 0b0111'1111) { - utf8[0] = DN_Cast(uint8_t) codepoint; + utf8[0] = DN_Cast(DN_U8) codepoint; return 1; } @@ -5127,7 +5391,7 @@ DN_API DN_F32 DN_PCG32NextF32(DN_PCG32 *rng) DN_API DN_F64 DN_PCG32NextF64(DN_PCG32 *rng) { DN_U64 x = DN_PCG32Next64(rng); - return (DN_F64)(int64_t)(x >> 11) * 0x1.0p-53; + return (DN_F64)(DN_I64)(x >> 11) * 0x1.0p-53; } DN_API void DN_PCG32Advance(DN_PCG32 *rng, DN_U64 delta) @@ -6871,15 +7135,15 @@ DN_API DN_Str8x256 DN_M4ColumnMajorString(DN_M4 mat) return result; } -DN_API bool operator==(DN_M2x3 const &lhs, DN_M2x3 const &rhs) +DN_API bool DN_M2x3Eq(DN_M2x3 const *lhs, DN_M2x3 const *rhs) { - bool result = DN_Memcmp(lhs.e, rhs.e, sizeof(lhs.e[0]) * DN_ArrayCountU(lhs.e)) == 0; + bool result = DN_Memcmp(lhs->e, rhs->e, sizeof(lhs->e[0]) * DN_ArrayCountU(lhs->e)) == 0; return result; } -DN_API bool operator!=(DN_M2x3 const &lhs, DN_M2x3 const &rhs) +DN_API bool DN_M2x3NotEq(DN_M2x3 const *lhs, DN_M2x3 const *rhs) { - bool result = !(lhs == rhs); + bool result = !DN_M2x3Eq(lhs, rhs); return result; } @@ -7072,12 +7336,6 @@ DN_API DN_Rect DN_M2x3MulRect(DN_M2x3 m1, DN_Rect rect) return result; } -DN_API bool operator==(const DN_Rect &lhs, const DN_Rect &rhs) -{ - bool result = (lhs.pos == rhs.pos) && (lhs.size == rhs.size); - return result; -} - DN_API DN_V2F32 DN_RectCenter(DN_Rect rect) { DN_V2F32 result = rect.pos + (rect.size * .5f); @@ -7313,3 +7571,1440 @@ DN_API DN_RaycastV2 DN_RaycastLineIntersectV2(DN_V2F32 origin_a, DN_V2F32 dir_a, } return result; } + +struct DN_ArrayFindEqMemcmpContext_ +{ + DN_USize elem_size; + void const *find; +}; + +DN_API void *DN_SliceAllocArena(void **data, DN_USize *slice_size_field, DN_USize size, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena) +{ + void *result = *data; + *data = DN_ArenaAlloc(arena, size * elem_size, align, zmem); + if (*data) + *slice_size_field = size; + return result; +} + +DN_API DN_ArrayFindResult DN_ArrayFind(void *data, DN_USize size, DN_USize elem_size, void const *find, DN_ArrayFindEqFunc *eq_func) +{ + DN_ArrayFindResult result = {}; + DN_Assert(data); + DN_Assert(elem_size); + if (find) { + for (DN_ForIndexU(index, size)) { + DN_U8 *it = DN_Cast(DN_U8 *) data + (index * elem_size); + if (eq_func(it, find)) { + result.index = index; + result.value = it; + result.success = true; + break; + } + } + } + return result; +} + +static bool DN_ArrayFindEqMemEqUnsafe_(void const *lhs, void const *find) +{ + DN_ArrayFindEqMemcmpContext_ *context = DN_Cast(DN_ArrayFindEqMemcmpContext_ *) find; + bool result = DN_MemEqUnsafe(lhs, context->find, context->elem_size); + return result; +} + +DN_API DN_ArrayFindResult DN_ArrayFindMemEq(void *data, DN_USize size, DN_USize elem_size, void const *find) +{ + DN_ArrayFindEqMemcmpContext_ context = {}; + context.elem_size = elem_size; + context.find = find; + DN_ArrayFindResult result = DN_ArrayFind(data, size, elem_size, &context, DN_ArrayFindEqMemEqUnsafe_); + return result; +} + +DN_API void *DN_ArrayInsertArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count) +{ + void *result = nullptr; + if (!data || !size || !items || count <= 0 || ((*size + count) > max)) + return result; + + DN_USize clamped_index = DN_Min(index, *size); + if (clamped_index != *size) { + char const *src = DN_Cast(char *)data + (clamped_index * elem_size); + char const *dest = DN_Cast(char *)data + ((clamped_index + count) * elem_size); + char const *end = DN_Cast(char *)data + (size[0] * elem_size); + DN_USize bytes_to_move = end - src; + DN_Memmove(DN_Cast(void *) dest, src, bytes_to_move); + } + + result = DN_Cast(char *)data + (clamped_index * elem_size); + DN_Memcpy(result, items, elem_size * count); + *size += count; + return result; +} + +DN_API void *DN_ArrayPopFront(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) +{ + if (!data || !size || *size == 0 || count == 0) + return nullptr; + + DN_USize pop_count = DN_Min(count, *size); + void *result = data; + + if (pop_count < *size) { + char *src = DN_Cast(char *)data + (pop_count * elem_size); + char *dest = DN_Cast(char *)data; + DN_USize bytes_to_move = (*size - pop_count) * elem_size; + DN_Memmove(dest, src, bytes_to_move); + } + + *size -= pop_count; + return result; +} + +DN_API void *DN_ArrayPopBack(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) +{ + if (!data || !size || *size == 0 || count == 0) + return nullptr; + + DN_USize pop_count = DN_Min(count, *size); + *size -= pop_count; + + return DN_Cast(char *)data + (*size * elem_size); +} + +DN_API DN_ArrayEraseResult DN_ArrayEraseRange(void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase) +{ + DN_ArrayEraseResult result = {}; + result.it_index = begin_index; + if (!data || !size || *size == 0 || count == 0) + return result; + + // Compute the range to erase + DN_USize start = 0, end = 0; + if (count < 0) { + // Erase backwards from begin_index, not inclusive of begin_index + // Range: [begin_index + count, begin_index) + // Which is: [begin_index - abs(count), begin_index) + DN_USize abs_count = DN_Abs(count); + start = (begin_index > abs_count) ? (begin_index - abs_count) : 0; + end = begin_index; + } else { + start = begin_index; + end = begin_index + count; + } + + // Clamp indices to valid bounds + start = DN_Min(start, *size); + end = DN_Min(end, *size); + + // Erase the range [start, end) + DN_USize erase_count = end > start ? end - start : 0; + if (erase_count) { + char *dest = (char *)data + (elem_size * start); + char *array_end = (char *)data + (elem_size * *size); + char *src = dest + (elem_size * erase_count); + if (erase == DN_ArrayErase_Stable) { + DN_USize move_size = array_end - src; + DN_Memmove(dest, src, move_size); + } else { + char *unstable_src = array_end - (elem_size * erase_count); + DN_USize move_size = array_end - unstable_src; + DN_Memcpy(dest, unstable_src, move_size); + } + *size -= erase_count; + } + + result.items_erased = erase_count; + // NOTE: If we are erasing from the current index of the iterator to the end of the array then + // there's no more elements in the array to iterate. So the returned index should b + // one-past-last index + if (begin_index == start && end >= *size) { + result.it_index = *size; + } else { + result.it_index = start ? start - 1 : 0; + } + return result; +} + +DN_API void *DN_ArrayMakeArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem) +{ + void *result = nullptr; + DN_USize new_size = *size + make_count; + if (new_size <= max) { + result = DN_Cast(char *) data + (elem_size * size[0]); + *size = new_size; + if (z_mem == DN_ZMem_Yes) + DN_Memset(result, 0, elem_size * make_count); + } + return result; +} + +DN_API void *DN_ArrayMakeArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem, DN_CallSite call_site) +{ + void *result = DN_ArrayMakeArray(data, size, max, elem_size, make_count, z_mem); + DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max); + return result; +} + +DN_API void *DN_ArrayAddArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add) +{ + void *result = DN_ArrayMakeArray(data, size, max, elem_size, elems_count, DN_ZMem_No); + if (result) { + if (add == DN_ArrayAdd_Append) { + DN_Memcpy(result, elems, elems_count * elem_size); + } else { + char *move_dest = DN_Cast(char *)data + (elems_count * elem_size); // Shift elements forward + char *move_src = DN_Cast(char *)data; + DN_Memmove(move_dest, move_src, elem_size * size[0]); + DN_Memcpy(data, elems, elem_size * elems_count); + } + } + return result; +} + +DN_API void *DN_ArrayAddArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add, DN_CallSite call_site) +{ + void *result = DN_ArrayAddArray(data, size, max, elem_size, elems, elems_count, add); + DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max); + return result; +} + +DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) +{ + bool result = true; + if (new_max != *max) { + DN_USize bytes_to_alloc = elem_size * new_max; + void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc); + if (buffer) { + DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); + DN_Memcpy(buffer, *data, bytes_to_copy); + DN_PoolDealloc(pool, *data); + *data = buffer; + *max = new_max; + *size = DN_Min(*size, new_max); + } else { + result = false; + } + } + + return result; +} + +DN_API bool DN_ArrayResizeFromPool(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) +{ + bool result = true; + if (new_max != *max) { + DN_USize bytes_to_alloc = elem_size * new_max; + void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc); + if (buffer) { + DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); + DN_Memcpy(buffer, *data, bytes_to_copy); + DN_PoolDealloc(pool, *data); + *data = buffer; + *max = new_max; + *size = DN_Min(*size, new_max); + } else { + result = false; + } + } + + return result; +} + +DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max) +{ + bool result = true; + if (new_max != *max) { + DN_USize bytes_to_alloc = elem_size * new_max; + void *buffer = DN_ArenaNewArray(arena, DN_U8, bytes_to_alloc, DN_ZMem_No); + if (buffer) { + DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); + DN_Memcpy(buffer, *data, bytes_to_copy); + *data = buffer; + *max = new_max; + *size = DN_Min(*size, new_max); + } else { + result = false; + } + } + + return result; +} + +DN_API bool DN_ArrayGrowFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) +{ + bool result = true; + if (new_max > *max) + result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max); + return result; +} + +DN_API bool DN_ArrayGrowFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max) +{ + bool result = true; + if (new_max > *max) + result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max); + return result; +} + + +DN_API bool DN_ArrayGrowIfNeededFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize add_count) +{ + bool result = true; + DN_USize new_size = size + add_count; + if (new_size > *max) { + DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8); + result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max); + } + return result; +} + +DN_API bool DN_ArrayGrowIfNeededFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize add_count) +{ + bool result = true; + DN_USize new_size = size + add_count; + if (new_size > *max) { + DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8); + result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max); + } + return result; +} + +DN_API void *DN_SinglyLLDetach(void **link, void **next) +{ + void *result = *link; + if (*link) { + *link = *next; + *next = nullptr; + } + return result; +} + +DN_API bool DN_RingHasSpace(DN_Ring const *ring, DN_U64 size) +{ + DN_U64 avail = ring->write_pos - ring->read_pos; + DN_U64 space = ring->size - avail; + bool result = space >= size; + return result; +} + +DN_API bool DN_RingHasData(DN_Ring const *ring, DN_U64 size) +{ + DN_U64 data = ring->write_pos - ring->read_pos; + bool result = data >= size; + return result; +} + +DN_API void DN_RingWrite(DN_Ring *ring, void const *src, DN_U64 src_size) +{ + DN_Assert(src_size <= ring->size); + DN_U64 offset = ring->write_pos % ring->size; + DN_U64 bytes_before_split = ring->size - offset; + DN_U64 pre_split_bytes = DN_Min(bytes_before_split, src_size); + DN_U64 post_split_bytes = src_size - pre_split_bytes; + void const *pre_split_data = src; + void const *post_split_data = ((char *)src + pre_split_bytes); + DN_Memcpy(ring->base + offset, pre_split_data, pre_split_bytes); + DN_Memcpy(ring->base, post_split_data, post_split_bytes); + ring->write_pos += src_size; +} + +DN_API void DN_RingRead(DN_Ring *ring, void *dest, DN_U64 dest_size) +{ + DN_Assert(dest_size <= ring->size); + DN_U64 offset = ring->read_pos % ring->size; + DN_U64 bytes_before_split = ring->size - offset; + DN_U64 pre_split_bytes = DN_Min(bytes_before_split, dest_size); + DN_U64 post_split_bytes = dest_size - pre_split_bytes; + DN_Memcpy(dest, ring->base + offset, pre_split_bytes); + DN_Memcpy((char *)dest + pre_split_bytes, ring->base, post_split_bytes); + ring->read_pos += dest_size; +} + +#if defined(__cplusplus) +template +DN_DSMap DN_DSMapInit(DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags) +{ + DN_AssertF(DN_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size); + DN_Assert(arena); + + DN_DSMap result = {}; + if (size <= 0) + return result; + if (!arena) + return result; + + result.arena = arena; + result.pool = DN_PoolFromArena(arena, DN_POOL_DEFAULT_ALIGN); + result.hash_to_slot = DN_ArenaNewArray(result.arena, DN_U32, size, DN_ZMem_Yes); + result.slots = DN_ArenaNewArray(result.arena, DN_DSMapSlot, size, DN_ZMem_Yes); + result.occupied = 1; // For sentinel + result.size = size; + result.initial_size = size; + result.flags = flags; + DN_AssertF(result.hash_to_slot && result.slots, "We pre-allocated a block of memory sufficient in size for the 2 arrays. Maybe the pointers needed extra space because of natural alignment?"); + return result; +} + +template +void DN_DSMapDeinit(DN_DSMap *map, DN_ZMem z_mem) +{ + if (!map) + return; + // TODO(doyle): Use z_mem + (void)z_mem; + DN_MemListDeinit(map->arena->mem); + *map = {}; +} + +template +bool DN_DSMapIsValid(DN_DSMap const *map) +{ + bool result = map && + map->arena && + map->hash_to_slot && // Hash to slot mapping array must be allocated + map->slots && // Slots array must be allocated + (map->size & (map->size - 1)) == 0 && // Must be power of two size + map->occupied >= 1; // DN_DS_MAP_SENTINEL_SLOT takes up one slot + return result; +} + +template +DN_U32 DN_DSMapHash(DN_DSMap const *map, DN_DSMapKey key) +{ + DN_U32 result = 0; + if (!map) + return result; + + if (key.type == DN_DSMapKeyType_U64NoHash) { + result = DN_Cast(DN_U32) key.u64; + return result; + } + + if (key.type == DN_DSMapKeyType_BufferAsU64NoHash) { + result = key.hash; + return result; + } + + DN_U32 seed = map->hash_seed ? map->hash_seed : DN_DS_MAP_DEFAULT_HASH_SEED; + if (map->hash_function) { + map->hash_function(key, seed); + } else { + // NOTE: Courtesy of Demetri Spanos (which this hash table was inspired + // from), the following is a hashing function snippet provided for + // reliable, quick and simple quality hashing functions for hash table + // use. + // Source: https://github.com/demetri/scribbles/blob/c475464756c104c91bab83ed4e14badefef12ab5/hashing/ub_aware_hash_functions.c + + char const *key_ptr = nullptr; + DN_U32 len = 0; + DN_U32 h = seed; + switch (key.type) { + case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/ + case DN_DSMapKeyType_U64NoHash: DN_InvalidCodePath; /*FALLTHRU*/ + case DN_DSMapKeyType_Invalid: break; + + case DN_DSMapKeyType_Buffer: + key_ptr = DN_Cast(char const *) key.buffer_data; + len = key.buffer_size; + break; + + case DN_DSMapKeyType_U64: + key_ptr = DN_Cast(char const *) & key.u64; + len = sizeof(key.u64); + break; + } + + // Murmur3 32-bit without UB unaligned accesses + // DN_U32 mur3_32_no_UB(const void *key, int len, DN_U32 h) + + // main body, work on 32-bit blocks at a time + for (DN_U32 i = 0; i < len / 4; i++) { + DN_U32 k; + memcpy(&k, &key_ptr[i * 4], sizeof(k)); + + k *= 0xcc9e2d51; + k = ((k << 15) | (k >> 17)) * 0x1b873593; + h = (((h ^ k) << 13) | ((h ^ k) >> 19)) * 5 + 0xe6546b64; + } + + // load/mix up to 3 remaining tail bytes into a tail block + DN_U32 t = 0; + uint8_t *tail = ((uint8_t *)key_ptr) + 4 * (len / 4); + switch (len & 3) { + case 3: t ^= tail[2] << 16; + case 2: t ^= tail[1] << 8; + case 1: { + t ^= tail[0] << 0; + h ^= ((0xcc9e2d51 * t << 15) | (0xcc9e2d51 * t >> 17)) * 0x1b873593; + } + } + + // finalization mix, including key length + h = ((h ^ len) ^ ((h ^ len) >> 16)) * 0x85ebca6b; + h = (h ^ (h >> 13)) * 0xc2b2ae35; + result = h ^ (h >> 16); + } + return result; +} + +template +DN_U32 DN_DSMapHashToSlotIndex(DN_DSMap const *map, DN_DSMapKey key) +{ + DN_Assert(key.type != DN_DSMapKeyType_Invalid); + DN_U32 result = DN_DS_MAP_SENTINEL_SLOT; + if (!DN_DSMapIsValid(map)) + return result; + + result = key.hash & (map->size - 1); + for (;;) { + if (result == DN_DS_MAP_SENTINEL_SLOT) // Sentinel is reserved + result++; + + if (map->hash_to_slot[result] == DN_DS_MAP_SENTINEL_SLOT) // Slot is vacant, can use + return result; + + DN_DSMapSlot *slot = map->slots + map->hash_to_slot[result]; + if (slot->key.type == DN_DSMapKeyType_Invalid || (slot->key.hash == key.hash && slot->key == key)) + return result; + + result = (result + 1) & (map->size - 1); + } +} + +template +DN_DSMapResult DN_DSMapFind(DN_DSMap const *map, DN_DSMapKey key) +{ + DN_DSMapResult result = {}; + if (DN_DSMapIsValid(map)) { + DN_U32 index = DN_DSMapHashToSlotIndex(map, key); + if (index != DN_DS_MAP_SENTINEL_SLOT && map->hash_to_slot[index] == DN_DS_MAP_SENTINEL_SLOT) { + result.slot = map->slots; // NOTE: Set to sentinel value + } else { + result.slot = map->slots + map->hash_to_slot[index]; + result.found = true; + } + result.value = &result.slot->value; + } + return result; +} + +template +DN_DSMapResult DN_DSMapMake(DN_DSMap *map, DN_DSMapKey key) +{ + DN_DSMapResult result = {}; + if (!DN_DSMapIsValid(map)) + return result; + + DN_U32 index = DN_DSMapHashToSlotIndex(map, key); + if (map->hash_to_slot[index] == DN_DS_MAP_SENTINEL_SLOT) { + // NOTE: Create the slot + if (index != DN_DS_MAP_SENTINEL_SLOT) + map->hash_to_slot[index] = map->occupied++; + + // NOTE: Check if resize is required + bool map_is_75pct_full = (map->occupied * 4) > (map->size * 3); + if (map_is_75pct_full) { + if (!DN_DSMapResize(map, map->size * 2)) + return result; + result = DN_DSMapMake(map, key); + } else { + result.slot = map->slots + map->hash_to_slot[index]; + result.slot->key = key; // NOTE: Assign key to new slot + if ((key.type == DN_DSMapKeyType_Buffer || + key.type == DN_DSMapKeyType_BufferAsU64NoHash) && + !key.no_copy_buffer) + result.slot->key.buffer_data = DN_PoolNewArrayCopy(&map->pool, char, key.buffer_data, key.buffer_size); + } + } else { + result.slot = map->slots + map->hash_to_slot[index]; + result.found = true; + } + + result.value = &result.slot->value; + DN_Assert(result.slot->key.type != DN_DSMapKeyType_Invalid); + return result; +} + +template +DN_DSMapResult DN_DSMapSet(DN_DSMap *map, DN_DSMapKey key, T const &value) +{ + DN_DSMapResult result = {}; + if (!DN_DSMapIsValid(map)) + return result; + + result = DN_DSMapMake(map, key); + result.slot->value = value; + return result; +} + +template +DN_DSMapResult DN_DSMapFindKeyU64(DN_DSMap const *map, DN_U64 key) +{ + DN_DSMapKey map_key = DN_DSMapKeyU64(map, key); + DN_DSMapResult result = DN_DSMapFind(map, map_key); + return result; +} + +template +DN_DSMapResult DN_DSMapMakeKeyU64(DN_DSMap *map, DN_U64 key) +{ + DN_DSMapKey map_key = DN_DSMapKeyU64(map, key); + DN_DSMapResult result = DN_DSMapMake(map, map_key); + return result; +} + +template +DN_DSMapResult DN_DSMapSetKeyU64(DN_DSMap *map, DN_U64 key, T const &value) +{ + DN_DSMapKey map_key = DN_DSMapKeyU64(map, key); + DN_DSMapResult result = DN_DSMapSet(map, map_key, value); + return result; +} + +template +DN_DSMapResult DN_DSMapFindKeyStr8(DN_DSMap const *map, DN_Str8 key) +{ + DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key); + DN_DSMapResult result = DN_DSMapFind(map, map_key); + return result; +} + +template +DN_DSMapResult DN_DSMapMakeKeyStr8(DN_DSMap *map, DN_Str8 key) +{ + DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key); + DN_DSMapResult result = DN_DSMapMake(map, map_key); + return result; +} + +template +DN_DSMapResult DN_DSMapSetKeyStr8(DN_DSMap *map, DN_Str8 key, T const &value) +{ + DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key); + DN_DSMapResult result = DN_DSMapSet(map, map_key); + return result; +} + +template +bool DN_DSMapResize(DN_DSMap *map, DN_U32 size) +{ + if (!DN_DSMapIsValid(map) || size < map->occupied || size < map->initial_size) + return false; + + DN_Arena *prev_arena = map->arena; + DN_MemList *new_mem = prev_arena->mem; + DN_MemList prev_mem = *prev_arena->mem; + prev_arena->mem = &prev_mem; + + *new_mem = {}; + new_mem->funcs = prev_mem.funcs; + new_mem->flags = prev_mem.flags; + + DN_Arena new_arena = {}; + new_arena.mem = new_mem; + + DN_DSMap new_map = DN_DSMapInit(&new_arena, size, map->flags); + if (!DN_DSMapIsValid(&new_map)) + return false; + + new_map.initial_size = map->initial_size; + for (DN_U32 old_index = 1 /*Sentinel*/; old_index < map->occupied; old_index++) { + DN_DSMapSlot *old_slot = map->slots + old_index; + DN_DSMapKey old_key = old_slot->key; + if (old_key.type == DN_DSMapKeyType_Invalid) + continue; + DN_DSMapSet(&new_map, old_key, old_slot->value); + } + + if ((map->flags & DN_DSMapFlags_DontFreeArenaOnResize) == 0) + DN_DSMapDeinit(map, DN_ZMem_No); + *map = new_map; // Update the map inplace + map->arena = prev_arena; // Restore the previous arena pointer, it's been de-init-ed + *map->arena = new_arena; // Re-init the old arena with the new data + map->pool.arena = map->arena; + return true; +} + +template +bool DN_DSMapErase(DN_DSMap *map, DN_DSMapKey key) +{ + if (!DN_DSMapIsValid(map)) + return false; + + DN_U32 index = DN_DSMapHashToSlotIndex(map, key); + if (index == 0) + return true; + + DN_U32 slot_index = map->hash_to_slot[index]; + if (slot_index == DN_DS_MAP_SENTINEL_SLOT) + return false; + + // NOTE: Mark the slot as unoccupied + map->hash_to_slot[index] = DN_DS_MAP_SENTINEL_SLOT; + + DN_DSMapSlot *slot = map->slots + slot_index; + if (!slot->key.no_copy_buffer) + DN_PoolDealloc(&map->pool, DN_Cast(void *) slot->key.buffer_data); + *slot = {}; // TODO: Optional? + + if (map->occupied > 1 /*Sentinel*/) { + // NOTE: Repair the hash chain, e.g. rehash all the items after the removed + // element and reposition them if necessary. + for (DN_U32 probe_index = index;;) { + probe_index = (probe_index + 1) & (map->size - 1); + if (map->hash_to_slot[probe_index] == DN_DS_MAP_SENTINEL_SLOT) + break; + + DN_DSMapSlot *probe = map->slots + map->hash_to_slot[probe_index]; + DN_U32 new_index = probe->key.hash & (map->size - 1); + if (index <= probe_index) { + if (index < new_index && new_index <= probe_index) + continue; + } else { + if (index < new_index || new_index <= probe_index) + continue; + } + + map->hash_to_slot[index] = map->hash_to_slot[probe_index]; + map->hash_to_slot[probe_index] = DN_DS_MAP_SENTINEL_SLOT; + index = probe_index; + } + + // NOTE: We have erased a slot from the hash table, this leaves a gap + // in our contiguous array. After repairing the chain, the hash mapping + // is correct. + // We will now fill in the vacant spot that we erased using the last + // element in the slot list. + if (map->occupied >= 3 /*Ignoring sentinel, at least 2 other elements to unstable erase*/) { + DN_U32 last_index = map->occupied - 1; + if (last_index != slot_index) { + // NOTE: Copy in last slot to the erase slot + DN_DSMapSlot *last_slot = map->slots + last_index; + map->slots[slot_index] = *last_slot; + + // NOTE: Update the hash-to-slot mapping for the value that was copied in + DN_U32 hash_to_slot_index = DN_DSMapHashToSlotIndex(map, last_slot->key); + map->hash_to_slot[hash_to_slot_index] = slot_index; + *last_slot = {}; // TODO: Optional? + } + } + } + + map->occupied--; + bool map_is_below_25pct_full = (map->occupied * 4) < (map->size * 1); + if (map_is_below_25pct_full && (map->size / 2) >= map->initial_size) + DN_DSMapResize(map, map->size / 2); + + return true; +} + +template +bool DN_DSMapEraseKeyU64(DN_DSMap *map, DN_U64 key) +{ + DN_DSMapKey map_key = DN_DSMapKeyU64(map, key); + bool result = DN_DSMapErase(map, map_key); + return result; +} + +template +bool DN_DSMapEraseKeyStr8(DN_DSMap *map, DN_Str8 key) +{ + DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key); + bool result = DN_DSMapErase(map, map_key); + return result; +} + +template +DN_DSMapKey DN_DSMapKeyBuffer(DN_DSMap const *map, void const *data, DN_USize size) +{ + DN_Assert(size > 0 && size <= UINT32_MAX); + DN_DSMapKey result = {}; + result.type = DN_DSMapKeyType_Buffer; + result.buffer_data = data; + result.buffer_size = DN_Cast(DN_U32) size; + result.hash = DN_DSMapHash(map, result); + return result; +} + +template +DN_DSMapKey DN_DSMapKeyBufferAsU64NoHash(DN_DSMap const *map, void const *data, DN_USize size) +{ + DN_DSMapKey result = {}; + result.type = DN_DSMapKeyType_BufferAsU64NoHash; + result.buffer_data = data; + result.buffer_size = DN_Cast(DN_U32) size; + DN_Assert(size >= sizeof(result.hash)); + DN_Memcpy(&result.hash, data, sizeof(result.hash)); + return result; +} + +template +DN_DSMapKey DN_DSMapKeyU64(DN_DSMap const *map, DN_U64 u64) +{ + DN_DSMapKey result = {}; + result.type = DN_DSMapKeyType_U64; + result.u64 = u64; + result.hash = DN_DSMapHash(map, result); + return result; +} + +template +DN_DSMapKey DN_DSMapKeyStr8(DN_DSMap const *map, DN_Str8 string) +{ + DN_DSMapKey result = DN_DSMapKeyBuffer(map, string.data, string.size); + return result; +} + +// NOTE: DN_DSMap +DN_API DN_DSMapKey DN_DSMapKeyU64NoHash(DN_U64 u64) +{ + DN_DSMapKey result = {}; + result.type = DN_DSMapKeyType_U64NoHash; + result.u64 = u64; + result.hash = DN_Cast(DN_U32) u64; + return result; +} + +DN_API bool DN_DSMapKeyEquals(DN_DSMapKey lhs, DN_DSMapKey rhs) +{ + bool result = false; + if (lhs.type == rhs.type && lhs.hash == rhs.hash) { + switch (lhs.type) { + case DN_DSMapKeyType_Invalid: result = true; break; + case DN_DSMapKeyType_U64NoHash: result = true; break; + case DN_DSMapKeyType_U64: result = lhs.u64 == rhs.u64; break; + + case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/ + case DN_DSMapKeyType_Buffer: { + if (lhs.buffer_size == rhs.buffer_size) + result = DN_Memcmp(lhs.buffer_data, rhs.buffer_data, lhs.buffer_size) == 0; + } break; + } + } + return result; +} + +DN_API bool operator==(DN_DSMapKey lhs, DN_DSMapKey rhs) +{ + bool result = DN_DSMapKeyEquals(lhs, rhs); + return result; +} +#endif // defined(__cplusplus) + +DN_API void DN_BinPackU64(DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item) +{ + DN_U64 const VALUE_MASK = 0b0111'1111; + DN_U8 const CONTINUE_BIT = 0b1000'0000; + + if (mode == DN_BinPackMode_Serialise) { + DN_U64 it = *item; + do { + DN_U8 write_value = DN_Cast(DN_U8)(it & VALUE_MASK); + it >>= 7; + if (it) + write_value |= CONTINUE_BIT; + DN_Str8BuilderAppendBytesCopy(&pack->writer, &write_value, sizeof(write_value)); + } while (it); + } else { + *item = 0; + DN_USize bits_read = 0; + for (DN_U8 src = CONTINUE_BIT; (src & CONTINUE_BIT) && bits_read < 64; bits_read += 7) { + src = pack->read.data[pack->read_index++]; + DN_U8 masked_src = src & VALUE_MASK; + *item |= (DN_Cast(DN_U64) masked_src << bits_read); + } + } +} + +DN_API void DN_BinPackVarInt_(DN_BinPack *pack, DN_BinPackMode mode, void *item, DN_USize size) +{ + DN_U64 value = 0; + DN_AssertF(size <= sizeof(value), + "An item larger than 64 bits (%zu) is trying to be packed as a variable integer which is not supported", + size * 8); + + if (mode == DN_BinPackMode_Serialise) // Read `item` into U64 `value` + DN_Memcpy(&value, item, size); + + DN_BinPackU64(pack, mode, &value); + + if (mode == DN_BinPackMode_Deserialise) // Write U64 `value` into `item` + DN_Memcpy(item, &value, size); +} + +DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack) +{ + bool result = pack->read_index == pack->read.size; + return result; +} + +DN_API void DN_BinPackUSize(DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackU32(DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackU16(DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackU8(DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackI64(DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackI32(DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackI16(DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackI8(DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackF64(DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackF32(DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackV2(DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item) +{ + DN_BinPackF32(pack, mode, &item->x); + DN_BinPackF32(pack, mode, &item->y); +} + +DN_API void DN_BinPackV4(DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item) +{ + DN_BinPackF32(pack, mode, &item->x); + DN_BinPackF32(pack, mode, &item->y); + DN_BinPackF32(pack, mode, &item->z); + DN_BinPackF32(pack, mode, &item->w); +} + +DN_API void DN_BinPackBool(DN_BinPack *pack, DN_BinPackMode mode, bool *item) +{ + DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); +} + +DN_API void DN_BinPackStr8FromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string) +{ + DN_BinPackVarInt_(pack, mode, &string->size, sizeof(string->size)); + if (mode == DN_BinPackMode_Serialise) { + DN_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size); + } else { + DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, string->size); + *string = DN_Str8FromStr8Arena(src, arena); + pack->read_index += src.size; + } +} + +DN_API void DN_BinPackStr8FromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string) +{ + DN_BinPackVarInt_(pack, mode, &string->size, sizeof(string->size)); + if (mode == DN_BinPackMode_Serialise) { + DN_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size); + } else { + DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, string->size); + *string = DN_Str8FromStr8Pool(src, pool); + pack->read_index += src.size; + } +} + +DN_API DN_Str8 DN_BinPackStr8FromBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max) +{ + DN_BinPackCBuffer(pack, mode, ptr, size, max); + DN_Str8 result = DN_Str8FromPtr(ptr, *size); + return result; +} + +DN_API void DN_BinPackBytesFromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size) +{ + DN_Str8 string = DN_Str8FromPtr(*ptr, *size); + DN_BinPackStr8FromArena(pack, arena, mode, &string); + *ptr = string.data; + *size = string.size; +} + +DN_API void DN_BinPackBytesFromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size) +{ + DN_Str8 string = DN_Str8FromPtr(*ptr, *size); + DN_BinPackStr8FromPool(pack, pool, mode, &string); + *ptr = string.data; + *size = string.size; +} + +DN_API void DN_BinPackCArray(DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size) +{ + DN_BinPackVarInt_(pack, mode, &size, sizeof(size)); + if (mode == DN_BinPackMode_Serialise) { + DN_Str8BuilderAppendBytesCopy(&pack->writer, ptr, size); + } else { + DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, size); + DN_Assert(src.size == size); + DN_Memcpy(ptr, src.data, DN_Min(src.size, size)); + pack->read_index += src.size; + } +} + +DN_API void DN_BinPackCBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max) +{ + if (mode == DN_BinPackMode_Serialise) { + DN_BinPackUSize(pack, mode, size); + DN_Str8BuilderAppendBytesCopy(&pack->writer, ptr, *size); + } else { + DN_U64 size_u64 = 0; + DN_BinPackU64(pack, mode, &size_u64); + DN_Assert(size_u64 < DN_USIZE_MAX); + DN_Assert(size_u64 <= max); + + *size = DN_Min(size_u64, max); + DN_Memcpy(ptr, pack->read.data + pack->read_index, *size); + pack->read_index += size_u64; + } +} + +DN_API DN_Str8 DN_BinPackBuild(DN_BinPack const *pack, DN_Arena *arena) +{ + DN_Str8 result = DN_Str8FromStr8BuilderArena(&pack->writer, arena); + return result; +} + +DN_API DN_CSVTokeniser DN_CSVTokeniserInit(DN_Str8 string, char delimiter) +{ + DN_CSVTokeniser result = {}; + result.string = string; + result.delimiter = delimiter; + return result; +} + +DN_API bool DN_CSVTokeniserValid(DN_CSVTokeniser *tokeniser) +{ + bool result = tokeniser && !tokeniser->bad; + return result; +} + +static void DN_CSVTokeniserEatNewLines_(DN_CSVTokeniser *tokeniser) +{ + char const *end = tokeniser->string.data + tokeniser->string.size; + while (tokeniser->it[0] == '\n' || tokeniser->it[0] == '\r') + if (++tokeniser->it == end) + break; +} + +DN_API bool DN_CSVTokeniserNextRow(DN_CSVTokeniser *tokeniser) +{ + bool result = false; + if (DN_CSVTokeniserValid(tokeniser) && tokeniser->string.size) { + // NOTE: First time querying row iterator is nil, let tokeniser advance + if (tokeniser->it) { + // NOTE: Only advance the tokeniser if we're at the end of the line and + // there's more to tokenise. + char const *end = tokeniser->string.data + tokeniser->string.size; + if (tokeniser->it != end && tokeniser->end_of_line) { + tokeniser->end_of_line = false; + result = true; + } + } + } + + return result; +} + +DN_API DN_Str8 DN_CSVTokeniserNextField(DN_CSVTokeniser *tokeniser) +{ + DN_Str8 result = {}; + if (!DN_CSVTokeniserValid(tokeniser)) + return result; + + if (tokeniser->string.size == 0) { + tokeniser->bad = true; + return result; + } + + // NOTE: First time tokeniser is invoked with a string, set up initial state. + char const *string_end = tokeniser->string.data + tokeniser->string.size; + if (!tokeniser->it) { + tokeniser->it = tokeniser->string.data; + DN_CSVTokeniserEatNewLines_(tokeniser); // NOTE: Skip any leading new lines + } + + // NOTE: Tokeniser pointing at end, no more valid data to parse. + if (tokeniser->it == string_end) + return result; + + // NOTE: Scan forward until the next control character. + // 1. '"' Double quoted field, extract everything between the quotes. + // 2. tokeniser->delimiter End of the field, extract everything leading up to the delimiter. + // 3. '\n' Last field in record, extract everything leading up the the new line. + char const *begin = tokeniser->it; + while (tokeniser->it != string_end && (tokeniser->it[0] != '"' && + tokeniser->it[0] != tokeniser->delimiter && + tokeniser->it[0] != '\n')) + tokeniser->it++; + + bool quoted_field = (tokeniser->it != string_end) && tokeniser->it[0] == '"'; + if (quoted_field) { + begin = ++tokeniser->it; // Begin after the quote + + // NOTE: Scan forward until the next '"' which marks the end + // of the field unless it is escaped by another '"'. + find_next_quote: + while (tokeniser->it != string_end && tokeniser->it[0] != '"') + tokeniser->it++; + + // NOTE: If we encounter a '"' right after, the quotes were escaped + // and we need to skip to the next instance of a '"'. + if (tokeniser->it != string_end && tokeniser->it + 1 != string_end && tokeniser->it[1] == '"') { + tokeniser->it += 2; + goto find_next_quote; + } + } + + // NOTE: Mark the end of the field + char const *end = tokeniser->it; + tokeniser->end_of_line = tokeniser->it == string_end || end[0] == '\n'; + + // NOTE: In files with \r\n style new lines ensure that we don't include + // the \r byte in the CSV field we produce. + if (end != string_end && end[0] == '\n') { + DN_Assert((uintptr_t)(end - 1) > (uintptr_t)tokeniser->string.data && + "Internal error: The string iterator is pointing behind the start of the string we're reading"); + if (end[-1] == '\r') + end = end - 1; + } + + // NOTE: Quoted fields may have whitespace after the closing quote, we skip + // until we reach the field terminator. + if (quoted_field) + while (tokeniser->it != string_end && (tokeniser->it[0] != tokeniser->delimiter && tokeniser->it[0] != '\n')) + tokeniser->it++; + + // NOTE: Advance the tokeniser past the field terminator. + if (tokeniser->it != string_end) + tokeniser->it++; + + // NOTE: Generate the record + result.data = DN_Cast(char *) begin; + result.size = DN_Cast(int)(end - begin); + return result; +} + +DN_API DN_Str8 DN_CSVTokeniserNextColumn(DN_CSVTokeniser *tokeniser) +{ + DN_Str8 result = {}; + if (!DN_CSVTokeniserValid(tokeniser)) + return result; + + // NOTE: End of line, the user must explicitly advance to the next row + if (tokeniser->end_of_line) + return result; + + // NOTE: Advance tokeniser to the next field in the row + result = DN_CSVTokeniserNextField(tokeniser); + return result; +} + +DN_API void DN_CSVTokeniserSkipLine(DN_CSVTokeniser *tokeniser) +{ + while (DN_CSVTokeniserValid(tokeniser) && !tokeniser->end_of_line) + DN_CSVTokeniserNextColumn(tokeniser); + DN_CSVTokeniserNextRow(tokeniser); +} + +DN_API int DN_CSVTokeniserNextN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator) +{ + if (!DN_CSVTokeniserValid(tokeniser) || !fields || fields_size <= 0) + return 0; + + int result = 0; + for (; result < fields_size; result++) { + fields[result] = column_iterator ? DN_CSVTokeniserNextColumn(tokeniser) : DN_CSVTokeniserNextField(tokeniser); + if (!DN_CSVTokeniserValid(tokeniser) || !fields[result].data) + break; + } + + return result; +} + +DN_API int DN_CSVTokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size) +{ + int result = DN_CSVTokeniserNextN(tokeniser, fields, fields_size, true /*column_iterator*/); + return result; +} + +DN_API int DN_CSVTokeniserNextFieldN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size) +{ + int result = DN_CSVTokeniserNextN(tokeniser, fields, fields_size, false /*column_iterator*/); + return result; +} + +DN_API void DN_CSVTokeniserSkipLineN(DN_CSVTokeniser *tokeniser, int count) +{ + for (int i = 0; i < count && DN_CSVTokeniserValid(tokeniser); i++) + DN_CSVTokeniserSkipLine(tokeniser); +} + +DN_API void DN_CSVPackU64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value) +{ + if (serialise == DN_CSVSerialise_Read) { + DN_Str8 csv_value = DN_CSVTokeniserNextColumn(&pack->read_tokeniser); + DN_U64FromResult to_u64 = DN_U64FromStr8(csv_value, 0); + DN_Assert(to_u64.success); + *value = to_u64.value; + } else { + DN_Str8BuilderAppendF(&pack->write_builder, "%s%I64u", pack->write_column++ ? "," : "", *value); + } +} + +DN_API void DN_CSVPackI64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value) +{ + if (serialise == DN_CSVSerialise_Read) { + DN_Str8 csv_value = DN_CSVTokeniserNextColumn(&pack->read_tokeniser); + DN_I64FromResult to_i64 = DN_I64FromStr8(csv_value, 0); + DN_Assert(to_i64.success); + *value = to_i64.value; + } else { + DN_Str8BuilderAppendF(&pack->write_builder, "%s%I64d", pack->write_column++ ? "," : "", *value); + } +} + +DN_API void DN_CSVPackI32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value) +{ + DN_I64 u64 = *value; + DN_CSVPackI64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = DN_SaturateCastI64ToI32(u64); +} + +DN_API void DN_CSVPackI16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value) +{ + DN_I64 u64 = *value; + DN_CSVPackI64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = DN_SaturateCastI64ToI16(u64); +} + +DN_API void DN_CSVPackI8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value) +{ + DN_I64 u64 = *value; + DN_CSVPackI64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = DN_SaturateCastI64ToI8(u64); +} + +DN_API void DN_CSVPackU32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value) +{ + DN_U64 u64 = *value; + DN_CSVPackU64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = DN_SaturateCastU64ToU32(u64); +} + +DN_API void DN_CSVPackU16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value) +{ + DN_U64 u64 = *value; + DN_CSVPackU64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = DN_SaturateCastU64ToU16(u64); +} + +DN_API void DN_CSVPackBoolAsU64(DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value) +{ + DN_U64 u64 = *value; + DN_CSVPackU64(pack, serialise, &u64); + if (serialise == DN_CSVSerialise_Read) + *value = u64 ? 1 : 0; +} + +DN_API void DN_CSVPackStr8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena) +{ + if (serialise == DN_CSVSerialise_Read) { + DN_Str8 csv_value = DN_CSVTokeniserNextColumn(&pack->read_tokeniser); + *str8 = DN_Str8FromStr8Arena(csv_value, arena); + } else { + DN_Str8BuilderAppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_Str8PrintFmt(*str8)); + } +} + +DN_API void DN_CSVPackBuffer(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, DN_USize *size) +{ + if (serialise == DN_CSVSerialise_Read) { + DN_Str8 csv_value = DN_CSVTokeniserNextColumn(&pack->read_tokeniser); + *size = DN_Min(*size, csv_value.size); + DN_Memcpy(dest, csv_value.data, *size); + } else { + DN_Str8BuilderAppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_Cast(int)(*size), DN_Cast(char *)dest); + } +} + +DN_API void DN_CSVPackBufferWithMax(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, DN_USize *size, DN_USize max) +{ + if (serialise == DN_CSVSerialise_Read) + *size = max; + DN_CSVPackBuffer(pack, serialise, dest, size); +} + +DN_API bool DN_CSVPackNewLine(DN_CSVPack *pack, DN_CSVSerialise serialise) +{ + bool result = true; + if (serialise == DN_CSVSerialise_Read) { + result = DN_CSVTokeniserNextRow(&pack->read_tokeniser); + } else { + pack->write_column = 0; + result = DN_Str8BuilderAppendRef(&pack->write_builder, DN_Str8Lit("\n")); + } + return result; +} + +DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, bool leak_permitted) +{ + if (!ptr) + return; + + DN_TicketMutex_Begin(&leak->alloc_table_mutex); + + DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); + DN_DSMap *alloc_table = &leak->alloc_table; + DN_DSMapResult alloc_entry = DN_DSMapMakeKeyU64(alloc_table, DN_Cast(DN_U64) ptr); + DN_LeakAlloc *alloc = alloc_entry.value; + if (alloc_entry.found) { + if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) { + DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); + DN_Str8x32 new_alloc_size = DN_Str8x32FromByteCountU64Auto(size); + DN_HardAssertF( + alloc->flags & DN_LeakAllocFlag_Freed, + "This pointer is already in the leak tracker, however it has not been freed yet. This " + "same pointer is being ask to be tracked twice in the allocation table, e.g. one if its " + "previous free calls has not being marked freed with an equivalent call to " + "DN_LeakTrackDealloc()\n" + "\n" + "The pointer (0x%p) originally allocated %.*s at:\n" + "\n" + "%.*s\n" + "\n" + "The pointer is allocating %.*s again at:\n" + "\n" + "%.*s\n", + ptr, + DN_Str8PrintFmt(alloc_size), + DN_Str8PrintFmt(alloc->stack_trace), + DN_Str8PrintFmt(new_alloc_size), + DN_Str8PrintFmt(stack_trace)); + } + + // NOTE: Pointer was reused, clean up the prior entry + leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.size; + leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->freed_stack_trace.size; + + DN_OS_MemDealloc(alloc->stack_trace.data); + DN_OS_MemDealloc(alloc->freed_stack_trace.data); + *alloc = {}; + } + + alloc->ptr = ptr; + alloc->size = size; + alloc->stack_trace = stack_trace; + alloc->flags |= leak_permitted ? DN_LeakAllocFlag_LeakPermitted : 0; + leak->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.size; + DN_TicketMutex_End(&leak->alloc_table_mutex); +} + +DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) +{ + if (!ptr) + return; + + DN_TicketMutex_Begin(&leak->alloc_table_mutex); + + DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); + DN_DSMap *alloc_table = &leak->alloc_table; + DN_DSMapResult alloc_entry = DN_DSMapFindKeyU64(alloc_table, DN_Cast(uintptr_t) ptr); + DN_HardAssertF(alloc_entry.found, + "Allocated pointer can not be removed as it does not exist in the " + "allocation table. When this memory was allocated, the pointer was " + "not added to the allocation table [ptr=%p]", + ptr); + + DN_LeakAlloc *alloc = alloc_entry.value; + if (alloc->flags & DN_LeakAllocFlag_Freed) { + DN_Str8x32 freed_size = DN_Str8x32FromByteCountU64Auto(alloc->freed_size); + DN_HardAssertF((alloc->flags & DN_LeakAllocFlag_Freed) == 0, + "Double free detected, pointer to free was already marked " + "as freed. Either the pointer was reallocated but not " + "traced, or, the pointer was freed twice.\n" + "\n" + "The pointer (0x%p) originally allocated %.*s at:\n" + "\n" + "%.*s\n" + "\n" + "The pointer was freed at:\n" + "\n" + "%.*s\n" + "\n" + "The pointer is being freed again at:\n" + "\n" + "%.*s\n", + ptr, + DN_Str8PrintFmt(freed_size), + DN_Str8PrintFmt(alloc->stack_trace), + DN_Str8PrintFmt(alloc->freed_stack_trace), + DN_Str8PrintFmt(stack_trace)); + } + + DN_Assert(alloc->freed_stack_trace.size == 0); + alloc->flags |= DN_LeakAllocFlag_Freed; + alloc->freed_stack_trace = stack_trace; + leak->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.size; + DN_TicketMutex_End(&leak->alloc_table_mutex); +} + +DN_API void DN_LeakDump_(DN_LeakTracker *leak) +{ + DN_U64 leak_count = 0; + DN_U64 leaked_bytes = 0; + for (DN_USize index = 1; index < leak->alloc_table.occupied; index++) { + DN_DSMapSlot *slot = leak->alloc_table.slots + index; + DN_LeakAlloc *alloc = &slot->value; + bool alloc_leaked = (alloc->flags & DN_LeakAllocFlag_Freed) == 0; + bool leak_permitted = (alloc->flags & DN_LeakAllocFlag_LeakPermitted); + if (alloc_leaked && !leak_permitted) { + leaked_bytes += alloc->size; + leak_count++; + DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); + DN_LogWarningF( + "Pointer (0x%p) leaked %.*s at:\n" + "%.*s", + alloc->ptr, + DN_Str8PrintFmt(alloc_size), + DN_Str8PrintFmt(alloc->stack_trace)); + } + } + + if (leak_count) { + DN_Str8x32 leak_size = DN_Str8x32FromByteCountU64Auto(leaked_bytes); + DN_LogWarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DN_Str8PrintFmt(leak_size)); + } +} diff --git a/Source/Base/dn_base.h b/Source/Base/dn_base.h index 34b3b4b..4038300 100644 --- a/Source/Base/dn_base.h +++ b/Source/Base/dn_base.h @@ -2,11 +2,12 @@ #define DN_BASE_H #if defined(_CLANGD) + #define DN_STR8_AVX512F 1 #include "../dn.h" #endif // NOTE: Compiler identification -// Warning! Order is important here, clang-cl on Windows defines _MSC_VER +// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER #if defined(_MSC_VER) #if defined(__clang__) #define DN_COMPILER_CLANG_CL @@ -21,7 +22,8 @@ #endif // NOTE: __has_feature -// MSVC for example does not support the feature detection macro for instance so we compile it out +// NOTE: MSVC for example does not support the feature detection macro for instance so we compile it +// out #if defined(__has_feature) #define DN_HAS_FEATURE(expr) __has_feature(expr) #else @@ -29,7 +31,7 @@ #endif // NOTE: __has_builtin -// MSVC for example does not support the feature detection macro for instance so we compile it out +// NOTE: MSVC for example does not support the feature detection macro for instance so we compile it out #if defined(__has_builtin) #define DN_HAS_BUILTIN(expr) __has_builtin(expr) #else @@ -244,15 +246,6 @@ typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \ DN_GCC_WARNING_POP -#define DN_Check(expr) DN_CheckF(expr, "") -#if defined(DN_NO_CHECK_BREAK) - #define DN_CheckF(expr, fmt, ...) \ - ((expr) ? true : (DN_LogWarningF(fmt, ##__VA_ARGS__), false)) -#else - #define DN_CheckF(expr, fmt, ...) \ - ((expr) ? true : (DN_LogErrorF(fmt, ##__VA_ARGS__), DN_StackTracePrint(128 /*limit*/), DN_DebugBreak, false)) -#endif - #if defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) #define DN_64_BIT #else @@ -1760,7 +1753,7 @@ DN_API DN_I16 DN_SaturateCastI64ToI16 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_USize DN_SaturateCastI64ToUSize (DN_I64 val); DN_API DN_U8 DN_SaturateCastI64ToU8 (DN_I64 val); DN_API DN_U16 DN_SaturateCastI64ToU16 (DN_I64 val); DN_API DN_U32 DN_SaturateCastI64ToU32 (DN_I64 val); @@ -1969,12 +1962,14 @@ DN_API DN_USize DN_CStr16Size #define DN_Str8Lit(c_str) DN_Literal(DN_Str8){(char *)(c_str), sizeof(c_str) - 1} #define DN_Str8PrintFmt(string) (int)((string).size), (string).data + #define DN_Str8FromPtr(data, size) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(size)} #define DN_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->size) #define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array)) DN_API DN_Str8 DN_Str8AllocAllocator (DN_USize size, DN_ZMem z_mem, DN_Allocator allocator); DN_API DN_Str8 DN_Str8AllocArena (DN_USize size, DN_ZMem z_mem, DN_Arena *arena); DN_API DN_Str8 DN_Str8AllocPool (DN_USize size, DN_Pool *pool); + DN_API DN_Str8 DN_Str8FromCStr8 (char const *src); DN_API DN_Str8 DN_Str8FromCStr8Arena (char const *src, DN_Arena *arena); DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize size, DN_Arena *arena); @@ -1988,6 +1983,7 @@ DN_API DN_Str8 DN_Str8FromFmtAllocator DN_API DN_Str8 DN_Str8FromFmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8 DN_Str8FromFmtVPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_Str8 DN_Str8FromFmtPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...); + DN_API DN_Str8x16 DN_Str8x16FromFmt (DN_FMT_ATTRIB char const *fmt, ...); DN_API DN_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args); DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...); @@ -2016,6 +2012,7 @@ DN_API void DN_Str8x512AppendFmt DN_API void DN_Str8x512AppendFmtV (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, va_list args); DN_API void DN_Str8x1024AppendFmt (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, ...); DN_API void DN_Str8x1024AppendFmtV (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, va_list args); + DN_API DN_Str8x32 DN_Str8x32FromU64 (DN_U64 val, char separator); DN_API bool DN_Str8IsAll (DN_Str8 string, DN_Str8IsAllType is_all); DN_API char * DN_Str8End (DN_Str8 string); @@ -2040,6 +2037,7 @@ DN_API bool DN_Str8StartsWithInsensitive DN_API bool DN_Str8EndsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive); DN_API bool DN_Str8EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix); DN_API bool DN_Str8HasChar (DN_Str8 string, char ch); + DN_API DN_Str8 DN_Str8TrimPrefix (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive); DN_API DN_Str8 DN_Str8TrimHexPrefix (DN_Str8 string); DN_API DN_Str8 DN_Str8TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive); @@ -2048,6 +2046,7 @@ DN_API DN_Str8 DN_Str8TrimHeadWhitespace DN_API DN_Str8 DN_Str8TrimTailWhitespace (DN_Str8 string); DN_API DN_Str8 DN_Str8TrimWhitespaceAround (DN_Str8 string); DN_API DN_Str8 DN_Str8TrimByteOrderMark (DN_Str8 string); + DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path); DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path); DN_API DN_Str8 DN_Str8FilePathNoExtension (DN_Str8 path); @@ -2071,6 +2070,15 @@ DN_API DN_Str8 DN_Str8LineBreakAllocator DN_API DN_Str8 DN_Str8LineBreakArena (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena); DN_API DN_Str8 DN_Str8Table (DN_Str8 const* rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena); +#if DN_STR8_AVX512F +DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F (DN_Str8 string, DN_Str8 find); +DN_API DN_USize DN_Str8SplitAVX512F (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags flags); +DN_API DN_Str8Slice DN_Str8SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags flags); +#endif + DN_API DN_Str8 DN_Str8SliceRender (DN_Str8Slice array, DN_Str8 separator, DN_Arena *arena); DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena); DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case); @@ -2552,8 +2560,8 @@ DN_API DN_M4 DN_M4MulF DN_API DN_M4 DN_M4DivF (DN_M4 lhs, DN_F32 rhs); DN_API DN_Str8x256 DN_M4ColumnMajorString (DN_M4 mat); -DN_API bool operator== (DN_M2x3 const &lhs, DN_M2x3 const &rhs); -DN_API bool operator!= (DN_M2x3 const &lhs, DN_M2x3 const &rhs); +DN_API bool DN_M2x3Eq (DN_M2x3 const *lhs, DN_M2x3 const *rhs); +DN_API bool DN_M2x3NotEq (DN_M2x3 const *lhs, DN_M2x3 const *rhs); DN_API DN_M2x3 DN_M2x3Identity (); DN_API DN_M2x3 DN_M2x3Translate (DN_V2F32 offset); DN_API DN_V2F32 DN_M2x3ScaleGet (DN_M2x3 m2x3); @@ -2573,7 +2581,6 @@ DN_API DN_Rect DN_M2x3MulRect #define DN_RectFrom4N(x, y, w, h) DN_Literal(DN_Rect){DN_Literal(DN_V2F32){{x, y}}, DN_Literal(DN_V2F32){{w, h}}} #define DN_RectZero DN_RectFrom4N(0, 0, 0, 0) -DN_API bool operator== (const DN_Rect& lhs, const DN_Rect& rhs); DN_API DN_V2F32 DN_RectCenter (DN_Rect rect); DN_API bool DN_RectContainsPoint (DN_Rect rect, DN_V2F32 p); DN_API bool DN_RectContainsRect (DN_Rect a, DN_Rect b); @@ -3040,9 +3047,10 @@ DN_API void DN_RingWrite (DN_Rin DN_API void DN_RingRead (DN_Ring *ring, void *dest, DN_U64 dest_size); #define DN_RingReadStruct(ring, dest) DN_RingRead((ring), (dest), sizeof(*(dest))) +// TODO: Replace with a C-style hash table +#if defined(__cplusplus) DN_U32 const DN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49; DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0; - template DN_DSMap DN_DSMapInit (DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags); template void DN_DSMapDeinit (DN_DSMap *map, DN_ZMem z_mem); template bool DN_DSMapIsValid (DN_DSMap const *map); @@ -3069,7 +3077,240 @@ template DN_DSMapKey DN_DSMapKeyStr8 (DN_DSMap DN_API DN_DSMapKey DN_DSMapKeyU64NoHash (DN_U64 u64); DN_API bool DN_DSMapKeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs); DN_API bool operator== (DN_DSMapKey lhs, DN_DSMapKey rhs); +#endif +enum DN_BinPackMode +{ + DN_BinPackMode_Serialise, + DN_BinPackMode_Deserialise, +}; + +struct DN_BinPack +{ + DN_Str8Builder writer; + DN_Str8 read; + DN_USize read_index; +}; + +DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack); +DN_API void DN_BinPackUSize (DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item); +DN_API void DN_BinPackU64 (DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item); +DN_API void DN_BinPackU32 (DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item); +DN_API void DN_BinPackU16 (DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item); +DN_API void DN_BinPackU8 (DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item); +DN_API void DN_BinPackI64 (DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item); +DN_API void DN_BinPackI32 (DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item); +DN_API void DN_BinPackI16 (DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item); +DN_API void DN_BinPackI8 (DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item); +DN_API void DN_BinPackF64 (DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item); +DN_API void DN_BinPackF32 (DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item); +DN_API void DN_BinPackV2 (DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item); +DN_API void DN_BinPackV4 (DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item); +DN_API void DN_BinPackBool (DN_BinPack *pack, DN_BinPackMode mode, bool *item); +DN_API void DN_BinPackStr8FromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string); +DN_API void DN_BinPackStr8FromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string); +DN_API DN_Str8 DN_BinPackStr8FromBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max); +DN_API void DN_BinPackBytesFromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size); +DN_API void DN_BinPackBytesFromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size); +DN_API void DN_BinPackCArray (DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size); +DN_API void DN_BinPackCBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max); +DN_API DN_Str8 DN_BinPackBuild (DN_BinPack const *pack, DN_Arena *arena); + +enum DN_CSVSerialise +{ + DN_CSVSerialise_Read, + DN_CSVSerialise_Write, +}; + +struct DN_CSVTokeniser +{ + bool bad; + DN_Str8 string; + char delimiter; + char const *it; + bool end_of_line; +}; + +struct DN_CSVPack +{ + DN_Str8Builder write_builder; + DN_USize write_column; + DN_CSVTokeniser read_tokeniser; +}; + +// NOTE: Data structures to create and parse CSV files, supports Python style escaped quotes (e.g. +// Using "" to escape quotes inside a quoted string). +// +// API +// DN_CSVTokeniserNextN: Reads the next N consecutive fields from the parser. If `column_iterator` +// is `false` then the read of the N consecutive fields does not proceed past the end of the +// current CSV row. If `true` then it reads the next N fields even if reading would progress onto +// the next row. +DN_API DN_CSVTokeniser DN_CSVTokeniserInit (DN_Str8 string, char delimiter); +DN_API bool DN_CSVTokeniserValid (DN_CSVTokeniser *tokeniser); +DN_API bool DN_CSVTokeniserNextRow (DN_CSVTokeniser *tokeniser); +DN_API DN_Str8 DN_CSVTokeniserNextField (DN_CSVTokeniser *tokeniser); +DN_API DN_Str8 DN_CSVTokeniserNextColumn (DN_CSVTokeniser *tokeniser); +DN_API void DN_CSVTokeniserSkipLine (DN_CSVTokeniser *tokeniser); +DN_API int DN_CSVTokeniserNextN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator); +DN_API int DN_CSVTokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size); +DN_API int DN_CSVTokeniserNextFieldN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size); +DN_API void DN_CSVTokeniserSkipLineN (DN_CSVTokeniser *tokeniser, int count); +DN_API void DN_CSVPackU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value); +DN_API void DN_CSVPackI64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value); +DN_API void DN_CSVPackI32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value); +DN_API void DN_CSVPackI16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value); +DN_API void DN_CSVPackI8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value); +DN_API void DN_CSVPackU32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value); +DN_API void DN_CSVPackU16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value); +DN_API void DN_CSVPackBoolAsU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value); +DN_API void DN_CSVPackStr8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena); +DN_API void DN_CSVPackBuffer (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size); +DN_API void DN_CSVPackBufferWithMax (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max); +DN_API bool DN_CSVPackNewLine (DN_CSVPack *pack, DN_CSVSerialise serialise); + +// TODO: Replace with a C implementation +template +using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs); + +template +bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); + +enum DN_BinarySearchType +{ + // Index of the match. If no match is found, found is set to false and the + // index is set to the index where the match should be inserted/exist, if + // it were in the array + DN_BinarySearchType_Match, + + // Index of the first element in the array that is `element >= find`. If no such + // item is found or the array is empty, then, the index is set to the array + // size and found is set to `false`. + // + // For example: + // int array[] = {0, 1, 2, 3, 4, 5}; + // DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_LowerBound); + // printf("%zu\n", result.index); // Prints index '4' + + DN_BinarySearchType_LowerBound, + + // Index of the first element in the array that is `element > find`. If no such + // item is found or the array is empty, then, the index is set to the array + // size and found is set to `false`. + // + // For example: + // int array[] = {0, 1, 2, 3, 4, 5}; + // DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_UpperBound); + // printf("%zu\n", result.index); // Prints index '5' + + DN_BinarySearchType_UpperBound, +}; + +struct DN_BinarySearchResult +{ + bool found; + DN_USize index; +}; + +template bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); +template DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc less_than = DN_BinarySearch_DefaultLessThan); + +template +bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs) +{ + bool result = lhs < rhs; + return result; +} + +template +DN_BinarySearchResult DN_BinarySearch(T const *array, + DN_USize array_size, + T const &find, + DN_BinarySearchType type, + DN_BinarySearchLessThanProc less_than) +{ + DN_BinarySearchResult result = {}; + if (!array || array_size <= 0 || !less_than) + return result; + + T const *end = array + array_size; + T const *first = array; + T const *last = end; + while (first != last) { + DN_USize count = last - first; + T const *it = first + (count / 2); + + bool advance_first = false; + if (type == DN_BinarySearchType_UpperBound) + advance_first = !less_than(find, it[0]); + else + advance_first = less_than(it[0], find); + + if (advance_first) + first = it + 1; + else + last = it; + } + + switch (type) { + case DN_BinarySearchType_Match: { + result.found = first != end && !less_than(find, *first); + } break; + + case DN_BinarySearchType_LowerBound: /*FALLTHRU*/ + case DN_BinarySearchType_UpperBound: { + result.found = first != end; + } break; + } + + result.index = first - array; + return result; +} + +enum DN_LeakAllocFlag +{ + DN_LeakAllocFlag_Freed = 1 << 0, + DN_LeakAllocFlag_LeakPermitted = 1 << 1, +}; + +struct DN_LeakAlloc +{ + void *ptr; // 8 Pointer to the allocation being tracked + DN_USize size; // 16 Size of the allocation + DN_USize freed_size; // 24 Store the size of the allocation when it is freed + DN_Str8 stack_trace; // 40 Stack trace at the point of allocation + DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed + DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag` +}; + +// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get +// expensive. Enforce that there is no unexpected padding. +DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively + +struct DN_LeakTracker +{ + DN_DSMap alloc_table; + DN_TicketMutex alloc_table_mutex; + DN_MemList alloc_table_mem; + DN_Arena alloc_table_arena; + DN_U64 alloc_table_bytes_allocated_for_stack_traces; +}; + +DN_API void DN_LeakTrackAlloc_ (DN_LeakTracker *leak, void *ptr, DN_USize size, bool alloc_can_leak); +DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr); +DN_API void DN_LeakDump_ (DN_LeakTracker *leak); + +#if defined(DN_LEAK_TRACKING) +#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) DN_LeakTrackAlloc_(leak, ptr, size, alloc_can_leak) +#define DN_LeakTrackDealloc(leak, ptr) DN_LeakTrackDealloc_(leak, ptr) +#define DN_LeakDump(leak) DN_LeakDump_(leak) +#else +#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0) +#define DN_LeakTrackDealloc(leak, ptr) do { (void)ptr; } while (0) +#define DN_LeakDump(leak) do { } while (0) +#endif + +// NOTE: Template implementations #if defined(__cplusplus) template T *DN_TMemCopyObj(T *dest, T const *src, DN_USize count) { diff --git a/Source/Base/dn_base_containers.cpp b/Source/Base/dn_base_containers.cpp deleted file mode 100644 index d969478..0000000 --- a/Source/Base/dn_base_containers.cpp +++ /dev/null @@ -1,821 +0,0 @@ -#if defined(_CLANGD) - #include "../dn.h" -#endif - -struct DN_ArrayFindEqMemcmpContext_ -{ - DN_USize elem_size; - void const *find; -}; - - -DN_API void *DN_SliceAllocArena(void **data, DN_USize *slice_size_field, DN_USize size, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena) -{ - void *result = *data; - *data = DN_ArenaAlloc(arena, size * elem_size, align, zmem); - if (*data) - *slice_size_field = size; - return result; -} - -DN_API DN_ArrayFindResult DN_ArrayFind(void *data, DN_USize size, DN_USize elem_size, void const *find, DN_ArrayFindEqFunc *eq_func) -{ - DN_ArrayFindResult result = {}; - DN_Assert(data); - DN_Assert(elem_size); - if (find) { - for (DN_ForIndexU(index, size)) { - DN_U8 *it = DN_Cast(DN_U8 *) data + (index * elem_size); - if (eq_func(it, find)) { - result.index = index; - result.value = it; - result.success = true; - break; - } - } - } - return result; -} - -static bool DN_ArrayFindEqMemEqUnsafe_(void const *lhs, void const *find) -{ - DN_ArrayFindEqMemcmpContext_ *context = DN_Cast(DN_ArrayFindEqMemcmpContext_ *) find; - bool result = DN_MemEqUnsafe(lhs, context->find, context->elem_size); - return result; -} - -DN_API DN_ArrayFindResult DN_ArrayFindMemEq(void *data, DN_USize size, DN_USize elem_size, void const *find) -{ - DN_ArrayFindEqMemcmpContext_ context = {}; - context.elem_size = elem_size; - context.find = find; - DN_ArrayFindResult result = DN_ArrayFind(data, size, elem_size, &context, DN_ArrayFindEqMemEqUnsafe_); - return result; -} - -DN_API void *DN_ArrayInsertArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count) -{ - void *result = nullptr; - if (!data || !size || !items || count <= 0 || ((*size + count) > max)) - return result; - - DN_USize clamped_index = DN_Min(index, *size); - if (clamped_index != *size) { - char const *src = DN_Cast(char *)data + (clamped_index * elem_size); - char const *dest = DN_Cast(char *)data + ((clamped_index + count) * elem_size); - char const *end = DN_Cast(char *)data + (size[0] * elem_size); - DN_USize bytes_to_move = end - src; - DN_Memmove(DN_Cast(void *) dest, src, bytes_to_move); - } - - result = DN_Cast(char *)data + (clamped_index * elem_size); - DN_Memcpy(result, items, elem_size * count); - *size += count; - return result; -} - -DN_API void *DN_ArrayPopFront(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) -{ - if (!data || !size || *size == 0 || count == 0) - return nullptr; - - DN_USize pop_count = DN_Min(count, *size); - void *result = data; - - if (pop_count < *size) { - char *src = DN_Cast(char *)data + (pop_count * elem_size); - char *dest = DN_Cast(char *)data; - DN_USize bytes_to_move = (*size - pop_count) * elem_size; - DN_Memmove(dest, src, bytes_to_move); - } - - *size -= pop_count; - return result; -} - -DN_API void *DN_ArrayPopBack(void *data, DN_USize *size, DN_USize elem_size, DN_USize count) -{ - if (!data || !size || *size == 0 || count == 0) - return nullptr; - - DN_USize pop_count = DN_Min(count, *size); - *size -= pop_count; - - return DN_Cast(char *)data + (*size * elem_size); -} - -DN_API DN_ArrayEraseResult DN_ArrayEraseRange(void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase) -{ - DN_ArrayEraseResult result = {}; - result.it_index = begin_index; - if (!data || !size || *size == 0 || count == 0) - return result; - - // Compute the range to erase - DN_USize start = 0, end = 0; - if (count < 0) { - // Erase backwards from begin_index, not inclusive of begin_index - // Range: [begin_index + count, begin_index) - // Which is: [begin_index - abs(count), begin_index) - DN_USize abs_count = DN_Abs(count); - start = (begin_index > abs_count) ? (begin_index - abs_count) : 0; - end = begin_index; - } else { - start = begin_index; - end = begin_index + count; - } - - // Clamp indices to valid bounds - start = DN_Min(start, *size); - end = DN_Min(end, *size); - - // Erase the range [start, end) - DN_USize erase_count = end > start ? end - start : 0; - if (erase_count) { - char *dest = (char *)data + (elem_size * start); - char *array_end = (char *)data + (elem_size * *size); - char *src = dest + (elem_size * erase_count); - if (erase == DN_ArrayErase_Stable) { - DN_USize move_size = array_end - src; - DN_Memmove(dest, src, move_size); - } else { - char *unstable_src = array_end - (elem_size * erase_count); - DN_USize move_size = array_end - unstable_src; - DN_Memcpy(dest, unstable_src, move_size); - } - *size -= erase_count; - } - - result.items_erased = erase_count; - // NOTE: If we are erasing from the current index of the iterator to the end of the array then - // there's no more elements in the array to iterate. So the returned index should b - // one-past-last index - if (begin_index == start && end >= *size) { - result.it_index = *size; - } else { - result.it_index = start ? start - 1 : 0; - } - return result; -} - -DN_API void *DN_ArrayMakeArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem) -{ - void *result = nullptr; - DN_USize new_size = *size + make_count; - if (new_size <= max) { - result = DN_Cast(char *) data + (elem_size * size[0]); - *size = new_size; - if (z_mem == DN_ZMem_Yes) - DN_Memset(result, 0, elem_size * make_count); - } - return result; -} - -DN_API void *DN_ArrayMakeArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem, DN_CallSite call_site) -{ - void *result = DN_ArrayMakeArray(data, size, max, elem_size, make_count, z_mem); - DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max); - return result; -} - -DN_API void *DN_ArrayAddArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add) -{ - void *result = DN_ArrayMakeArray(data, size, max, elem_size, elems_count, DN_ZMem_No); - if (result) { - if (add == DN_ArrayAdd_Append) { - DN_Memcpy(result, elems, elems_count * elem_size); - } else { - char *move_dest = DN_Cast(char *)data + (elems_count * elem_size); // Shift elements forward - char *move_src = DN_Cast(char *)data; - DN_Memmove(move_dest, move_src, elem_size * size[0]); - DN_Memcpy(data, elems, elem_size * elems_count); - } - } - return result; -} - -DN_API void *DN_ArrayAddArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add, DN_CallSite call_site) -{ - void *result = DN_ArrayAddArray(data, size, max, elem_size, elems, elems_count, add); - DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max); - return result; -} - -DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) -{ - bool result = true; - if (new_max != *max) { - DN_USize bytes_to_alloc = elem_size * new_max; - void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc); - if (buffer) { - DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); - DN_Memcpy(buffer, *data, bytes_to_copy); - DN_PoolDealloc(pool, *data); - *data = buffer; - *max = new_max; - *size = DN_Min(*size, new_max); - } else { - result = false; - } - } - - return result; -} - -DN_API bool DN_ArrayResizeFromPool(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) -{ - bool result = true; - if (new_max != *max) { - DN_USize bytes_to_alloc = elem_size * new_max; - void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc); - if (buffer) { - DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); - DN_Memcpy(buffer, *data, bytes_to_copy); - DN_PoolDealloc(pool, *data); - *data = buffer; - *max = new_max; - *size = DN_Min(*size, new_max); - } else { - result = false; - } - } - - return result; -} - -DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max) -{ - bool result = true; - if (new_max != *max) { - DN_USize bytes_to_alloc = elem_size * new_max; - void *buffer = DN_ArenaNewArray(arena, DN_U8, bytes_to_alloc, DN_ZMem_No); - if (buffer) { - DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max); - DN_Memcpy(buffer, *data, bytes_to_copy); - *data = buffer; - *max = new_max; - *size = DN_Min(*size, new_max); - } else { - result = false; - } - } - - return result; -} - -DN_API bool DN_ArrayGrowFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max) -{ - bool result = true; - if (new_max > *max) - result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max); - return result; -} - -DN_API bool DN_ArrayGrowFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max) -{ - bool result = true; - if (new_max > *max) - result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max); - return result; -} - - -DN_API bool DN_ArrayGrowIfNeededFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize add_count) -{ - bool result = true; - DN_USize new_size = size + add_count; - if (new_size > *max) { - DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8); - result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max); - } - return result; -} - -DN_API bool DN_ArrayGrowIfNeededFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize add_count) -{ - bool result = true; - DN_USize new_size = size + add_count; - if (new_size > *max) { - DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8); - result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max); - } - return result; -} - -DN_API void *DN_SinglyLLDetach(void **link, void **next) -{ - void *result = *link; - if (*link) { - *link = *next; - *next = nullptr; - } - return result; -} - -DN_API bool DN_RingHasSpace(DN_Ring const *ring, DN_U64 size) -{ - DN_U64 avail = ring->write_pos - ring->read_pos; - DN_U64 space = ring->size - avail; - bool result = space >= size; - return result; -} - -DN_API bool DN_RingHasData(DN_Ring const *ring, DN_U64 size) -{ - DN_U64 data = ring->write_pos - ring->read_pos; - bool result = data >= size; - return result; -} - -DN_API void DN_RingWrite(DN_Ring *ring, void const *src, DN_U64 src_size) -{ - DN_Assert(src_size <= ring->size); - DN_U64 offset = ring->write_pos % ring->size; - DN_U64 bytes_before_split = ring->size - offset; - DN_U64 pre_split_bytes = DN_Min(bytes_before_split, src_size); - DN_U64 post_split_bytes = src_size - pre_split_bytes; - void const *pre_split_data = src; - void const *post_split_data = ((char *)src + pre_split_bytes); - DN_Memcpy(ring->base + offset, pre_split_data, pre_split_bytes); - DN_Memcpy(ring->base, post_split_data, post_split_bytes); - ring->write_pos += src_size; -} - -DN_API void DN_RingRead(DN_Ring *ring, void *dest, DN_U64 dest_size) -{ - DN_Assert(dest_size <= ring->size); - DN_U64 offset = ring->read_pos % ring->size; - DN_U64 bytes_before_split = ring->size - offset; - DN_U64 pre_split_bytes = DN_Min(bytes_before_split, dest_size); - DN_U64 post_split_bytes = dest_size - pre_split_bytes; - DN_Memcpy(dest, ring->base + offset, pre_split_bytes); - DN_Memcpy((char *)dest + pre_split_bytes, ring->base, post_split_bytes); - ring->read_pos += dest_size; -} - -template -DN_DSMap DN_DSMapInit(DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags) -{ - DN_DSMap result = {}; - if (!DN_CheckF(DN_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size)) - return result; - if (size <= 0) - return result; - if (!DN_Check(arena)) - return result; - result.arena = arena; - result.pool = DN_PoolFromArena(arena, DN_POOL_DEFAULT_ALIGN); - result.hash_to_slot = DN_ArenaNewArray(result.arena, DN_U32, size, DN_ZMem_Yes); - result.slots = DN_ArenaNewArray(result.arena, DN_DSMapSlot, size, DN_ZMem_Yes); - result.occupied = 1; // For sentinel - result.size = size; - result.initial_size = size; - result.flags = flags; - DN_AssertF(result.hash_to_slot && result.slots, "We pre-allocated a block of memory sufficient in size for the 2 arrays. Maybe the pointers needed extra space because of natural alignment?"); - return result; -} - -template -void DN_DSMapDeinit(DN_DSMap *map, DN_ZMem z_mem) -{ - if (!map) - return; - // TODO(doyle): Use z_mem - (void)z_mem; - DN_MemListDeinit(map->arena->mem); - *map = {}; -} - -template -bool DN_DSMapIsValid(DN_DSMap const *map) -{ - bool result = map && - map->arena && - map->hash_to_slot && // Hash to slot mapping array must be allocated - map->slots && // Slots array must be allocated - (map->size & (map->size - 1)) == 0 && // Must be power of two size - map->occupied >= 1; // DN_DS_MAP_SENTINEL_SLOT takes up one slot - return result; -} - -template -DN_U32 DN_DSMapHash(DN_DSMap const *map, DN_DSMapKey key) -{ - DN_U32 result = 0; - if (!map) - return result; - - if (key.type == DN_DSMapKeyType_U64NoHash) { - result = DN_Cast(DN_U32) key.u64; - return result; - } - - if (key.type == DN_DSMapKeyType_BufferAsU64NoHash) { - result = key.hash; - return result; - } - - DN_U32 seed = map->hash_seed ? map->hash_seed : DN_DS_MAP_DEFAULT_HASH_SEED; - if (map->hash_function) { - map->hash_function(key, seed); - } else { - // NOTE: Courtesy of Demetri Spanos (which this hash table was inspired - // from), the following is a hashing function snippet provided for - // reliable, quick and simple quality hashing functions for hash table - // use. - // Source: https://github.com/demetri/scribbles/blob/c475464756c104c91bab83ed4e14badefef12ab5/hashing/ub_aware_hash_functions.c - - char const *key_ptr = nullptr; - DN_U32 len = 0; - DN_U32 h = seed; - switch (key.type) { - case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/ - case DN_DSMapKeyType_U64NoHash: DN_InvalidCodePath; /*FALLTHRU*/ - case DN_DSMapKeyType_Invalid: break; - - case DN_DSMapKeyType_Buffer: - key_ptr = DN_Cast(char const *) key.buffer_data; - len = key.buffer_size; - break; - - case DN_DSMapKeyType_U64: - key_ptr = DN_Cast(char const *) & key.u64; - len = sizeof(key.u64); - break; - } - - // Murmur3 32-bit without UB unaligned accesses - // DN_U32 mur3_32_no_UB(const void *key, int len, DN_U32 h) - - // main body, work on 32-bit blocks at a time - for (DN_U32 i = 0; i < len / 4; i++) { - DN_U32 k; - memcpy(&k, &key_ptr[i * 4], sizeof(k)); - - k *= 0xcc9e2d51; - k = ((k << 15) | (k >> 17)) * 0x1b873593; - h = (((h ^ k) << 13) | ((h ^ k) >> 19)) * 5 + 0xe6546b64; - } - - // load/mix up to 3 remaining tail bytes into a tail block - DN_U32 t = 0; - uint8_t *tail = ((uint8_t *)key_ptr) + 4 * (len / 4); - switch (len & 3) { - case 3: t ^= tail[2] << 16; - case 2: t ^= tail[1] << 8; - case 1: { - t ^= tail[0] << 0; - h ^= ((0xcc9e2d51 * t << 15) | (0xcc9e2d51 * t >> 17)) * 0x1b873593; - } - } - - // finalization mix, including key length - h = ((h ^ len) ^ ((h ^ len) >> 16)) * 0x85ebca6b; - h = (h ^ (h >> 13)) * 0xc2b2ae35; - result = h ^ (h >> 16); - } - return result; -} - -template -DN_U32 DN_DSMapHashToSlotIndex(DN_DSMap const *map, DN_DSMapKey key) -{ - DN_Assert(key.type != DN_DSMapKeyType_Invalid); - DN_U32 result = DN_DS_MAP_SENTINEL_SLOT; - if (!DN_DSMapIsValid(map)) - return result; - - result = key.hash & (map->size - 1); - for (;;) { - if (result == DN_DS_MAP_SENTINEL_SLOT) // Sentinel is reserved - result++; - - if (map->hash_to_slot[result] == DN_DS_MAP_SENTINEL_SLOT) // Slot is vacant, can use - return result; - - DN_DSMapSlot *slot = map->slots + map->hash_to_slot[result]; - if (slot->key.type == DN_DSMapKeyType_Invalid || (slot->key.hash == key.hash && slot->key == key)) - return result; - - result = (result + 1) & (map->size - 1); - } -} - -template -DN_DSMapResult DN_DSMapFind(DN_DSMap const *map, DN_DSMapKey key) -{ - DN_DSMapResult result = {}; - if (DN_DSMapIsValid(map)) { - DN_U32 index = DN_DSMapHashToSlotIndex(map, key); - if (index != DN_DS_MAP_SENTINEL_SLOT && map->hash_to_slot[index] == DN_DS_MAP_SENTINEL_SLOT) { - result.slot = map->slots; // NOTE: Set to sentinel value - } else { - result.slot = map->slots + map->hash_to_slot[index]; - result.found = true; - } - result.value = &result.slot->value; - } - return result; -} - -template -DN_DSMapResult DN_DSMapMake(DN_DSMap *map, DN_DSMapKey key) -{ - DN_DSMapResult result = {}; - if (!DN_DSMapIsValid(map)) - return result; - - DN_U32 index = DN_DSMapHashToSlotIndex(map, key); - if (map->hash_to_slot[index] == DN_DS_MAP_SENTINEL_SLOT) { - // NOTE: Create the slot - if (index != DN_DS_MAP_SENTINEL_SLOT) - map->hash_to_slot[index] = map->occupied++; - - // NOTE: Check if resize is required - bool map_is_75pct_full = (map->occupied * 4) > (map->size * 3); - if (map_is_75pct_full) { - if (!DN_DSMapResize(map, map->size * 2)) - return result; - result = DN_DSMapMake(map, key); - } else { - result.slot = map->slots + map->hash_to_slot[index]; - result.slot->key = key; // NOTE: Assign key to new slot - if ((key.type == DN_DSMapKeyType_Buffer || - key.type == DN_DSMapKeyType_BufferAsU64NoHash) && - !key.no_copy_buffer) - result.slot->key.buffer_data = DN_PoolNewArrayCopy(&map->pool, char, key.buffer_data, key.buffer_size); - } - } else { - result.slot = map->slots + map->hash_to_slot[index]; - result.found = true; - } - - result.value = &result.slot->value; - DN_Assert(result.slot->key.type != DN_DSMapKeyType_Invalid); - return result; -} - -template -DN_DSMapResult DN_DSMapSet(DN_DSMap *map, DN_DSMapKey key, T const &value) -{ - DN_DSMapResult result = {}; - if (!DN_DSMapIsValid(map)) - return result; - - result = DN_DSMapMake(map, key); - result.slot->value = value; - return result; -} - -template -DN_DSMapResult DN_DSMapFindKeyU64(DN_DSMap const *map, DN_U64 key) -{ - DN_DSMapKey map_key = DN_DSMapKeyU64(map, key); - DN_DSMapResult result = DN_DSMapFind(map, map_key); - return result; -} - -template -DN_DSMapResult DN_DSMapMakeKeyU64(DN_DSMap *map, DN_U64 key) -{ - DN_DSMapKey map_key = DN_DSMapKeyU64(map, key); - DN_DSMapResult result = DN_DSMapMake(map, map_key); - return result; -} - -template -DN_DSMapResult DN_DSMapSetKeyU64(DN_DSMap *map, DN_U64 key, T const &value) -{ - DN_DSMapKey map_key = DN_DSMapKeyU64(map, key); - DN_DSMapResult result = DN_DSMapSet(map, map_key, value); - return result; -} - -template -DN_DSMapResult DN_DSMapFindKeyStr8(DN_DSMap const *map, DN_Str8 key) -{ - DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key); - DN_DSMapResult result = DN_DSMapFind(map, map_key); - return result; -} - -template -DN_DSMapResult DN_DSMapMakeKeyStr8(DN_DSMap *map, DN_Str8 key) -{ - DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key); - DN_DSMapResult result = DN_DSMapMake(map, map_key); - return result; -} - -template -DN_DSMapResult DN_DSMapSetKeyStr8(DN_DSMap *map, DN_Str8 key, T const &value) -{ - DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key); - DN_DSMapResult result = DN_DSMapSet(map, map_key); - return result; -} - -template -bool DN_DSMapResize(DN_DSMap *map, DN_U32 size) -{ - if (!DN_DSMapIsValid(map) || size < map->occupied || size < map->initial_size) - return false; - - DN_Arena *prev_arena = map->arena; - DN_MemList *new_mem = prev_arena->mem; - DN_MemList prev_mem = *prev_arena->mem; - prev_arena->mem = &prev_mem; - - *new_mem = {}; - new_mem->funcs = prev_mem.funcs; - new_mem->flags = prev_mem.flags; - - DN_Arena new_arena = {}; - new_arena.mem = new_mem; - - DN_DSMap new_map = DN_DSMapInit(&new_arena, size, map->flags); - if (!DN_DSMapIsValid(&new_map)) - return false; - - new_map.initial_size = map->initial_size; - for (DN_U32 old_index = 1 /*Sentinel*/; old_index < map->occupied; old_index++) { - DN_DSMapSlot *old_slot = map->slots + old_index; - DN_DSMapKey old_key = old_slot->key; - if (old_key.type == DN_DSMapKeyType_Invalid) - continue; - DN_DSMapSet(&new_map, old_key, old_slot->value); - } - - if ((map->flags & DN_DSMapFlags_DontFreeArenaOnResize) == 0) - DN_DSMapDeinit(map, DN_ZMem_No); - *map = new_map; // Update the map inplace - map->arena = prev_arena; // Restore the previous arena pointer, it's been de-init-ed - *map->arena = new_arena; // Re-init the old arena with the new data - map->pool.arena = map->arena; - return true; -} - -template -bool DN_DSMapErase(DN_DSMap *map, DN_DSMapKey key) -{ - if (!DN_DSMapIsValid(map)) - return false; - - DN_U32 index = DN_DSMapHashToSlotIndex(map, key); - if (index == 0) - return true; - - DN_U32 slot_index = map->hash_to_slot[index]; - if (slot_index == DN_DS_MAP_SENTINEL_SLOT) - return false; - - // NOTE: Mark the slot as unoccupied - map->hash_to_slot[index] = DN_DS_MAP_SENTINEL_SLOT; - - DN_DSMapSlot *slot = map->slots + slot_index; - if (!slot->key.no_copy_buffer) - DN_PoolDealloc(&map->pool, DN_Cast(void *) slot->key.buffer_data); - *slot = {}; // TODO: Optional? - - if (map->occupied > 1 /*Sentinel*/) { - // NOTE: Repair the hash chain, e.g. rehash all the items after the removed - // element and reposition them if necessary. - for (DN_U32 probe_index = index;;) { - probe_index = (probe_index + 1) & (map->size - 1); - if (map->hash_to_slot[probe_index] == DN_DS_MAP_SENTINEL_SLOT) - break; - - DN_DSMapSlot *probe = map->slots + map->hash_to_slot[probe_index]; - DN_U32 new_index = probe->key.hash & (map->size - 1); - if (index <= probe_index) { - if (index < new_index && new_index <= probe_index) - continue; - } else { - if (index < new_index || new_index <= probe_index) - continue; - } - - map->hash_to_slot[index] = map->hash_to_slot[probe_index]; - map->hash_to_slot[probe_index] = DN_DS_MAP_SENTINEL_SLOT; - index = probe_index; - } - - // NOTE: We have erased a slot from the hash table, this leaves a gap - // in our contiguous array. After repairing the chain, the hash mapping - // is correct. - // We will now fill in the vacant spot that we erased using the last - // element in the slot list. - if (map->occupied >= 3 /*Ignoring sentinel, at least 2 other elements to unstable erase*/) { - DN_U32 last_index = map->occupied - 1; - if (last_index != slot_index) { - // NOTE: Copy in last slot to the erase slot - DN_DSMapSlot *last_slot = map->slots + last_index; - map->slots[slot_index] = *last_slot; - - // NOTE: Update the hash-to-slot mapping for the value that was copied in - DN_U32 hash_to_slot_index = DN_DSMapHashToSlotIndex(map, last_slot->key); - map->hash_to_slot[hash_to_slot_index] = slot_index; - *last_slot = {}; // TODO: Optional? - } - } - } - - map->occupied--; - bool map_is_below_25pct_full = (map->occupied * 4) < (map->size * 1); - if (map_is_below_25pct_full && (map->size / 2) >= map->initial_size) - DN_DSMapResize(map, map->size / 2); - - return true; -} - -template -bool DN_DSMapEraseKeyU64(DN_DSMap *map, DN_U64 key) -{ - DN_DSMapKey map_key = DN_DSMapKeyU64(map, key); - bool result = DN_DSMapErase(map, map_key); - return result; -} - -template -bool DN_DSMapEraseKeyStr8(DN_DSMap *map, DN_Str8 key) -{ - DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key); - bool result = DN_DSMapErase(map, map_key); - return result; -} - -template -DN_DSMapKey DN_DSMapKeyBuffer(DN_DSMap const *map, void const *data, DN_USize size) -{ - DN_Assert(size > 0 && size <= UINT32_MAX); - DN_DSMapKey result = {}; - result.type = DN_DSMapKeyType_Buffer; - result.buffer_data = data; - result.buffer_size = DN_Cast(DN_U32) size; - result.hash = DN_DSMapHash(map, result); - return result; -} - -template -DN_DSMapKey DN_DSMapKeyBufferAsU64NoHash(DN_DSMap const *map, void const *data, DN_USize size) -{ - DN_DSMapKey result = {}; - result.type = DN_DSMapKeyType_BufferAsU64NoHash; - result.buffer_data = data; - result.buffer_size = DN_Cast(DN_U32) size; - DN_Assert(size >= sizeof(result.hash)); - DN_Memcpy(&result.hash, data, sizeof(result.hash)); - return result; -} - -template -DN_DSMapKey DN_DSMapKeyU64(DN_DSMap const *map, DN_U64 u64) -{ - DN_DSMapKey result = {}; - result.type = DN_DSMapKeyType_U64; - result.u64 = u64; - result.hash = DN_DSMapHash(map, result); - return result; -} - -template -DN_DSMapKey DN_DSMapKeyStr8(DN_DSMap const *map, DN_Str8 string) -{ - DN_DSMapKey result = DN_DSMapKeyBuffer(map, string.data, string.size); - return result; -} - -// NOTE: DN_DSMap -DN_API DN_DSMapKey DN_DSMapKeyU64NoHash(DN_U64 u64) -{ - DN_DSMapKey result = {}; - result.type = DN_DSMapKeyType_U64NoHash; - result.u64 = u64; - result.hash = DN_Cast(DN_U32) u64; - return result; -} - -DN_API bool DN_DSMapKeyEquals(DN_DSMapKey lhs, DN_DSMapKey rhs) -{ - bool result = false; - if (lhs.type == rhs.type && lhs.hash == rhs.hash) { - switch (lhs.type) { - case DN_DSMapKeyType_Invalid: result = true; break; - case DN_DSMapKeyType_U64NoHash: result = true; break; - case DN_DSMapKeyType_U64: result = lhs.u64 == rhs.u64; break; - - case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/ - case DN_DSMapKeyType_Buffer: { - if (lhs.buffer_size == rhs.buffer_size) - result = DN_Memcmp(lhs.buffer_data, rhs.buffer_data, lhs.buffer_size) == 0; - } break; - } - } - return result; -} - -DN_API bool operator==(DN_DSMapKey lhs, DN_DSMapKey rhs) -{ - bool result = DN_DSMapKeyEquals(lhs, rhs); - return result; -} diff --git a/Source/Base/dn_base_leak.cpp b/Source/Base/dn_base_leak.cpp deleted file mode 100644 index 7082ded..0000000 --- a/Source/Base/dn_base_leak.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#define DN_BASE_LEAK_CPP - -#if defined(_CLANGD) -#include "../dn.h" -#endif - -DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, bool leak_permitted) -{ - if (!ptr) - return; - - DN_TicketMutex_Begin(&leak->alloc_table_mutex); - - DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); - DN_DSMap *alloc_table = &leak->alloc_table; - DN_DSMapResult alloc_entry = DN_DSMapMakeKeyU64(alloc_table, DN_Cast(DN_U64) ptr); - DN_LeakAlloc *alloc = alloc_entry.value; - if (alloc_entry.found) { - if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) { - DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); - DN_Str8x32 new_alloc_size = DN_Str8x32FromByteCountU64Auto(size); - DN_HardAssertF( - alloc->flags & DN_LeakAllocFlag_Freed, - "This pointer is already in the leak tracker, however it has not been freed yet. This " - "same pointer is being ask to be tracked twice in the allocation table, e.g. one if its " - "previous free calls has not being marked freed with an equivalent call to " - "DN_LeakTrackDealloc()\n" - "\n" - "The pointer (0x%p) originally allocated %.*s at:\n" - "\n" - "%.*s\n" - "\n" - "The pointer is allocating %.*s again at:\n" - "\n" - "%.*s\n", - ptr, - DN_Str8PrintFmt(alloc_size), - DN_Str8PrintFmt(alloc->stack_trace), - DN_Str8PrintFmt(new_alloc_size), - DN_Str8PrintFmt(stack_trace)); - } - - // NOTE: Pointer was reused, clean up the prior entry - leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.size; - leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->freed_stack_trace.size; - - DN_OS_MemDealloc(alloc->stack_trace.data); - DN_OS_MemDealloc(alloc->freed_stack_trace.data); - *alloc = {}; - } - - alloc->ptr = ptr; - alloc->size = size; - alloc->stack_trace = stack_trace; - alloc->flags |= leak_permitted ? DN_LeakAllocFlag_LeakPermitted : 0; - leak->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.size; - DN_TicketMutex_End(&leak->alloc_table_mutex); -} - -DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr) -{ - if (!ptr) - return; - - DN_TicketMutex_Begin(&leak->alloc_table_mutex); - - DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/); - DN_DSMap *alloc_table = &leak->alloc_table; - DN_DSMapResult alloc_entry = DN_DSMapFindKeyU64(alloc_table, DN_Cast(uintptr_t) ptr); - DN_HardAssertF(alloc_entry.found, - "Allocated pointer can not be removed as it does not exist in the " - "allocation table. When this memory was allocated, the pointer was " - "not added to the allocation table [ptr=%p]", - ptr); - - DN_LeakAlloc *alloc = alloc_entry.value; - if (alloc->flags & DN_LeakAllocFlag_Freed) { - DN_Str8x32 freed_size = DN_Str8x32FromByteCountU64Auto(alloc->freed_size); - DN_HardAssertF((alloc->flags & DN_LeakAllocFlag_Freed) == 0, - "Double free detected, pointer to free was already marked " - "as freed. Either the pointer was reallocated but not " - "traced, or, the pointer was freed twice.\n" - "\n" - "The pointer (0x%p) originally allocated %.*s at:\n" - "\n" - "%.*s\n" - "\n" - "The pointer was freed at:\n" - "\n" - "%.*s\n" - "\n" - "The pointer is being freed again at:\n" - "\n" - "%.*s\n", - ptr, - DN_Str8PrintFmt(freed_size), - DN_Str8PrintFmt(alloc->stack_trace), - DN_Str8PrintFmt(alloc->freed_stack_trace), - DN_Str8PrintFmt(stack_trace)); - } - - DN_Assert(alloc->freed_stack_trace.size == 0); - alloc->flags |= DN_LeakAllocFlag_Freed; - alloc->freed_stack_trace = stack_trace; - leak->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.size; - DN_TicketMutex_End(&leak->alloc_table_mutex); -} - -DN_API void DN_LeakDump_(DN_LeakTracker *leak) -{ - DN_U64 leak_count = 0; - DN_U64 leaked_bytes = 0; - for (DN_USize index = 1; index < leak->alloc_table.occupied; index++) { - DN_DSMapSlot *slot = leak->alloc_table.slots + index; - DN_LeakAlloc *alloc = &slot->value; - bool alloc_leaked = (alloc->flags & DN_LeakAllocFlag_Freed) == 0; - bool leak_permitted = (alloc->flags & DN_LeakAllocFlag_LeakPermitted); - if (alloc_leaked && !leak_permitted) { - leaked_bytes += alloc->size; - leak_count++; - DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size); - DN_LogWarningF( - "Pointer (0x%p) leaked %.*s at:\n" - "%.*s", - alloc->ptr, - DN_Str8PrintFmt(alloc_size), - DN_Str8PrintFmt(alloc->stack_trace)); - } - } - - if (leak_count) { - DN_Str8x32 leak_size = DN_Str8x32FromByteCountU64Auto(leaked_bytes); - DN_LogWarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DN_Str8PrintFmt(leak_size)); - } -} diff --git a/Source/Base/dn_base_leak.h b/Source/Base/dn_base_leak.h deleted file mode 100644 index ecc7632..0000000 --- a/Source/Base/dn_base_leak.h +++ /dev/null @@ -1,50 +0,0 @@ -#if !defined(DN_BASE_LEAK_H) -#define DN_BASE_LEAK_H - -#if defined(_CLANGD) - #include "../dn.h" -#endif - -enum DN_LeakAllocFlag -{ - DN_LeakAllocFlag_Freed = 1 << 0, - DN_LeakAllocFlag_LeakPermitted = 1 << 1, -}; - -struct DN_LeakAlloc -{ - void *ptr; // 8 Pointer to the allocation being tracked - DN_USize size; // 16 Size of the allocation - DN_USize freed_size; // 24 Store the size of the allocation when it is freed - DN_Str8 stack_trace; // 40 Stack trace at the point of allocation - DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed - DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag` -}; - -// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get -// expensive. Enforce that there is no unexpected padding. -DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively - -struct DN_LeakTracker -{ - DN_DSMap alloc_table; - DN_TicketMutex alloc_table_mutex; - DN_MemList alloc_table_mem; - DN_Arena alloc_table_arena; - DN_U64 alloc_table_bytes_allocated_for_stack_traces; -}; - -DN_API void DN_LeakTrackAlloc_ (DN_LeakTracker *leak, void *ptr, DN_USize size, bool alloc_can_leak); -DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr); -DN_API void DN_LeakDump_ (DN_LeakTracker *leak); - -#if defined(DN_LEAK_TRACKING) -#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) DN_LeakTrackAlloc_(leak, ptr, size, alloc_can_leak) -#define DN_LeakTrackDealloc(leak, ptr) DN_LeakTrackDealloc_(leak, ptr) -#define DN_LeakDump(leak) DN_LeakDump_(leak) -#else -#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0) -#define DN_LeakTrackDealloc(leak, ptr) do { (void)ptr; } while (0) -#define DN_LeakDump(leak) do { } while (0) -#endif -#endif // DN_BASE_LEAK_H diff --git a/Source/Extra/dn_async.cpp b/Source/Extra/dn_async.cpp deleted file mode 100644 index 2368f8e..0000000 --- a/Source/Extra/dn_async.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#define DN_ASYNC_CPP - -#if defined(_CLANGD) - #define DN_H_WITH_OS 1 - #include "../dn.h" - #include "dn_async.h" -#endif - -static DN_I32 DN_ASYNC_ThreadEntryPoint_(DN_OSThread *thread) -{ - DN_OS_ThreadSetNameFmt("%.*s", DN_Str8PrintFmt(thread->name)); - DN_ASYNCCore *async = DN_Cast(DN_ASYNCCore *) thread->user_context; - DN_Ring *ring = &async->ring; - for (;;) { - DN_OS_SemaphoreWait(&async->worker_sem, UINT32_MAX); - if (async->join_threads) - break; - - DN_ASYNCTask task = {}; - for (DN_OS_MutexScope(&async->ring_mutex)) { - if (DN_RingHasData(ring, sizeof(task))) - DN_RingRead(ring, &task, sizeof(task)); - } - - if (task.work.func) { - DN_OS_ConditionVariableBroadcast(&async->ring_write_cv); // Resume any blocked ring write(s) - - DN_ASYNCWorkArgs args = {}; - args.input = task.work.input; - args.thread = thread; - - DN_AtomicAddU32(&async->busy_threads, 1); - task.work.func(args); - DN_AtomicSubU32(&async->busy_threads, 1); - - if (task.completion_sem.handle != 0) - DN_OS_SemaphoreIncrement(&task.completion_sem, 1); - } - } - - return 0; -} - -DN_API void DN_ASYNC_Init(DN_ASYNCCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size) -{ - DN_Assert(async); - async->ring.size = base_size; - async->ring.base = base; - async->ring_mutex = DN_OS_MutexInit(); - async->ring_write_cv = DN_OS_ConditionVariableInit(); - async->worker_sem = DN_OS_SemaphoreInit(0); - async->thread_count = threads_size; - async->threads = threads; - for (DN_ForIndexU(index, async->thread_count)) { - DN_OSThread *thread = async->threads + index; - DN_OS_ThreadInit(thread, DN_ASYNC_ThreadEntryPoint_, /*lane=*/ nullptr, DN_TCInitArgsDefault(), async); - } -} - -DN_API void DN_ASYNC_Deinit(DN_ASYNCCore *async) -{ - DN_Assert(async); - DN_AtomicSetValue32(&async->join_threads, true); - DN_OS_SemaphoreIncrement(&async->worker_sem, async->thread_count); - for (DN_ForItSize(it, DN_OSThread, async->threads, async->thread_count)) - DN_OS_ThreadJoin(it.data, DN_TCDeinitArenas_Yes); -} - -static bool DN_ASYNC_QueueTask_(DN_ASYNCCore *async, DN_ASYNCTask const *task, DN_U64 wait_time_ms) { - DN_U64 end_time_ms = DN_OS_DateUnixTimeMs() + wait_time_ms; - bool result = false; - for (DN_OS_MutexScope(&async->ring_mutex)) { - for (;;) { - if (DN_RingHasSpace(&async->ring, sizeof(*task))) { - DN_RingWriteStruct(&async->ring, task); - result = true; - break; - } - DN_OS_ConditionVariableWaitUntil(&async->ring_write_cv, &async->ring_mutex, end_time_ms); - if (DN_OS_DateUnixTimeMs() >= end_time_ms) - break; - } - } - - if (result) - DN_OS_SemaphoreIncrement(&async->worker_sem, 1); // Flag that a job is available - - return result; -} - -DN_API bool DN_ASYNC_QueueWork(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms) -{ - DN_ASYNCTask task = {}; - task.work.func = func; - task.work.input = input; - bool result = DN_ASYNC_QueueTask_(async, &task, wait_time_ms); - return result; -} - -DN_API DN_ASYNCTask DN_ASYNC_QueueTask(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms) -{ - DN_ASYNCTask result = {}; - result.work.func = func; - result.work.input = input; - result.completion_sem = DN_OS_SemaphoreInit(0); - result.queued = DN_ASYNC_QueueTask_(async, &result, wait_time_ms); - if (!result.queued) - DN_OS_SemaphoreDeinit(&result.completion_sem); - return result; -} - -DN_API bool DN_ASYNC_WaitTask(DN_ASYNCTask *task, DN_U32 timeout_ms) -{ - bool result = true; - if (!task->queued) - return result; - - DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&task->completion_sem, timeout_ms); - result = wait == DN_OSSemaphoreWaitResult_Success; - if (result) - DN_OS_SemaphoreDeinit(&task->completion_sem); - return result; -} - diff --git a/Source/Extra/dn_async.h b/Source/Extra/dn_async.h deleted file mode 100644 index 0ed17c1..0000000 --- a/Source/Extra/dn_async.h +++ /dev/null @@ -1,56 +0,0 @@ -#if !defined(DN_ASYNC_H) -#define DN_ASYNC_H - -#if defined(_CLANGD) - #define DN_H_WITH_OS 1 - #include "../dn.h" -#endif - -enum DN_ASYNCPriority -{ - DN_ASYNCPriority_Low, - DN_ASYNCPriority_High, - DN_ASYNCPriority_Count, -}; - -struct DN_ASYNCCore -{ - DN_OSMutex ring_mutex; - DN_OSConditionVariable ring_write_cv; - DN_OSSemaphore worker_sem; - DN_Ring ring; - DN_OSThread *threads; - DN_U32 thread_count; - DN_U32 busy_threads; - DN_U32 join_threads; -}; - -struct DN_ASYNCWorkArgs -{ - DN_OSThread *thread; - void *input; -}; - -typedef void(DN_ASYNCWorkFunc)(DN_ASYNCWorkArgs work_args); - -struct DN_ASYNCWork -{ - DN_ASYNCWorkFunc *func; - void *input; - void *output; -}; - -struct DN_ASYNCTask -{ - bool queued; - DN_ASYNCWork work; - DN_OSSemaphore completion_sem; -}; - -DN_API void DN_ASYNC_Init (DN_ASYNCCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size); -DN_API void DN_ASYNC_Deinit (DN_ASYNCCore *async); -DN_API bool DN_ASYNC_QueueWork(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms); -DN_API DN_ASYNCTask DN_ASYNC_QueueTask(DN_ASYNCCore *async, DN_ASYNCWorkFunc *func, void *input, DN_U64 wait_time_ms); -DN_API void DN_ASYNC_WaitTask (DN_OSSemaphore *sem, DN_U32 timeout_ms); - -#endif // DN_ASYNC_H diff --git a/Source/Extra/dn_bin_pack.cpp b/Source/Extra/dn_bin_pack.cpp deleted file mode 100644 index 46432ab..0000000 --- a/Source/Extra/dn_bin_pack.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#define DN_BIN_PACK_CPP - -#if defined(_CLANGD) - #include "dn_bin_pack.h" -#endif - -DN_API void DN_BinPackU64(DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item) -{ - DN_U64 const VALUE_MASK = 0b0111'1111; - DN_U8 const CONTINUE_BIT = 0b1000'0000; - - if (mode == DN_BinPackMode_Serialise) { - DN_U64 it = *item; - do { - DN_U8 write_value = DN_Cast(DN_U8)(it & VALUE_MASK); - it >>= 7; - if (it) - write_value |= CONTINUE_BIT; - DN_Str8BuilderAppendBytesCopy(&pack->writer, &write_value, sizeof(write_value)); - } while (it); - } else { - *item = 0; - DN_USize bits_read = 0; - for (DN_U8 src = CONTINUE_BIT; (src & CONTINUE_BIT) && bits_read < 64; bits_read += 7) { - src = pack->read.data[pack->read_index++]; - DN_U8 masked_src = src & VALUE_MASK; - *item |= (DN_Cast(DN_U64) masked_src << bits_read); - } - } -} - -DN_API void DN_BinPackVarInt_(DN_BinPack *pack, DN_BinPackMode mode, void *item, DN_USize size) -{ - DN_U64 value = 0; - DN_AssertF(size <= sizeof(value), - "An item larger than 64 bits (%zu) is trying to be packed as a variable integer which is not supported", - size * 8); - - if (mode == DN_BinPackMode_Serialise) // Read `item` into U64 `value` - DN_Memcpy(&value, item, size); - - DN_BinPackU64(pack, mode, &value); - - if (mode == DN_BinPackMode_Deserialise) // Write U64 `value` into `item` - DN_Memcpy(item, &value, size); -} - -DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack) -{ - bool result = pack->read_index == pack->read.size; - return result; -} - -DN_API void DN_BinPackUSize(DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackU32(DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackU16(DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackU8(DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackI64(DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackI32(DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackI16(DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackI8(DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackF64(DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackF32(DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -#if defined(DN_MATH_H) -DN_API void DN_BinPackV2(DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item) -{ - DN_BinPackF32(pack, mode, &item->x); - DN_BinPackF32(pack, mode, &item->y); -} - -DN_API void DN_BinPackV4(DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item) -{ - DN_BinPackF32(pack, mode, &item->x); - DN_BinPackF32(pack, mode, &item->y); - DN_BinPackF32(pack, mode, &item->z); - DN_BinPackF32(pack, mode, &item->w); -} -#endif - -DN_API void DN_BinPackBool(DN_BinPack *pack, DN_BinPackMode mode, bool *item) -{ - DN_BinPackVarInt_(pack, mode, item, sizeof(*item)); -} - -DN_API void DN_BinPackStr8FromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string) -{ - DN_BinPackVarInt_(pack, mode, &string->size, sizeof(string->size)); - if (mode == DN_BinPackMode_Serialise) { - DN_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size); - } else { - DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, string->size); - *string = DN_Str8FromStr8Arena(src, arena); - pack->read_index += src.size; - } -} - -DN_API void DN_BinPackStr8FromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string) -{ - DN_BinPackVarInt_(pack, mode, &string->size, sizeof(string->size)); - if (mode == DN_BinPackMode_Serialise) { - DN_Str8BuilderAppendBytesCopy(&pack->writer, string->data, string->size); - } else { - DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, string->size); - *string = DN_Str8FromStr8Pool(src, pool); - pack->read_index += src.size; - } -} - -DN_API DN_Str8 DN_BinPackStr8FromBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max) -{ - DN_BinPackCBuffer(pack, mode, ptr, size, max); - DN_Str8 result = DN_Str8FromPtr(ptr, *size); - return result; -} - -DN_API void DN_BinPackBytesFromArena(DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size) -{ - DN_Str8 string = DN_Str8FromPtr(*ptr, *size); - DN_BinPackStr8FromArena(pack, arena, mode, &string); - *ptr = string.data; - *size = string.size; -} - -DN_API void DN_BinPackBytesFromPool(DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size) -{ - DN_Str8 string = DN_Str8FromPtr(*ptr, *size); - DN_BinPackStr8FromPool(pack, pool, mode, &string); - *ptr = string.data; - *size = string.size; -} - -DN_API void DN_BinPackCArray(DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size) -{ - DN_BinPackVarInt_(pack, mode, &size, sizeof(size)); - if (mode == DN_BinPackMode_Serialise) { - DN_Str8BuilderAppendBytesCopy(&pack->writer, ptr, size); - } else { - DN_Str8 src = DN_Str8Subset(pack->read, pack->read_index, size); - DN_Assert(src.size == size); - DN_Memcpy(ptr, src.data, DN_Min(src.size, size)); - pack->read_index += src.size; - } -} - -DN_API void DN_BinPackCBuffer(DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max) -{ - if (mode == DN_BinPackMode_Serialise) { - DN_BinPackUSize(pack, mode, size); - DN_Str8BuilderAppendBytesCopy(&pack->writer, ptr, *size); - } else { - DN_U64 size_u64 = 0; - DN_BinPackU64(pack, mode, &size_u64); - DN_Assert(size_u64 < DN_USIZE_MAX); - DN_Assert(size_u64 <= max); - - *size = DN_Min(size_u64, max); - DN_Memcpy(ptr, pack->read.data + pack->read_index, *size); - pack->read_index += size_u64; - } -} - -DN_API DN_Str8 DN_BinPackBuild(DN_BinPack const *pack, DN_Arena *arena) -{ - DN_Str8 result = DN_Str8FromStr8BuilderArena(&pack->writer, arena); - return result; -} diff --git a/Source/Extra/dn_bin_pack.h b/Source/Extra/dn_bin_pack.h deleted file mode 100644 index b139ad8..0000000 --- a/Source/Extra/dn_bin_pack.h +++ /dev/null @@ -1,47 +0,0 @@ -#if !defined(DN_BIN_PACK_H) -#define DN_BIN_PACK_H - -#if defined(_CLANGD) - #include "../dn.h" -#endif - -enum DN_BinPackMode -{ - DN_BinPackMode_Serialise, - DN_BinPackMode_Deserialise, -}; - -struct DN_BinPack -{ - DN_Str8Builder writer; - DN_Str8 read; - DN_USize read_index; -}; - -DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack); -DN_API void DN_BinPackUSize (DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item); -DN_API void DN_BinPackU64 (DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item); -DN_API void DN_BinPackU32 (DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item); -DN_API void DN_BinPackU16 (DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item); -DN_API void DN_BinPackU8 (DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item); -DN_API void DN_BinPackI64 (DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item); -DN_API void DN_BinPackI32 (DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item); -DN_API void DN_BinPackI16 (DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item); -DN_API void DN_BinPackI8 (DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item); -DN_API void DN_BinPackF64 (DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item); -DN_API void DN_BinPackF32 (DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item); -#if defined (DN_MATH_H) -DN_API void DN_BinPackV2 (DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item); -DN_API void DN_BinPackV4 (DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item); -#endif -DN_API void DN_BinPackBool (DN_BinPack *pack, DN_BinPackMode mode, bool *item); -DN_API void DN_BinPackStr8FromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string); -DN_API void DN_BinPackStr8FromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string); -DN_API DN_Str8 DN_BinPackStr8FromBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max); -DN_API void DN_BinPackBytesFromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size); -DN_API void DN_BinPackBytesFromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size); -DN_API void DN_BinPackCArray (DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size); -DN_API void DN_BinPackCBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max); -DN_API DN_Str8 DN_BinPackBuild (DN_BinPack const *pack, DN_Arena *arena); - -#endif // !defined(DN_BIN_PACK_H) diff --git a/Source/Extra/dn_cgen.cpp b/Source/Extra/dn_cgen.cpp deleted file mode 100644 index 0da7df7..0000000 --- a/Source/Extra/dn_cgen.cpp +++ /dev/null @@ -1,1295 +0,0 @@ -/* -//////////////////////////////////////////////////////////////////////////////////////////////////// -// -// $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\ -// $$ __$$\ $$ __$$\ $$ _____|$$$\ $$ | -// $$ / \__|$$ / \__|$$ | $$$$\ $$ | -// $$ | $$ |$$$$\ $$$$$\ $$ $$\$$ | -// $$ | $$ |\_$$ |$$ __| $$ \$$$$ | -// $$ | $$\ $$ | $$ |$$ | $$ |\$$$ | -// \$$$$$$ |\$$$$$$ |$$$$$$$$\ $$ | \$$ | -// \______/ \______/ \________|\__| \__| -// -// dn_cgen.cpp -// -//////////////////////////////////////////////////////////////////////////////////////////////////// -*/ - -DN_CGenMapNodeToEnum const DN_CGEN_TABLE_KEY_LIST[] = { - {DN_CGenTableKeyType_Name, DN_Str8Lit("name")}, - {DN_CGenTableKeyType_Type, DN_Str8Lit("type")}, -}; - -DN_CGenMapNodeToEnum const DN_CGEN_TABLE_TYPE_LIST[] = { - {DN_CGenTableType_Data, DN_Str8Lit("data") }, - {DN_CGenTableType_CodeGenBuiltinTypes, DN_Str8Lit("code_gen_builtin_types")}, - {DN_CGenTableType_CodeGenStruct, DN_Str8Lit("code_gen_struct") }, - {DN_CGenTableType_CodeGenEnum, DN_Str8Lit("code_gen_enum") }, -}; - -DN_CGenMapNodeToEnum const DN_CGEN_TABLE_ROW_TAG_LIST[] = { - {DN_CGenTableRowTagType_CommentDivider, DN_Str8Lit("comment_divider")}, - {DN_CGenTableRowTagType_EmptyLine, DN_Str8Lit("empty_line") }, -}; - -DN_CGenMapNodeToEnum const DN_CGEN_TABLE_ROW_TAG_COMMENT_DIVIDER_KEY_LIST[] = { - {DN_CGenTableRowTagCommentDivider_Label, DN_Str8Lit("label")}, -}; - -DN_CGenTableHeaderType const DN_CGEN_TABLE_CODE_GEN_STRUCT_HEADER_LIST[] = { - DN_CGenTableHeaderType_Name, - DN_CGenTableHeaderType_Table, - DN_CGenTableHeaderType_CppType, - DN_CGenTableHeaderType_CppName, - DN_CGenTableHeaderType_CppIsPtr, - DN_CGenTableHeaderType_CppOpEquals, - DN_CGenTableHeaderType_CppArraySize, - DN_CGenTableHeaderType_CppArraySizeField, - DN_CGenTableHeaderType_CppLabel, - DN_CGenTableHeaderType_GenTypeInfo, -}; - -DN_CGenTableHeaderType const DN_CGEN_TABLE_CODE_GEN_ENUM_HEADER_LIST[] = { - DN_CGenTableHeaderType_Name, - DN_CGenTableHeaderType_Table, - DN_CGenTableHeaderType_CppName, - DN_CGenTableHeaderType_CppValue, - DN_CGenTableHeaderType_CppLabel, - DN_CGenTableHeaderType_GenTypeInfo, - DN_CGenTableHeaderType_GenEnumCount, -}; - -DN_CGenTableHeaderType const DN_CGEN_TABLE_CODE_GEN_BUILTIN_TYPES_HEADER_LIST[] = { - DN_CGenTableHeaderType_Name, -}; - -static bool DN_CGen_GatherTables_(DN_CGen *cgen, DN_ErrSink *err) -{ - bool result = false; - if (!cgen || !cgen->file_list || !cgen->arena) - return result; - - // NOTE: Gather the tables ///////////////////////////////////////////////////////////////////// - for (MD_EachNode(ref, cgen->file_list->first_child)) { - MD_Node *root = MD_ResolveNodeFromReference(ref); - for (MD_EachNode(node, root->first_child)) { - MD_Node *table_tag = MD_TagFromString(node, MD_S8Lit("table"), 0); - if (MD_NodeIsNil(table_tag)) - continue; - - DN_CGenTable *table = MD_PushArray(cgen->arena, DN_CGenTable, 1); - table->node = node; - table->name = DN_CGen_MDToDNStr8(table_tag->first_child->first_child->string); - MD_MapInsert(cgen->arena, &cgen->table_map, MD_MapKeyStr(DN_CGen_DNToMDStr8(table->name)), table); - MD_QueuePush(cgen->first_table, cgen->last_table, table); - - for (MD_EachNode(key, table_tag->first_child)) { - DN_CGenMapNodeToEnum key_mapping = DN_CGen_MapNodeToEnumOrExit(key, - DN_CGEN_TABLE_KEY_LIST, - DN_ArrayCountU(DN_CGEN_TABLE_KEY_LIST), - "Table specified invalid key"); - switch (DN_Cast(DN_CGenTableKeyType) key_mapping.enum_val) { - case DN_CGenTableKeyType_Nil: DN_InvalidCodePath; - - case DN_CGenTableKeyType_Name: { - table->name = DN_CGen_MDToDNStr8(key->first_child->string); - } break; - - case DN_CGenTableKeyType_Type: { - MD_Node *table_type_value = key->first_child; - DN_CGenMapNodeToEnum table_type_validator = DN_CGen_MapNodeToEnumOrExit(table_type_value, - DN_CGEN_TABLE_TYPE_LIST, - DN_ArrayCountU(DN_CGEN_TABLE_TYPE_LIST), - "Table 'type' specified invalid value"); - table->type = DN_Cast(DN_CGenTableType) table_type_validator.enum_val; - - DN_Assert(table->type <= DN_CGenTableType_Count); - cgen->table_counts[table->type]++; - } break; - } - } - } - } - - // NOTE: Parse the tables ////////////////////////////////////////////////////////////////////// - DN_USize const BEGIN_COLUMN_INDEX = 1; /*Reserve 0th slot for nil-entry*/ - for (DN_CGenTable *table = cgen->first_table; table; table = table->next) { - table->column_count = BEGIN_COLUMN_INDEX; - table->headers_node = table->node->first_child; - for (MD_EachNode(column_node, table->headers_node->first_child)) - table->column_count++; - - if (table->column_count == BEGIN_COLUMN_INDEX) - continue; - - MD_Node *row_it = table->headers_node->next; - for (MD_EachNode(row_node, row_it)) - table->row_count++; - - table->rows = MD_PushArray(cgen->arena, DN_CGenTableRow, table->row_count); - table->headers = MD_PushArray(cgen->arena, DN_CGenTableHeader, table->column_count); - for (DN_USize index = 0; index < table->row_count; index++) { - DN_CGenTableRow *row = table->rows + index; - row->columns = MD_PushArray(cgen->arena, DN_CGenTableColumn, table->column_count); - } - - // NOTE: Collect table headers ///////////////////////////////////////////////////////////// - table->headers_map = MD_MapMake(cgen->arena); - DN_USize column_index = BEGIN_COLUMN_INDEX; - for (MD_EachNode(header_column, table->headers_node->first_child)) { - DN_Assert(column_index < table->column_count); - - // NOTE: Detect builtin headers and cache the index for that table ///////////////////// - for (DN_USize enum_index = 0; enum_index < DN_CGenTableHeaderType_Count; enum_index++) { - DN_Str8 decl_str8 = DN_CGen_TableHeaderTypeToDeclStr8(DN_Cast(DN_CGenTableHeaderType) enum_index); - if (!DN_Str8Eq(decl_str8, DN_Str8FromPtr(header_column->string.str, header_column->string.size))) - continue; - table->column_indexes[enum_index] = column_index; - break; - } - - MD_MapInsert(cgen->arena, &table->headers_map, MD_MapKeyStr(header_column->string), DN_Cast(void *) column_index); - table->headers[column_index++].name = header_column->string; - } - - // NOTE: Validate table headers //////////////////////////////////////////////////////////// - switch (table->type) { - case DN_CGenTableType_Nil: DN_InvalidCodePath; - case DN_CGenTableType_Count: DN_InvalidCodePath; - - case DN_CGenTableType_Data: { - } break; - - case DN_CGenTableType_CodeGenStruct: { - for (DN_CGenTableHeaderType enum_val : DN_CGEN_TABLE_CODE_GEN_STRUCT_HEADER_LIST) { - if (table->column_indexes[enum_val] == 0) { - DN_Str8 expected_value = DN_CGen_TableHeaderTypeToDeclStr8(enum_val); - DN_CGen_LogF(MD_MessageKind_Error, table->headers_node, err, "Struct code generation table is missing column '%.*s'", DN_Str8PrintFmt(expected_value)); - return false; - } - } - } break; - - case DN_CGenTableType_CodeGenEnum: { - for (DN_CGenTableHeaderType enum_val : DN_CGEN_TABLE_CODE_GEN_ENUM_HEADER_LIST) { - if (table->column_indexes[enum_val] == 0) { - DN_Str8 expected_value = DN_CGen_TableHeaderTypeToDeclStr8(enum_val); - DN_CGen_LogF(MD_MessageKind_Error, table->headers_node, err, "Enum code generation table is missing column '%.*s'", DN_Str8PrintFmt(expected_value)); - return false; - } - } - } break; - - case DN_CGenTableType_CodeGenBuiltinTypes: { - for (DN_CGenTableHeaderType enum_val : DN_CGEN_TABLE_CODE_GEN_BUILTIN_TYPES_HEADER_LIST) { - if (table->column_indexes[enum_val] == 0) { - DN_Str8 expected_value = DN_CGen_TableHeaderTypeToDeclStr8(enum_val); - DN_CGen_LogF(MD_MessageKind_Error, table->headers_node, err, "Enum code generation table is missing column '%.*s'", DN_Str8PrintFmt(expected_value)); - return false; - } - } - } break; - } - - // NOTE: Parse each row in table /////////////////////////////////////////////////////////// - DN_USize row_index = 0; - for (MD_EachNode(row_node, row_it)) { - column_index = BEGIN_COLUMN_INDEX; - DN_Assert(row_index < table->row_count); - - // NOTE: Parse any tags set on the row ///////////////////////////////////////////////// - DN_CGenTableRow *row = table->rows + row_index++; - for (MD_EachNode(row_tag, row_node->first_tag)) { - DN_CGenMapNodeToEnum row_mapping = DN_CGen_MapNodeToEnumOrExit(row_tag, - DN_CGEN_TABLE_ROW_TAG_LIST, - DN_ArrayCountU(DN_CGEN_TABLE_ROW_TAG_LIST), - "Table specified invalid row tag"); - DN_CGenTableRowTag *tag = MD_PushArray(cgen->arena, DN_CGenTableRowTag, 1); - tag->type = DN_Cast(DN_CGenTableRowTagType) row_mapping.enum_val; - MD_QueuePush(row->first_tag, row->last_tag, tag); - - switch (tag->type) { - case DN_CGenTableRowTagType_Nil: DN_InvalidCodePath; - - case DN_CGenTableRowTagType_CommentDivider: { - for (MD_EachNode(tag_key, row_tag->first_child)) { - DN_CGenMapNodeToEnum tag_mapping = DN_CGen_MapNodeToEnumOrExit(tag_key, - DN_CGEN_TABLE_ROW_TAG_COMMENT_DIVIDER_KEY_LIST, - DN_ArrayCountU(DN_CGEN_TABLE_ROW_TAG_COMMENT_DIVIDER_KEY_LIST), - "Table specified invalid row tag"); - switch (DN_Cast(DN_CGenTableRowTagCommentDivider) tag_mapping.enum_val) { - case DN_CGenTableRowTagCommentDivider_Nil: DN_InvalidCodePath; - case DN_CGenTableRowTagCommentDivider_Label: { - tag->comment = tag_key->first_child->string; - } break; - } - } - } break; - - case DN_CGenTableRowTagType_EmptyLine: break; - } - } - - for (MD_EachNode(column_node, row_node->first_child)) { - table->headers[column_index].longest_string = DN_Max(table->headers[column_index].longest_string, DN_Cast(int) column_node->string.size); - row->columns[column_index].string = DN_CGen_MDToDNStr8(column_node->string); - row->columns[column_index].node = column_node; - column_index++; - } - } - } - - // NOTE: Validate codegen ////////////////////////////////////////////////////////////////////// - DN_CGenTableHeaderType const CHECK_COLUMN_LINKS[] = { - DN_CGenTableHeaderType_CppName, - DN_CGenTableHeaderType_CppType, - DN_CGenTableHeaderType_CppValue, - DN_CGenTableHeaderType_CppIsPtr, - DN_CGenTableHeaderType_CppArraySize, - DN_CGenTableHeaderType_CppArraySizeField, - }; - - result = true; - for (DN_CGenTable *table = cgen->first_table; table; table = table->next) { - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - for (DN_CGenTableHeaderType check_column : CHECK_COLUMN_LINKS) { - DN_CGenTableColumn column = it.cgen_table_row->columns[table->column_indexes[check_column]]; - if (column.string.size == 0) { - // NOTE: The code generation table did not bind a code generation parameter to - // a column in the target table. We skip it. - continue; - } - - // NOTE: Check if the column to bind to exists in the target table. - if (!MD_MapLookup(&it.table->headers_map, MD_MapKeyStr(DN_CGen_DNToMDStr8(column.string)))) { - result = false; - DN_Str8 header_type_str8 = DN_CGen_TableHeaderTypeToDeclStr8(check_column); - DN_CGen_LogF(MD_MessageKind_Error, column.node, err, - "Code generation table binds '%.*s' to '%.*s', but the column '%.*s' does not exist in table '%.*s'\n" - "NOTE: If you want '%.*s' to omit the column '%.*s' you can bind to the empty string `` to skip it, otherwise, please ensure the table '%.*s' has the column '%.*s'", - DN_Str8PrintFmt(column.string), - DN_Str8PrintFmt(header_type_str8), - DN_Str8PrintFmt(column.string), - DN_Str8PrintFmt(it.table->name), - DN_Str8PrintFmt(it.table->name), - DN_Str8PrintFmt(header_type_str8), - DN_Str8PrintFmt(it.table->name), - DN_Str8PrintFmt(header_type_str8)); - } - } - } - } - - return result; -} - -DN_API DN_CGen DN_CGen_FromFilesArgV(int argc, char const **argv, DN_ErrSink *err) -{ - DN_CGen result = {}; - result.arena = MD_ArenaAlloc(); - result.file_list = MD_MakeList(result.arena); - result.table_map = MD_MapMake(result.arena); - - bool has_error = false; - for (DN_ISize arg_index = 0; arg_index < argc; arg_index++) { - MD_String8 file_name = MD_S8CString(DN_Cast(char *) argv[arg_index]); - MD_ParseResult parse_result = MD_ParseWholeFile(result.arena, file_name); - for (MD_Message *message = parse_result.errors.first; message != 0; message = message->next) { - has_error = true; - DN_CGen_LogF(message->kind, message->node, err, "%.*s", MD_S8VArg(message->string)); - } - MD_PushNewReference(result.arena, result.file_list, parse_result.node); - } - - if (!has_error) - DN_CGen_GatherTables_(&result, err); - return result; -} - -DN_API DN_Str8 DN_CGen_TableHeaderTypeToDeclStr8(DN_CGenTableHeaderType type) -{ - DN_Str8 result = {}; - switch (type) { - case DN_CGenTableHeaderType_Name: result = DN_Str8Lit("name"); break; - case DN_CGenTableHeaderType_Table: result = DN_Str8Lit("table"); break; - case DN_CGenTableHeaderType_CppType: result = DN_Str8Lit("cpp_type"); break; - case DN_CGenTableHeaderType_CppName: result = DN_Str8Lit("cpp_name"); break; - case DN_CGenTableHeaderType_CppValue: result = DN_Str8Lit("cpp_value"); break; - case DN_CGenTableHeaderType_CppIsPtr: result = DN_Str8Lit("cpp_is_ptr"); break; - case DN_CGenTableHeaderType_CppOpEquals: result = DN_Str8Lit("cpp_op_equals"); break; - case DN_CGenTableHeaderType_CppArraySize: result = DN_Str8Lit("cpp_array_size"); break; - case DN_CGenTableHeaderType_CppArraySizeField: result = DN_Str8Lit("cpp_array_size_field"); break; - case DN_CGenTableHeaderType_CppLabel: result = DN_Str8Lit("cpp_label"); break; - case DN_CGenTableHeaderType_GenTypeInfo: result = DN_Str8Lit("gen_type_info"); break; - case DN_CGenTableHeaderType_GenEnumCount: result = DN_Str8Lit("gen_enum_count"); break; - case DN_CGenTableHeaderType_Count: result = DN_Str8Lit("XX BAD ENUM VALUE XX"); break; - default: result = DN_Str8Lit("XX INVALID ENUM VALUE XX"); break; - } - return result; -} - -DN_API DN_CGenMapNodeToEnum DN_CGen_MapNodeToEnumOrExit(MD_Node const *node, DN_CGenMapNodeToEnum const *valid_keys, DN_USize valid_keys_size, char const *fmt, ...) -{ - DN_CGenMapNodeToEnum result = {}; - for (DN_USize index = 0; index < valid_keys_size; index++) { - DN_CGenMapNodeToEnum const *validator = valid_keys + index; - if (DN_Str8Eq(DN_Str8FromPtr(node->string.str, node->string.size), validator->node_string)) { - result = *validator; - break; - } - } - - if (result.enum_val == 0) { - MD_CodeLoc loc = MD_CodeLocFromNode(DN_Cast(MD_Node *) node); - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - va_list args; - va_start(args, fmt); - DN_Str8 user_msg = DN_Str8FromFmtVArena(tmem.arena, fmt, args); - va_end(args); - - DN_Str8Builder builder = {}; - builder.arena = tmem.arena; - - DN_Str8BuilderAppendF(&builder, "%.*s: '%.*s' is not recognised, the supported values are ", DN_Str8PrintFmt(user_msg), MD_S8VArg(node->string)); - for (DN_USize index = 0; index < valid_keys_size; index++) { - DN_CGenMapNodeToEnum const *validator = valid_keys + index; - DN_Str8BuilderAppendF(&builder, DN_Cast(char *) "%s'%.*s'", index ? ", " : "", DN_Str8PrintFmt(validator->node_string)); - } - - DN_Str8 error_msg = DN_Str8FromStr8BuilderArena(&builder, tmem.arena); - DN_TCScratchEnd(&tmem); - MD_PrintMessageFmt(stderr, loc, MD_MessageKind_Error, DN_Cast(char *) "%.*s", DN_Str8PrintFmt(error_msg)); - DN_OS_Exit(DN_Cast(uint32_t) - 1); - } - return result; -} - -DN_API DN_USize DN_CGen_NodeChildrenCount(MD_Node const *node) -{ - DN_USize result = 0; - for (MD_EachNode(item, node->first_child)) - result++; - return result; -} - -DN_API void DN_CGen_LogF(MD_MessageKind kind, MD_Node *node, DN_ErrSink *err, char const *fmt, ...) -{ - if (!err) - return; - - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - DN_Str8Builder builder = {}; - builder.arena = tmem.arena; - - MD_String8 kind_string = MD_StringFromMessageKind(kind); - MD_CodeLoc loc = MD_CodeLocFromNode(node); - DN_Str8BuilderAppendF(&builder, "" MD_FmtCodeLoc " %.*s: ", MD_CodeLocVArg(loc), MD_S8VArg(kind_string)); - - va_list args; - va_start(args, fmt); - DN_Str8BuilderAppendFV(&builder, fmt, args); - va_end(args); - - DN_Str8 msg = DN_Str8FromStr8BuilderArena(&builder, err->arena); - DN_TCScratchEnd(&tmem); - DN_OS_ErrSinkAppendF(err, DN_Cast(uint32_t) - 1, "%.*s", DN_Str8PrintFmt(msg)); -} - -DN_API bool DN_CGen_TableHasHeaders(DN_CGenTable const *table, DN_Str8 const *headers, DN_USize header_count, DN_ErrSink *err) -{ - bool result = true; - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - DN_Str8Builder builder = {}; - builder.arena = tmem.arena; - - for (DN_USize index = 0; index < header_count; index++) { - DN_Str8 header = headers[index]; - MD_String8 header_md = {DN_Cast(MD_u8 *) header.data, header.size}; - MD_MapSlot *slot = MD_MapLookup(DN_Cast(MD_Map *) & table->headers_map, MD_MapKeyStr(header_md)); - if (!slot) { - result = false; - DN_Str8BuilderAppendF(&builder, "%s%.*s", builder.count ? ", " : "", DN_Str8PrintFmt(header)); - } - } - - if (!result) { - DN_Str8 missing_headers = DN_Str8FromStr8BuilderArena(&builder, tmem.arena); - DN_CGen_LogF(MD_MessageKind_Error, - table->headers_node, - err, - "Table '%.*s' is missing the header(s): %.*s", - DN_Str8PrintFmt(table->name), - DN_Str8PrintFmt(missing_headers)); - } - - DN_TCScratchEnd(&tmem); - return result; -} - -DN_API DN_CGenLookupColumnAtHeader DN_CGen_LookupColumnAtHeader(DN_CGenTable *table, DN_Str8 header, DN_CGenTableRow const *row) -{ - DN_CGenLookupColumnAtHeader result = {}; - if (!table || !row) - return result; - - MD_String8 header_md = {DN_Cast(MD_u8 *) header.data, header.size}; - MD_MapSlot *slot = MD_MapLookup(&table->headers_map, MD_MapKeyStr(header_md)); - if (!slot) - return result; - - DN_USize column_index = DN_Cast(DN_USize) slot->val; - DN_Assert(column_index < table->column_count); - { - DN_USize begin = DN_Cast(uintptr_t)(table->rows); - DN_USize end = DN_Cast(uintptr_t)(table->rows + table->row_count); - DN_USize ptr = DN_Cast(uintptr_t) row; - DN_AssertF(ptr >= begin && ptr <= end, "The row to lookup does not belong to the table passed in"); - } - - result.index = column_index; - result.column = row->columns[column_index]; - result.header = table->headers[column_index]; - return result; -} - -DN_API bool DN_CGen_LookupNextTableInCodeGenTable(DN_CGen *cgen, DN_CGenTable *cgen_table, DN_CGenLookupTableIterator *it) -{ - if (!cgen_table) - return false; - - if (it->row_index >= cgen_table->row_count) - return false; - - if (cgen_table->type != DN_CGenTableType_CodeGenEnum && cgen_table->type != DN_CGenTableType_CodeGenStruct && cgen_table->type != DN_CGenTableType_CodeGenBuiltinTypes) - return false; - - // NOTE: Lookup the table in this row that we will code generate from. Not - // applicable when we are doing builtin types as the types are just put - // in-line into the code generation table itself. - it->cgen_table = cgen_table; - it->cgen_table_row = cgen_table->rows + it->row_index++; - if (cgen_table->type != DN_CGenTableType_CodeGenBuiltinTypes) { - DN_CGenTableColumn cgen_table_column = it->cgen_table_row->columns[cgen_table->column_indexes[DN_CGenTableHeaderType_Table]]; - MD_String8 key = {DN_Cast(MD_u8 *) cgen_table_column.string.data, cgen_table_column.string.size}; - MD_MapSlot *table_slot = MD_MapLookup(&cgen->table_map, MD_MapKeyStr(key)); - if (!table_slot) { - MD_CodeLoc loc = MD_CodeLocFromNode(cgen_table_column.node); - MD_PrintMessageFmt(stderr, - loc, - MD_MessageKind_Warning, - DN_Cast(char *) "Code generation table references non-existent table '%.*s'", - DN_Str8PrintFmt(cgen_table_column.string)); - return false; - } - it->table = DN_Cast(DN_CGenTable *) table_slot->val; - } - - for (DN_USize type = 0; type < DN_CGenTableHeaderType_Count; type++) - it->cgen_table_column[type] = it->cgen_table_row->columns[cgen_table->column_indexes[type]]; - return true; -} - -DN_API bool DN_CGen_WillCodeGenTypeName(DN_CGen const *cgen, DN_Str8 name) -{ - if (name.size == 0) - return false; - - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - if (table->type != DN_CGenTableType_CodeGenStruct && table->type != DN_CGenTableType_CodeGenEnum) - continue; - - for (DN_USize row_index = 0; row_index < table->row_count; row_index++) { - DN_CGenTableRow const *row = table->rows + row_index; - DN_CGenTableColumn const *column = row->columns + table->column_indexes[DN_CGenTableHeaderType_Name]; - if (DN_Str8Eq(column->string, name)) - return true; - } - } - - return false; -} - -static void DN_CGen_EmitRowWhitespace_(DN_CGenTableRow const *row, DN_CppFile *cpp) -{ - for (DN_CGenTableRowTag *tag = row->first_tag; tag; tag = tag->next) { - switch (tag->type) { - case DN_CGenTableRowTagType_Nil: DN_InvalidCodePath; - - case DN_CGenTableRowTagType_CommentDivider: { - if (tag->comment.size <= 0) - break; - - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - DN_Str8 prefix = DN_Str8FromFmtArena(tmem.arena, "// NOTE: %.*s ", MD_S8VArg(tag->comment)); - int line_padding = DN_Max(100 - (DN_Cast(int) prefix.size + (DN_CppSpacePerIndent(cpp) * cpp->indent)), 0); - DN_CppPrint(cpp, "%.*s", DN_Str8PrintFmt(prefix)); - for (int index = 0; index < line_padding; index++) - DN_CppAppend(cpp, "/"); - DN_CppAppend(cpp, "\n"); - DN_TCScratchEnd(&tmem); - } break; - - case DN_CGenTableRowTagType_EmptyLine: { - DN_CppAppend(cpp, "\n"); - } break; - } - } -} - -DN_Str8 DN_CGen_ConvertTemplatesToEmittableLiterals_(DN_Arena *arena, DN_Str8 type) -{ - DN_TCScratch tmem = DN_TCScratchBegin(&arena, 1); - DN_Str8 result = DN_Str8TrimWhitespaceAround(type); - result = DN_Str8Replace(result, /*find*/ DN_Str8Lit("<"), /*replace*/ DN_Str8Lit("_"), /*start_index*/ 0, arena, DN_Str8EqCase_Sensitive); - result = DN_Str8Replace(result, /*find*/ DN_Str8Lit(">"), /*replace*/ DN_Str8Lit(""), /*start_index*/ 0, arena, DN_Str8EqCase_Sensitive); - result = DN_Str8TrimWhitespaceAround(result); - DN_TCScratchEnd(&tmem); - return result; -} - -DN_Str8 DN_CGen_StripQualifiersOnCppType_(DN_Arena *arena, DN_Str8 type) -{ - DN_TCScratch tmem = DN_TCScratchBegin(&arena, 1); - DN_Str8 result = DN_Str8TrimWhitespaceAround(type); - result = DN_Str8Replace(result, /*find*/ DN_Str8Lit("*"), /*replace*/ DN_Str8Lit(""), /*start_index*/ 0, tmem.arena, DN_Str8EqCase_Sensitive); - result = DN_Str8Replace(result, /*find*/ DN_Str8Lit("constexpr"), /*replace*/ DN_Str8Lit(""), /*start_index*/ 0, tmem.arena, DN_Str8EqCase_Sensitive); - result = DN_Str8Replace(result, /*find*/ DN_Str8Lit("const"), /*replace*/ DN_Str8Lit(""), /*start_index*/ 0, tmem.arena, DN_Str8EqCase_Sensitive); - result = DN_Str8Replace(result, /*find*/ DN_Str8Lit("static"), /*replace*/ DN_Str8Lit(""), /*start_index*/ 0, tmem.arena, DN_Str8EqCase_Sensitive); - result = DN_Str8Replace(result, /*find*/ DN_Str8Lit(" "), /*replace*/ DN_Str8Lit(""), /*start_index*/ 0, arena, DN_Str8EqCase_Sensitive); - result = DN_Str8TrimWhitespaceAround(result); - DN_TCScratchEnd(&tmem); - return result; -} - -DN_API void DN_CGen_EmitCodeForTables(DN_CGen *cgen, DN_CGenEmit emit, DN_CppFile *cpp, DN_Str8 emit_prefix) -{ - if (emit & DN_CGenEmit_Prototypes) { - // NOTE: Generate type info enums ////////////////////////////////////////////////////////////// - DN_CppEnumBlock(cpp, "%.*sType", DN_Str8PrintFmt(emit_prefix)) - { - DN_CppLine(cpp, "%.*sType_Nil,", DN_Str8PrintFmt(emit_prefix)); - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - DN_Str8 enum_name = DN_CGen_ConvertTemplatesToEmittableLiterals_(tmem.arena, it.cgen_table_column[DN_CGenTableHeaderType_Name].string); - DN_CppLine(cpp, "%.*sType_%.*s,", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(enum_name)); - DN_TCScratchEnd(&tmem); - } - DN_CppLine(cpp, "%.*sType_Count,", DN_Str8PrintFmt(emit_prefix)); - } - DN_CppNewLine(cpp); - - // NOTE: Generate structs + enums ////////////////////////////////////////////////////////////// - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - switch (table->type) { - case DN_CGenTableType_Nil: DN_InvalidCodePath; - case DN_CGenTableType_Count: DN_InvalidCodePath; - case DN_CGenTableType_CodeGenBuiltinTypes: continue; - case DN_CGenTableType_Data: continue; - - case DN_CGenTableType_CodeGenStruct: { - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - // TODO(doyle): Verify the codegen table has the headers from the table it references - int longest_type_name = 0; - for (DN_USize row_index = 0; row_index < it.table->row_count; row_index++) { - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - DN_CGenTableRow const *row = it.table->rows + row_index; - DN_CGenLookupColumnAtHeader cpp_type = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppType].string, row); - - DN_USize length = cpp_type.column.string.size; - DN_Str8 find_name = DN_CGen_StripQualifiersOnCppType_(tmem.arena, cpp_type.column.string); - if (DN_CGen_WillCodeGenTypeName(cgen, find_name)) - length += emit_prefix.size; - - longest_type_name = DN_Max(longest_type_name, DN_Cast(int) length); - DN_TCScratchEnd(&tmem); - } - - DN_CppStructBlock(cpp, "%.*s%.*s", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(it.cgen_table_column[DN_CGenTableHeaderType_Name].string)) - { - for (DN_USize row_index = 0; row_index < it.table->row_count; row_index++) { - DN_CGenTableRow const *row = it.table->rows + row_index; - DN_CGenLookupColumnAtHeader cpp_name = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppName].string, row); - DN_CGenLookupColumnAtHeader cpp_type = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppType].string, row); - DN_CGenLookupColumnAtHeader cpp_array_size = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppArraySize].string, row); - if (cpp_name.column.string.size <= 0 || cpp_type.column.string.size <= 0) - continue; - - // NOTE: Generate cpp array size - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - DN_Str8 array_size = {}; - if (cpp_array_size.column.string.size) - array_size = DN_Str8FromFmtArena(tmem.arena, "[%.*s]", DN_Str8PrintFmt(cpp_array_size.column.string)); - - // NOTE: Check if we're referencing a code generated type. If we - // are, append the `emit_prefix` - DN_Str8 emit_cpp_type = cpp_type.column.string; - { - DN_Str8 find_name = DN_CGen_StripQualifiersOnCppType_(tmem.arena, emit_cpp_type); - if (DN_CGen_WillCodeGenTypeName(cgen, find_name)) - emit_cpp_type = DN_Str8FromFmtArena(tmem.arena, "%.*s%.*s", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(cpp_type.column.string)); - } - DN_TCScratchEnd(&tmem); - - int name_to_type_padding = 1 + longest_type_name - DN_Cast(int) emit_cpp_type.size; - - // NOTE: Emit decl ///////////////////////////////////////////////// - DN_CGen_EmitRowWhitespace_(row, cpp); - DN_CppLine(cpp, - "%.*s%*s%.*s%.*s;", - DN_Str8PrintFmt(emit_cpp_type), - name_to_type_padding, - "", - DN_Str8PrintFmt(cpp_name.column.string), - DN_Str8PrintFmt(array_size)); - } - } - DN_CppNewLine(cpp); - DN_CppNewLine(cpp); - } - - } break; - - case DN_CGenTableType_CodeGenEnum: { - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_CppEnumBlock(cpp, "%.*s%.*s", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(it.cgen_table_column[DN_CGenTableHeaderType_Name].string)) - { - DN_USize enum_count = 0; - for (DN_USize row_index = 0; row_index < it.table->row_count; row_index++) { - DN_CGenTableRow const *row = it.table->rows + row_index; - DN_CGenLookupColumnAtHeader cpp_name = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppName].string, row); - DN_CGenLookupColumnAtHeader cpp_value = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppValue].string, row); - - if (cpp_name.column.string.size <= 0) - continue; - - DN_CGen_EmitRowWhitespace_(row, cpp); - if (cpp_value.column.string.size) - DN_CppLine(cpp, - "%.*s%.*s_%.*s = %.*s,", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(it.cgen_table_column[DN_CGenTableHeaderType_Name].string), - DN_Str8PrintFmt(cpp_name.column.string), - DN_Str8PrintFmt(cpp_value.column.string)); - else - DN_CppLine(cpp, - "%.*s%.*s_%.*s = %zu,", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(it.cgen_table_column[DN_CGenTableHeaderType_Name].string), - DN_Str8PrintFmt(cpp_name.column.string), - row_index); - enum_count++; - } - - DN_CGenTableColumn gen_enum_count_column = it.cgen_table_column[DN_CGenTableHeaderType_GenEnumCount]; - if (gen_enum_count_column.string.size) - DN_CppLine(cpp, - "%.*s%.*s_%.*s = %zu,", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(it.cgen_table_column[DN_CGenTableHeaderType_Name].string), - DN_Str8PrintFmt(gen_enum_count_column.string), - enum_count); - } - DN_CppNewLine(cpp); - } - } break; - } - } - - // NOTE: Generate enums for struct fields ////////////////////////////////////////////////// - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - switch (table->type) { - case DN_CGenTableType_Nil: DN_InvalidCodePath; - case DN_CGenTableType_Count: DN_InvalidCodePath; - case DN_CGenTableType_Data: continue; - case DN_CGenTableType_CodeGenBuiltinTypes: continue; - case DN_CGenTableType_CodeGenEnum: continue; - - case DN_CGenTableType_CodeGenStruct: { - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_Str8 struct_name = it.cgen_table_column[DN_CGenTableHeaderType_Name].string; - DN_CppEnumBlock(cpp, "%.*s%.*sTypeField", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(struct_name)) - { - for (DN_USize row_index = 0; row_index < it.table->row_count; row_index++) { - DN_CGenTableRow const *row = it.table->rows + row_index; - DN_CGenLookupColumnAtHeader cpp_name = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppName].string, row); - if (cpp_name.column.string.size <= 0) - continue; - DN_CGen_EmitRowWhitespace_(row, cpp); - DN_CppLine(cpp, "%.*s%.*sTypeField_%.*s,", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(struct_name), DN_Str8PrintFmt(cpp_name.column.string)); - } - DN_CppLine(cpp, "%.*s%.*sTypeField_Count,", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(struct_name)); - } - DN_CppNewLine(cpp); - } - } break; - } - } - - // NOTE: Str8 to enum conversion /////////////////////////////////////////////////////////////// - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - if (table->type != DN_CGenTableType_CodeGenEnum) - continue; - - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_Str8 type_name = it.cgen_table_column[DN_CGenTableHeaderType_Name].string; - DN_CppStructBlock(cpp, "%.*s%.*sStr8ToEnumResult", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)) - { - DN_CppLine(cpp, "bool success;"); - DN_CppLine(cpp, "%.*s%.*s value;", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)); - } - DN_CppNewLine(cpp); - } - } - - // NOTE: Type to DN_TypeField function - DN_CppLine(cpp, - "DN_TypeInfo const *%.*sType_Info(%.*sType type);", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(emit_prefix)); - - // NOTE: Str8 <-> Enum conversion functions - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - if (table->type != DN_CGenTableType_CodeGenEnum) - continue; - - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_Str8 type_name = it.cgen_table_column[DN_CGenTableHeaderType_Name].string; - DN_CppLine(cpp, - "%.*s%.*sStr8ToEnumResult %.*s%.*s_NameStr8ToEnum(DN_Str8 string);", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(type_name), - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(type_name)); - DN_CppLine(cpp, - "%.*s%.*sStr8ToEnumResult %.*s%.*s_LabelStr8ToEnum(DN_Str8 string);", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(type_name), - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(type_name)); - } - } - - - // NOTE: Operator == and != //////////////////////////////////////////////////////////////// - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - if (table->type != DN_CGenTableType_CodeGenStruct) - continue; - - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_Str8 cpp_op_equals = it.cgen_table_column[DN_CGenTableHeaderType_CppOpEquals].string; - if (!DN_Str8Eq(cpp_op_equals, DN_Str8Lit("true"))) - continue; - - DN_Str8 type_name = it.cgen_table_column[DN_CGenTableHeaderType_Name].string; - DN_CppLine(cpp, - "bool operator==(%.*s%.*s const &lhs, %.*s%.*s const &rhs);", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(type_name), - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(type_name)); - DN_CppLine(cpp, - "bool operator!=(%.*s%.*s const &lhs, %.*s%.*s const &rhs);", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(type_name), - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(type_name)); - } - } - } - - if (emit & DN_CGenEmit_Implementation) { - // NOTE: Generate type info //////////////////////////////////////////////////////////////////// - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - if (table->type == DN_CGenTableType_CodeGenBuiltinTypes) - continue; - - DN_Str8 struct_or_enum_name = it.cgen_table_column[DN_CGenTableHeaderType_Name].string; - DN_CppBlock(cpp, ";\n\n", "DN_TypeField const g_%.*s%.*s_type_fields[] =", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(struct_or_enum_name)) - { - if (table->type == DN_CGenTableType_CodeGenStruct) { - // NOTE: Construct the cpp type string first. We will prepend `emit_prefix` - // for types that are declared in the same mdesk file. We will also - // calculate the longest type name that we will generate for whitespace - // padding purposes. - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - DN_USize longest_cpp_type_name = 0; - auto cpp_type_list = DN_SArray_Init(tmem.arena, it.table->row_count, DN_ZMem_Yes); - - for (DN_USize row_index = 0; row_index < it.table->row_count; row_index++) { - DN_CGenTableRow const *row = it.table->rows + row_index; - DN_CGenLookupColumnAtHeader cpp_type = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppType].string, row); - - // NOTE: CHeck the length of the string after turning it into emittable code - DN_Str8 cpp_type_name = DN_CGen_StripQualifiersOnCppType_(tmem.arena, cpp_type.column.string); - if (DN_CGen_WillCodeGenTypeName(cgen, cpp_type_name)) - cpp_type_name = DN_Str8FromFmtArena(tmem.arena, "%.*s%.*s", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(cpp_type_name)); - - DN_Str8 cpp_type_name_no_templates = DN_CGen_ConvertTemplatesToEmittableLiterals_(tmem.arena, cpp_type_name); - longest_cpp_type_name = DN_Max(longest_cpp_type_name, cpp_type_name_no_templates.size); - - DN_SArray_Add(&cpp_type_list, cpp_type_name); - } - - // NOTE: Iterate each row and emit the C++ declarations //////////////////// - for (DN_USize row_index = 0; row_index < it.table->row_count; row_index++) { - DN_CGenTableRow const *row = it.table->rows + row_index; - DN_CGenLookupColumnAtHeader cpp_name = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppName].string, row); - DN_CGenLookupColumnAtHeader cpp_type = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppType].string, row); - DN_CGenLookupColumnAtHeader cpp_is_ptr = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppIsPtr].string, row); - DN_CGenLookupColumnAtHeader cpp_array_size = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppArraySize].string, row); - DN_CGenLookupColumnAtHeader cpp_array_size_field = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppArraySizeField].string, row); - DN_CGenLookupColumnAtHeader cpp_label = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppLabel].string, row); - - bool cpp_is_ptr_b32 = DN_Str8Eq(cpp_is_ptr.column.string, DN_Str8Lit("true")); - DN_Str8 cpp_array_size_str8 = cpp_array_size.column.string.size ? cpp_array_size.column.string : DN_Str8Lit("0"); - - DN_CGenTableColumn struct_name = it.cgen_table_row->columns[table->column_indexes[DN_CGenTableHeaderType_Name]]; - DN_Str8 cpp_array_size_field_str8 = DN_Str8Lit("NULL"); - if (cpp_array_size_field.column.string.size) { - // TODO(doyle): Check that array_size_field references a valid field in the table - // NOTE: We use a raw index for the reference because the struct might - // not have type info being generated so we can't rely on the enum. - DN_USize index_the_field_references = 0; - for (DN_USize sub_row_index = 0; sub_row_index < it.table->row_count; sub_row_index++) { - DN_CGenTableRow const *sub_row = it.table->rows + sub_row_index; - DN_CGenTableColumn sub_cpp_name = sub_row->columns[cpp_name.index]; - if (DN_Str8Eq(sub_cpp_name.string, cpp_array_size_field.column.string)) - index_the_field_references = sub_row_index; - } - cpp_array_size_field_str8 = - DN_Str8FromFmtArena(tmem.arena, "&g_%.*s%.*s_type_fields[%zu]", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(struct_name.string), - index_the_field_references); - } - - DN_Str8 cpp_type_name = cpp_type_list.data[row_index]; - DN_Str8 orig_cpp_type = DN_CGen_StripQualifiersOnCppType_(tmem.arena, cpp_type.column.string); - DN_Str8 orig_cpp_type_no_templates = DN_CGen_ConvertTemplatesToEmittableLiterals_(tmem.arena, orig_cpp_type); - - DN_USize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size; - DN_USize cpp_type_padding = 1 + longest_cpp_type_name - cpp_type_name.size; - - DN_Str8 cpp_type_enum = DN_Str8FromFmtArena(tmem.arena, "%.*sType_%.*s", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(orig_cpp_type_no_templates)); - DN_USize cpp_type_enum_padding = cpp_type_padding + (orig_cpp_type.size - cpp_type_name.size); - - DN_Str8 cpp_label_str8 = cpp_name.column.string; - DN_USize cpp_label_str8_padding = cpp_name_padding; - if (cpp_label.column.string.size) { - cpp_label_str8 = cpp_label.column.string; - cpp_label_str8_padding = 1 + it.table->headers[cpp_label.index].longest_string - cpp_label.column.string.size; - } - - DN_Str8Builder builder = DN_Str8BuilderFromArena(tmem.arena); - - // NOTE: row - DN_Str8BuilderAppendF(&builder, "{%2d, ", row_index); - - // NOTE: name - DN_Str8BuilderAppendF(&builder, - "DN_Str8Lit(\"%.*s\"),%*s", - DN_Str8PrintFmt(cpp_name.column.string), - cpp_name_padding, - ""); - - // NOTE: label - DN_Str8BuilderAppendF(&builder, - "DN_Str8Lit(\"%.*s\"),%*s", - DN_Str8PrintFmt(cpp_label_str8), - cpp_label_str8_padding, - ""); - - // NOTE: value - DN_Str8BuilderAppendF(&builder, - "/*value*/ 0, "); - - // NOTE: offsetof(a, b) - DN_Str8BuilderAppendF(&builder, - "offsetof(%.*s%.*s, %.*s),%*s", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(struct_or_enum_name), - DN_Str8PrintFmt(cpp_name.column.string), - cpp_name_padding, - ""); - - // NOTE: sizeof(a->b) - DN_Str8BuilderAppendF(&builder, - "sizeof(((%.*s%.*s*)0)->%.*s),%*s", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(struct_or_enum_name), - DN_Str8PrintFmt(cpp_name.column.string), - cpp_name_padding, - ""); - - // NOTE: alignof(a) - if (DN_Str8Eq(cpp_type_name, DN_Str8Lit("void"))) { - DN_Str8 proxy_type = DN_Str8Lit("char"); - DN_USize proxy_type_padding = 1 + longest_cpp_type_name - proxy_type.size; - DN_Str8BuilderAppendF(&builder, "alignof(%.*s),%*s", DN_Str8PrintFmt(proxy_type), proxy_type_padding, ""); - } else { - DN_Str8BuilderAppendF(&builder, - "alignof(%.*s),%*s", - DN_Str8PrintFmt(cpp_type_name), - cpp_type_padding, - ""); - } - - // NOTE: Type string - DN_Str8BuilderAppendF(&builder, - "DN_Str8Lit(\"%.*s\"),%*s", - DN_Str8PrintFmt(cpp_type_name), - cpp_type_padding, - ""); - - // NOTE: Type as enum - DN_Str8BuilderAppendF(&builder, - "%.*s,%*s", - DN_Str8PrintFmt(cpp_type_enum), - cpp_type_enum_padding, - ""); - - DN_Str8BuilderAppendF(&builder, - "/*is_pointer*/ %s,%s /*array_size*/ %.*s, /*array_size_field*/ %.*s},", - cpp_is_ptr_b32 ? "true" : "false", - cpp_is_ptr_b32 ? " " : "", - DN_Str8PrintFmt(cpp_array_size_str8), - DN_Str8PrintFmt(cpp_array_size_field_str8)); - - DN_Str8 line = DN_Str8FromStr8BuilderArena(&builder, tmem.arena); - DN_CppLine(cpp, "%.*s", DN_Str8PrintFmt(line)); - } - } else { - DN_Assert(table->type == DN_CGenTableType_CodeGenEnum); - for (DN_USize row_index = 0; row_index < it.table->row_count; row_index++) { - DN_CGenTableRow const *row = it.table->rows + row_index; - DN_CGenLookupColumnAtHeader cpp_name = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppName].string, row); - DN_CGenLookupColumnAtHeader cpp_value = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppValue].string, row); - DN_CGenLookupColumnAtHeader cpp_label = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppLabel].string, row); - if (cpp_name.column.string.size <= 0) - continue; - - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - DN_USize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size; - DN_Str8 cpp_value_str8 = cpp_value.column.string.size ? cpp_value.column.string : DN_Str8FromFmtArena(tmem.arena, "%zu", row_index); - DN_Str8 cpp_type_enum = DN_Str8FromFmtArena(tmem.arena, "%.*sType_%.*s", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(struct_or_enum_name)); - - DN_Str8 cpp_label_str8 = cpp_name.column.string; - DN_USize cpp_label_str8_padding = cpp_name_padding; - if (cpp_label.column.string.size) { - cpp_label_str8 = cpp_label.column.string; - cpp_label_str8_padding = 1 + it.table->headers[cpp_label.index].longest_string - cpp_label.column.string.size; - } - - DN_Str8Builder builder = {}; - builder.arena = tmem.arena; - // NOTE: row - DN_Str8BuilderAppendF(&builder, "{%2d, ", row_index); - - // NOTE: name - DN_Str8BuilderAppendF(&builder, - "DN_Str8Lit(\"%.*s\"),%*s", - DN_Str8PrintFmt(cpp_name.column.string), - cpp_name_padding, - ""); - - // NOTE: label - DN_Str8BuilderAppendF(&builder, - "DN_Str8Lit(\"%.*s\"),%*s", - DN_Str8PrintFmt(cpp_label_str8), - cpp_label_str8_padding, - ""); - - // NOTE: value - DN_Str8BuilderAppendF(&builder, "/*value*/ %.*s, ", DN_Str8PrintFmt(cpp_value_str8)); - - // NOTE: offsetof(a, b) - DN_Str8BuilderAppendF(&builder, "/*offsetof*/ 0, "); - - // NOTE: sizeof(a->b) - DN_Str8BuilderAppendF(&builder, - "sizeof(%.*s%.*s), ", - DN_Str8PrintFmt(emit_prefix), - DN_Str8PrintFmt(struct_or_enum_name)); - - // NOTE: alignof(a->b) - DN_Str8BuilderAppendF(&builder, "alignof(%.*s%.*s), ", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(struct_or_enum_name)); - - // TODO: Type string - DN_Str8BuilderAppendF(&builder, "DN_Str8Lit(\"\"), "); - - // NOTE: Type as enum - DN_Str8BuilderAppendF(&builder, "%.*s, ", DN_Str8PrintFmt(cpp_type_enum)); - - DN_Str8BuilderAppendF(&builder, "/*is_pointer*/ false, "); - DN_Str8BuilderAppendF(&builder, "/*array_size*/ 0, "); - DN_Str8BuilderAppendF(&builder, "/*array_size_field*/ NULL},"); - - DN_Str8 line = DN_Str8FromStr8BuilderArena(&builder, tmem.arena); - DN_TCScratchEnd(&tmem); - DN_CppLine(cpp, "%.*s", DN_Str8PrintFmt(line)); - } - } - DN_TCScratchEnd(&tmem); - } - } - } - - int longest_name_across_all_tables = 0; - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - DN_Str8 type_name = it.cgen_table_column[DN_CGenTableHeaderType_Name].string; - if (DN_CGen_WillCodeGenTypeName(cgen, type_name)) - type_name = DN_Str8FromFmtArena(tmem.arena, "%.*s%.*s", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)); - - longest_name_across_all_tables = DN_Max(longest_name_across_all_tables, DN_Cast(int) type_name.size); - DN_TCScratchEnd(&tmem); - } - } - - DN_CppBlock(cpp, ";\n\n", "DN_TypeInfo const g_%.*stypes[] =", DN_Str8PrintFmt(emit_prefix)) - { - DN_CppLine(cpp, "{DN_Str8Lit(\"\"),%*sDN_TypeKind_Nil, 0, /*fields*/ NULL, /*count*/ 0},", 1 + longest_name_across_all_tables, ""); - - DN_USize longest_type_name = 0; - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - DN_Str8 type_name = it.cgen_table_column[DN_CGenTableHeaderType_Name].string; - if (DN_CGen_WillCodeGenTypeName(cgen, type_name)) - type_name = DN_Str8FromFmtArena(tmem.arena, "%.*s%.*s", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)); - longest_type_name = DN_Max(longest_type_name, type_name.size); - DN_TCScratchEnd(&tmem); - } - } - - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - DN_Str8 type_name = it.cgen_table_column[DN_CGenTableHeaderType_Name].string; - if (DN_CGen_WillCodeGenTypeName(cgen, type_name)) - type_name = DN_Str8FromFmtArena(tmem.arena, "%.*s%.*s", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)); - - int name_padding = 1 + longest_name_across_all_tables - DN_Cast(int) type_name.size; - DN_Str8 type_info_kind = {}; - char const *type_info_kind_padding = ""; - if (table->type == DN_CGenTableType_CodeGenEnum) { - type_info_kind = DN_Str8Lit("DN_TypeKind_Enum"); - type_info_kind_padding = " "; - } else if (table->type == DN_CGenTableType_CodeGenStruct) { - type_info_kind = DN_Str8Lit("DN_TypeKind_Struct"); - } else { - DN_Assert(table->type == DN_CGenTableType_CodeGenBuiltinTypes); - type_info_kind = DN_Str8Lit("DN_TypeKind_Basic"); - type_info_kind_padding = " "; - } - - DN_Str8 fields = DN_Str8Lit("NULL"); - int fields_padding = 1; - if (table->type != DN_CGenTableType_CodeGenBuiltinTypes) { - fields_padding = name_padding; - fields = DN_Str8FromFmtArena(tmem.arena, "g_%.*s_type_fields", DN_Str8PrintFmt(type_name)); - } - - DN_Str8 fields_count = DN_Str8Lit("0"); - if (!DN_Str8Eq(fields, DN_Str8Lit("NULL"))) - fields_count = DN_Str8FromFmtArena(tmem.arena, "sizeof(%.*s)/sizeof(%.*s[0])", DN_Str8PrintFmt(fields), DN_Str8PrintFmt(fields)); - - DN_Str8Builder builder = {}; - builder.arena = tmem.arena; - // NOTE: name - DN_Str8BuilderAppendF(&builder, "{DN_Str8Lit(\"%.*s\"),%*s", DN_Str8PrintFmt(type_name), name_padding, ""); - - // NOTE: DN_TypeKind_{Nil|Basic|Enum|Struct} - DN_Str8BuilderAppendF(&builder, "%.*s,%s", DN_Str8PrintFmt(type_info_kind), type_info_kind_padding); - - // NOTE: sizeof(T) - if (DN_Str8Eq(type_name, DN_Str8Lit("void"))) - DN_Str8BuilderAppendF(&builder, "0,%*s", name_padding, ""); - else - DN_Str8BuilderAppendF(&builder, "sizeof(%.*s),%*s", DN_Str8PrintFmt(type_name), name_padding, ""); - - // NOTE: Pointer to DN_TypeField[] - DN_Str8BuilderAppendF(&builder, "/*fields*/ %.*s,%*s", DN_Str8PrintFmt(fields), fields_padding, ""); - - // NOTE: DN_TypeField length - DN_Str8BuilderAppendF(&builder, "/*count*/ %.*s},", DN_Str8PrintFmt(fields_count)); - - DN_Str8 line = DN_Str8FromStr8BuilderArena(&builder, tmem.arena); - DN_TCScratchEnd(&tmem); - DN_CppLine(cpp, "%.*s", DN_Str8PrintFmt(line)); - } - } - } - - // NOTE: Type to DN_TypeField function - DN_CppFuncBlock(cpp, "DN_TypeInfo const *%.*sType_Info(%.*sType type)", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(emit_prefix)) - { - DN_CppLine(cpp, "DN_TypeInfo const *result = g_%.*stypes + 0;", DN_Str8PrintFmt(emit_prefix)); - DN_CppSwitchBlock(cpp, "type") - { - DN_CppLine(cpp, "case %.*sType_Nil: break;", DN_Str8PrintFmt(emit_prefix)); - DN_CppLine(cpp, "case %.*sType_Count: break;", DN_Str8PrintFmt(emit_prefix)); - DN_TCScratch tmem = DN_TCScratchBegin(nullptr, 0); - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_Str8 enum_name = DN_CGen_ConvertTemplatesToEmittableLiterals_(tmem.arena, it.cgen_table_column[DN_CGenTableHeaderType_Name].string); - DN_Str8 full_enum_name = DN_Str8FromFmtArena(tmem.arena, "%.*sType_%.*s", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(enum_name)); - - // NOTE: Builtin types don't have type info - DN_Str8 full_variable_name = DN_Str8FromFmtArena(tmem.arena, "g_%.*s%.*s_type_fields", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(enum_name)); - if (it.cgen_table->type == DN_CGenTableType_CodeGenBuiltinTypes) - full_variable_name = DN_Str8Lit("nullptr"); - - DN_CppLine(cpp, "case %.*s: result = g_%.*stypes + %.*s; break;", DN_Str8PrintFmt(full_enum_name), DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(full_enum_name)); - } - } - DN_TCScratchEnd(&tmem); - } - DN_CppLine(cpp, "return result;"); - } - - // NOTE: Str8 to enum conversion //////////////////////////////////////////////////////////// - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - if (table->type != DN_CGenTableType_CodeGenEnum) - continue; - - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_Str8 type_name = it.cgen_table_column[DN_CGenTableHeaderType_Name].string; - - DN_CppFuncBlock(cpp, "%.*s%.*sStr8ToEnumResult %.*s%.*s_NameStr8ToEnum(DN_Str8 string)", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name), DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)) - { - DN_CppLine(cpp, "%.*s%.*sStr8ToEnumResult result = {};", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)); - DN_CppForBlock(cpp, "DN_USize index = 0; !result.success && index < DN_ArrayCountU(g_%.*s%.*s_type_fields); index++", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)) - { - DN_CppIfChain(cpp) - { - DN_CppLine(cpp, "DN_TypeField field = g_%.*s%.*s_type_fields[index];", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)); - DN_CppIfOrElseIfBlock(cpp, "DN_Str8EqInsensitive(string, field.name)") - { - DN_CppLine(cpp, "result.success = true;"); - DN_CppLine(cpp, "result.value = (%.*s%.*s)index;", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)); - } - } - } - DN_CppLine(cpp, "return result;"); - } - DN_CppNewLine(cpp); - - DN_CppFuncBlock(cpp, "%.*s%.*sStr8ToEnumResult %.*s%.*s_LabelStr8ToEnum(DN_Str8 string)", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name), DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)) - { - DN_CppLine(cpp, "%.*s%.*sStr8ToEnumResult result = {};", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)); - DN_CppForBlock(cpp, "DN_USize index = 0; !result.success && index < DN_ArrayCountU(g_%.*s%.*s_type_fields); index++", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)) - { - DN_CppIfChain(cpp) - { - DN_CppLine(cpp, "DN_TypeField field = g_%.*s%.*s_type_fields[index];", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)); - DN_CppIfOrElseIfBlock(cpp, "DN_Str8EqInsensitive(string, field.label)") - { - DN_CppLine(cpp, "result.success = true;"); - DN_CppLine(cpp, "result.value = (%.*s%.*s)index;", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)); - } - } - } - DN_CppLine(cpp, "return result;"); - } - DN_CppNewLine(cpp); - } - } - - // NOTE: Operator == and != //////////////////////////////////////////////////////////////// - for (DN_CGenTable *table = cgen->first_table; table != 0; table = table->next) { - if (table->type != DN_CGenTableType_CodeGenStruct) - continue; - - for (DN_CGenLookupTableIterator it = {}; DN_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { - DN_Str8 cpp_op_equals = it.cgen_table_column[DN_CGenTableHeaderType_CppOpEquals].string; - if (!DN_Str8Eq(cpp_op_equals, DN_Str8Lit("true"))) - continue; - - DN_Str8 type_name = it.cgen_table_column[DN_CGenTableHeaderType_Name].string; - DN_CppFuncBlock(cpp, "bool operator==(%.*s%.*s const &lhs, %.*s%.*s const &rhs)", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name), DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)) - { - for (DN_USize row_index = 0; row_index < it.table->row_count; row_index++) { - DN_CGenTableRow const *row = it.table->rows + row_index; - DN_CGenLookupColumnAtHeader cpp_name = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppName].string, row); - DN_CGenLookupColumnAtHeader cpp_is_ptr = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppIsPtr].string, row); - DN_CGenLookupColumnAtHeader cpp_array_size = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppArraySize].string, row); - DN_CGenLookupColumnAtHeader cpp_array_size_field = DN_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[DN_CGenTableHeaderType_CppArraySizeField].string, row); - - // TODO(doyle): Check if we're an integral type or not to double check if we - // can use memcmp or operator== - if (cpp_array_size_field.column.string.size) { - DN_CppIfChain(cpp) - { - DN_CppIfOrElseIfBlock(cpp, - "lhs.%.*s != rhs.%.*s", - DN_Str8PrintFmt(cpp_array_size_field.column.string), - DN_Str8PrintFmt(cpp_array_size_field.column.string)) - { - DN_CppLine(cpp, "return false;"); - } - } - DN_CppIfChain(cpp) - { - DN_CppIfOrElseIfBlock(cpp, - "DN_Memcmp(lhs.%.*s, rhs.%.*s, lhs.%.*s) != 0", - DN_Str8PrintFmt(cpp_name.column.string), - DN_Str8PrintFmt(cpp_name.column.string), - DN_Str8PrintFmt(cpp_array_size_field.column.string)) - { - DN_CppLine(cpp, "return false;"); - } - } - } else if (cpp_array_size.column.string.size) { - DN_CppIfChain(cpp) - { - DN_CppIfOrElseIfBlock(cpp, - "DN_Memcmp(lhs.%.*s, rhs.%.*s, %.*s) != 0", - DN_Str8PrintFmt(cpp_name.column.string), - DN_Str8PrintFmt(cpp_name.column.string), - DN_Str8PrintFmt(cpp_array_size.column.string)) - { - DN_CppLine(cpp, "return false;"); - } - } - } else if (DN_Str8Eq(cpp_is_ptr.column.string, DN_Str8Lit("true"))) { - DN_CppIfChain(cpp) - { - DN_CppIfOrElseIfBlock(cpp, - "*lhs.%.*s != *rhs.%.*s", - DN_Str8PrintFmt(cpp_name.column.string), - DN_Str8PrintFmt(cpp_name.column.string)) - { - DN_CppLine(cpp, "return false;"); - } - } - } else { - DN_CppIfChain(cpp) - { - DN_CppIfOrElseIfBlock(cpp, - "lhs.%.*s != rhs.%.*s", - DN_Str8PrintFmt(cpp_name.column.string), - DN_Str8PrintFmt(cpp_name.column.string)) - { - DN_CppLine(cpp, "return false;"); - } - } - } - } - DN_CppLine(cpp, "return true;"); - } - DN_CppNewLine(cpp); - - DN_CppFuncBlock(cpp, "bool operator!=(%.*s%.*s const &lhs, %.*s%.*s const &rhs)", DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name), DN_Str8PrintFmt(emit_prefix), DN_Str8PrintFmt(type_name)) - { - DN_CppLine(cpp, "bool result = !(lhs == rhs);"); - DN_CppLine(cpp, "return result;"); - } - DN_CppNewLine(cpp); - } - } - } -} diff --git a/Source/Extra/dn_cgen.h b/Source/Extra/dn_cgen.h deleted file mode 100644 index bfcb6e4..0000000 --- a/Source/Extra/dn_cgen.h +++ /dev/null @@ -1,185 +0,0 @@ -#if !defined(DN_CGEN_H) -#define DN_CGEN_H - -#if defined(_CLANGD) - #define DN_H_WITH_OS 1 - #include "../dn.h" - #include "../Standalone/dn_cpp_file.h" -#endif - -#if !defined(DN_NO_METADESK) - #if !defined(_CRT_SECURE_NO_WARNINGS) - #define _CRT_SECURE_NO_WARNINGS - #define DN_UNDO_CRT_SECURE_NO_WARNINGS - #endif - - // NOTE: Metadesk does not have the header for 'size_t' - #if defined(DN_COMPILER_GCC) - #include - #endif - - #define MD_DEFAULT_SPRINTF 0 - #define MD_IMPL_Vsnprintf DN_VSNPrintF - #include "../External/metadesk/md.h" - #if defined(DN_UNDO_CRT_SECURE_NO_WARNINGS) - #undef _CRT_SECURE_NO_WARNINGS - #endif -#endif - -#if !defined(DN_CPP_FILE_H) - #error dn_cpp_file.h must be included before this -#endif - -#if defined(DN_PLATFORM_WINDOWS) && !defined(DN_NO_WINDOWS_H_REPLACEMENT_HEADER) - #error DN replacement header must be disabled with DN_NO_WINDOWS_H_REPLACEMENT_HEADER since Metadesk includes -#endif - -#if !defined(MD_H) -#error Metadesk 'md.h' must be included before 'dn_cgen.h' -#endif - -enum DN_CGenTableKeyType -{ - DN_CGenTableKeyType_Nil, - DN_CGenTableKeyType_Name, - DN_CGenTableKeyType_Type, -}; - -enum DN_CGenTableType -{ - DN_CGenTableType_Nil, - DN_CGenTableType_Data, - DN_CGenTableType_CodeGenBuiltinTypes, - DN_CGenTableType_CodeGenStruct, - DN_CGenTableType_CodeGenEnum, - DN_CGenTableType_Count, -}; - -enum DN_CGenTableRowTagType -{ - DN_CGenTableRowTagType_Nil, - DN_CGenTableRowTagType_CommentDivider, - DN_CGenTableRowTagType_EmptyLine, -}; - -enum DN_CGenTableRowTagCommentDivider -{ - DN_CGenTableRowTagCommentDivider_Nil, - DN_CGenTableRowTagCommentDivider_Label, -}; - -enum DN_CGenTableHeaderType -{ - DN_CGenTableHeaderType_Name, - DN_CGenTableHeaderType_Table, - DN_CGenTableHeaderType_CppType, - DN_CGenTableHeaderType_CppName, - DN_CGenTableHeaderType_CppValue, - DN_CGenTableHeaderType_CppIsPtr, - DN_CGenTableHeaderType_CppOpEquals, - DN_CGenTableHeaderType_CppArraySize, - DN_CGenTableHeaderType_CppArraySizeField, - DN_CGenTableHeaderType_CppLabel, - DN_CGenTableHeaderType_GenTypeInfo, - DN_CGenTableHeaderType_GenEnumCount, - DN_CGenTableHeaderType_Count, -}; - -struct DN_CGenTableHeader -{ - MD_String8 name; - int longest_string; -}; - -struct DN_CGenTableRowTag -{ - DN_CGenTableRowTagType type; - MD_String8 comment; - DN_CGenTableRowTag *next; -}; - -struct DN_CGenTableColumn -{ - MD_Node *node; - DN_Str8 string; -}; - -struct DN_CGenTableRow -{ - DN_CGenTableRowTag *first_tag; - DN_CGenTableRowTag *last_tag; - DN_CGenTableColumn *columns; -}; - -struct DN_CGenTable -{ - DN_CGenTableType type; - DN_Str8 name; - MD_Map headers_map; - DN_CGenTableHeader *headers; - DN_CGenTableRow *rows; - size_t column_count; - size_t row_count; - - MD_Node *node; - MD_Node *headers_node; - DN_USize column_indexes[DN_CGenTableHeaderType_Count]; - DN_CGenTable *next; -}; - -struct DN_CGen -{ - MD_Arena *arena; - MD_Node *file_list; - MD_Map table_map; - DN_CGenTable *first_table; - DN_CGenTable *last_table; - DN_USize table_counts[DN_CGenTableType_Count]; -}; - -struct DN_CGenMapNodeToEnum -{ - uint32_t enum_val; - DN_Str8 node_string; -}; - -struct DN_CGenLookupTableIterator -{ - DN_CGenTable *cgen_table; - DN_CGenTableRow *cgen_table_row; - DN_CGenTableColumn cgen_table_column[DN_CGenTableHeaderType_Count]; - DN_CGenTable *table; - DN_USize row_index; -}; - -struct DN_CGenLookupColumnAtHeader -{ - DN_USize index; - DN_CGenTableHeader header; - DN_CGenTableColumn column; -}; - -enum DN_CGenEmit -{ - DN_CGenEmit_Prototypes = 1 << 0, - DN_CGenEmit_Implementation = 1 << 1, -}; - -#define DN_CGen_MDToDNStr8(str8) DN_Str8FromPtr((str8).str, (str8).size) -#define DN_CGen_DNToMDStr8(str8) \ - { \ - DN_Cast(MD_u8 *) \ - (str8).data, \ - (str8).size \ - } - -DN_API DN_CGen DN_CGen_InitFilesArgV(int argc, char const **argv, DN_ErrSink *err); -DN_API DN_Str8 DN_CGen_TableHeaderTypeToDeclStr8(DN_CGenTableHeaderType type); -DN_API DN_CGenMapNodeToEnum DN_CGen_MapNodeToEnumOrExit(MD_Node const *node, DN_CGenMapNodeToEnum const *valid_keys, DN_USize valid_keys_size, char const *fmt, ...); -DN_API DN_USize DN_CGen_NodeChildrenCount(MD_Node const *node); -DN_API void DN_CGen_LogF(MD_MessageKind kind, MD_Node *node, DN_ErrSink *err, char const *fmt, ...); -DN_API bool DN_CGen_TableHasHeaders(DN_CGenTable const *table, DN_Str8 const *headers, DN_USize header_count, DN_ErrSink *err); -DN_API DN_CGenLookupColumnAtHeader DN_CGen_LookupColumnAtHeader(DN_CGenTable *table, DN_Str8 header, DN_CGenTableRow const *row); -DN_API bool DN_CGen_LookupNextTableInCodeGenTable(DN_CGen *cgen, DN_CGenTable *cgen_table, DN_CGenLookupTableIterator *it); -DN_API void DN_CGen_EmitCodeForTables(DN_CGen *cgen, DN_CGenEmit emit, DN_CppFile *cpp, DN_Str8 emit_prefix); -#endif // DN_CGEN_H diff --git a/Source/Extra/dn_csv.cpp b/Source/Extra/dn_csv.cpp deleted file mode 100644 index 8d1158f..0000000 --- a/Source/Extra/dn_csv.cpp +++ /dev/null @@ -1,290 +0,0 @@ -#define DN_CSV_CPP - -#include "dn_csv.h" - -DN_CSVTokeniser DN_CSV_TokeniserInit(DN_Str8 string, char delimiter) -{ - DN_CSVTokeniser result = {}; - result.string = string; - result.delimiter = delimiter; - return result; -} - -bool DN_CSV_TokeniserValid(DN_CSVTokeniser *tokeniser) -{ - bool result = tokeniser && !tokeniser->bad; - return result; -} - -static void DN_CSV_TokeniserEatNewLines_(DN_CSVTokeniser *tokeniser) -{ - char const *end = tokeniser->string.data + tokeniser->string.size; - while (tokeniser->it[0] == '\n' || tokeniser->it[0] == '\r') - if (++tokeniser->it == end) - break; -} - -bool DN_CSV_TokeniserNextRow(DN_CSVTokeniser *tokeniser) -{ - bool result = false; - if (DN_CSV_TokeniserValid(tokeniser) && tokeniser->string.size) { - // NOTE: First time querying row iterator is nil, let tokeniser advance - if (tokeniser->it) { - // NOTE: Only advance the tokeniser if we're at the end of the line and - // there's more to tokenise. - char const *end = tokeniser->string.data + tokeniser->string.size; - if (tokeniser->it != end && tokeniser->end_of_line) { - tokeniser->end_of_line = false; - result = true; - } - } - } - - return result; -} - -DN_Str8 DN_CSV_TokeniserNextField(DN_CSVTokeniser *tokeniser) -{ - DN_Str8 result = {}; - if (!DN_CSV_TokeniserValid(tokeniser)) - return result; - - if (tokeniser->string.size == 0) { - tokeniser->bad = true; - return result; - } - - // NOTE: First time tokeniser is invoked with a string, set up initial state. - char const *string_end = tokeniser->string.data + tokeniser->string.size; - if (!tokeniser->it) { - tokeniser->it = tokeniser->string.data; - DN_CSV_TokeniserEatNewLines_(tokeniser); // NOTE: Skip any leading new lines - } - - // NOTE: Tokeniser pointing at end, no more valid data to parse. - if (tokeniser->it == string_end) - return result; - - // NOTE: Scan forward until the next control character. - // 1. '"' Double quoted field, extract everything between the quotes. - // 2. tokeniser->delimiter End of the field, extract everything leading up to the delimiter. - // 3. '\n' Last field in record, extract everything leading up the the new line. - char const *begin = tokeniser->it; - while (tokeniser->it != string_end && (tokeniser->it[0] != '"' && - tokeniser->it[0] != tokeniser->delimiter && - tokeniser->it[0] != '\n')) - tokeniser->it++; - - bool quoted_field = (tokeniser->it != string_end) && tokeniser->it[0] == '"'; - if (quoted_field) { - begin = ++tokeniser->it; // Begin after the quote - - // NOTE: Scan forward until the next '"' which marks the end - // of the field unless it is escaped by another '"'. - find_next_quote: - while (tokeniser->it != string_end && tokeniser->it[0] != '"') - tokeniser->it++; - - // NOTE: If we encounter a '"' right after, the quotes were escaped - // and we need to skip to the next instance of a '"'. - if (tokeniser->it != string_end && tokeniser->it + 1 != string_end && tokeniser->it[1] == '"') { - tokeniser->it += 2; - goto find_next_quote; - } - } - - // NOTE: Mark the end of the field - char const *end = tokeniser->it; - tokeniser->end_of_line = tokeniser->it == string_end || end[0] == '\n'; - - // NOTE: In files with \r\n style new lines ensure that we don't include - // the \r byte in the CSV field we produce. - if (end != string_end && end[0] == '\n') { - DN_Assert((uintptr_t)(end - 1) > (uintptr_t)tokeniser->string.data && - "Internal error: The string iterator is pointing behind the start of the string we're reading"); - if (end[-1] == '\r') - end = end - 1; - } - - // NOTE: Quoted fields may have whitespace after the closing quote, we skip - // until we reach the field terminator. - if (quoted_field) - while (tokeniser->it != string_end && (tokeniser->it[0] != tokeniser->delimiter && tokeniser->it[0] != '\n')) - tokeniser->it++; - - // NOTE: Advance the tokeniser past the field terminator. - if (tokeniser->it != string_end) - tokeniser->it++; - - // NOTE: Generate the record - result.data = DN_Cast(char *) begin; - result.size = DN_Cast(int)(end - begin); - return result; -} - -DN_Str8 DN_CSV_TokeniserNextColumn(DN_CSVTokeniser *tokeniser) -{ - DN_Str8 result = {}; - if (!DN_CSV_TokeniserValid(tokeniser)) - return result; - - // NOTE: End of line, the user must explicitly advance to the next row - if (tokeniser->end_of_line) - return result; - - // NOTE: Advance tokeniser to the next field in the row - result = DN_CSV_TokeniserNextField(tokeniser); - return result; -} - -void DN_CSV_TokeniserSkipLine(DN_CSVTokeniser *tokeniser) -{ - while (DN_CSV_TokeniserValid(tokeniser) && !tokeniser->end_of_line) - DN_CSV_TokeniserNextColumn(tokeniser); - DN_CSV_TokeniserNextRow(tokeniser); -} - -int DN_CSV_TokeniserNextN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator) -{ - if (!DN_CSV_TokeniserValid(tokeniser) || !fields || fields_size <= 0) - return 0; - - int result = 0; - for (; result < fields_size; result++) { - fields[result] = column_iterator ? DN_CSV_TokeniserNextColumn(tokeniser) : DN_CSV_TokeniserNextField(tokeniser); - if (!DN_CSV_TokeniserValid(tokeniser) || !fields[result].data) - break; - } - - return result; -} - -int DN_CSV_TokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size) -{ - int result = DN_CSV_TokeniserNextN(tokeniser, fields, fields_size, true /*column_iterator*/); - return result; -} - -int DN_CSV_TokeniserNextFieldN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size) -{ - int result = DN_CSV_TokeniserNextN(tokeniser, fields, fields_size, false /*column_iterator*/); - return result; -} - -void DN_CSV_TokeniserSkipLineN(DN_CSVTokeniser *tokeniser, int count) -{ - for (int i = 0; i < count && DN_CSV_TokeniserValid(tokeniser); i++) - DN_CSV_TokeniserSkipLine(tokeniser); -} - -void DN_CSV_PackU64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value) -{ - if (serialise == DN_CSVSerialise_Read) { - DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser); - DN_U64FromResult to_u64 = DN_U64FromStr8(csv_value, 0); - DN_Assert(to_u64.success); - *value = to_u64.value; - } else { - DN_Str8BuilderAppendF(&pack->write_builder, "%s%" PRIu64, pack->write_column++ ? "," : "", *value); - } -} - -void DN_CSV_PackI64(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value) -{ - if (serialise == DN_CSVSerialise_Read) { - DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser); - DN_I64FromResult to_i64 = DN_I64FromStr8(csv_value, 0); - DN_Assert(to_i64.success); - *value = to_i64.value; - } else { - DN_Str8BuilderAppendF(&pack->write_builder, "%s%" PRIu64, pack->write_column++ ? "," : "", *value); - } -} - -void DN_CSV_PackI32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value) -{ - DN_I64 u64 = *value; - DN_CSV_PackI64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = DN_SaturateCastI64ToI32(u64); -} - -void DN_CSV_PackI16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value) -{ - DN_I64 u64 = *value; - DN_CSV_PackI64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = DN_SaturateCastI64ToI16(u64); -} - -void DN_CSV_PackI8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value) -{ - DN_I64 u64 = *value; - DN_CSV_PackI64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = DN_SaturateCastI64ToI8(u64); -} - -void DN_CSV_PackU32(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value) -{ - DN_U64 u64 = *value; - DN_CSV_PackU64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = DN_SaturateCastU64ToU32(u64); -} - -void DN_CSV_PackU16(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value) -{ - DN_U64 u64 = *value; - DN_CSV_PackU64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = DN_SaturateCastU64ToU16(u64); -} - -void DN_CSV_PackBoolAsU64(DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value) -{ - DN_U64 u64 = *value; - DN_CSV_PackU64(pack, serialise, &u64); - if (serialise == DN_CSVSerialise_Read) - *value = u64 ? 1 : 0; -} - -void DN_CSV_PackStr8(DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena) -{ - if (serialise == DN_CSVSerialise_Read) { - DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser); - *str8 = DN_Str8FromStr8Arena(csv_value, arena); - } else { - DN_Str8BuilderAppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_Str8PrintFmt(*str8)); - } -} - -void DN_CSV_PackBuffer(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size) -{ - if (serialise == DN_CSVSerialise_Read) { - DN_Str8 csv_value = DN_CSV_TokeniserNextColumn(&pack->read_tokeniser); - *size = DN_Min(*size, csv_value.size); - DN_Memcpy(dest, csv_value.data, *size); - } else { - DN_Str8BuilderAppendF(&pack->write_builder, "%s%.*s", pack->write_column++ ? "," : "", DN_Cast(int)(*size), dest); - } -} - -void DN_CSV_PackBufferWithMax(DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max) -{ - if (serialise == DN_CSVSerialise_Read) - *size = max; - DN_CSV_PackBuffer(pack, serialise, dest, size); -} - -bool DN_CSV_PackNewLine(DN_CSVPack *pack, DN_CSVSerialise serialise) -{ - bool result = true; - if (serialise == DN_CSVSerialise_Read) { - result = DN_CSV_TokeniserNextRow(&pack->read_tokeniser); - } else { - pack->write_column = 0; - result = DN_Str8BuilderAppendRef(&pack->write_builder, DN_Str8Lit("\n")); - } - return result; -} diff --git a/Source/Extra/dn_csv.h b/Source/Extra/dn_csv.h deleted file mode 100644 index 1fbd6d9..0000000 --- a/Source/Extra/dn_csv.h +++ /dev/null @@ -1,62 +0,0 @@ -#if !defined(DN_CSV_H) -#define DN_CSV_H - -// NOTE: Data structures to create and parse CSV files, supports Python style escaped quotes (e.g. -// Using "" to escape quotes inside a quoted string). -// -// API -// DN_CSV_TokeniserNextN: Reads the next N consecutive fields from the parser. If `column_iterator` -// is `false` then the read of the N consecutive fields does not proceed past the end of the -// current CSV row. If `true` then it reads the next N fields even if reading would progress onto -// the next row. - - #if defined(_CLANGD) - #include "../dn.h" - #endif - -enum DN_CSVSerialise -{ - DN_CSVSerialise_Read, - DN_CSVSerialise_Write, -}; - -struct DN_CSVTokeniser -{ - bool bad; - DN_Str8 string; - char delimiter; - char const *it; - bool end_of_line; -}; - -struct DN_CSVPack -{ - DN_Str8Builder write_builder; - DN_USize write_column; - DN_CSVTokeniser read_tokeniser; -}; - -DN_CSVTokeniser DN_CSV_TokeniserInit (DN_Str8 string, char delimiter); -bool DN_CSV_TokeniserValid (DN_CSVTokeniser *tokeniser); -bool DN_CSV_TokeniserNextRow (DN_CSVTokeniser *tokeniser); -DN_Str8 DN_CSV_TokeniserNextField (DN_CSVTokeniser *tokeniser); -DN_Str8 DN_CSV_TokeniserNextColumn (DN_CSVTokeniser *tokeniser); -void DN_CSV_TokeniserSkipLine (DN_CSVTokeniser *tokeniser); -int DN_CSV_TokeniserNextN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator); -int DN_CSV_TokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size); -int DN_CSV_TokeniserNextFieldN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size); -void DN_CSV_TokeniserSkipLineN (DN_CSVTokeniser *tokeniser, int count); -void DN_CSV_PackU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value); -void DN_CSV_PackI64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value); -void DN_CSV_PackI32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value); -void DN_CSV_PackI16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value); -void DN_CSV_PackI8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value); -void DN_CSV_PackU32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value); -void DN_CSV_PackU16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value); -void DN_CSV_PackBoolAsU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value); -void DN_CSV_PackStr8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena); -void DN_CSV_PackBuffer (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size); -void DN_CSV_PackBufferWithMax (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max); -bool DN_CSV_PackNewLine (DN_CSVPack *pack, DN_CSVSerialise serialise); - -#endif // !defined(DN_CSV_H) diff --git a/Source/Extra/dn_demo.cpp b/Source/Extra/dn_demo.cpp deleted file mode 100644 index 37f78da..0000000 --- a/Source/Extra/dn_demo.cpp +++ /dev/null @@ -1,1148 +0,0 @@ -#if defined(_CLANGD) - #include "../dn.h" -#endif - -DN_MSVC_WARNING_PUSH -DN_MSVC_WARNING_DISABLE(4702) // unreachable code -void DN_Demo() -{ -// NOTE: Before using anything in the library, DN_Core_Init() must be -// called, for example: -#if 0 - DN_Core core = {}; - DN_Core_Init(&core, DN_CoreOnInit_Nil); -#endif - - // NOTE: DN_AtomicSetValue64 - // NOTE: DN_AtomicSetValue32 - // Atomically set the value into the target using an atomic compare and swap - // idiom. The return value of the function is the value that was last stored - // in the target. - { - uint64_t target = 8; - uint64_t value_to_set = 0xCAFE; - if (DN_AtomicSetValue64(&target, value_to_set) == 8) { - // Atomic swap was successful, e.g. the last value that this thread - // observed was '8' which is the value we initialised with e.g. no - // other thread has modified the value. - } - } - - // NOTE: DN_HexFromBytes - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - unsigned char bytes[2] = {0xFA, 0xCE}; - DN_Str8 hex = DN_HexFromPtrBytesArena(bytes, sizeof(bytes), &scratch.arena, DN_TrimLeadingZero_No); - DN_Assert(DN_Str8Eq(hex, DN_Str8Lit("face"))); // NOTE: Guaranteed to be null-terminated - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_BytesFromHex - { - unsigned char bytes[2]; - DN_USize bytes_written = DN_BytesFromHex(DN_Str8Lit("0xFACE"), bytes, sizeof(bytes)); - DN_Assert(bytes_written == 2); - DN_Assert(bytes[0] == 0xFA); - DN_Assert(bytes[1] == 0xCE); - } - - // NOTE: DN_Check - // - // Check the expression trapping in debug, whilst in release- trapping is - // removed and the expression is evaluated as if it were a normal 'if' branch. - // - // This allows handling of the condition gracefully when compiled out but - // traps to notify the developer in builds when it's compiled in. - { - bool flag = true; - if (DN_CheckF(flag, "Flag was false!")) { - /// This branch will execute! - } - } - - // NOTE: DN_CPUID - // Execute the 'CPUID' instruction which lets you query the capabilities of - // the current CPU. - - // NOTE: DN_DEFER - // - // A macro that expands to a C++ lambda that executes arbitrary code on - // scope exit. - { - int x = 0; - DN_DEFER - { - x = 3; - }; - x = 1; - // On scope exit, DN_DEFER object executes and assigns x = 3 - } - - // NOTE: DN_DSMap - // - // A hash table configured using the presets recommended by Demitri Spanos - // from the Handmade Network (HMN), - // - // - power of two capacity - // - grow by 2x on load >= 75% - // - open-addressing with linear probing - // - separate large values (esp. variable length values) into a separate table - // - use a well-known hash function: MurmurHash3 (or xxhash, city, spooky ...) - // - chain-repair on delete (rehash items in the probe chain after delete) - // - shrink by 1/2 on load < 25% (suggested by Martins Mmozeiko of HMN) - // - // Source: discord.com/channels/239737791225790464/600063880533770251/941835678424129597 - // - // This hash-table stores slots (values) separate from the hash mapping. - // Hashes are mapped to slots using the hash-to-slot array which is an array - // of slot indexes. This array intentionally only stores indexes to maximise - // usage of the cache line. Linear probing on collision will only cost a - // couple of cycles to fetch from L1 cache the next slot index to attempt. - // - // The slots array stores values contiguously, non-sorted allowing iteration - // of the map. On element erase, the last element is swapped into the - // deleted element causing the non-sorted property of this table. - // - // The 0th slot (DN_DS_MAP_SENTINEL_SLOT) in the slots array is reserved - // for a sentinel value, e.g. all zeros value. After map initialisation the - // 'occupied' value of the array will be set to 1 to exclude the sentinel - // from the capacity of the table. Skip the first value if you are iterating - // the hash table! - // - // This hash-table accept either a U64 or a buffer (ptr + len) as the key. - // In practice this covers a majority of use cases (with string, buffer and - // number keys). It also allows us to minimise our C++ templates to only - // require 1 variable which is the Value part of the hash-table simplifying - // interface complexity and cruft brought by C++. - // - // Keys are value-copied into the hash-table. If the key uses a pointer to a - // buffer, this buffer must be valid throughout the lifetime of the hash - // table! - { - // NOTE: DN_DSMapInit - // NOTE: DN_DSMapDeinit - // - // Initialise a hash table where the table size *must* be a - // power-of-two, otherwise an assert will be triggered. If - // initialisation fails (e.g. memory allocation failure) the table is - // returned zero-initialised where a call to 'IsValid' will return - // false. - // - // The map takes ownership of the arena. This means in practice that if the - // map needs to resize (e.g. because the load threshold of the table is - // exceeded), the arena associated with it will be released and the memory - // will be reallocated with the larger capacity and reassigned to the arena. - // - // In simple terms, when the map resizes it invalidates all memory that was - // previously allocated with the given arena! - // - // A 'Deinit' of the map will similarly deallocate the passed in arena (as - // the map takes ownership of the arena). - DN_Arena arena = DN_ArenaFromVMem(0, 0, DN_ArenaFlags_Nil); - DN_DSMap map = DN_DSMapInit(&arena, /*size*/ 1024, DN_DSMapFlags_Nil); // Size must be PoT! - DN_Assert(DN_DSMapIsValid(&map)); // Valid if no initialisation failure (e.g. mem alloc failure) - - // NOTE: DN_DSMapKeyCStringLit - // NOTE: DN_DSMapKeyU64 - // NOTE: DN_DSMapKeyU64NoHash - // NOTE: DN_DSMapKeyBuffer - // NOTE: DN_DSMapKeyStr8 - // NOTE: DN_DSMapKeyStr8Copy - // Create a hash-table key where: - // - // KeyCStringLit: Uses a Hash(cstring literal) - // KeyU64: Uses a Hash(U64) - // KeyU64NoHash: Uses a U64 (where it's truncated to 4 bytes) - // KeyBuffer: Uses a Hash(ptr+len) slice of bytes - // KeyStr8: Uses a Hash(string) - // KeyStr8Copy: Uses a Hash(string) that is copied first using the arena - // - // Buffer-based keys memory must persist throughout lifetime of the map. - // Keys are valued copied into the map, alternatively, copy the - // key/buffer before constructing the key. - // - // You *can't* use the map's arena to allocate keys because on resize it - // will deallocate then reallocate the entire arena. - // - // KeyU64NoHash may be useful if you have a source of data that is - // already sufficiently uniformly distributed already (e.g. using 8 - // bytes taken from a SHA256 hash as the key) and the first 4 bytes - // will be used verbatim. - DN_DSMapKey key = DN_DSMapKeyStr8(&map, DN_Str8Lit("Sample Key")); - - // NOTE: DN_DSMapFind - // NOTE: DN_DSMapMake - // NOTE: DN_DSMapSet - // - // Query or commit key-value pair to the table, where: - // - // Find: does a key-lookup on the table and returns the hash table slot's value - // Make: assigns the key to the table and returns the hash table slot's value - // Set: assigns the key-value to the table and returns the hash table slot's value - // - // A find query will set 'found' to false if it does not exist. - // - // For 'Make' and 'Set', 'found' can be set to 'true' if the item already - // existed in the map prior to the call. If it's the first time the - // key-value pair is being inserted 'found' will be set to 'false'. - // - // If by adding the key-value pair to the table puts the table over 75% load, - // the table will be grown to 2x the current the size before insertion - // completes. - { - DN_DSMapResult set_result = DN_DSMapSet(&map, key, 0xCAFE); - DN_Assert(!set_result.found); // First time we are setting the key-value pair, it wasn't previously in the table - DN_Assert(map.occupied == 2); // Sentinel + new element == 2 - } - - // Iterating elements in the array, note that index '0' is the sentinel - // slot! You typically don't care about it! - for (DN_USize index = 1; index < map.occupied; index++) { - DN_DSMapSlot *it = map.slots + index; - DN_DSMapKey it_key = it->key; - int *it_value = &it->value; - DN_Assert(*it_value == 0xCAFE); - - DN_Assert(DN_Str8Eq(DN_Str8FromPtr(it_key.buffer_data, it_key.buffer_size), DN_Str8Lit("Sample Key"))); - } - - // NOTE: DN_DSMapErase - // - // Remove the key-value pair from the table. If by erasing the key-value - // pair from the table puts the table under 25% load, the table will be - // shrunk by 1/2 the current size after erasing. The table will not shrink - // below the initial size that the table was initialised as. - { - bool erased = DN_DSMapErase(&map, key); - DN_Assert(erased); - DN_Assert(map.occupied == 1); // Sentinel element - } - - DN_DSMapDeinit(&map, DN_ZMem_Yes); // Deallocates the 'arena' for us! - } - -// NOTE: DN_DSMapHash -// -// Hash the input key using the custom hash function if it's set on the map, -// otherwise uses the default hashing function (32bit Murmur3). - -// NOTE: DN_DSMapHashToSlotIndex -// -// Calculate the index into the map's 'slots' array from the given hash. - -// NOTE: DN_DSMapResize -// -// Resize the table and move all elements to the new map, note that the new -// size must be a power of two. This function wil fail on memory allocation -// failure, or the requested size is smaller than the current number of -// elements in the map to resize. - -// NOTE: DN_ErrSink -// -// Error sinks are a way of accumulating errors from API calls related or -// unrelated into 1 unified error handling pattern. The implemenation of a -// sink requires 2 fundamental design constraints on the APIs supporting -// this pattern. -// -// 1. Pipelining of errors -// Errors emitted over the course of several API calls are accumulated -// into a sink which save the error code and message of the first error -// encountered and can be checked later. -// -// 2. Error proof APIs -// Functions that produce errors must return objects/handles that are -// marked to trigger no-ops used in subsequent functions dependent on it. -// -// Consider the following example demonstrating a conventional error -// handling approach (error values by return/sentinel values) and error -// handling using error-proof and pipelining. - -// (A) Conventional error checking patterns using return/sentinel values -#if 0 - DN_OSFile *file = DN_OS_FileOpen("/path/to/file", ...); - if (file) { - if (!DN_OS_FileWrite(file, "abc")) { - // Error handling! - } - Dnq_OS_FileClose(file); - } else { - // Error handling! - } -#endif - - // (B) Error handling using pipelining and and error proof APIs. APIs that - // produce errors take in the error sink as a parameter. - if (0) { - DN_ErrSink *error = DN_TCErrSinkBegin(DN_ErrSinkMode_Nil); - DN_OSFile file = DN_OS_FileOpen(DN_Str8Lit("/path/to/file"), DN_OSFileOpen_OpenIfExist, DN_OSFileAccess_ReadWrite, error); - DN_OS_FileWrite(&file, DN_Str8Lit("abc"), error); - DN_OS_FileClose(&file); - if (DN_ErrSinkEndLogErrorF(error, "Failed to write to file")) { - // Do error handling! - } - } - - // Pipeling and error-proof APIs lets you write sequence of instructions and - // defer error checking until it is convenient or necessary. Functions are - // *guaranteed* to return an object that is usable. There are no hidden - // exceptions to be thrown. Functions may opt to still return error values - // by way of return values thereby *not* precluding the ability to check - // every API call either. - // - // Ultimately, this error handling approach gives more flexibility on the - // manner in how errors are handled with less code. - // - // Error sinks can nest begin and end statements. This will open a new scope - // whereby the current captured error pushed onto a stack and the sink will - // be populated by the first error encountered in that scope. - - if (0) { - DN_ErrSink *error = DN_TCErrSinkBegin(DN_ErrSinkMode_Nil); - DN_OSFile file = DN_OS_FileOpen(DN_Str8Lit("/path/to/file"), DN_OSFileOpen_OpenIfExist, DN_OSFileAccess_ReadWrite, error); - DN_OS_FileWrite(&file, DN_Str8Lit("abc"), error); - DN_OS_FileClose(&file); - - { - // NOTE: My error sinks are thread-local, so the returned 'error' is - // the same as the 'error' value above. - DN_TCErrSinkBegin(DN_ErrSinkMode_Nil); - DN_OS_FileWriteAll(DN_Str8Lit("/path/to/another/file"), DN_Str8Lit("123"), error); - DN_ErrSinkEndLogErrorF(error, "Failed to write to another file"); - } - - if (DN_ErrSinkEndLogErrorF(error, "Failed to write to file")) { - // Do error handling! - } - } - - // NOTE: DN_JSONBuilder_Build - // - // Convert the internal JSON buffer in the builder into a string. - - // NOTE: DN_JSONBuilder_KeyValue, DN_JSONBuilder_KeyValueF - // - // Add a JSON key value pair untyped. The value is emitted directly without - // checking the contents of value. - // - // All other functions internally call into this function which is the main - // workhorse of the builder. - - // NOTE: DN_JSON_Builder_ObjectEnd - // - // End a JSON object in the builder, generates internally a '}' string - - // NOTE: DN_JSON_Builder_ArrayEnd - // - // End a JSON array in the builder, generates internally a ']' string - - // NOTE: DN_JSONBuilder_LiteralNamed - // - // Add a named JSON key-value object whose value is directly written to - // the following '"": ' (e.g. useful for emitting the 'null' - // value) - - // NOTE: DN_JSONBuilder_U64 - // NOTE: DN_JSONBuilder_U64Named - // NOTE: DN_JSONBuilder_I64 - // NOTE: DN_JSONBuilder_I64Named - // NOTE: DN_JSONBuilder_F64 - // NOTE: DN_JSONBuilder_F64Named - // NOTE: DN_JSONBuilder_Bool - // NOTE: DN_JSONBuilder_BoolNamed - // - // Add the named JSON data type as a key-value object. The named variants - // generates internally the key-value pair, e.g. - // - // "": - // - // And the non-named version emit just the 'value' portion - - // NOTE: DN_LOGProc - // - // Function prototype of the logging interface exposed by this library. Logs - // emitted using the DN_LOG_* family of functions are routed through this - // routine. - - // NOTE: DN_FNV1A -#if 0 - { - // Using the default hash as defined by DN_FNV1A32_SEED and - // DN_FNV1A64_SEED for 32/64bit hashes respectively - uint32_t buffer1 = 0xCAFE0000; - uint32_t buffer2 = 0xDEAD0000; - { - uint64_t hash = DN_FNV1A64_Hash(&buffer1, sizeof(buffer1)); - hash = DN_FNV1A64_Iterate(&buffer2, sizeof(buffer2), hash); // Chained hashing - (void)hash; - } - - // You can use a custom seed by skipping the 'Hash' call and instead - // calling 'Iterate' immediately. - { - uint64_t custom_seed = 0xABCDEF12; - uint64_t hash = DN_FNV1A64_Iterate(&buffer1, sizeof(buffer1), custom_seed); - hash = DN_FNV1A64_Iterate(&buffer2, sizeof(buffer2), hash); - (void)hash; - } - } -#endif - - // NOTE: DN_MurmurHash3 - // MurmurHash3 was written by Austin Appleby, and is placed in the public - // domain. The author (Austin Appleby) hereby disclaims copyright to this source - // code. - // - // Note - The x86 and x64 versions do _not_ produce the same results, as the - // algorithms are optimized for their respective platforms. You can still - // compile and run any of them on any platform, but your performance with the - // non-native version will be less than optimal. - - // NOTE: DN_OS_DateUnixTime - // - // Produce the time elapsed since the unix epoch - { - uint64_t now = DN_OS_DateUnixTimeS(); - (void)now; - } - - // NOTE: DN_OS_DirIterate - // - // Iterate the files within the passed in folder - for (DN_OSDirIterator it = {}; DN_OS_PathIterateDir(DN_Str8Lit("."), &it);) { - // printf("%.*s\n", DN_Str8PrintFmt(it.file_name)); - } - - // NOTE: DN_OS_FileDelete - // - // This function can only delete files and it can *only* delete directories - // if it is empty otherwise this function fails. - - // NOTE: DN_OS_WriteAllSafe - // Writes the file at the path first by appending '.tmp' to the 'path' to - // write to. If the temporary file is written successfully then the file is - // copied into 'path', for example: - // - // path: C:/Home/my.txt - // tmp_path: C:/Home/my.txt.tmp - // - // If 'tmp_path' is written to successfuly, the file will be copied over into - // 'path'. - if (0) { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_ErrSink *error = DN_TCErrSinkBegin(DN_ErrSinkMode_Nil); - DN_OS_FileWriteAllSafe(/*path*/ DN_Str8Lit("C:/Home/my.txt"), /*buffer*/ DN_Str8Lit("Hello world"), error); - DN_ErrSinkEndLogErrorF(error, ""); - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_OS_EstimateTSCPerSecond - // - // Estimate how many timestamp count's (TSC) there are per second. TSC - // is evaluated by calling __rdtsc() or the equivalent on the platform. This - // value can be used to convert TSC durations into seconds. - // - // The 'duration_ms_to_gauge_tsc_frequency' parameter specifies how many - // milliseconds to spend measuring the TSC rate of the current machine. - // 100ms is sufficient to produce a fairly accurate result with minimal - // blocking in applications if calculated on startup.. - // - // This may return 0 if querying the CPU timestamp counter is not supported - // on the platform (e.g. __rdtsc() or __builtin_readcyclecounter() returns 0). - - // NOTE: DN_OS_EXEDir - // - // Retrieve the executable directory without the trailing '/' or ('\' for - // windows). If this fails an empty string is returned. - - // NOTE: DN_OS_PerfCounterFrequency - // - // Get the number of ticks in the performance counter per second for the - // operating system you're running on. This value can be used to calculate - // duration from OS performance counter ticks. - - // NOTE: DN_OS_Path* - // Construct paths ensuring the native OS path separators are used in the - // string. In 99% of cases you can use 'PathConvertF' which converts the - // given path in one shot ensuring native path separators in the string. - // - // path: C:\Home/My/Folder - // converted: C:/Home/My/Folder (On Unix) - // C:\Home\My\Folder (On Windows) - // - // If you need to construct a path dynamically you can use the builder-esque - // interface to build a path's step-by-step using the 'OSPath' data structure. - // With this API you can append paths piece-meal to build the path after all - // pieces are appended. - // - // You may append a singular or nested path to the builder. In the builder, - // the string is scanned and separated into path separated chunks and stored - // in the builder, e.g. these are all valid to pass into 'PathAdd', - // 'PathAddRef' ... e.t.c - // - // "path/to/your/desired/folder" is valid - // "path" is valid - // "path/to\your/desired\folder" is valid - // - // 'PathPop' removes the last appended path from the current path stored in - // the 'OSPath': - // - // path: path/to/your/desired/folder - // popped_path: path/to/your/desired - - // NOTE: DN_OS_SecureRNGBytes - // - // Generate cryptographically secure bytes - -#if 0 - // NOTE: DN_PCG32 - // - // Random number generator of the PCG family. Implementation taken from - // Martins Mmozeiko from Handmade Network. - // https://gist.github.com/mmozeiko/1561361cd4105749f80bb0b9223e9db8 - { - DN_PCG32 rng = DN_PCG32_Init(0xb917'a66c'1d9b'3bd8); - - // NOTE: DN_PCG32_Range - // - // Generate a value in the [low, high) interval - uint32_t u32_value = DN_PCG32_Range(&rng, 32, 64); - DN_Assert(u32_value >= 32 && u32_value < 64); - - // NOTE: DN_PCG32_NextF32 - // NOTE: DN_PCG32_NextF64 - // - // Generate a float/double in the [0, 1) interval - DN_F64 f64_value = DN_PCG32_NextF64(&rng); - DN_Assert(f64_value >= 0.f && f64_value < 1.f); - - // NOTE: DN_PCG32_Advance - // - // Step the random number generator by 'delta' steps - DN_PCG32_Advance(&rng, /*delta*/ 5); - } -#endif - - // NOTE: DN_Profiler - // - // A profiler based off Casey Muratori's Computer Enhance course, Performance - // Aware Programming. This profiler measures function elapsed time using the - // CPU's time stamp counter (e.g. rdtsc) providing a rough cycle count - // that can be converted into a duration. - #if defined(DN_OS_CPP) - { - enum DemoZone - { - DemoZone_MainLoop, - DemoZone_Count - }; - - #if defined(DN_PLATFORM_EMSCRIPTEN) - DN_ProfilerTSCNowFunc *tsc_now = DN_OS_PerfCounterNow; - DN_U64 tsc_frequency = DN_OS_PerfCounterFrequency(); - #else - DN_ProfilerTSCNowFunc *tsc_now = nullptr; - DN_U64 tsc_frequency = DN_OS_EstimateTSCPerSecond(100); - #endif - - DN_ProfilerAnchor anchors[4] = {}; - DN_USize anchors_count = DN_ArrayCountU(anchors); - DN_USize anchors_per_frame = anchors_count / 2; - DN_Profiler profiler = DN_ProfilerInit(anchors, anchors_count, anchors_per_frame, tsc_now, tsc_frequency); - - for (DN_USize it = 0; it < 1; it++) { - DN_ProfilerNewFrame(&profiler); - DN_ProfilerZone zone = DN_ProfilerBeginZone(&profiler, DN_Str8Lit("Main Loop"), DemoZone_MainLoop); - DN_OS_SleepMs(100); - DN_ProfilerEndZone(zone); - DN_ProfilerFmtToStdout(&profiler); - } - } - #endif - - // NOTE: DN_Raycast_LineIntersectV2 - // Calculate the intersection point of 2 rays returning a `t` value - // which is how much along the direction of the 'ray' did the intersection - // occur. - // - // The arguments passed in do not need to be normalised for the function to - // work. - - // NOTE: DN_Safe_* - // - // Performs the arithmetic operation and uses DN_Check on the operation to - // check if it overflows. If it overflows the MAX value of the integer is - // returned in add and multiply operations, and, the minimum is returned in - // subtraction and division. - - // NOTE: DN_SaturateCast* - // - // Truncate the passed in value to the return type clamping the resulting - // value to the max value of the desired data type. It DN_Check's the - // truncation. - // - // The following sentinel values are returned when saturated, - // USize -> Int: INT_MAX - // USize -> I8: INT8_MAX - // USize -> I16: INT16_MAX - // USize -> I32: INT32_MAX - // USize -> I64: INT64_MAX - // - // U64 -> UInt: UINT_MAX - // U64 -> U8: UINT8_MAX - // U64 -> U16: UINT16_MAX - // U64 -> U32: UINT32_MAX - // - // USize -> U8: UINT8_MAX - // USize -> U16: UINT16_MAX - // USize -> U32: UINT32_MAX - // USize -> U64: UINT64_MAX - // - // ISize -> Int: INT_MIN or INT_MAX - // ISize -> I8: INT8_MIN or INT8_MAX - // ISize -> I16: INT16_MIN or INT16_MAX - // ISize -> I32: INT32_MIN or INT32_MAX - // ISize -> I64: INT64_MIN or INT64_MAX - // - // ISize -> UInt: 0 or UINT_MAX - // ISize -> U8: 0 or UINT8_MAX - // ISize -> U16: 0 or UINT16_MAX - // ISize -> U32: 0 or UINT32_MAX - // ISize -> U64: 0 or UINT64_MAX - // - // I64 -> ISize: DN_ISIZE_MIN or DN_ISIZE_MAX - // I64 -> I8: INT8_MIN or INT8_MAX - // I64 -> I16: INT16_MIN or INT16_MAX - // I64 -> I32: INT32_MIN or INT32_MAX - // - // Int -> I8: INT8_MIN or INT8_MAX - // Int -> I16: INT16_MIN or INT16_MAX - // Int -> U8: 0 or UINT8_MAX - // Int -> U16: 0 or UINT16_MAX - // Int -> U32: 0 or UINT32_MAX - // Int -> U64: 0 or UINT64_MAX - - // NOTE: DN_StackTrace - // Emit stack traces at the calling site that these functions are invoked - // from. - // - // For some applications, it may be viable to generate raw stack traces and - // store just the base addresses of the call stack from the 'Walk' - // functions. This reduces the memory overhead and required to hold onto - // stack traces and resolve the addresses on-demand when required. - // - // However if your application is loading and/or unloading shared libraries, - // on Windows it may be impossible for the application to resolve raw base - // addresses if they become invalid over time. In these applications you - // must convert the raw stack traces before the unloading occurs, and when - // loading new shared libraries, 'ReloadSymbols' must be called to ensure - // the debug APIs are aware of how to resolve the new addresses imported - // into the address space. - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - - // NOTE: DN_StackTraceFromArena - // - // Generate a stack trace as a series of addresses to the base of the - // functions on the call-stack at the current instruction pointer. The - // addresses are stored in order from the current executing function - // first to the most ancestor function last in the walk. - DN_StackTrace walk = DN_StackTraceFromArena(&scratch.arena, /*depth limit*/ 128); - - // Loop over the addresses produced in the stack trace - for (DN_StackTraceIterator it = {}; DN_StackTraceIterate(&it, &walk);) { - // NOTE: DN_StackTraceRawFrameToFrame - // - // Converts the base address into a human readable stack trace - // entry (e.g. address, line number, file and function name). - DN_StackTraceFrame frame = DN_StackTraceRawFrameToFrame(&scratch.arena, it.raw_frame); - - // You may then print out the frame like so - if (0) - printf("%.*s(%" PRIu64 "): %.*s\n", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name)); - } - - // If you load new shared-libraries into the address space it maybe - // necessary to call into 'ReloadSymbols' to ensure that the OS is able - // to resolve the new addresses. - DN_StackTraceReloadSymbols(); - - // NOTE: DN_OS_StackTraceGetFrames - // - // Helper function to create a stack trace and automatically convert the - // raw frames into human readable frames. This function effectively - // calls 'Walk' followed by 'RawFrameToFrame'. - DN_StackTraceFrameSlice frames = DN_StackTraceGetFrames(&scratch.arena, /*depth limit*/ 128); - (void)frames; - - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_Str8FromArena - // - // Allocates a string with the requested 'size'. An additional byte is - // always requested from the allocator to null-terminate the buffer. This - // allows the string to be used with C-style string APIs. - // - // The returned string's 'size' member variable does *not* include this - // additional null-terminating byte. - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_Str8 string = DN_Str8AllocArena(/*size*/ 1, DN_ZMem_Yes, &scratch.arena); - DN_Assert(string.size == 1); - DN_Assert(string.data[string.size] == 0); // It is null-terminated! - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_Str8BSplit - // - // Splits a string into 2 substrings occuring prior and after the first - // occurence of the delimiter. Neither strings include the matched - // delimiter. If no delimiter is found, the 'rhs' of the split will be - // empty. - { - DN_Str8BSplitResult dot_split = DN_Str8BSplit(/*string*/ DN_Str8Lit("abc.def.ghi"), /*delimiter*/ DN_Str8Lit(".")); - DN_Str8BSplitResult slash_split = DN_Str8BSplit(/*string*/ DN_Str8Lit("abc.def.ghi"), /*delimiter*/ DN_Str8Lit("/")); - DN_Assert(DN_Str8Eq(dot_split.lhs, DN_Str8Lit("abc")) && DN_Str8Eq(dot_split.rhs, DN_Str8Lit("def.ghi"))); - DN_Assert(DN_Str8Eq(slash_split.lhs, DN_Str8Lit("abc.def.ghi")) && DN_Str8Eq(slash_split.rhs, DN_Str8Lit(""))); - - // Loop that walks the string and produces ("abc", "def", "ghi") - for (DN_Str8 it = DN_Str8Lit("abc.def.ghi"); it.size;) { - DN_Str8BSplitResult split = DN_Str8BSplit(it, DN_Str8Lit(".")); - DN_Str8 chunk = split.lhs; // "abc", "def", ... - it = split.rhs; - (void)chunk; - } - } - - // NOTE: DN_Str8FileNameFromPath - // - // Takes a slice to the file name from a file path. The file name is - // evaluated by searching from the end of the string backwards to the first - // occurring path separator '/' or '\'. If no path separator is found, the - // original string is returned. This function preserves the file extension - // if there were any. - { - { - DN_Str8 string = DN_Str8FileNameFromPath(DN_Str8Lit("C:/Folder/item.txt")); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("item.txt"))); - } - { - // TODO(doyle): Intuitively this seems incorrect. Empty string instead? - DN_Str8 string = DN_Str8FileNameFromPath(DN_Str8Lit("C:/Folder/")); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("C:/Folder"))); - } - { - DN_Str8 string = DN_Str8FileNameFromPath(DN_Str8Lit("C:/Folder")); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("Folder"))); - } - } - - // NOTE: DN_Str8FilePathNoExtension - // - // This function preserves the original string if no extension was found. - // An extension is defined as the substring after the last '.' encountered - // in the string. - { - DN_Str8 string = DN_Str8FilePathNoExtension(DN_Str8Lit("C:/Folder/item.txt.bak")); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("C:/Folder/item.txt"))); - } - - // NOTE: DN_Str8FileNameNoExtension - // - // This function is the same as calling 'FileNameFromPath' followed by - // 'FilePathNoExtension' - { - DN_Str8 string = DN_Str8FileNameNoExtension(DN_Str8Lit("C:/Folder/item.txt.bak")); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("item.txt"))); - } - - // NOTE: DN_Str8Replace - // NOTE: DN_Str8ReplaceInsensitive - // - // Replace any matching substring 'find' with 'replace' in the passed in - // 'string'. The 'start_index' may be specified to offset which index the - // string will start doing replacements from. - // - // String replacements are not done inline and the returned string will - // always be a newly allocated copy, irrespective of if any replacements - // were done or not. - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_Str8 string = DN_Str8Replace(/*string*/ DN_Str8Lit("Foo Foo Bar"), - /*find*/ DN_Str8Lit("Foo"), - /*replace*/ DN_Str8Lit("Moo"), - /*start_index*/ 1, - /*arena*/ &scratch.arena, - /*eq_case*/ DN_Str8EqCase_Sensitive); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("Foo Moo Bar"))); - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_Str8Segment - // - // Add a delimiting 'segment_char' every 'segment_size' number of characters - // in the string. - // - // Reverse segment delimits the string counting 'segment_size' from the back - // of the string. - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_Str8 string = DN_Str8Segment(&scratch.arena, /*string*/ DN_Str8Lit("123456789"), /*segment_size*/ 3, /*segment_char*/ ','); - DN_Assert(DN_Str8Eq(string, DN_Str8Lit("123,456,789"))); - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_Str8Split - { - // Splits the string at each delimiter into substrings occuring prior and - // after until the next delimiter. - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - { - DN_Str8SplitResult splits = DN_Str8SplitArena(/*string*/ DN_Str8Lit("192.168.8.1"), - /*delimiter*/ DN_Str8Lit("."), - DN_Str8SplitFlags_ExcludeEmptyStrings, - &scratch.arena); - DN_Assert(splits.count == 4); - DN_Assert(DN_Str8Eq(splits.data[0], DN_Str8Lit("192")) && - DN_Str8Eq(splits.data[1], DN_Str8Lit("168")) && - DN_Str8Eq(splits.data[2], DN_Str8Lit("8")) && - DN_Str8Eq(splits.data[3], DN_Str8Lit("1"))); - } - - // You can include empty strings that occur when splitting by setting - // the split mode to include empty strings. - { - DN_Str8SplitResult splits = DN_Str8SplitArena(/*string*/ DN_Str8Lit("a--b"), - /*delimiter*/ DN_Str8Lit("-"), - DN_Str8SplitFlags_Nil, - &scratch.arena); - DN_Assert(splits.count == 3); - DN_Assert(DN_Str8Eq(splits.data[0], DN_Str8Lit("a")) && - DN_Str8Eq(splits.data[1], DN_Str8Lit("")) && - DN_Str8Eq(splits.data[2], DN_Str8Lit("b"))); - } - - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_I64FromStr8, DN_U64FromStr8 - // - // Convert a number represented as a string to a signed 64 bit number. - // - // The 'separator' is an optional digit separator for example, if - // 'separator' is set to ',' then '1,234' will successfully be parsed to - // '1234'. If no separator is desired, you may pass in '0' in which - // '1,234' will *not* be succesfully parsed. - // - // Real numbers are truncated. Both '-' and '+' prefixed strings are permitted, - // i.e. "+1234" -> 1234 and "-1234" -> -1234. Strings must consist entirely of - // digits, the seperator or the permitted prefixes as previously mentioned - // otherwise this function will return false, i.e. "1234 dog" will cause the - // function to return false, however, the output is greedily converted and - // will be evaluated to "1234". - // - // 'ToU64' only '+' prefix is permitted - // 'ToI64' either '+' or '-' prefix is permitted - { - { - DN_I64FromResult result = DN_I64FromStr8(DN_Str8Lit("-1,234"), /*separator*/ ','); - DN_Assert(result.success && result.value == -1234); - } - { - DN_I64FromResult result = DN_I64FromStr8(DN_Str8Lit("-1,234"), /*separator*/ 0); - DN_Assert(!result.success && result.value == 1); // 1 because it's a greedy conversion - } - } - - // NOTE: DN_Str8TrimByteOrderMark - // - // Removes a leading UTF8, UTF16 BE/LE, UTF32 BE/LE byte order mark from the - // string if it's present. - - // NOTE: DN_Str8PrintFmt - // - // Unpacks a string struct that has the fields {.data, .size} for printing a - // pointer and length style string using the printf format specifier "%.*s" - // - // printf("%.*s\n", DN_Str8PrintFmt(DN_Str8Lit("Hello world"))); - - // NOTE: DN_Str8BuilderAppendF - // NOTE: DN_Str8BuilderAppendFV - // NOTE: DN_Str8BuilderAppendRef - // NOTE: DN_Str8BuilderAppendCopy - // - // - Appends a string to the string builder as follows - // - // AppendRef: Stores the string slice by value - // AppendCopy: Stores the string slice by copy (with builder's arena) - // AppendF/V: Constructs a format string and calls 'AppendRef' - - // NOTE: DN_Str8BuilderBuild - // NOTE: DN_Str8BuilderBuildCRT - // - // Constructs the final string by merging all the appended strings into - // one merged string. - // - // The CRT variant calls into 'malloc' and the string *must* be released - // using 'free'. - - // NOTE: DN_Str8BuilderBuildSlice - // - // Constructs the final string into an array of strings (e.g. a slice) - - // NOTE: DN_TicketMutex - // - // A mutex implemented using an atomic compare and swap on tickets handed - // out for each critical section. - // - // This mutex serves ticket in order and will block all other threads until - // the tickets are returned in order. The thread with the oldest ticket that - // has not been returned has right of way to execute, all other threads will - // be blocked in an atomic compare and swap loop. block execution by going - // into an atomic - // - // When a thread is blocked by this mutex, a spinlock intrinsic '_mm_pause' is - // used to yield the CPU and reduce spinlock on the thread. This mutex is not - // ideal for long blocking operations. This mutex does not issue any syscalls - // and relies entirely on atomic instructions. - { - DN_TicketMutex mutex = {}; - DN_TicketMutex_Begin(&mutex); // Simple procedural mutual exclusion lock - DN_TicketMutex_End(&mutex); - - // NOTE: DN_TicketMutex_MakeTicket - // - // Request the next available ticket for locking from the mutex. - DN_UInt ticket = DN_TicketMutex_MakeTicket(&mutex); - - if (DN_TicketMutex_CanLock(&mutex, ticket)) { - // NOTE: DN_TicketMutex_BeginTicket - // - // Locks the mutex using the given ticket if possible. If it's not - // the next ticket to be locked the executing thread will block - // until the mutex can lock the ticket, i.e. All prior tickets are - // returned, in sequence, to the mutex. - DN_TicketMutex_BeginTicket(&mutex, ticket); - DN_TicketMutex_End(&mutex); - } - } - - // NOTE: DN_ThreadContext - // - // Each thread is assigned in their thread-local storage (TLS) scratch and - // permanent arena allocators. These can be used for allocations with a - // lifetime scoped to the lexical scope or for storing data permanently - // using the arena paradigm. - // - // TLS in this implementation is implemented using the `thread_local` C/C++ - // keyword. - // - // 99% of the time you will want DN_OS_TLSTMem...) which returns you a - // temporary arena for function lifetime allocations. On scope exit, the - // arena is cleared out. - // - // This library's paradigm revolves heavily around arenas including scratch - // arenas into child functions for temporary calculations. If an arena is - // passed into a function, this poses a problem sometimes known as - // 'arena aliasing'. - // - // If an arena aliases another arena (e.g. the arena passed in) is the same - // as the scratch arena requested in the function, we risk the scratch arena - // on scope exit deallocating memory belonging to the caller. - // - // To avoid this we the 'DN_OS_TLSTMem...)' API takes in a list of arenas - // to ensure that we provide a scratch arena that *won't* alias with the - // caller's arena. If arena aliasing occurs, with ASAN on, generally - // the library will trap and report use-after-poison once violated. - { - DN_TCScratch scratch_a = DN_TCScratchBeginArena(nullptr, 0); - - // Now imagine we call a function where we pass scratch_a.arena down - // into it .. If we call scratch again, we need to pass in the arena - // to prevent aliasing. - DN_Arena *conflicts[] = {&scratch_a.arena}; - DN_TCScratch scratch_b = DN_TCScratchBeginArena(conflicts, DN_ArrayCountU(conflicts)); - DN_Assert(scratch_a.arena.mem != scratch_b.arena.mem); - - DN_TCScratchEnd(&scratch_b); - DN_TCScratchEnd(&scratch_a); - } - - // @proc DN_Thread_Getscratch - // @desc Retrieve the per-thread temporary arena allocator that is reset on scope - // exit. - - // The scratch arena must be deconflicted with any existing arenas in the - // function to avoid trampling over each other's memory. Consider the situation - // where the scratch arena is passed into the function. Inside the function, if - // the same arena is reused then, if both arenas allocate, when the inner arena - // is reset, this will undo the passed in arena's allocations in the function. - - // @param[in] conflict_arena A pointer to the arena currently being used in the - // function - - // NOTE: DN_Str8x32FromFmt - { - DN_Str8x32 string = DN_Str8x32FromFmt("%d", 123123); - if (0) // Prints "123123" - printf("%.*s", DN_Str8PrintFmt(string)); - } - - // NOTE: DN_CVT_AgeFromU64 - { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_Str8x128 string = DN_AgeStr8FromSecF64(DN_SecFromHours(2) + DN_SecFromMins(30), DN_AgeUnit_All); - DN_Assert(DN_Str8Eq(DN_Str8FromStruct(&string), DN_Str8Lit("2h 30m"))); - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_VArray - // - // An array that is backed by virtual memory by reserving addressing space - // and comitting pages as items are allocated in the array. This array never - // reallocs, instead you should reserve the upper bound of the memory you - // will possibly ever need (e.g. 16GB) and let the array commit physical - // pages on demand. - // - // On 64 bit operating systems you are given 48 bits of addressable space - // giving you 256 TB of reservable memory. This gives you practically - // an unlimited array capacity that avoids reallocs and only consumes memory - // that is actually occupied by the array. - // - // Each page that is committed into the array will be at page/allocation - // granularity which are always cache aligned. This array essentially retains - // all the benefits of normal arrays, - // - // - contiguous memory - // - O(1) random access - // - O(N) iterate - // - // In addition to no realloc on expansion or shrinking. - // - { - // NOTE: DN_OS_VArrayInit - // NOTE: DN_OS_VArrayInitByteSize - // - // Initialise an array with the requested byte size or item capacity - // respectively. The returned array may have a higher capacity than the - // requested amount since requested memory from the OS may have a certain - // alignment requirement (e.g. on Windows reserve/commit are 64k/4k - // aligned). - DN_VArray array = DN_OS_VArrayInit(1024); - DN_Assert(array.size == 0 && array.max >= 1024); - - // NOTE: DN_OS_VArrayMake - // NOTE: DN_OS_VArrayAdd - // NOTE: DN_OS_VArrayMakeArray - // NOTE: DN_OS_VArrayAddArray - // - // Allocate items from the array where: - // - // Make: creates a zero-init item from the array - // Add: creates a zero-init item and memcpy passed in data into the item - // - // If the array has run out of capacity or was never initialised, a null - // pointer is returned. - int *item = DN_OS_VArrayAdd(&array, 0xCAFE); - DN_Assert(*item == 0xCAFE && array.size == 1); - - // NOTE: DN_OS_VArrayAddCArray - DN_OS_VArrayAddCArray(&array, {1, 2, 3}); - DN_Assert(array.size == 4); - -// TODO(doyle): There's a bug here with the negative erase! -// Loop over the array items and erase 1 item. -#if 0 - for (DN_USize index = 0; index < array.size; index++) { - if (index != 1) - continue; - - // NOTE: DN_OS_VArrayEraseRange - // - // Erase the next 'count' items at 'begin_index' in the array. - // 'count' can be positive or negative which dictates the if we - // erase forward from the 'begin_index' or in reverse. - // - // This operation will invalidate all pointers to the array! - // - // A stable erase will shift all elements after the erase ranged - // into the range preserving the order of prior elements. Unstable - // erase will move the tail elements into the range being erased. - // - // Erase range returns a result that contains the next iterator - // index that can be used to update the your for loop index if you - // are trying to iterate over the array. - - // TODO(doyle): There's a bug here! This doesn't work. - // Erase index 0 with the negative count! - DN_ArrayEraseResult erase_result = DN_OS_VArrayEraseRange(&array, - /*begin_index*/ index, - /*count*/ -1, - /*erase*/ DN_ArrayErase_Stable); - DN_Assert(erase_result.items_erased == 1); - - // Use the index returned to continue linearly iterating the array - index = erase_result.it_index; - DN_Assert(array.data[index + 1] == 2); // Next loop iteration will process item '2' - } - - DN_Assert(array.size == 3 && - array.data[0] == 1 && - array.data[1] == 2 && - array.data[2] == 3); -#endif - - // NOTE: DN_OS_VArrayReserve - // - // Ensure that the requested number of items are backed by physical pages - // from the OS. Calling this pre-emptively will minimise syscalls into the - // kernel to request memory. The requested items will be rounded up to the - // in bytes to the allocation granularity of OS allocation APIs hence the - // reserved space may be greater than the requested amount (e.g. this is 4k - // on Windows). - DN_OS_VArrayReserve(&array, /*count*/ 8); - - DN_OS_VArrayDeinit(&array); - } - - // NOTE: DN_W32_LastError - // NOTE: DN_W32_ErrorCodeToMsg - #if defined(DN_PLATFORM_WIN32) - if (0) { - // Generate the error string for the last Win32 API called that return - // an error value. - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_OSW32Error get_last_error = DN_OS_W32LastError(&scratch.arena); - printf("Error (%lu): %.*s", get_last_error.code, DN_Str8PrintFmt(get_last_error.msg)); - - // Alternatively, pass in the error code directly - DN_OSW32Error error_msg_for_code = DN_OS_W32ErrorCodeToMsg(&scratch.arena, /*error_code*/ 0); - printf("Error (%lu): %.*s", error_msg_for_code.code, DN_Str8PrintFmt(error_msg_for_code.msg)); - DN_TCScratchEnd(&scratch); - } - - // NOTE: DN_W32_MakeProcessDPIAware - // - // Call once at application start-up to ensure that the application is DPI - // aware on Windows and ensure that application UI is scaled up - // appropriately for the monitor. - - // NOTE: DN_W32_Str8ToStr16 - // NOTE: DN_W32_Str8ToStr16Buffer - // NOTE: DN_W32_Str16ToStr8 - // NOTE: DN_W32_Str16ToStr8Buffer - // - // Convert a UTF8 <-> UTF16 string. - // - // The exact size buffer required for this function can be determined by - // calling this function with the 'dest' set to null and 'dest_size' set to - // 0, the return size is the size required for conversion not-including - // space for the null-terminator. This function *always* null-terminates the - // input buffer. - // - // Returns the number of u8's (for UTF16->8) OR u16's (for UTF8->16) - // written/required for conversion. 0 if there was a conversion error and can be - // queried using 'DN_W32_LastError' - #endif -} -DN_MSVC_WARNING_POP diff --git a/Source/Extra/dn_helpers.cpp b/Source/Extra/dn_helpers.cpp deleted file mode 100644 index bdebaf0..0000000 --- a/Source/Extra/dn_helpers.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#define DN_HELPERS_CPP - -#if defined(_CLANGD) - #include "dn_helpers.h" -#endif - -DN_API DN_JSONBuilder DN_JSONBuilder_Init(DN_Arena *arena, int spaces_per_indent) -{ - DN_JSONBuilder result = {}; - result.spaces_per_indent = spaces_per_indent; - result.string_builder.arena = arena; - return result; -} - -DN_API DN_Str8 DN_JSONBuilder_Build(DN_JSONBuilder const *builder, DN_Arena *arena) -{ - DN_Str8 result = DN_Str8FromStr8BuilderArena(&builder->string_builder, arena); - return result; -} - -DN_API void DN_JSONBuilder_KeyValue(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value) -{ - if (key.size == 0 && value.size == 0) - return; - - DN_JSONBuilderItem item = DN_JSONBuilderItem_KeyValue; - if (value.size >= 1) { - if (value.data[0] == '{' || value.data[0] == '[') - item = DN_JSONBuilderItem_OpenContainer; - else if (value.data[0] == '}' || value.data[0] == ']') - item = DN_JSONBuilderItem_CloseContainer; - } - - bool adding_to_container_with_items = - item != DN_JSONBuilderItem_CloseContainer && (builder->last_item == DN_JSONBuilderItem_KeyValue || - builder->last_item == DN_JSONBuilderItem_CloseContainer); - - uint8_t prefix_size = 0; - char prefix[2] = {0}; - if (adding_to_container_with_items) - prefix[prefix_size++] = ','; - - if (builder->last_item != DN_JSONBuilderItem_Empty) - prefix[prefix_size++] = '\n'; - - if (item == DN_JSONBuilderItem_CloseContainer) - builder->indent_level--; - - int spaces_per_indent = builder->spaces_per_indent ? builder->spaces_per_indent : 2; - int spaces = builder->indent_level * spaces_per_indent; - - if (key.size) - DN_Str8BuilderAppendF(&builder->string_builder, - "%.*s%*c\"%.*s\": %.*s", - prefix_size, - prefix, - spaces, - ' ', - DN_Str8PrintFmt(key), - DN_Str8PrintFmt(value)); - else if (spaces == 0) - DN_Str8BuilderAppendF(&builder->string_builder, "%.*s%.*s", prefix_size, prefix, DN_Str8PrintFmt(value)); - else - DN_Str8BuilderAppendF(&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DN_Str8PrintFmt(value)); - - if (item == DN_JSONBuilderItem_OpenContainer) - builder->indent_level++; - - builder->last_item = item; -} - -DN_API void DN_JSONBuilder_KeyValueFV(DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, va_list args) -{ - DN_TCScratch scratch = DN_TCScratchBeginArena(&builder->string_builder.arena, 1); - DN_Str8 value = DN_Str8FromFmtVArena(&scratch.arena, value_fmt, args); - DN_JSONBuilder_KeyValue(builder, key, value); - DN_TCScratchEnd(&scratch); -} - -DN_API void DN_JSONBuilder_KeyValueF(DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...) -{ - va_list args; - va_start(args, value_fmt); - DN_JSONBuilder_KeyValueFV(builder, key, value_fmt, args); - va_end(args); -} - -DN_API void DN_JSONBuilder_ObjectBeginNamed(DN_JSONBuilder *builder, DN_Str8 name) -{ - DN_JSONBuilder_KeyValue(builder, name, DN_Str8Lit("{")); -} - -DN_API void DN_JSONBuilder_ObjectEnd(DN_JSONBuilder *builder) -{ - DN_JSONBuilder_KeyValue(builder, DN_Str8Lit(""), DN_Str8Lit("}")); -} - -DN_API void DN_JSONBuilder_ArrayBeginNamed(DN_JSONBuilder *builder, DN_Str8 name) -{ - DN_JSONBuilder_KeyValue(builder, name, DN_Str8Lit("[")); -} - -DN_API void DN_JSONBuilder_ArrayEnd(DN_JSONBuilder *builder) -{ - DN_JSONBuilder_KeyValue(builder, DN_Str8Lit(""), DN_Str8Lit("]")); -} - -DN_API void DN_JSONBuilder_Str8Named(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value) -{ - DN_JSONBuilder_KeyValueF(builder, key, "\"%.*s\"", value.size, value.data); -} - -DN_API void DN_JSONBuilder_LiteralNamed(DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value) -{ - DN_JSONBuilder_KeyValueF(builder, key, "%.*s", value.size, value.data); -} - -DN_API void DN_JSONBuilder_U64Named(DN_JSONBuilder *builder, DN_Str8 key, uint64_t value) -{ - DN_JSONBuilder_KeyValueF(builder, key, "%I64u", value); -} - -DN_API void DN_JSONBuilder_I64Named(DN_JSONBuilder *builder, DN_Str8 key, int64_t value) -{ - DN_JSONBuilder_KeyValueF(builder, key, "%I64d", value); -} - -DN_API void DN_JSONBuilder_F64Named(DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places) -{ - if (!builder) - return; - - if (decimal_places >= 16) - decimal_places = 16; - - // NOTE: Generate the format string for the float, depending on how many - // decimals places it wants. - char float_fmt[16]; - if (decimal_places > 0) { - // NOTE: Emit the format string "%.f" i.e. %.1f - DN_SNPrintF(float_fmt, sizeof(float_fmt), "%%.%df", decimal_places); - } else { - // NOTE: Emit the format string "%f" - DN_SNPrintF(float_fmt, sizeof(float_fmt), "%%f"); - } - DN_JSONBuilder_KeyValueF(builder, key, float_fmt, value); -} - -DN_API void DN_JSONBuilder_BoolNamed(DN_JSONBuilder *builder, DN_Str8 key, bool value) -{ - DN_Str8 value_string = value ? DN_Str8Lit("true") : DN_Str8Lit("false"); - DN_JSONBuilder_KeyValueF(builder, key, "%.*s", value_string.size, value_string.data); -} diff --git a/Source/Extra/dn_helpers.h b/Source/Extra/dn_helpers.h deleted file mode 100644 index 357cc39..0000000 --- a/Source/Extra/dn_helpers.h +++ /dev/null @@ -1,185 +0,0 @@ -#if !defined(DN_HELPERS_H) -#define DN_HELPERS_H - -#if defined(_CLANGD) - #include "../dn.h" -#endif - -/* -//////////////////////////////////////////////////////////////////////////////////////////////////// -// -// $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\ -// $$ | $$ |$$ _____|$$ | $$ __$$\ $$ _____|$$ __$$\ $$ __$$\ -// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ |$$ / \__| -// $$$$$$$$ |$$$$$\ $$ | $$$$$$$ |$$$$$\ $$$$$$$ |\$$$$$$\ -// $$ __$$ |$$ __| $$ | $$ ____/ $$ __| $$ __$$< \____$$\ -// $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |$$\ $$ | -// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$ | $$$$$$$$\ $$ | $$ |\$$$$$$ | -// \__| \__|\________|\________|\__| \________|\__| \__| \______/ -// -// dn_helpers.h -- Helper functions/data structures -// -//////////////////////////////////////////////////////////////////////////////////////////////////// -*/ - -#if !defined(DN_NO_JSON_BUILDER) -enum DN_JSONBuilderItem -{ - DN_JSONBuilderItem_Empty, - DN_JSONBuilderItem_OpenContainer, - DN_JSONBuilderItem_CloseContainer, - DN_JSONBuilderItem_KeyValue, -}; - -struct DN_JSONBuilder -{ - bool use_stdout; // When set, ignore the string builder and dump immediately to stdout - DN_Str8Builder string_builder; // (Internal) - int indent_level; // (Internal) - int spaces_per_indent; // The number of spaces per indent level - DN_JSONBuilderItem last_item; -}; -#endif // !defined(DN_NO_JSON_BUIDLER) - -template -using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs); - -template -bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); - -enum DN_BinarySearchType -{ - // Index of the match. If no match is found, found is set to false and the - // index is set to the index where the match should be inserted/exist, if - // it were in the array - DN_BinarySearchType_Match, - - // Index of the first element in the array that is `element >= find`. If no such - // item is found or the array is empty, then, the index is set to the array - // size and found is set to `false`. - // - // For example: - // int array[] = {0, 1, 2, 3, 4, 5}; - // DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_LowerBound); - // printf("%zu\n", result.index); // Prints index '4' - - DN_BinarySearchType_LowerBound, - - // Index of the first element in the array that is `element > find`. If no such - // item is found or the array is empty, then, the index is set to the array - // size and found is set to `false`. - // - // For example: - // int array[] = {0, 1, 2, 3, 4, 5}; - // DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_UpperBound); - // printf("%zu\n", result.index); // Prints index '5' - - DN_BinarySearchType_UpperBound, -}; - -struct DN_BinarySearchResult -{ - bool found; - DN_USize index; -}; - -#if !defined(DN_NO_JSON_BUILDER) -// NOTE: DN_JSONBuilder -#define DN_JSONBuilder_Object(builder) \ - DN_DeferLoop(DN_JSONBuilder_ObjectBegin(builder), \ - DN_JSONBuilder_ObjectEnd(builder)) - -#define DN_JSONBuilder_ObjectNamed(builder, name) \ - DN_DeferLoop(DN_JSONBuilder_ObjectBeginNamed(builder, name), \ - DN_JSONBuilder_ObjectEnd(builder)) - -#define DN_JSONBuilder_Array(builder) \ - DN_DeferLoop(DN_JSONBuilder_ArrayBegin(builder), \ - DN_JSONBuilder_ArrayEnd(builder)) - -#define DN_JSONBuilder_ArrayNamed(builder, name) \ - DN_DeferLoop(DN_JSONBuilder_ArrayBeginNamed(builder, name), \ - DN_JSONBuilder_ArrayEnd(builder)) - -DN_API DN_JSONBuilder DN_JSONBuilder_Init (DN_Arena *arena, int spaces_per_indent); -DN_API DN_Str8 DN_JSONBuilder_Build (DN_JSONBuilder const *builder, DN_Arena *arena); -DN_API void DN_JSONBuilder_KeyValue (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value); -DN_API void DN_JSONBuilder_KeyValueF (DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...); -DN_API void DN_JSONBuilder_ObjectBeginNamed (DN_JSONBuilder *builder, DN_Str8 name); -DN_API void DN_JSONBuilder_ObjectEnd (DN_JSONBuilder *builder); -DN_API void DN_JSONBuilder_ArrayBeginNamed (DN_JSONBuilder *builder, DN_Str8 name); -DN_API void DN_JSONBuilder_ArrayEnd (DN_JSONBuilder *builder); -DN_API void DN_JSONBuilder_Str8Named (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value); -DN_API void DN_JSONBuilder_LiteralNamed (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value); -DN_API void DN_JSONBuilder_U64Named (DN_JSONBuilder *builder, DN_Str8 key, uint64_t value); -DN_API void DN_JSONBuilder_I64Named (DN_JSONBuilder *builder, DN_Str8 key, int64_t value); -DN_API void DN_JSONBuilder_F64Named (DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places); -DN_API void DN_JSONBuilder_BoolNamed (DN_JSONBuilder *builder, DN_Str8 key, bool value); - -#define DN_JSONBuilder_ObjectBegin(builder) DN_JSONBuilder_ObjectBeginNamed(builder, DN_Str8Lit("")) -#define DN_JSONBuilder_ArrayBegin(builder) DN_JSONBuilder_ArrayBeginNamed(builder, DN_Str8Lit("")) -#define DN_JSONBuilder_Str8(builder, value) DN_JSONBuilder_Str8Named(builder, DN_Str8Lit(""), value) -#define DN_JSONBuilder_Literal(builder, value) DN_JSONBuilder_LiteralNamed(builder, DN_Str8Lit(""), value) -#define DN_JSONBuilder_U64(builder, value) DN_JSONBuilder_U64Named(builder, DN_Str8Lit(""), value) -#define DN_JSONBuilder_I64(builder, value) DN_JSONBuilder_I64Named(builder, DN_Str8Lit(""), value) -#define DN_JSONBuilder_F64(builder, value) DN_JSONBuilder_F64Named(builder, DN_Str8Lit(""), value) -#define DN_JSONBuilder_Bool(builder, value) DN_JSONBuilder_BoolNamed(builder, DN_Str8Lit(""), value) -#endif // !defined(DN_NO_JSON_BUILDER) - -// NOTE: DN_BinarySearch -template bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); -template DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc less_than = DN_BinarySearch_DefaultLessThan); - -// NOTE: DN_BinarySearch -template -bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs) -{ - bool result = lhs < rhs; - return result; -} - -template -DN_BinarySearchResult DN_BinarySearch(T const *array, - DN_USize array_size, - T const &find, - DN_BinarySearchType type, - DN_BinarySearchLessThanProc less_than) -{ - DN_BinarySearchResult result = {}; - if (!array || array_size <= 0 || !less_than) - return result; - - T const *end = array + array_size; - T const *first = array; - T const *last = end; - while (first != last) { - DN_USize count = last - first; - T const *it = first + (count / 2); - - bool advance_first = false; - if (type == DN_BinarySearchType_UpperBound) - advance_first = !less_than(find, it[0]); - else - advance_first = less_than(it[0], find); - - if (advance_first) - first = it + 1; - else - last = it; - } - - switch (type) { - case DN_BinarySearchType_Match: { - result.found = first != end && !less_than(find, *first); - } break; - - case DN_BinarySearchType_LowerBound: /*FALLTHRU*/ - case DN_BinarySearchType_UpperBound: { - result.found = first != end; - } break; - } - - result.index = first - array; - return result; -} -#endif // !defined(DN_HELPERS_H) diff --git a/Source/Extra/dn_json.cpp b/Source/Extra/dn_json.cpp deleted file mode 100644 index 6a78812..0000000 --- a/Source/Extra/dn_json.cpp +++ /dev/null @@ -1,428 +0,0 @@ -#define DN_JSON_CPP - -// NOTE: DN_JSON ////////////////////////////////////////////////////////////////////////////////// -void *DN_JSON_ArenaAllocFunc(void *user_data, size_t count) -{ - void *result = NULL; - if (!user_data) - return result; - - DN_Arena *arena = DN_Cast(DN_Arena*)user_data; - result = DN_ArenaAlloc(arena, count, alignof(json_value_s), DN_ZMem_No); - return result; -} - -char const *DN_JSON_TypeEnumCString(json_type_e type, size_t *size) -{ - switch (type) { - case json_type_string: { if (size) { *size = sizeof("string") - 1; } return "string"; } - case json_type_number: { if (size) { *size = sizeof("number") - 1; } return "number"; } - case json_type_object: { if (size) { *size = sizeof("object") - 1; } return "object"; } - case json_type_array: { if (size) { *size = sizeof("array") - 1; } return "array"; } - case json_type_true: { if (size) { *size = sizeof("true (boolean)") - 1; } return "true (boolean)"; } - case json_type_false: { if (size) { *size = sizeof("false (boolean)") - 1; } return "false (boolean)"; } - - default: /*FALLTHRU*/ - case json_type_null: { if (size) { *size = sizeof("(null)") - 1; } return "(null)"; } - } -} - -bool DN_JSON_String8Cmp(json_string_s const *lhs, DN_Str8 key) -{ - bool result = false; - if (lhs && key.size) { - DN_Str8 lhs_string = DN_Str8FromPtr(lhs->string, lhs->string_size); - result = DN_Str8Eq(lhs_string, key); - } - return result; -} - -// NOTE: DN_JSON_It /////////////////////////////////////////////////////////////////////////////// -DN_JSONIt DN_JSON_LoadFileToIt(DN_Arena *arena, DN_Str8 json) -{ - json_parse_result_s parse_result = {}; - json_value_ex_s *ex_value = - DN_Cast(json_value_ex_s *) json_parse_ex(json.data, - json.size, - json_parse_flags_allow_location_information, - DN_JSON_ArenaAllocFunc, - arena, - &parse_result); - - DN_JSONIt result = {}; - DN_JSON_ItPushValue(&result, &ex_value->value); - return result; -} - -// NOTE: DN_JSON_ItPush/Pop /////////////////////////////////////////////////////////////////////// -bool DN_JSON_ItPushObjElement(DN_JSONIt *it, json_object_element_s *element) -{ - if (!it || !element) - return false; - DN_Assert(it->stack_count < DN_ArrayCountI(it->stack)); - it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeObjElement, element}; - return true; -} - -bool DN_JSON_ItPushObj(DN_JSONIt *it, json_object_s *obj) -{ - if (!it || !obj) - return false; - DN_Assert(it->stack_count < DN_ArrayCountI(it->stack)); - it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeObj, obj}; - return true; -} - -bool DN_JSON_ItPushArrayElement(DN_JSONIt *it, json_array_element_s *element) -{ - if (!it || !element) - return false; - DN_Assert(it->stack_count < DN_ArrayCountI(it->stack)); - it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeArrayElement, element}; - return true; -} - -bool DN_JSON_ItPushArray(DN_JSONIt *it, json_value_s *value) -{ - if (!it || !value || json_value_as_array(value) == nullptr) - return false; - DN_Assert(it->stack_count < DN_ArrayCountI(it->stack)); - it->stack[it->stack_count++] = {DN_JSON_ItEntryTypeArray, value}; - return true; -} - -bool DN_JSON_ItPushValue(DN_JSONIt *it, json_value_s *value) -{ - bool result = false; - if (!it || !value) - return result; - - if (value->type == json_type_object) { - result = DN_JSON_ItPushObj(it, json_value_as_object(value)); - } else if (value->type == json_type_array) { - result = DN_JSON_ItPushArray(it, value); - } - - return result; -} - -void DN_JSON_ItPop(DN_JSONIt *it) -{ - if (!it) - return; - DN_Assert(it->stack_count > 0); - if (it->stack_count > 0) - it->stack_count--; -} - -// NOTE: DN_JSON_It JSON tree navigation ////////////////////////////////////////////////////////// -json_value_s *DN_JSON_ItPushCurrValue(DN_JSONIt *it) -{ - json_value_s *result = nullptr; - DN_JSONItEntry *curr = DN_JSON_ItCurr(it); - if (!curr) - return result; - - if (curr->type == DN_JSON_ItEntryTypeObjElement) { - json_object_element_s *element = DN_Cast(json_object_element_s *) curr->value; - result = element->value; - } else if (curr->type == DN_JSON_ItEntryTypeArrayElement) { - json_array_element_s *element = DN_Cast(json_array_element_s *) curr->value; - result = element->value; - } else { - result = DN_Cast(json_value_s *) curr->value; - } - - if (result->type == json_type_array) { - json_array_s *array = json_value_as_array(result); - DN_Assert(array); - DN_JSON_ItPushArray(it, result); - } else if (result->type == json_type_object) { - json_object_s *obj = json_value_as_object(result); - DN_Assert(obj); - DN_JSON_ItPushObj(it, obj); - } - - return result; -} - -bool DN_JSON_ItNext(DN_JSONIt *it) -{ - DN_JSONItEntry *curr = DN_JSON_ItCurr(it); - if (!curr) - return false; - - json_object_element_s *obj_element = nullptr; - json_array_element_s *array_element = nullptr; - if (curr->type == DN_JSON_ItEntryTypeObj) { - auto *obj = DN_Cast(json_object_s *) curr->value; - obj_element = obj->start; - } else if (curr->type == DN_JSON_ItEntryTypeObjElement) { - auto *element = DN_Cast(json_object_element_s *) curr->value; - obj_element = element->next; - DN_JSON_ItPop(it); - } else if (curr->type == DN_JSON_ItEntryTypeArray) { - auto *value = DN_Cast(json_value_s *) curr->value; - auto *array = json_value_as_array(value); - array_element = array->start; - } else if (curr->type == DN_JSON_ItEntryTypeArrayElement) { - auto *element = DN_Cast(json_array_element_s *) curr->value; - array_element = element->next; - DN_JSON_ItPop(it); - } else { - DN_JSON_ItPop(it); - } - - if (obj_element) - DN_JSON_ItPushObjElement(it, obj_element); - else if (array_element) - DN_JSON_ItPushArrayElement(it, array_element); - - bool result = obj_element || array_element; - return result; -} - -// NOTE: DN_JSON_ItCurr /////////////////////////////////////////////////////////////////////////// -DN_JSONItEntry *DN_JSON_ItCurr(DN_JSONIt *it) -{ - DN_JSONItEntry *result = nullptr; - if (!it || it->stack_count <= 0) - return result; - - result = &it->stack[it->stack_count - 1]; - return result; -} - -json_value_s *DN_JSON_ItCurrValue(DN_JSONIt *it) -{ - json_value_s *result = nullptr; - DN_JSONItEntry *curr = DN_JSON_ItCurr(it); - if (!curr) - return result; - - if (curr->type == DN_JSON_ItEntryTypeObjElement) { - auto *element = DN_Cast(json_object_element_s *)curr->value; - result = element->value; - } else if (curr->type == DN_JSON_ItEntryTypeArrayElement) { - auto *element = DN_Cast(json_array_element_s *)curr->value; - result = element->value; - } else if (curr->type == DN_JSON_ItEntryTypeString || - curr->type == DN_JSON_ItEntryTypeNumber || - curr->type == DN_JSON_ItEntryTypeObj || - curr->type == DN_JSON_ItEntryTypeArray) - { - result = DN_Cast(json_value_s *)curr->value; - } - - return result; -} - -json_object_element_s *DN_JSON_ItCurrObjElement(DN_JSONIt *it) -{ - DN_JSONItEntry *curr = DN_JSON_ItCurr(it); - auto *result = (curr && curr->type == DN_JSON_ItEntryTypeObjElement) - ? DN_Cast(json_object_element_s *) curr->value - : nullptr; - return result; -} - -// NOTE: DN_JSON_ItValueIs //////////////////////////////////////////////////////////////////////// -json_value_s *DN_JSON_ItValueIs(DN_JSONIt *it, json_type_e type) -{ - json_value_s *curr = DN_JSON_ItCurrValue(it); - json_value_s *result = (curr && type == curr->type) ? curr : nullptr; - return result; -} - -json_object_s *DN_JSON_ItValueIsObj(DN_JSONIt *it) -{ - json_value_s *curr = DN_JSON_ItCurrValue(it); - json_object_s *result = curr ? json_value_as_object(curr) : nullptr; - return result; -} - -json_array_s *DN_JSON_ItValueIsArray(DN_JSONIt *it) -{ - json_value_s *curr = DN_JSON_ItCurrValue(it); - json_array_s *result = curr ? json_value_as_array(curr) : nullptr; - return result; -} - -json_string_s *DN_JSON_ItValueIsString(DN_JSONIt *it) -{ - json_value_s *curr = DN_JSON_ItCurrValue(it); - json_string_s *result = curr ? json_value_as_string(curr) : nullptr; - return result; -} - -json_number_s *DN_JSON_ItValueIsNumber(DN_JSONIt *it) -{ - json_value_s *curr = DN_JSON_ItCurrValue(it); - json_number_s *result = curr ? json_value_as_number(curr) : nullptr; - return result; -} - -json_value_s *DN_JSON_ItValueIsBool(DN_JSONIt *it) -{ - json_value_s *curr = DN_JSON_ItCurrValue(it); - json_value_s *result = (curr && (curr->type == json_type_true || curr->type == json_type_false)) ? curr : nullptr; - return result; -} - -json_value_s *DN_JSON_ItValueIsNull(DN_JSONIt *it) -{ - json_value_s *curr = DN_JSON_ItCurrValue(it); - json_value_s *result = (curr && (curr->type == json_type_null)) ? curr : nullptr; - return result; -} - -size_t DN_JSON_ItValueArraySize(DN_JSONIt *it) -{ - size_t result = 0; - if (json_array_s *curr = DN_JSON_ItValueIsArray(it)) - result = curr->length; - return result; -} - - -// NOTE: DN_JSON_ItKeyValueIs ///////////////////////////////////////////////////////////////////// -DN_Str8 DN_JSON_ItKey(DN_JSONIt *it) -{ - json_object_element_s *curr = DN_JSON_ItCurrObjElement(it); - DN_Str8 result = {}; - if (curr) { - result.data = DN_Cast(char *)curr->name->string; - result.size = curr->name->string_size; - } - return result; -} - -bool DN_JSON_ItKeyIs(DN_JSONIt *it, DN_Str8 key) -{ - json_object_element_s *curr = DN_JSON_ItCurrObjElement(it); - bool result = DN_JSON_String8Cmp(curr->name, key); - return result; -} - -json_object_s *DN_JSON_ItKeyValueIsObj(DN_JSONIt *it, DN_Str8 key) -{ - json_object_s *result = nullptr; - json_object_element_s *curr = DN_JSON_ItCurrObjElement(it); - if (curr && DN_JSON_String8Cmp(curr->name, key)) - result = json_value_as_object(curr->value); - return result; -} - -json_array_s *DN_JSON_ItKeyValueIsArray(DN_JSONIt *it, DN_Str8 key) -{ - json_array_s *result = nullptr; - json_object_element_s *curr = DN_JSON_ItCurrObjElement(it); - if (curr && DN_JSON_String8Cmp(curr->name, key)) - result = json_value_as_array(curr->value); - return result; -} - -json_string_s *DN_JSON_ItKeyValueIsString(DN_JSONIt *it, DN_Str8 key) -{ - json_object_element_s *curr = DN_JSON_ItCurrObjElement(it); - json_string_s *result = nullptr; - if (curr && DN_JSON_String8Cmp(curr->name, key)) - result = json_value_as_string(curr->value); - return result; -} - -json_number_s *DN_JSON_ItKeyValueIsNumber(DN_JSONIt *it, DN_Str8 key) -{ - json_object_element_s *curr = DN_JSON_ItCurrObjElement(it); - json_number_s *result = nullptr; - if (curr && DN_JSON_String8Cmp(curr->name, key)) - result = json_value_as_number(curr->value); - return result; -} - -json_value_s *DN_JSON_ItKeyValueIsBool(DN_JSONIt *it, DN_Str8 key) -{ - json_object_element_s *curr = DN_JSON_ItCurrObjElement(it); - json_value_s *result = nullptr; - if (curr && DN_JSON_String8Cmp(curr->name, key)) - result = curr->value->type == json_type_true || curr->value->type == json_type_false ? curr->value : nullptr; - return result; -} - -json_value_s *DN_JSON_ItKeyValueIsNull(DN_JSONIt *it, DN_Str8 key) -{ - json_object_element_s *curr = DN_JSON_ItCurrObjElement(it); - json_value_s *result = nullptr; - if (curr && DN_JSON_String8Cmp(curr->name, key)) - result = curr->value->type == json_type_null ? curr->value : nullptr; - return result; -} - - -// NOTE: DN_JSON_ItValueTo //////////////////////////////////////////////////////////////////////// -DN_Str8 DN_JSON_ItValueToString(DN_JSONIt *it) -{ - DN_Str8 result = {}; - if (json_string_s *curr = DN_JSON_ItValueIsString(it)) - result = DN_Str8FromPtr(curr->string, curr->string_size); - return result; -} - -int64_t DN_JSON_ItValueToI64(DN_JSONIt *it) -{ - int64_t result = {}; - if (json_number_s *curr = DN_JSON_ItValueIsNumber(it)) - result = DN_I64FromStr8(DN_Str8FromPtr(curr->number, curr->number_size), 0 /*separator*/).value; - return result; -} - -uint64_t DN_JSON_ItValueToU64(DN_JSONIt *it) -{ - uint64_t result = {}; - if (json_number_s *curr = DN_JSON_ItValueIsNumber(it)) - result = DN_U64FromStr8(DN_Str8FromPtr(curr->number, curr->number_size), 0 /*separator*/).value; - return result; -} - -bool DN_JSON_ItValueToBool(DN_JSONIt *it) -{ - bool result = {}; - if (json_value_s *curr = DN_JSON_ItValueIsBool(it)) - result = curr->type == json_type_true; - return result; -} - -void DN_JSON_ItErrorUnknownKeyValue_(DN_JSONIt *it, DN_CallSite call_site) -{ - if (!it) - return; - - json_object_element_s const *curr = DN_JSON_ItCurrObjElement(it); - if (!curr) - return; - - size_t value_type_size = 0; - char const *value_type = DN_JSON_TypeEnumCString(DN_Cast(json_type_e)curr->value->type, &value_type_size); - - json_string_s const *key = curr->name; - if (it->flags & json_parse_flags_allow_location_information) { - json_string_ex_s const *info = DN_Cast(json_string_ex_s const *)key; - DN_LogEmitFromType(DN_LogMakeU32LogTypeParam(DN_LogType_Warning), - call_site, - "Unknown key-value pair in object [loc=%zu:%zu, key=%.*s, value=%.*s]", - info->line_no, - info->row_no, - DN_Cast(int) key->string_size, - key->string, - DN_Cast(int) value_type_size, - value_type); - } else { - DN_LogEmitFromType(DN_LogMakeU32LogTypeParam(DN_LogType_Warning), - call_site, - "Unknown key-value pair in object [key=%.*s, value=%.*s]", - DN_Cast(int) key->string_size, - key->string, - DN_Cast(int) value_type_size, - value_type); - } -} diff --git a/Source/Extra/dn_json.h b/Source/Extra/dn_json.h deleted file mode 100644 index 1021a47..0000000 --- a/Source/Extra/dn_json.h +++ /dev/null @@ -1,96 +0,0 @@ -#if !defined(DN_JSON_H) -#define DN_JSON_H - -#if defined(_CLANGD) - #include "../dn.h" - #include "../External/json.h" -#endif - -#if !defined(SHEREDOM_JSON_H_INCLUDED) - #error Sheredom json.h (github.com/sheredom/json.h) must be included before this file -#endif - -// NOTE: DN_JSON - -void *DN_JSON_ArenaAllocFunc (void *user_data, size_t count); -char const *DN_JSON_TypeEnumCString(json_type_e type, size_t *size); -bool DN_JSON_String8Cmp (json_string_s const *lhs, DN_Str8 rhs); - -// NOTE: DN_JSON_It -enum DN_JSONItEntryType -{ - DN_JSON_ItEntryTypeObjElement, - DN_JSON_ItEntryTypeObj, - DN_JSON_ItEntryTypeArrayElement, - DN_JSON_ItEntryTypeArray, - DN_JSON_ItEntryTypeString, - DN_JSON_ItEntryTypeNumber, -}; - -struct DN_JSONItEntry -{ - DN_JSONItEntryType type; - void *value; -}; - -struct DN_JSONIt -{ - DN_JSONItEntry stack[128]; - int stack_count; - size_t flags; -}; - -DN_JSONIt DN_JSON_LoadFileToIt(DN_Arena *arena, DN_Str8 json); - -// NOTE: DN_JSON_ItPush/Pop -bool DN_JSON_ItPushObjElement (DN_JSONIt *it, json_object_element_s *element); -bool DN_JSON_ItPushObj (DN_JSONIt *it, json_object_s *obj); -bool DN_JSON_ItPushArrayElement(DN_JSONIt *it, json_array_element_s *element); -bool DN_JSON_ItPushArray (DN_JSONIt *it, json_value_s *value); -bool DN_JSON_ItPushValue (DN_JSONIt *it, json_value_s *value); -void DN_JSON_ItPop (DN_JSONIt *it); - -// NOTE: DN_JSON_It tree navigation -json_value_s *DN_JSON_ItPushCurrValue(DN_JSONIt *it); -bool DN_JSON_ItNext(DN_JSONIt *it); - -#define DN_JSON_ItPushCurrValueIterateThenPop(it) \ - for(void *DN_UniqueName(ptr) = DN_JSON_ItPushCurrValue(it); DN_UniqueName(ptr); DN_JSON_ItPop(it), DN_UniqueName(ptr) = nullptr) \ - while (DN_JSON_ItNext(it)) - -// NOTE: DN_JSON_ItCurr -DN_JSONItEntry *DN_JSON_ItCurr(DN_JSONIt *it); -json_value_s *DN_JSON_ItCurrValue(DN_JSONIt *it); -json_object_element_s *DN_JSON_ItCurrObjElement(DN_JSONIt *it); - -// NOTE: DN_JSON_ItValueIs -json_value_s *DN_JSON_ItValueIs(DN_JSONIt *it, json_type_e type); -json_object_s *DN_JSON_ItValueIsObj(DN_JSONIt *it); -json_array_s *DN_JSON_ItValueIsArray(DN_JSONIt *it); -json_string_s *DN_JSON_ItValueIsString(DN_JSONIt *it); -json_number_s *DN_JSON_ItValueIsNumber(DN_JSONIt *it); -json_value_s *DN_JSON_ItValueIsBool(DN_JSONIt *it); -json_value_s *DN_JSON_ItValueIsNull(DN_JSONIt *it); - -size_t DN_JSON_ItValueArraySize(DN_JSONIt *it); - -// NOTE: DN_JSON_ItKeyValueIs -DN_Str8 DN_JSON_ItKey(DN_JSONIt *it); -bool DN_JSON_ItKeyIs(DN_JSONIt *it, DN_Str8 key); -json_object_s *DN_JSON_ItKeyValueIsObj(DN_JSONIt *it, DN_Str8 key); -json_array_s *DN_JSON_ItKeyValueIsArray(DN_JSONIt *it, DN_Str8 key); -json_string_s *DN_JSON_ItKeyValueIsString(DN_JSONIt *it, DN_Str8 key); -json_number_s *DN_JSON_ItKeyValueIsNumber(DN_JSONIt *it, DN_Str8 key); -json_value_s *DN_JSON_ItKeyValueIsBool(DN_JSONIt *it, DN_Str8 key); -json_value_s *DN_JSON_ItKeyValueIsNull(DN_JSONIt *it, DN_Str8 key); - -// NOTE: DN_JSON_ItValueTo -DN_Str8 DN_JSON_ItValueToString(DN_JSONIt *it); -int64_t DN_JSON_ItValueToI64(DN_JSONIt *it); -uint64_t DN_JSON_ItValueToU64(DN_JSONIt *it); -bool DN_JSON_ItValueToBool(DN_JSONIt *it); - -#define DN_JSON_ItErrorUnknownKeyValue(it) DN_JSON_ItErrorUnknownKeyValue_(it, DN_CALL_SITE) -void DN_JSON_ItErrorUnknownKeyValue_(DN_JSONIt *it, DN_CallSite call_site); - -#endif // !defined(DN_JSON_H) diff --git a/Source/Extra/dn_tests.cpp b/Source/Extra/dn_tests.cpp index 5e68f37..affc480 100644 --- a/Source/Extra/dn_tests.cpp +++ b/Source/Extra/dn_tests.cpp @@ -1342,90 +1342,6 @@ static DN_UTCore DN_TST_BaseArray() return result; } -static DN_UTCore DN_TST_BaseVArray() -{ - DN_UTCore result = DN_UT_Init(); - DN_UT_LogF(&result, "DN_VArray\n"); - { - { - DN_VArray array = DN_OS_VArrayInitByteSize(DN_Kilobytes(64)); - DN_DEFER - { - DN_OS_VArrayDeinit(&array); - }; - - for (DN_UT_Test(&result, "Test adding an array of items to the array")) { - DN_U32 array_literal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - DN_OS_VArrayAddArray(&array, array_literal, DN_ArrayCountU(array_literal)); - DN_UT_Assert(&result, array.size == DN_ArrayCountU(array_literal)); - DN_UT_Assert(&result, DN_Memcmp(array.data, array_literal, DN_ArrayCountU(array_literal) * sizeof(array_literal[0])) == 0); - } - - for (DN_UT_Test(&result, "Test adding an array of items")) { - DN_U32 array_literal[] = {0, 1, 2, 3}; - DN_OS_VArrayAddArray(&array, array_literal, DN_ArrayCountU(array_literal)); - - DN_U32 expected_literal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3}; - DN_UT_Assert(&result, array.size == DN_ArrayCountU(expected_literal)); - DN_UT_Assert(&result, DN_Memcmp(array.data, expected_literal, DN_ArrayCountU(expected_literal) * sizeof(expected_literal[0])) == 0); - } - } - - for (DN_UT_Test(&result, "Array of unaligned objects are contiguously laid out in memory")) { - // NOTE: Since we allocate from a virtual memory block, each time - // we request memory from the block we can demand some alignment - // on the returned pointer from the memory block. If there's - // additional alignment done in that function then we can no - // longer access the items in the array contiguously leading to - // confusing memory "corruption" errors. - // - // This result makes sure that the unaligned objects are allocated - // from the memory block (and hence the array) contiguously - // when the size of the object is not aligned with the required - // alignment of the object. - DN_MSVC_WARNING_PUSH - DN_MSVC_WARNING_DISABLE(4324) // warning C4324: 'TestVArray::UnalignedObject': structure was padded due to alignment specifier - - struct alignas(8) UnalignedObject - { - char data[511]; - }; - - DN_MSVC_WARNING_POP - - DN_VArray array = DN_OS_VArrayInitByteSize(DN_Kilobytes(64)); - DN_DEFER - { - DN_OS_VArrayDeinit(&array); - }; - - // NOTE: Verify that the items returned from the data array are - // contiguous in memory. - UnalignedObject *make_item_a = DN_OS_VArrayMakeArray(&array, 1, DN_ZMem_Yes); - UnalignedObject *make_item_b = DN_OS_VArrayMakeArray(&array, 1, DN_ZMem_Yes); - DN_Memset(make_item_a->data, 'a', sizeof(make_item_a->data)); - DN_Memset(make_item_b->data, 'b', sizeof(make_item_b->data)); - DN_UT_Assert(&result, (uintptr_t)make_item_b == (uintptr_t)(make_item_a + 1)); - - // NOTE: Verify that accessing the items from the data array yield - // the same object. - DN_UT_Assert(&result, array.size == 2); - UnalignedObject *data_item_a = array.data + 0; - UnalignedObject *data_item_b = array.data + 1; - DN_UT_Assert(&result, (uintptr_t)data_item_b == (uintptr_t)(data_item_a + 1)); - DN_UT_Assert(&result, (uintptr_t)data_item_b == (uintptr_t)(make_item_a + 1)); - DN_UT_Assert(&result, (uintptr_t)data_item_b == (uintptr_t)make_item_b); - - for (DN_USize i = 0; i < sizeof(data_item_a->data); i++) - DN_UT_Assert(&result, data_item_a->data[i] == 'a'); - - for (DN_USize i = 0; i < sizeof(data_item_b->data); i++) - DN_UT_Assert(&result, data_item_b->data[i] == 'b'); - } - } - return result; -} - #if defined(DN_UNIT_TESTS_WITH_KECCAK) DN_GCC_WARNING_PUSH DN_GCC_WARNING_DISABLE(-Wunused-parameter) @@ -2695,7 +2611,6 @@ DN_TSTResult DN_TST_RunSuite(DN_TSTPrint print) DN_TST_BaseDSMap(), DN_TST_BaseIArray(), DN_TST_BaseArray(), - DN_TST_BaseVArray(), DN_TST_Keccak(), DN_TST_M4(), DN_TST_OS(), diff --git a/Source/Extra/dn_type_info.cpp b/Source/Extra/dn_type_info.cpp deleted file mode 100644 index f649cd7..0000000 --- a/Source/Extra/dn_type_info.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#define DN_TYPE_INFO_CPP - -#if defined(_CLANGD) - #include "dn_type_info.h" -#endif - -/* -//////////////////////////////////////////////////////////////////////////////////////////////////// -// -// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\ -// \__$$ __|\$$\ $$ |$$ __$$\ $$ _____| \_$$ _|$$$\ $$ |$$ _____|$$ __$$\ -// $$ | \$$\ $$ / $$ | $$ |$$ | $$ | $$$$\ $$ |$$ | $$ / $$ | -// $$ | \$$$$ / $$$$$$$ |$$$$$\ $$ | $$ $$\$$ |$$$$$\ $$ | $$ | -// $$ | \$$ / $$ ____/ $$ __| $$ | $$ \$$$$ |$$ __| $$ | $$ | -// $$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ | $$ | -// $$ | $$ | $$ | $$$$$$$$\ $$$$$$\ $$ | \$$ |$$ | $$$$$$ | -// \__| \__| \__| \________| \______|\__| \__|\__| \______/ -// -// dn_type_info.cpp -// -//////////////////////////////////////////////////////////////////////////////////////////////////// -*/ - -DN_TypeGetField DN_Type_GetField(DN_TypeInfo const *type_info, DN_Str8 name) -{ - DN_TypeGetField result = {}; - for (DN_USize index = 0; index < type_info->fields_count; index++) { - DN_TypeField const *type_field = type_info->fields + index; - if (DN_Str8Eq(type_field->name, name)) { - result.success = true; - result.index = index; - result.field = DN_Cast(DN_TypeField *)type_field; - break; - } - } - return result; -} diff --git a/Source/Extra/dn_type_info.h b/Source/Extra/dn_type_info.h deleted file mode 100644 index fe72119..0000000 --- a/Source/Extra/dn_type_info.h +++ /dev/null @@ -1,68 +0,0 @@ -#if !defined(DN_TYPE_INFO_H) -#define DN_TYPE_INFO_H - -#if defined(_CLANGD) - #include "../dn.h" -#endif - -/* -//////////////////////////////////////////////////////////////////////////////////////////////////// -// -// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\ -// \__$$ __|\$$\ $$ |$$ __$$\ $$ _____| \_$$ _|$$$\ $$ |$$ _____|$$ __$$\ -// $$ | \$$\ $$ / $$ | $$ |$$ | $$ | $$$$\ $$ |$$ | $$ / $$ | -// $$ | \$$$$ / $$$$$$$ |$$$$$\ $$ | $$ $$\$$ |$$$$$\ $$ | $$ | -// $$ | \$$ / $$ ____/ $$ __| $$ | $$ \$$$$ |$$ __| $$ | $$ | -// $$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ | $$ | -// $$ | $$ | $$ | $$$$$$$$\ $$$$$$\ $$ | \$$ |$$ | $$$$$$ | -// \__| \__| \__| \________| \______|\__| \__|\__| \______/ -// -// dn_type_info.h -- C++ type introspection -// -//////////////////////////////////////////////////////////////////////////////////////////////////// -*/ - -enum DN_TypeKind -{ - DN_TypeKind_Nil, - DN_TypeKind_Basic, - DN_TypeKind_Enum, - DN_TypeKind_Struct, -}; - -struct DN_TypeField -{ - uint16_t index; - DN_Str8 name; - DN_Str8 label; - DN_ISize value; - DN_USize offset_of; - DN_USize size_of; - DN_USize align_of; - DN_Str8 type_decl; - uint32_t type_enum; - bool is_pointer; - uint16_t array_size; - DN_TypeField const *array_size_field; -}; - -struct DN_TypeInfo -{ - DN_Str8 name; - DN_TypeKind kind; - DN_USize size_of; - DN_TypeField const *fields; - uint16_t fields_count; -}; - -struct DN_TypeGetField -{ - bool success; - DN_USize index; - DN_TypeField *field; -}; - -DN_TypeGetField DN_Type_GetField(DN_TypeInfo const *type_info, DN_Str8 name); - -#endif // !defined(DN_TYPE_INFO_H) - diff --git a/Source/OS/dn_os.cpp b/Source/OS/dn_os.cpp index c4916e9..2cfe3d2 100644 --- a/Source/OS/dn_os.cpp +++ b/Source/OS/dn_os.cpp @@ -883,27 +883,122 @@ DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip(DN_OSThreadLane lane) return result; } -// NOTE: DN_OSHttp -DN_API void DN_OS_HttpRequestWait(DN_OSHttpResponse *response) +static DN_I32 DN_OS_AsyncThreadEntryPoint_(DN_OSThread *thread) { - if (response && response->on_complete_semaphore.handle != 0) - DN_OS_SemaphoreWait(&response->on_complete_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT); + DN_OS_ThreadSetNameFmt("%.*s", DN_Str8PrintFmt(thread->name)); + DN_OSAsyncCore *async = DN_Cast(DN_OSAsyncCore *) thread->user_context; + DN_Ring *ring = &async->ring; + for (;;) { + DN_OS_SemaphoreWait(&async->worker_sem, UINT32_MAX); + if (async->join_threads) + break; + + DN_OSAsyncTask task = {}; + for (DN_OS_MutexScope(&async->ring_mutex)) { + if (DN_RingHasData(ring, sizeof(task))) + DN_RingRead(ring, &task, sizeof(task)); + } + + if (task.work.func) { + DN_OS_ConditionVariableBroadcast(&async->ring_write_cv); // Resume any blocked ring write(s) + + DN_OSAsyncWorkArgs args = {}; + args.input = task.work.input; + args.thread = thread; + + DN_AtomicAddU32(&async->busy_threads, 1); + task.work.func(args); + DN_AtomicSubU32(&async->busy_threads, 1); + + if (task.completion_sem.handle != 0) + DN_OS_SemaphoreIncrement(&task.completion_sem, 1); + } + } + + return 0; } -DN_API DN_OSHttpResponse DN_OS_HttpRequest(DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers) +DN_API void DN_OS_AsyncInit(DN_OSAsyncCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size) { - // TODO(doyle): Revise the memory allocation and its lifetime - DN_OSHttpResponse result = {}; - DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1); - result.scratch_arena = scratch.arena; + DN_Assert(async); + async->ring.size = base_size; + async->ring.base = base; + async->ring_mutex = DN_OS_MutexInit(); + async->ring_write_cv = DN_OS_ConditionVariableInit(); + async->worker_sem = DN_OS_SemaphoreInit(0); + async->thread_count = threads_size; + async->threads = threads; + for (DN_ForIndexU(index, async->thread_count)) { + DN_OSThread *thread = async->threads + index; + DN_OS_ThreadInit(thread, DN_OS_AsyncThreadEntryPoint_, /*lane=*/ nullptr, DN_TCInitArgsDefault(), async); + } +} + +DN_API void DN_OS_AsyncDeinit(DN_OSAsyncCore *async) +{ + DN_Assert(async); + DN_AtomicSetValue32(&async->join_threads, true); + DN_OS_SemaphoreIncrement(&async->worker_sem, async->thread_count); + for (DN_ForItSize(it, DN_OSThread, async->threads, async->thread_count)) + DN_OS_ThreadJoin(it.data, DN_TCDeinitArenas_Yes); +} + +static bool DN_OS_AsyncQueueTask_(DN_OSAsyncCore *async, DN_OSAsyncTask const *task, DN_U64 wait_time_ms) { + DN_U64 end_time_ms = DN_OS_DateUnixTimeMs() + wait_time_ms; + bool result = false; + for (DN_OS_MutexScope(&async->ring_mutex)) { + for (;;) { + if (DN_RingHasSpace(&async->ring, sizeof(*task))) { + DN_RingWriteStruct(&async->ring, task); + result = true; + break; + } + DN_OS_ConditionVariableWaitUntil(&async->ring_write_cv, &async->ring_mutex, end_time_ms); + if (DN_OS_DateUnixTimeMs() >= end_time_ms) + break; + } + } + + if (result) + DN_OS_SemaphoreIncrement(&async->worker_sem, 1); // Flag that a job is available - DN_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers); - DN_OS_HttpRequestWait(&result); - DN_TCScratchEnd(&scratch); return result; } -// NOTE: DN_OSPrint +DN_API bool DN_OS_AsyncQueueWork(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms) +{ + DN_OSAsyncTask task = {}; + task.work.func = func; + task.work.input = input; + bool result = DN_OS_AsyncQueueTask_(async, &task, wait_time_ms); + return result; +} + +DN_API DN_OSAsyncTask DN_OS_AsyncQueueTask(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms) +{ + DN_OSAsyncTask result = {}; + result.work.func = func; + result.work.input = input; + result.completion_sem = DN_OS_SemaphoreInit(0); + result.queued = DN_OS_AsyncQueueTask_(async, &result, wait_time_ms); + if (!result.queued) + DN_OS_SemaphoreDeinit(&result.completion_sem); + return result; +} + +DN_API bool DN_OS_AsyncWaitTask(DN_OSAsyncTask *task, DN_U32 timeout_ms) +{ + bool result = true; + if (!task->queued) + return result; + + DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&task->completion_sem, timeout_ms); + result = wait == DN_OSSemaphoreWaitResult_Success; + if (result) + DN_OS_SemaphoreDeinit(&task->completion_sem); + return result; +} + DN_API DN_LogStyle DN_OS_PrintStyleColour(uint8_t r, uint8_t g, uint8_t b, DN_LogBold bold) { DN_LogStyle result = {}; @@ -1077,173 +1172,6 @@ DN_API void DN_OS_PrintLnFVStyle(DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ DN_OS_Print(dest, DN_Str8Lit("\n")); } -// NOTE: DN_VArray -template -DN_VArray DN_OS_VArrayInitByteSize(DN_USize byte_size) -{ - DN_VArray result = {}; - result.data = DN_Cast(T *) DN_OS_MemReserve(byte_size, DN_MemCommit_No, DN_MemPage_ReadWrite); - if (result.data) - result.max = byte_size / sizeof(T); - return result; -} - -template -DN_VArray DN_OS_VArrayInit(DN_USize max) -{ - DN_VArray result = DN_OS_VArrayInitByteSize(max * sizeof(T)); - DN_Assert(result.max >= max); - return result; -} - -template -DN_VArray DN_OS_VArrayInitCArray(T const (&items)[N], DN_USize max) -{ - DN_USize real_max = DN_Max(N, max); - DN_VArray result = DN_OS_VArrayInit(real_max); - if (DN_OS_VArrayIsValid(&result)) - DN_OS_VArrayAddArray(&result, items, N); - return result; -} - -template -void DN_OS_VArrayDeinit(DN_VArray *array) -{ - DN_OS_MemRelease(array->data, array->max * sizeof(T)); - *array = {}; -} - -template -bool DN_OS_VArrayIsValid(DN_VArray const *array) -{ - bool result = array->data && array->size <= array->max; - return result; -} - -template -T *DN_OS_VArrayAddArray(DN_VArray *array, T const *items, DN_USize count) -{ - T *result = DN_OS_VArrayMakeArray(array, count, DN_ZMem_No); - if (result) - DN_Memcpy(result, items, count * sizeof(T)); - return result; -} - -template -T *DN_OS_VArrayAddCArray(DN_VArray *array, T const (&items)[N]) -{ - T *result = DN_OS_VArrayAddArray(array, items, N); - return result; -} - -template -T *DN_OS_VArrayAdd(DN_VArray *array, T const &item) -{ - T *result = DN_OS_VArrayAddArray(array, &item, 1); - return result; -} - -template -T *DN_OS_VArrayMakeArray(DN_VArray *array, DN_USize count, DN_ZMem z_mem) -{ - if (!DN_OS_VArrayIsValid(array)) - return nullptr; - - if (!DN_CheckF((array->size + count) < array->max, "Array is out of space (user requested +%zu items, array has %zu/%zu items)", count, array->size, array->max)) - return nullptr; - - if (!DN_OS_VArrayReserve(array, count)) - return nullptr; - - // TODO: Use placement new - T *result = array->data + array->size; - array->size += count; - if (z_mem == DN_ZMem_Yes) - DN_Memset(result, 0, count * sizeof(T)); - return result; -} - -template -T *DN_OS_VArrayMake(DN_VArray *array, DN_ZMem z_mem) -{ - T *result = DN_OS_VArrayMakeArray(array, 1, z_mem); - return result; -} - -template -T *DN_OS_VArrayInsertArray(DN_VArray *array, DN_USize index, T const *items, DN_USize count) -{ - T *result = nullptr; - if (!DN_OS_VArrayIsValid(array)) - return result; - if (DN_OS_VArrayReserve(array, array->size + count)) - result = DN_ArrayInsertArray(array->data, &array->size, array->max, index, items, count); - return result; -} - -template -T *DN_OS_VArrayInsertCArray(DN_VArray *array, DN_USize index, T const (&items)[N]) -{ - T *result = DN_OS_VArrayInsertArray(array, index, items, N); - return result; -} - -template -T *DN_OS_VArrayInsert(DN_VArray *array, DN_USize index, T const &item) -{ - T *result = DN_OS_VArrayInsertArray(array, index, &item, 1); - return result; -} - -template -T *DN_OS_VArrayPopFront(DN_VArray *array, DN_USize count) -{ - T *result = DN_Cast(T *)DN_ArrayPopFront(array->data, &array->size, sizeof(T), count); - return result; -} - -template -T *DN_OS_VArrayPopBack(DN_VArray *array, DN_USize count) -{ - T *result = DN_Cast(T *)DN_ArrayPopBack(array->data, &array->size, sizeof(T), count); - return result; -} - -template -DN_ArrayEraseResult DN_OS_VArrayEraseRange(DN_VArray *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase) -{ - DN_ArrayEraseResult result = {}; - if (!DN_OS_VArrayIsValid(array)) - return result; - result = DN_ArrayEraseRange(array->data, &array->size, sizeof(T), begin_index, count, erase); - return result; -} - -template -void DN_OS_VArrayClear(DN_VArray *array, DN_ZMem z_mem) -{ - if (array) { - if (z_mem == DN_ZMem_Yes) - DN_Memset(array->data, 0, array->size * sizeof(T)); - array->size = 0; - } -} - -template -bool DN_OS_VArrayReserve(DN_VArray *array, DN_USize count) -{ - if (!DN_OS_VArrayIsValid(array) || count == 0) - return false; - - DN_USize real_commit = (array->size + count) * sizeof(T); - DN_USize aligned_commit = DN_AlignUpPowerOfTwo(real_commit, DN_Get()->os.page_size); - if (array->commit >= aligned_commit) - return true; - bool result = DN_OS_MemCommit(array->data, aligned_commit, DN_MemPage_ReadWrite); - array->commit = aligned_commit; - return result; -} - DN_API DN_StackTrace DN_StackTraceFromAllocator(DN_Allocator allocator, DN_U16 limit) { DN_StackTrace result = {}; diff --git a/Source/OS/dn_os.h b/Source/OS/dn_os.h index 8366ef5..2b49b4e 100644 --- a/Source/OS/dn_os.h +++ b/Source/OS/dn_os.h @@ -31,10 +31,6 @@ #endif #endif -#if defined(DN_PLATFORM_EMSCRIPTEN) - #include // emscripten_fetch (for DN_OSHttpResponse) -#endif - extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count]; struct DN_OSTimer /// Record time between two time-points using the OS's performance counter. @@ -229,42 +225,6 @@ struct DN_OSThread DN_TCInitArgs tc_init_args; }; -// NOTE: DN_OSHttp -enum DN_OSHttpRequestSecure -{ - DN_OSHttpRequestSecure_No, - DN_OSHttpRequestSecure_Yes, -}; - -struct DN_OSHttpResponse -{ - // NOTE: Response data - DN_U32 error_code; - DN_Str8 error_msg; - DN_U16 http_status; - DN_Str8 body; - DN_B32 done; - - // NOTE: Book-keeping - DN_Arena *arena; // Allocates memory for the response - - // NOTE: Async book-keeping - // Synchronous HTTP response uses the TLS scratch arena whereas async - // calls use their own dedicated arena. - DN_Arena tmp_arena; - DN_Arena scratch_arena; - DN_Str8Builder builder; - DN_OSSemaphore on_complete_semaphore; - - #if defined(DN_PLATFORM_EMSCRIPTEN) - emscripten_fetch_t *em_handle; - #elif defined(DN_PLATFORM_WIN32) - HINTERNET w32_request_session; - HINTERNET w32_request_connection; - HINTERNET w32_request_handle; - #endif -}; - struct DN_OSCore { DN_CPUReport cpu_report; @@ -509,10 +469,52 @@ DN_API DN_OSThreadLane* DN_OS_TCThreadLane () DN_API void DN_OS_TCThreadLaneSync (void **ptr_to_share); DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip (DN_OSThreadLane lane); -DN_API void DN_OS_HttpRequestAsync (DN_OSHttpResponse *response, DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers); -DN_API void DN_OS_HttpRequestWait (DN_OSHttpResponse *response); -DN_API void DN_OS_HttpRequestFree (DN_OSHttpResponse *response); -DN_API DN_OSHttpResponse DN_OS_HttpRequest (DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers); +enum DN_OSAsyncPriority +{ + DN_OSAsyncPriority_Low, + DN_OSAsyncPriority_High, + DN_OSAsyncPriority_Count, +}; + +struct DN_OSAsyncCore +{ + DN_OSMutex ring_mutex; + DN_OSConditionVariable ring_write_cv; + DN_OSSemaphore worker_sem; + DN_Ring ring; + DN_OSThread *threads; + DN_U32 thread_count; + DN_U32 busy_threads; + DN_U32 join_threads; +}; + +struct DN_OSAsyncWorkArgs +{ + DN_OSThread *thread; + void *input; +}; + +typedef void(DN_OSAsyncWorkFunc)(DN_OSAsyncWorkArgs work_args); + +struct DN_OSAsyncWork +{ + DN_OSAsyncWorkFunc *func; + void *input; + void *output; +}; + +struct DN_OSAsyncTask +{ + bool queued; + DN_OSAsyncWork work; + DN_OSSemaphore completion_sem; +}; + +DN_API void DN_OS_AsyncInit (DN_OSAsyncCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size); +DN_API void DN_OS_AsyncDeinit (DN_OSAsyncCore *async); +DN_API bool DN_OS_AsyncQueueWork(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms); +DN_API DN_OSAsyncTask DN_OS_AsyncQueueTask(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms); +DN_API bool DN_OS_AsyncWaitTask (DN_OSAsyncTask *task, DN_U32 timeout_ms); // NOTE: DN_OSPrint enum DN_OSPrintDest @@ -570,46 +572,4 @@ DN_API void DN_OS_PrintLnFV (DN_OSPrintDest dest, DN_FMT_ATTRIB char DN_API void DN_OS_PrintLnStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_Str8 string); DN_API void DN_OS_PrintLnFStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, ...); DN_API void DN_OS_PrintLnFVStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, va_list args); - -// NOTE: DN_VArray -// TODO(doyle): Add an API for shrinking the array by decomitting pages back to the OS. -template struct DN_VArray -{ - T *data; // Pointer to the start of the array items in the block of memory - DN_USize size; // Number of items currently in the array - DN_USize max; // Maximum number of items this array can store - DN_USize commit; // Bytes committed - - T *begin() { return data; } - T *end () { return data + size; } - T const *begin() const { return data; } - T const *end () const { return data + size; } -}; - -template DN_VArray DN_OS_VArrayInitByteSize (DN_USize byte_size); -template DN_VArray DN_OS_VArrayInit (DN_USize max); -template DN_VArray DN_OS_VArrayInitCArray (T const (&items)[N], DN_USize max); -template void DN_OS_VArrayDeinit (DN_VArray *array); -template bool DN_OS_VArrayIsValid (DN_VArray const *array); -template bool DN_OS_VArrayReserve (DN_VArray *array, DN_USize count); -template T * DN_OS_VArrayAddArray (DN_VArray *array, T const *items, DN_USize count); -template T * DN_OS_VArrayAddCArray (DN_VArray *array, T const (&items)[N]); -template T * DN_OS_VArrayAdd (DN_VArray *array, T const &item); -#define DN_OS_VArrayAddArrayAssert(...) DN_HardAssert(DN_OS_VArrayAddArray(__VA_ARGS__)) -#define DN_OS_VArrayAddCArrayAssert(...) DN_HardAssert(DN_OS_VArrayAddCArray(__VA_ARGS__)) -#define DN_OS_VArrayAddAssert(...) DN_HardAssert(DN_OS_VArrayAdd(__VA_ARGS__)) -template T * DN_OS_VArrayMakeArray (DN_VArray *array, DN_USize count, DN_ZMem z_mem); -template T * DN_OS_VArrayMake (DN_VArray *array, DN_ZMem z_mem); -#define DN_OS_VArrayMakeArrayAssert(...) DN_HardAssert(DN_OS_VArrayMakeArray(__VA_ARGS__)) -#define DN_OS_VArrayMakeAssert(...) DN_HardAssert(DN_OS_VArrayMake(__VA_ARGS__)) -template T * DN_OS_VArrayInsertArray (DN_VArray *array, DN_USize index, T const *items, DN_USize count); -template T * DN_OS_VArrayInsertCArray (DN_VArray *array, DN_USize index, T const (&items)[N]); -template T * DN_OS_VArrayInsert (DN_VArray *array, DN_USize index, T const &item); -#define DN_OS_VArrayInsertArrayAssert(...) DN_HardAssert(DN_OS_VArrayInsertArray(__VA_ARGS__)) -#define DN_OS_VArrayInsertCArrayAssert(...) DN_HardAssert(DN_OS_VArrayInsertCArray(__VA_ARGS__)) -#define DN_OS_VArrayInsertAssert(...) DN_HardAssert(DN_OS_VArrayInsert(__VA_ARGS__)) -template T DN_OS_VArrayPopFront (DN_VArray *array, DN_USize count); -template T DN_OS_VArrayPopBack (DN_VArray *array, DN_USize count); -template DN_ArrayEraseResult DN_OS_VArrayEraseRange (DN_VArray *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase); -template void DN_OS_VArrayClear (DN_VArray *array, DN_ZMem z_mem); #endif // !defined(DN_OS_H) diff --git a/Source/OS/dn_os_posix.cpp b/Source/OS/dn_os_posix.cpp index cd8967f..2bb3ed9 100644 --- a/Source/OS/dn_os_posix.cpp +++ b/Source/OS/dn_os_posix.cpp @@ -1444,154 +1444,3 @@ DN_API DN_OSPosixProcSelfStatus DN_OS_PosixProcSelfStatus() DN_OS_FileClose(&file); return result; } - -// NOTE: DN_OSHttp ///////////////////////////////////////////////////////////////////////////////// -#if 0 // TODO(doyle): Implement websockets for Windows and Emscripten -static EM_BOOL EMWebSocketOnOpenCallback(int type, const EmscriptenWebSocketOpenEvent *event, void *user_context) -{ - (void)user_context; - (void)type; - (void)event; - // EMSCRIPTEN_RESULT result = emscripten_websocket_send_utf8_text(event->socket, R"({"jsonrpc":"2.0","id":1,"method": "eth_subscribe","params":["newHeads"]})"); - // if (result) - // DN_LogInfoF("Failed to emscripten_websocket_send_utf8_text(): %d\n", result); - return EM_TRUE; -} - -static EM_BOOL EMWebSocketOnMsgCallback(int type, const EmscriptenWebSocketMessageEvent *event __attribute__((nonnull)), void *user_context) -{ - (void)type; - (void)user_context; - (void)event; - if (event->isText) { - DN_LogInfoF("Received: %.*s", event->numBytes, event->data); - } else { - DN_LogInfoF("Received: %d bytes", event->numBytes); - } - return EM_TRUE; -} - -static EM_BOOL EMWebSocketOnErrorCallback(int type, const EmscriptenWebSocketErrorEvent *event, void *user_context) -{ - (void)user_context; - (void)type; - (void)event; - return EM_TRUE; -} - -static EM_BOOL EMWebSocketOnCloseCallback(int type, const EmscriptenWebSocketCloseEvent *event, void *user_context) -{ - (void)user_context; - (void)type; - (void)event; - return EM_TRUE; -} -#endif - -#if defined(DN_PLATFORM_EMSCRIPTEN) -static void DN_OS_HttpRequestEMFetchOnSuccessCallback(emscripten_fetch_t *fetch) -{ - DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData; - if (!DN_Check(response)) - return; - - response->http_status = DN_Cast(DN_U32) fetch->status; - response->body = DN_Str8AllocArena(fetch->numBytes, DN_ZMem_No, response->arena); - if (response->body.data) - DN_Memcpy(response->body.data, fetch->data, fetch->numBytes); - - DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1); - DN_AtomicAddU32(&response->done, 1); -} - -static void DN_OS_HttpRequestEMFetchOnErrorCallback(emscripten_fetch_t *fetch) -{ - DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData; - if (!DN_Check(response)) - return; - - response->http_status = DN_Cast(DN_U32) fetch->status; - response->body = DN_Str8AllocArena(fetch->numBytes, DN_ZMem_No, response->arena); - if (response->body.size) - DN_Memcpy(response->body.data, fetch->data, fetch->numBytes); - - DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1); - DN_AtomicAddU32(&response->done, 1); -} -#endif - -DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response, - DN_Arena *arena, - DN_Str8 host, - DN_Str8 path, - DN_OSHttpRequestSecure secure, - DN_Str8 method, - DN_Str8 body, - DN_Str8 headers) -{ - if (!response || !arena) - return; - - response->arena = arena; - response->builder.arena = response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena; - - DN_Arena *scratch = &response->scratch_arena; - DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1); - DN_DEFER { DN_TCScratchEnd(&scratch_); }; - if (!scratch) - scratch = &scratch_.arena; - -#if defined(DN_PLATFORM_EMSCRIPTEN) - emscripten_fetch_attr_t fetch_attribs = {}; - emscripten_fetch_attr_init(&fetch_attribs); - - if (method.size >= sizeof(fetch_attribs.requestMethod)) { - response->error_msg = - DN_Str8FromFmtArena(arena, - "Request method in EM has a size limit of 31 characters, method was " - "'%.*s' which is %zu characters long", - DN_Str8PrintFmt(method), - method.size); - DN_CheckF(method.size < sizeof(fetch_attribs.requestMethod), - "%.*s", - DN_Str8PrintFmt(response->error_msg)); - response->error_code = DN_Cast(DN_U32) - 1; - DN_AtomicAddU32(&response->done, 1); - return; - } - - DN_Memcpy(fetch_attribs.requestMethod, method.data, method.size); - - fetch_attribs.requestData = body.data; - fetch_attribs.requestDataSize = body.size; - fetch_attribs.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; - fetch_attribs.onsuccess = DN_OS_HttpRequestEMFetchOnSuccessCallback; - fetch_attribs.onerror = DN_OS_HttpRequestEMFetchOnErrorCallback; - fetch_attribs.userData = response; - - DN_Str8 url = DN_Str8FromFmtArena(scratch, "%.*s%.*s", DN_Str8PrintFmt(host), DN_Str8PrintFmt(path)); - DN_LogInfoF("Initiating HTTP '%s' request to '%.*s' with payload '%.*s'", - fetch_attribs.requestMethod, - DN_Str8PrintFmt(url), - DN_Str8PrintFmt(body)); - response->on_complete_semaphore = DN_OS_SemaphoreInit(0); - response->em_handle = emscripten_fetch(&fetch_attribs, url.data); -#else // #elif defined(DN_OS_WIN32) - DN_InvalidCodePathF("Unimplemented function"); -#endif -} - -DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response) -{ -// NOTE: Cleanup -#if defined(DN_PLATFORM_EMSCRIPTEN) - if (response->em_handle) { - emscripten_fetch_close(response->em_handle); - response->em_handle = nullptr; - } -#endif // #elif defined(DN_OS_WIN32) - - DN_MemListDeinit(response->tmp_arena.mem); - DN_OS_SemaphoreDeinit(&response->on_complete_semaphore); - *response = {}; -} diff --git a/Source/OS/dn_os_w32.cpp b/Source/OS/dn_os_w32.cpp index 1c9f7e1..5f06947 100644 --- a/Source/OS/dn_os_w32.cpp +++ b/Source/OS/dn_os_w32.cpp @@ -395,15 +395,9 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size if (!file || !file->handle || file->error || !buffer || size <= 0) return result; - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - if (!DN_Check(size <= (unsigned long)-1)) { - DN_Str8x32 buffer_size_str8 = DN_Str8x32FromByteCountU64Auto(size); - DN_ErrSinkAppendF( - err, - 1 /*error_code*/, - "Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO", - DN_Str8PrintFmt(buffer_size_str8)); - DN_TCScratchEnd(&scratch); + if (size > ULONG_MAX) { + DN_Str8x32 desc = DN_Str8x32FromByteCountU64Auto(size); + DN_ErrSinkAppendF(err, 1 /*error_code*/, "Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO", DN_Str8PrintFmt(desc)); return result; } @@ -414,6 +408,7 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size /*LPDWORD lpNumberOfByesRead*/ &bytes_read, /*LPOVERLAPPED lpOverlapped*/ nullptr); if (read_result == 0) { + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); DN_ErrSinkAppendF(err, win_error.code, "Failed to read data from file: (%u) %.*s", win_error.code, DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); @@ -421,6 +416,7 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size } if (bytes_read != size) { + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); DN_ErrSinkAppendF( err, @@ -436,7 +432,6 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size result.bytes_read = bytes_read; result.success = true; - DN_TCScratchEnd(&scratch); return result; } @@ -732,7 +727,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle, DN_ErrSinkAppendF(err, result.os_error_code, "Executed command failed to terminate: %.*s", DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; - } else if (DN_Check(exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0)) { + } else if (exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0) { // NOTE: Read stdout from process // If the pipes are full, the process will block. We periodically // flush the pipes to make sure this doesn't happen @@ -1341,204 +1336,6 @@ DN_API void DN_OS_W32ThreadSetName(DN_Str8 name) DN_TCScratchEnd(&scratch); } -void DN_OS_HttpRequestWin32Callback(HINTERNET session, DWORD *dwContext, DWORD dwInternetStatus, VOID *lpvStatusInformation, DWORD dwStatusInformationLength) -{ - (void)session; - (void)dwStatusInformationLength; - - DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) dwContext; - HINTERNET request = DN_Cast(HINTERNET) response->w32_request_handle; - DN_OSW32Error error = {}; - DWORD const READ_BUFFER_SIZE = DN_Megabytes(1); - - if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESOLVING_NAME) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_NAME_RESOLVED) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDING_REQUEST) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_SENT) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CREATED) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DETECTING_PROXY) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REDIRECT) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE) { - DWORD status = 0; - DWORD status_size = sizeof(status_size); - if (WinHttpQueryHeaders(request, - WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, - WINHTTP_HEADER_NAME_BY_INDEX, - &status, - &status_size, - WINHTTP_NO_HEADER_INDEX)) { - response->http_status = DN_Cast(uint16_t) status; - - // NOTE: You can normally call into WinHttpQueryDataAvailable which means the kernel - // will buffer the response into a single buffer and return us the full size of the - // request. - // - // or - // - // You may call WinHttpReadData directly to write the memory into our buffer directly. - // This is advantageous to avoid a copy from the kernel buffer into our buffer. If the - // end user application knows the typical payload size then they can optimise for this - // to prevent unnecessary allocation on the user side. - void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No); - if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr)) - error = DN_OS_W32LastError(&response->tmp_arena); - } else { - error = DN_OS_W32LastError(&response->tmp_arena); - } - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE) { - DWORD bytes_read = dwStatusInformationLength; - if (bytes_read) { - DN_Str8 prev_buffer = DN_Str8FromPtr(DN_Cast(char *) lpvStatusInformation, bytes_read); - DN_Str8BuilderAppendRef(&response->builder, prev_buffer); - - void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No); - if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr)) - error = DN_OS_W32LastError(&response->tmp_arena); - } - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE) { - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR) { - WINHTTP_ASYNC_RESULT *async_result = DN_Cast(WINHTTP_ASYNC_RESULT *) lpvStatusInformation; - error = DN_OS_W32ErrorCodeToMsg(&response->tmp_arena, DN_Cast(DN_U32) async_result->dwError); - } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE) { - if (!WinHttpReceiveResponse(request, 0)) - error = DN_OS_W32LastError(&response->tmp_arena); - } - - // NOTE: If the request handle is missing, then, the response has been freed. - // MSDN says that this callback can still be called after closing the handle - // and trigger the WINHTTP_CALLBACK_STATUS_REQUEST_ERROR. - if (request) { - bool read_complete = dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE && dwStatusInformationLength == 0; - if (read_complete) - response->body = DN_Str8FromStr8BuilderArena(&response->builder, response->arena); - - if (read_complete || dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR || error.code) { - DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1); - DN_AtomicAddU32(&response->done, 1); - } - - if (error.code) { - response->error_code = error.code; - response->error_msg = error.msg; - } - } -} - -DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response, - DN_Arena *arena, - DN_Str8 host, - DN_Str8 path, - DN_OSHttpRequestSecure secure, - DN_Str8 method, - DN_Str8 body, - DN_Str8 headers) -{ - if (!response || !arena) - return; - - response->arena = arena; - response->builder = DN_Str8BuilderFromArena(response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena); - - DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1); - if (!response->scratch_arena.mem) - response->scratch_arena = scratch_.arena; - - DN_OSW32Error error = {}; - DN_DEFER - { - response->error_msg = error.msg; - response->error_code = error.code; - if (error.code) { - // NOTE: 'Wait' handles failures gracefully, skipping the wait and - // cleans up the request - DN_OS_HttpRequestWait(response); - DN_AtomicAddU32(&response->done, 1); - } - DN_TCScratchEnd(&scratch_); - }; - - response->w32_request_session = WinHttpOpen(nullptr /*user agent*/, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC); - if (!response->w32_request_session) { - error = DN_OS_W32LastError(&response->tmp_arena); - return; - } - - DWORD callback_flags = WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE | - WINHTTP_CALLBACK_STATUS_READ_COMPLETE | - WINHTTP_CALLBACK_STATUS_REQUEST_ERROR | - WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE; - if (WinHttpSetStatusCallback(response->w32_request_session, - DN_Cast(WINHTTP_STATUS_CALLBACK) DN_OS_HttpRequestWin32Callback, - callback_flags, - DN_Cast(DWORD_PTR) nullptr /*dwReserved*/) == WINHTTP_INVALID_STATUS_CALLBACK) { - error = DN_OS_W32LastError(&response->tmp_arena); - return; - } - - DN_Str16 host16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, host); - response->w32_request_connection = WinHttpConnect(response->w32_request_session, host16.data, secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0 /*reserved*/); - if (!response->w32_request_connection) { - error = DN_OS_W32LastError(&response->tmp_arena); - return; - } - - DN_Str16 method16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, method); - DN_Str16 path16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, path); - response->w32_request_handle = WinHttpOpenRequest(response->w32_request_connection, - method16.data, - path16.data, - nullptr /*version*/, - nullptr /*referrer*/, - nullptr /*accept types*/, - secure ? WINHTTP_FLAG_SECURE : 0); - if (!response->w32_request_handle) { - error = DN_OS_W32LastError(&response->tmp_arena); - return; - } - - DN_Str16 headers16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, headers); - response->on_complete_semaphore = DN_OS_SemaphoreInit(0); - if (!WinHttpSendRequest(response->w32_request_handle, - headers16.data, - DN_Cast(DWORD) headers16.size, - body.data /*optional data*/, - DN_Cast(DWORD) body.size /*optional length*/, - DN_Cast(DWORD) body.size /*total content length*/, - DN_Cast(DWORD_PTR) response)) { - error = DN_OS_W32LastError(&response->tmp_arena); - return; - } -} - -DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response) -{ - // NOTE: Cleanup - // NOTE: These calls are synchronous even when the HTTP request is async. - WinHttpCloseHandle(response->w32_request_handle); - WinHttpCloseHandle(response->w32_request_connection); - WinHttpCloseHandle(response->w32_request_session); - - response->w32_request_session = nullptr; - response->w32_request_connection = nullptr; - response->w32_request_handle = nullptr; - DN_MemListDeinit(response->tmp_arena.mem); - DN_OS_SemaphoreDeinit(&response->on_complete_semaphore); - - *response = {}; -} - -// NOTE: DN_OS_W32 DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code) { DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; @@ -1638,7 +1435,8 @@ DN_API DN_Str16 DN_OS_W32Str8ToStr16(DN_Arena *arena, DN_Str8 src) return result; int chars_written = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, buffer, required_size); - if (DN_Check(chars_written == required_size)) { + DN_Assert(chars_written == required_size); + if (chars_written == required_size) { result.data = buffer; result.size = chars_written; result.data[result.size] = 0; @@ -1700,7 +1498,8 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8(DN_Arena *arena, DN_Str16 src) DN_Str8 buffer = DN_Str8AllocArena(required_size, DN_ZMem_No, &temp); if (buffer.size) { int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr); - if (DN_Check(chars_written == required_size)) { + DN_Assert(chars_written == required_size); + if (chars_written == required_size) { result = buffer; result.data[result.size] = 0; } @@ -1730,7 +1529,8 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap(DN_Str16 src) return result; int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr); - if (DN_Check(chars_written == required_size)) { + DN_Assert(chars_written == required_size); + if (chars_written == required_size) { result = buffer; result.data[result.size] = 0; } else { diff --git a/Source/SIMD/dn_simd_avx512f.cpp b/Source/SIMD/dn_simd_avx512f.cpp deleted file mode 100644 index e8a71c7..0000000 --- a/Source/SIMD/dn_simd_avx512f.cpp +++ /dev/null @@ -1,271 +0,0 @@ -#define DN_SIMD_AVX512F_CPP - -#include - -DN_API DN_Str8FindResult DN_SIMD_Str8FindAVX512F(DN_Str8 string, DN_Str8 find) -{ - // NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html - DN_Str8FindResult result = {}; - if (string.size == 0 || find.size == 0 || find.size > string.size) - return result; - - __m512i const find_first_ch = _mm512_set1_epi8(find.data[0]); - __m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]); - - DN_USize const search_size = string.size - find.size; - DN_USize simd_iterations = search_size / sizeof(__m512i); - char const *ptr = string.data; - - while (simd_iterations--) { - __m512i find_first_ch_block = _mm512_loadu_si512(ptr); - __m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1); - - // NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit - // where matches are found. - __m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch); - - // NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one - // operation using the ternarylogic intrinsic. - // - // A = first_ch_matches (find_first_ch_block ^ find_first_ch) - // B = find_last_ch_block - // C = find_last_ch - // - // ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6 - // - // / A / B / C / B ^ C / A | (B ^ C) / - // | 0 | 0 | 0 | 0 | 0 | - // | 0 | 0 | 1 | 1 | 1 | - // | 0 | 1 | 0 | 1 | 1 | - // | 0 | 1 | 1 | 0 | 0 | - // | 1 | 0 | 0 | 0 | 1 | - // | 1 | 0 | 1 | 1 | 1 | - // | 1 | 1 | 0 | 1 | 1 | - // | 1 | 1 | 1 | 0 | 1 | - - __m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6); - - // NOTE: Matches were XOR-ed and are hence indicated as zero so we mask - // out which 32 bit elements in the vector had zero bytes. This uses a - // bit twiddling trick - // https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord - __mmask16 zero_byte_mask = {}; - { - const __m512i v01 = _mm512_set1_epi32(0x01010101u); - const __m512i v80 = _mm512_set1_epi32(0x80808080u); - const __m512i v1 = _mm512_sub_epi32(ch_matches, v01); - const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20); - zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1); - } - - while (zero_byte_mask) { - uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask); - char const *base_ptr = ptr + (4 * lsb_zero_pos); - - if (DN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) { - result.found = true; - result.index = base_ptr - string.data; - } else if (DN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) { - result.found = true; - result.index = base_ptr - string.data + 1; - } else if (DN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) { - result.found = true; - result.index = base_ptr - string.data + 2; - } else if (DN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) { - result.found = true; - result.index = base_ptr - string.data + 3; - } - - if (result.found) { - result.start_to_before_match = DN_Str8FromPtr(string.data, result.index); - result.match = DN_Str8FromPtr(string.data + result.index, find.size); - result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - result.index); - result.after_match_to_end_of_buffer = DN_Str8Advance(result.match_to_end_of_buffer, find.size); - return result; - } - - zero_byte_mask = DN_Bit_ClearNextLSB(zero_byte_mask); - } - - ptr += sizeof(__m512i); - } - - for (DN_USize index = ptr - string.data; index < string.size; index++) { - DN_Str8 string_slice = DN_Str8Slice(string, index, find.size); - if (DN_Str8Eq(string_slice, find)) { - result.found = true; - result.index = index; - result.start_to_before_match = DN_Str8FromPtr(string.data, index); - result.match = DN_Str8FromPtr(string.data + index, find.size); - result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - index); - result.after_match_to_end_of_buffer = DN_Str8Advance(result.match_to_end_of_buffer, find.size); - return result; - } - } - - return result; -} - -DN_API DN_Str8FindResult DN_SIMD_Str8FindLastAVX512F(DN_Str8 string, DN_Str8 find) -{ - // NOTE: Algorithm as described in http://0x80.pl/articles/simd-strfind.html - DN_Str8FindResult result = {}; - if (string.size == 0 || find.size == 0 || find.size > string.size) - return result; - - __m512i const find_first_ch = _mm512_set1_epi8(find.data[0]); - __m512i const find_last_ch = _mm512_set1_epi8(find.data[find.size - 1]); - - DN_USize const search_size = string.size - find.size; - DN_USize simd_iterations = search_size / sizeof(__m512i); - char const *ptr = string.data + search_size + 1; - - while (simd_iterations--) { - ptr -= sizeof(__m512i); - __m512i find_first_ch_block = _mm512_loadu_si512(ptr); - __m512i find_last_ch_block = _mm512_loadu_si512(ptr + find.size - 1); - - // NOTE: AVX512F does not have a cmpeq so we use XOR to place a 0 bit - // where matches are found. - __m512i first_ch_matches = _mm512_xor_si512(find_first_ch_block, find_first_ch); - - // NOTE: We can combine the 2nd XOR and merge the 2 XOR results into one - // operation using the ternarylogic intrinsic. - // - // A = first_ch_matches (find_first_ch_block ^ find_first_ch) - // B = find_last_ch_block - // C = find_last_ch - // - // ternarylogic op => A | (B ^ C) => 0b1111'0110 => 0xf6 - // - // / A / B / C / B ^ C / A | (B ^ C) / - // | 0 | 0 | 0 | 0 | 0 | - // | 0 | 0 | 1 | 1 | 1 | - // | 0 | 1 | 0 | 1 | 1 | - // | 0 | 1 | 1 | 0 | 0 | - // | 1 | 0 | 0 | 0 | 1 | - // | 1 | 0 | 1 | 1 | 1 | - // | 1 | 1 | 0 | 1 | 1 | - // | 1 | 1 | 1 | 0 | 1 | - - __m512i ch_matches = _mm512_ternarylogic_epi32(first_ch_matches, find_last_ch_block, find_last_ch, 0xf6); - - // NOTE: Matches were XOR-ed and are hence indicated as zero so we mask - // out which 32 bit elements in the vector had zero bytes. This uses a - // bit twiddling trick - // https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord - __mmask16 zero_byte_mask = {}; - { - const __m512i v01 = _mm512_set1_epi32(0x01010101u); - const __m512i v80 = _mm512_set1_epi32(0x80808080u); - const __m512i v1 = _mm512_sub_epi32(ch_matches, v01); - const __m512i tmp1 = _mm512_ternarylogic_epi32(v1, ch_matches, v80, 0x20); - zero_byte_mask = _mm512_test_epi32_mask(tmp1, tmp1); - } - - while (zero_byte_mask) { - uint64_t const lsb_zero_pos = _tzcnt_u64(zero_byte_mask); - char const *base_ptr = ptr + (4 * lsb_zero_pos); - - if (DN_MEMCMP(base_ptr + 0, find.data, find.size) == 0) { - result.found = true; - result.index = base_ptr - string.data; - } else if (DN_MEMCMP(base_ptr + 1, find.data, find.size) == 0) { - result.found = true; - result.index = base_ptr - string.data + 1; - } else if (DN_MEMCMP(base_ptr + 2, find.data, find.size) == 0) { - result.found = true; - result.index = base_ptr - string.data + 2; - } else if (DN_MEMCMP(base_ptr + 3, find.data, find.size) == 0) { - result.found = true; - result.index = base_ptr - string.data + 3; - } - - if (result.found) { - result.start_to_before_match = DN_Str8FromPtr(string.data, result.index); - result.match = DN_Str8FromPtr(string.data + result.index, find.size); - result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - result.index); - return result; - } - - zero_byte_mask = DN_Bit_ClearNextLSB(zero_byte_mask); - } - } - - for (DN_USize index = ptr - string.data - 1; index < string.size; index--) { - DN_Str8 string_slice = DN_Str8Slice(string, index, find.size); - if (DN_Str8Eq(string_slice, find)) { - result.found = true; - result.index = index; - result.start_to_before_match = DN_Str8FromPtr(string.data, index); - result.match = DN_Str8FromPtr(string.data + index, find.size); - result.match_to_end_of_buffer = DN_Str8FromPtr(result.match.data, string.size - index); - return result; - } - } - - return result; -} - -DN_API DN_Str8BSplitResult DN_SIMD_Str8BSplitAVX512F(DN_Str8 string, DN_Str8 find) -{ - DN_Str8BSplitResult result = {}; - DN_Str8FindResult find_result = DN_SIMD_Str8FindAVX512F(string, find); - if (find_result.found) { - result.lhs.data = string.data; - result.lhs.size = find_result.index; - result.rhs = DN_Str8Advance(find_result.match_to_end_of_buffer, find.size); - } else { - result.lhs = string; - } - - return result; -} - -DN_API DN_Str8BSplitResult DN_SIMD_Str8BSplitLastAVX512F(DN_Str8 string, DN_Str8 find) -{ - DN_Str8BSplitResult result = {}; - DN_Str8FindResult find_result = DN_SIMD_Str8FindLastAVX512F(string, find); - if (find_result.found) { - result.lhs.data = string.data; - result.lhs.size = find_result.index; - result.rhs = DN_Str8Advance(find_result.match_to_end_of_buffer, find.size); - } else { - result.lhs = string; - } - - return result; -} - -DN_API DN_USize DN_SIMD_Str8SplitAVX512F(DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitIncludeEmptyStrings mode) -{ - DN_USize result = 0; // The number of splits in the actual string. - if (string.size == 0 || delimiter.size == 0 || delimiter.size <= 0) - return result; - - DN_Str8BSplitResult split = {}; - DN_Str8 first = string; - do { - split = DN_SIMD_Str8BSplitAVX512F(first, delimiter); - if (split.lhs.size || mode == DN_Str8SplitIncludeEmptyStrings_Yes) { - if (splits && result < splits_count) - splits[result] = split.lhs; - result++; - } - first = split.rhs; - } while (first.size); - - return result; -} - -DN_API DN_Slice DN_SIMD_Str8SplitAllocAVX512F(DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode) -{ - DN_Slice result = {}; - DN_USize splits_required = DN_SIMD_Str8SplitAVX512F(string, delimiter, /*splits*/ nullptr, /*count*/ 0, mode); - result.data = DN_ArenaNewArray(arena, DN_Str8, splits_required, DN_ZMem_No); - if (result.data) { - result.size = DN_SIMD_Str8SplitAVX512F(string, delimiter, result.data, splits_required, mode); - DN_Assert(splits_required == result.size); - } - return result; -} diff --git a/Source/SIMD/dn_simd_avx512f.h b/Source/SIMD/dn_simd_avx512f.h deleted file mode 100644 index 2930949..0000000 --- a/Source/SIMD/dn_simd_avx512f.h +++ /dev/null @@ -1,28 +0,0 @@ -#if !defined(DN_SIMD_AVX512F_H) -#define DN_SIMD_AVX512F_H - -/* -//////////////////////////////////////////////////////////////////////////////////////////////////// -// -// $$$$$$\ $$\ $$\ $$\ $$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$$\ -// $$ __$$\ $$ | $$ |$$ | $$ | $$ ____| $$$$ | $$ __$$\ $$ _____| -// $$ / $$ |$$ | $$ |\$$\ $$ | $$ | \_$$ | \__/ $$ |$$ | -// $$$$$$$$ |\$$\ $$ | \$$$$ /$$$$$$\ $$$$$$$\ $$ | $$$$$$ |$$$$$\ -// $$ __$$ | \$$\$$ / $$ $$< \______|\_____$$\ $$ | $$ ____/ $$ __| -// $$ | $$ | \$$$ / $$ /\$$\ $$\ $$ | $$ | $$ | $$ | -// $$ | $$ | \$ / $$ / $$ | \$$$$$$ |$$$$$$\ $$$$$$$$\ $$ | -// \__| \__| \_/ \__| \__| \______/ \______|\________|\__| -// -// dn_avx512f.h -- Functions implemented w/ AVX512 -// -//////////////////////////////////////////////////////////////////////////////////////////////////// -*/ - -DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F (DN_Str8 string, DN_Str8 find); -DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F (DN_Str8 string, DN_Str8 find); -DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F (DN_Str8 string, DN_Str8 find); -DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F(DN_Str8 string, DN_Str8 find); -DN_API DN_USize DN_Str8SplitAVX512F (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitIncludeEmptyStrings mode); -DN_API DN_Slice DN_Str8SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode); - -#endif // DN_SIMD_AVX512F_H diff --git a/Source/dn.cpp b/Source/dn.cpp index 98066dd..f5dd7f7 100644 --- a/Source/dn.cpp +++ b/Source/dn.cpp @@ -4,8 +4,6 @@ #endif #include "Base/dn_base.cpp" -#include "Base/dn_base_containers.cpp" -#include "Base/dn_base_leak.cpp" DN_Core *g_dn_; @@ -224,14 +222,6 @@ DN_API void DN_BeginFrame() #endif } -#if DN_H_WITH_HELPERS -#include "Extra/dn_helpers.cpp" -#endif - -#if DN_H_WITH_ASYNC -#include "Extra/dn_async.cpp" -#endif - #if DN_H_WITH_NET #include "Extra/dn_net.cpp" #endif @@ -239,8 +229,3 @@ DN_API void DN_BeginFrame() #if DN_CPP_WITH_TESTS #include "Extra/dn_tests.cpp" #endif - -#if DN_CPP_WITH_DEMO -#include "Extra/dn_demo.cpp" -#endif - diff --git a/Source/dn.h b/Source/dn.h index e473791..b4a3388 100644 --- a/Source/dn.h +++ b/Source/dn.h @@ -141,9 +141,13 @@ // Tracing incurs an additional much heavier performance penalty than the UAF guard due to // the stacktrace that is stored per region to report to the user when a UAF guard violation // occurs. +// +// Str8 AVX512F variants +// We have some AVX512 string functions that can be enabled by defining the following +// +// #define DN_STR8_AVX512F 1 #include "Base/dn_base.h" -#include "Base/dn_base_leak.h" #if DN_H_WITH_OS #if defined(DN_PLATFORM_WIN32) @@ -191,14 +195,6 @@ DN_API void DN_Set (DN_Core *dn); DN_API DN_Core *DN_Get (); DN_API void DN_BeginFrame(); -#if DN_H_WITH_HELPERS -#include "Extra/dn_helpers.h" -#endif - -#if DN_H_WITH_ASYNC -#include "Extra/dn_async.h" -#endif - #if DN_H_WITH_NET #include "Extra/dn_net.h" #endif diff --git a/single_header_generator.cpp b/single_header_generator.cpp index 0d5e948..f398c7f 100644 --- a/single_header_generator.cpp +++ b/single_header_generator.cpp @@ -109,8 +109,6 @@ int main(int argc, char **argv) DN_Str8 const REL_FILE_PATHS[] = { DN_Str8Lit("dn"), - DN_Str8Lit("Extra/dn_bin_pack"), - DN_Str8Lit("Extra/dn_csv"), }; for (DN_ForIndexU(type, FileType_Count)) {