Simplify, simplify, simplify. Kill code that was unloved and unused
This commit is contained in:
+1037
-2804
File diff suppressed because it is too large
Load Diff
+311
-514
@@ -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)
|
#if !defined(DN_H)
|
||||||
#define DN_H
|
#define DN_H
|
||||||
@@ -143,17 +143,23 @@
|
|||||||
// Tracing incurs an additional much heavier performance penalty than the UAF guard due to
|
// 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
|
// the stacktrace that is stored per region to report to the user when a UAF guard violation
|
||||||
// occurs.
|
// 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"
|
// DN: Single header generator commented out => #include "Base/dn_base.h"
|
||||||
#if !defined(DN_BASE_H)
|
#if !defined(DN_BASE_H)
|
||||||
#define DN_BASE_H
|
#define DN_BASE_H
|
||||||
|
|
||||||
// DN: Single header generator commented out => #if defined(_CLANGD)
|
// DN: Single header generator commented out => #if defined(_CLANGD)
|
||||||
|
// #define DN_STR8_AVX512F 1
|
||||||
// #include "../dn.h"
|
// #include "../dn.h"
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// NOTE: Compiler identification
|
// 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(_MSC_VER)
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#define DN_COMPILER_CLANG_CL
|
#define DN_COMPILER_CLANG_CL
|
||||||
@@ -168,7 +174,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NOTE: __has_feature
|
// 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)
|
#if defined(__has_feature)
|
||||||
#define DN_HAS_FEATURE(expr) __has_feature(expr)
|
#define DN_HAS_FEATURE(expr) __has_feature(expr)
|
||||||
#else
|
#else
|
||||||
@@ -176,7 +183,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NOTE: __has_builtin
|
// 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)
|
#if defined(__has_builtin)
|
||||||
#define DN_HAS_BUILTIN(expr) __has_builtin(expr)
|
#define DN_HAS_BUILTIN(expr) __has_builtin(expr)
|
||||||
#else
|
#else
|
||||||
@@ -391,15 +398,6 @@
|
|||||||
typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \
|
typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \
|
||||||
DN_GCC_WARNING_POP
|
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)
|
#if defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
|
||||||
#define DN_64_BIT
|
#define DN_64_BIT
|
||||||
#else
|
#else
|
||||||
@@ -3835,7 +3833,7 @@ DN_API DN_I16 DN_SaturateCastI64ToI16
|
|||||||
DN_API DN_I32 DN_SaturateCastI64ToI32 (DN_I64 val);
|
DN_API DN_I32 DN_SaturateCastI64ToI32 (DN_I64 val);
|
||||||
|
|
||||||
DN_API DN_UInt DN_SaturateCastI64ToUInt (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_U8 DN_SaturateCastI64ToU8 (DN_I64 val);
|
||||||
DN_API DN_U16 DN_SaturateCastI64ToU16 (DN_I64 val);
|
DN_API DN_U16 DN_SaturateCastI64ToU16 (DN_I64 val);
|
||||||
DN_API DN_U32 DN_SaturateCastI64ToU32 (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_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_Str8PrintFmt(string) (int)((string).size), (string).data
|
||||||
|
|
||||||
#define DN_Str8FromPtr(data, size) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(size)}
|
#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_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->size)
|
||||||
#define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array))
|
#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_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_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_Str8AllocPool (DN_USize size, DN_Pool *pool);
|
||||||
|
|
||||||
DN_API DN_Str8 DN_Str8FromCStr8 (char const *src);
|
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_Str8FromCStr8Arena (char const *src, DN_Arena *arena);
|
||||||
DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize size, 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_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_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_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_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_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||||
DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...);
|
DN_API DN_Str8x32 DN_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_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_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 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 DN_Str8x32 DN_Str8x32FromU64 (DN_U64 val, char separator);
|
||||||
DN_API bool DN_Str8IsAll (DN_Str8 string, DN_Str8IsAllType is_all);
|
DN_API bool DN_Str8IsAll (DN_Str8 string, DN_Str8IsAllType is_all);
|
||||||
DN_API char * DN_Str8End (DN_Str8 string);
|
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_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_Str8EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix);
|
||||||
DN_API bool DN_Str8HasChar (DN_Str8 string, char ch);
|
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_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_Str8TrimHexPrefix (DN_Str8 string);
|
||||||
DN_API DN_Str8 DN_Str8TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
DN_API DN_Str8 DN_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_Str8TrimTailWhitespace (DN_Str8 string);
|
||||||
DN_API DN_Str8 DN_Str8TrimWhitespaceAround (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_Str8TrimByteOrderMark (DN_Str8 string);
|
||||||
|
|
||||||
DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path);
|
DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path);
|
||||||
DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path);
|
DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path);
|
||||||
DN_API DN_Str8 DN_Str8FilePathNoExtension (DN_Str8 path);
|
DN_API DN_Str8 DN_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_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);
|
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_Str8SliceRender (DN_Str8Slice array, DN_Str8 separator, DN_Arena *arena);
|
||||||
DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena);
|
DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena);
|
||||||
DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case);
|
DN_API int DN_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_M4 DN_M4DivF (DN_M4 lhs, DN_F32 rhs);
|
||||||
DN_API DN_Str8x256 DN_M4ColumnMajorString (DN_M4 mat);
|
DN_API DN_Str8x256 DN_M4ColumnMajorString (DN_M4 mat);
|
||||||
|
|
||||||
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 operator!= (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_M2x3Identity ();
|
||||||
DN_API DN_M2x3 DN_M2x3Translate (DN_V2F32 offset);
|
DN_API DN_M2x3 DN_M2x3Translate (DN_V2F32 offset);
|
||||||
DN_API DN_V2F32 DN_M2x3ScaleGet (DN_M2x3 m2x3);
|
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_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)
|
#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 DN_V2F32 DN_RectCenter (DN_Rect rect);
|
||||||
DN_API bool DN_RectContainsPoint (DN_Rect rect, DN_V2F32 p);
|
DN_API bool DN_RectContainsPoint (DN_Rect rect, DN_V2F32 p);
|
||||||
DN_API bool DN_RectContainsRect (DN_Rect a, DN_Rect b);
|
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);
|
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)))
|
#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_DEFAULT_HASH_SEED = 0x8a1ced49;
|
||||||
DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0;
|
DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0;
|
||||||
|
|
||||||
template <typename T> DN_DSMap<T> DN_DSMapInit (DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags);
|
template <typename T> DN_DSMap<T> DN_DSMapInit (DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags);
|
||||||
template <typename T> void DN_DSMapDeinit (DN_DSMap<T> *map, DN_ZMem z_mem);
|
template <typename T> void DN_DSMapDeinit (DN_DSMap<T> *map, DN_ZMem z_mem);
|
||||||
template <typename T> bool DN_DSMapIsValid (DN_DSMap<T> const *map);
|
template <typename T> bool DN_DSMapIsValid (DN_DSMap<T> const *map);
|
||||||
@@ -5144,7 +5157,240 @@ template <typename T> DN_DSMapKey DN_DSMapKeyStr8 (DN_DSMap
|
|||||||
DN_API DN_DSMapKey DN_DSMapKeyU64NoHash (DN_U64 u64);
|
DN_API DN_DSMapKey DN_DSMapKeyU64NoHash (DN_U64 u64);
|
||||||
DN_API bool DN_DSMapKeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs);
|
DN_API bool DN_DSMapKeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs);
|
||||||
DN_API bool operator== (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 <typename T>
|
||||||
|
using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||||
|
|
||||||
|
enum DN_BinarySearchType
|
||||||
|
{
|
||||||
|
// Index of the match. If no match is found, found is set to false and the
|
||||||
|
// index is set to the index where the match should be inserted/exist, if
|
||||||
|
// it were in the array
|
||||||
|
DN_BinarySearchType_Match,
|
||||||
|
|
||||||
|
// Index of the first element in the array that is `element >= find`. If no such
|
||||||
|
// item is found or the array is empty, then, the index is set to the array
|
||||||
|
// size and found is set to `false`.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
// int array[] = {0, 1, 2, 3, 4, 5};
|
||||||
|
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_LowerBound);
|
||||||
|
// printf("%zu\n", result.index); // Prints index '4'
|
||||||
|
|
||||||
|
DN_BinarySearchType_LowerBound,
|
||||||
|
|
||||||
|
// Index of the first element in the array that is `element > find`. If no such
|
||||||
|
// item is found or the array is empty, then, the index is set to the array
|
||||||
|
// size and found is set to `false`.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
// int array[] = {0, 1, 2, 3, 4, 5};
|
||||||
|
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_UpperBound);
|
||||||
|
// printf("%zu\n", result.index); // Prints index '5'
|
||||||
|
|
||||||
|
DN_BinarySearchType_UpperBound,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DN_BinarySearchResult
|
||||||
|
{
|
||||||
|
bool found;
|
||||||
|
DN_USize index;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||||
|
template <typename T> DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc<T> less_than = DN_BinarySearch_DefaultLessThan);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
|
||||||
|
{
|
||||||
|
bool result = lhs < rhs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
DN_BinarySearchResult DN_BinarySearch(T const *array,
|
||||||
|
DN_USize array_size,
|
||||||
|
T const &find,
|
||||||
|
DN_BinarySearchType type,
|
||||||
|
DN_BinarySearchLessThanProc<T> less_than)
|
||||||
|
{
|
||||||
|
DN_BinarySearchResult result = {};
|
||||||
|
if (!array || array_size <= 0 || !less_than)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
T const *end = array + array_size;
|
||||||
|
T const *first = array;
|
||||||
|
T const *last = end;
|
||||||
|
while (first != last) {
|
||||||
|
DN_USize count = last - first;
|
||||||
|
T const *it = first + (count / 2);
|
||||||
|
|
||||||
|
bool advance_first = false;
|
||||||
|
if (type == DN_BinarySearchType_UpperBound)
|
||||||
|
advance_first = !less_than(find, it[0]);
|
||||||
|
else
|
||||||
|
advance_first = less_than(it[0], find);
|
||||||
|
|
||||||
|
if (advance_first)
|
||||||
|
first = it + 1;
|
||||||
|
else
|
||||||
|
last = it;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case DN_BinarySearchType_Match: {
|
||||||
|
result.found = first != end && !less_than(find, *first);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DN_BinarySearchType_LowerBound: /*FALLTHRU*/
|
||||||
|
case DN_BinarySearchType_UpperBound: {
|
||||||
|
result.found = first != end;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.index = first - array;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DN_LeakAllocFlag
|
||||||
|
{
|
||||||
|
DN_LeakAllocFlag_Freed = 1 << 0,
|
||||||
|
DN_LeakAllocFlag_LeakPermitted = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DN_LeakAlloc
|
||||||
|
{
|
||||||
|
void *ptr; // 8 Pointer to the allocation being tracked
|
||||||
|
DN_USize size; // 16 Size of the allocation
|
||||||
|
DN_USize freed_size; // 24 Store the size of the allocation when it is freed
|
||||||
|
DN_Str8 stack_trace; // 40 Stack trace at the point of allocation
|
||||||
|
DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
|
||||||
|
DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag`
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get
|
||||||
|
// expensive. Enforce that there is no unexpected padding.
|
||||||
|
DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively
|
||||||
|
|
||||||
|
struct DN_LeakTracker
|
||||||
|
{
|
||||||
|
DN_DSMap<DN_LeakAlloc> alloc_table;
|
||||||
|
DN_TicketMutex alloc_table_mutex;
|
||||||
|
DN_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)
|
#if defined(__cplusplus)
|
||||||
template <typename T> T *DN_TMemCopyObj(T *dest, T const *src, DN_USize count)
|
template <typename T> 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(__cplusplus)
|
||||||
#endif // !defined(DN_BASE_H)
|
#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<DN_LeakAlloc> 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 DN_H_WITH_OS
|
||||||
#if defined(DN_PLATFORM_WIN32)
|
#if defined(DN_PLATFORM_WIN32)
|
||||||
@@ -6884,10 +7079,6 @@ DN_API DN_OSPosixProcSelfStatus DN_OS_PosixProcSelfStatus();
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
||||||
#include <emscripten/fetch.h> // emscripten_fetch (for DN_OSHttpResponse)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
|
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.
|
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;
|
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
|
struct DN_OSCore
|
||||||
{
|
{
|
||||||
DN_CPUReport cpu_report;
|
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 void DN_OS_TCThreadLaneSync (void **ptr_to_share);
|
||||||
DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip (DN_OSThreadLane lane);
|
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);
|
enum DN_OSAsyncPriority
|
||||||
DN_API void DN_OS_HttpRequestWait (DN_OSHttpResponse *response);
|
{
|
||||||
DN_API void DN_OS_HttpRequestFree (DN_OSHttpResponse *response);
|
DN_OSAsyncPriority_Low,
|
||||||
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_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
|
// NOTE: DN_OSPrint
|
||||||
enum DN_OSPrintDest
|
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_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_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);
|
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 <typename T> struct DN_VArray
|
|
||||||
{
|
|
||||||
T *data; // Pointer to the start of the array items in the block of memory
|
|
||||||
DN_USize size; // Number of items currently in the array
|
|
||||||
DN_USize max; // Maximum number of items this array can store
|
|
||||||
DN_USize commit; // Bytes committed
|
|
||||||
|
|
||||||
T *begin() { return data; }
|
|
||||||
T *end () { return data + size; }
|
|
||||||
T const *begin() const { return data; }
|
|
||||||
T const *end () const { return data + size; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T> DN_VArray<T> DN_OS_VArrayInitByteSize (DN_USize byte_size);
|
|
||||||
template <typename T> DN_VArray<T> DN_OS_VArrayInit (DN_USize max);
|
|
||||||
template <typename T, DN_USize N> DN_VArray<T> DN_OS_VArrayInitCArray (T const (&items)[N], DN_USize max);
|
|
||||||
template <typename T> void DN_OS_VArrayDeinit (DN_VArray<T> *array);
|
|
||||||
template <typename T> bool DN_OS_VArrayIsValid (DN_VArray<T> const *array);
|
|
||||||
template <typename T> bool DN_OS_VArrayReserve (DN_VArray<T> *array, DN_USize count);
|
|
||||||
template <typename T> T * DN_OS_VArrayAddArray (DN_VArray<T> *array, T const *items, DN_USize count);
|
|
||||||
template <typename T, DN_USize N> T * DN_OS_VArrayAddCArray (DN_VArray<T> *array, T const (&items)[N]);
|
|
||||||
template <typename T> T * DN_OS_VArrayAdd (DN_VArray<T> *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 <typename T> T * DN_OS_VArrayMakeArray (DN_VArray<T> *array, DN_USize count, DN_ZMem z_mem);
|
|
||||||
template <typename T> T * DN_OS_VArrayMake (DN_VArray<T> *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 <typename T> T * DN_OS_VArrayInsertArray (DN_VArray<T> *array, DN_USize index, T const *items, DN_USize count);
|
|
||||||
template <typename T, DN_USize N> T * DN_OS_VArrayInsertCArray (DN_VArray<T> *array, DN_USize index, T const (&items)[N]);
|
|
||||||
template <typename T> T * DN_OS_VArrayInsert (DN_VArray<T> *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 <typename T> T DN_OS_VArrayPopFront (DN_VArray<T> *array, DN_USize count);
|
|
||||||
template <typename T> T DN_OS_VArrayPopBack (DN_VArray<T> *array, DN_USize count);
|
|
||||||
template <typename T> DN_ArrayEraseResult DN_OS_VArrayEraseRange (DN_VArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
|
||||||
template <typename T> void DN_OS_VArrayClear (DN_VArray<T> *array, DN_ZMem z_mem);
|
|
||||||
#endif // !defined(DN_OS_H)
|
#endif // !defined(DN_OS_H)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -7502,255 +7657,6 @@ DN_API void DN_Set (DN_Core *dn);
|
|||||||
DN_API DN_Core *DN_Get ();
|
DN_API DN_Core *DN_Get ();
|
||||||
DN_API void DN_BeginFrame();
|
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 <typename T>
|
|
||||||
using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
|
||||||
|
|
||||||
enum DN_BinarySearchType
|
|
||||||
{
|
|
||||||
// Index of the match. If no match is found, found is set to false and the
|
|
||||||
// index is set to the index where the match should be inserted/exist, if
|
|
||||||
// it were in the array
|
|
||||||
DN_BinarySearchType_Match,
|
|
||||||
|
|
||||||
// Index of the first element in the array that is `element >= find`. If no such
|
|
||||||
// item is found or the array is empty, then, the index is set to the array
|
|
||||||
// size and found is set to `false`.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// int array[] = {0, 1, 2, 3, 4, 5};
|
|
||||||
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_LowerBound);
|
|
||||||
// printf("%zu\n", result.index); // Prints index '4'
|
|
||||||
|
|
||||||
DN_BinarySearchType_LowerBound,
|
|
||||||
|
|
||||||
// Index of the first element in the array that is `element > find`. If no such
|
|
||||||
// item is found or the array is empty, then, the index is set to the array
|
|
||||||
// size and found is set to `false`.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// int array[] = {0, 1, 2, 3, 4, 5};
|
|
||||||
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_UpperBound);
|
|
||||||
// printf("%zu\n", result.index); // Prints index '5'
|
|
||||||
|
|
||||||
DN_BinarySearchType_UpperBound,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DN_BinarySearchResult
|
|
||||||
{
|
|
||||||
bool found;
|
|
||||||
DN_USize index;
|
|
||||||
};
|
|
||||||
|
|
||||||
#if !defined(DN_NO_JSON_BUILDER)
|
|
||||||
// NOTE: DN_JSONBuilder
|
|
||||||
#define DN_JSONBuilder_Object(builder) \
|
|
||||||
DN_DeferLoop(DN_JSONBuilder_ObjectBegin(builder), \
|
|
||||||
DN_JSONBuilder_ObjectEnd(builder))
|
|
||||||
|
|
||||||
#define DN_JSONBuilder_ObjectNamed(builder, name) \
|
|
||||||
DN_DeferLoop(DN_JSONBuilder_ObjectBeginNamed(builder, name), \
|
|
||||||
DN_JSONBuilder_ObjectEnd(builder))
|
|
||||||
|
|
||||||
#define DN_JSONBuilder_Array(builder) \
|
|
||||||
DN_DeferLoop(DN_JSONBuilder_ArrayBegin(builder), \
|
|
||||||
DN_JSONBuilder_ArrayEnd(builder))
|
|
||||||
|
|
||||||
#define DN_JSONBuilder_ArrayNamed(builder, name) \
|
|
||||||
DN_DeferLoop(DN_JSONBuilder_ArrayBeginNamed(builder, name), \
|
|
||||||
DN_JSONBuilder_ArrayEnd(builder))
|
|
||||||
|
|
||||||
DN_API DN_JSONBuilder DN_JSONBuilder_Init (DN_Arena *arena, int spaces_per_indent);
|
|
||||||
DN_API DN_Str8 DN_JSONBuilder_Build (DN_JSONBuilder const *builder, DN_Arena *arena);
|
|
||||||
DN_API void DN_JSONBuilder_KeyValue (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
|
||||||
DN_API void DN_JSONBuilder_KeyValueF (DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...);
|
|
||||||
DN_API void DN_JSONBuilder_ObjectBeginNamed (DN_JSONBuilder *builder, DN_Str8 name);
|
|
||||||
DN_API void DN_JSONBuilder_ObjectEnd (DN_JSONBuilder *builder);
|
|
||||||
DN_API void DN_JSONBuilder_ArrayBeginNamed (DN_JSONBuilder *builder, DN_Str8 name);
|
|
||||||
DN_API void DN_JSONBuilder_ArrayEnd (DN_JSONBuilder *builder);
|
|
||||||
DN_API void DN_JSONBuilder_Str8Named (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
|
||||||
DN_API void DN_JSONBuilder_LiteralNamed (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
|
||||||
DN_API void DN_JSONBuilder_U64Named (DN_JSONBuilder *builder, DN_Str8 key, uint64_t value);
|
|
||||||
DN_API void DN_JSONBuilder_I64Named (DN_JSONBuilder *builder, DN_Str8 key, int64_t value);
|
|
||||||
DN_API void DN_JSONBuilder_F64Named (DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places);
|
|
||||||
DN_API void DN_JSONBuilder_BoolNamed (DN_JSONBuilder *builder, DN_Str8 key, bool value);
|
|
||||||
|
|
||||||
#define DN_JSONBuilder_ObjectBegin(builder) DN_JSONBuilder_ObjectBeginNamed(builder, DN_Str8Lit(""))
|
|
||||||
#define DN_JSONBuilder_ArrayBegin(builder) DN_JSONBuilder_ArrayBeginNamed(builder, DN_Str8Lit(""))
|
|
||||||
#define DN_JSONBuilder_Str8(builder, value) DN_JSONBuilder_Str8Named(builder, DN_Str8Lit(""), value)
|
|
||||||
#define DN_JSONBuilder_Literal(builder, value) DN_JSONBuilder_LiteralNamed(builder, DN_Str8Lit(""), value)
|
|
||||||
#define DN_JSONBuilder_U64(builder, value) DN_JSONBuilder_U64Named(builder, DN_Str8Lit(""), value)
|
|
||||||
#define DN_JSONBuilder_I64(builder, value) DN_JSONBuilder_I64Named(builder, DN_Str8Lit(""), value)
|
|
||||||
#define DN_JSONBuilder_F64(builder, value) DN_JSONBuilder_F64Named(builder, DN_Str8Lit(""), value)
|
|
||||||
#define DN_JSONBuilder_Bool(builder, value) DN_JSONBuilder_BoolNamed(builder, DN_Str8Lit(""), value)
|
|
||||||
#endif // !defined(DN_NO_JSON_BUILDER)
|
|
||||||
|
|
||||||
// NOTE: DN_BinarySearch
|
|
||||||
template <typename T> bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
|
||||||
template <typename T> DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc<T> less_than = DN_BinarySearch_DefaultLessThan);
|
|
||||||
|
|
||||||
// NOTE: DN_BinarySearch
|
|
||||||
template <typename T>
|
|
||||||
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
|
|
||||||
{
|
|
||||||
bool result = lhs < rhs;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_BinarySearchResult DN_BinarySearch(T const *array,
|
|
||||||
DN_USize array_size,
|
|
||||||
T const &find,
|
|
||||||
DN_BinarySearchType type,
|
|
||||||
DN_BinarySearchLessThanProc<T> less_than)
|
|
||||||
{
|
|
||||||
DN_BinarySearchResult result = {};
|
|
||||||
if (!array || array_size <= 0 || !less_than)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
T const *end = array + array_size;
|
|
||||||
T const *first = array;
|
|
||||||
T const *last = end;
|
|
||||||
while (first != last) {
|
|
||||||
DN_USize count = last - first;
|
|
||||||
T const *it = first + (count / 2);
|
|
||||||
|
|
||||||
bool advance_first = false;
|
|
||||||
if (type == DN_BinarySearchType_UpperBound)
|
|
||||||
advance_first = !less_than(find, it[0]);
|
|
||||||
else
|
|
||||||
advance_first = less_than(it[0], find);
|
|
||||||
|
|
||||||
if (advance_first)
|
|
||||||
first = it + 1;
|
|
||||||
else
|
|
||||||
last = it;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case DN_BinarySearchType_Match: {
|
|
||||||
result.found = first != end && !less_than(find, *first);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case DN_BinarySearchType_LowerBound: /*FALLTHRU*/
|
|
||||||
case DN_BinarySearchType_UpperBound: {
|
|
||||||
result.found = first != end;
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.index = first - array;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#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
|
#if DN_H_WITH_NET
|
||||||
// DN: Single header generator commented out => #include "Extra/dn_net.h"
|
// DN: Single header generator commented out => #include "Extra/dn_net.h"
|
||||||
#if !defined(DN_NET_H)
|
#if !defined(DN_NET_H)
|
||||||
@@ -7888,113 +7794,4 @@ void DN_NET_EndFinishedRequest (DN_NETRequest *request);
|
|||||||
|
|
||||||
#endif // DN_NET_H
|
#endif // DN_NET_H
|
||||||
#endif
|
#endif
|
||||||
#endif // !defined(DN_H)
|
#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)
|
|
||||||
+1860
-165
File diff suppressed because it is too large
Load Diff
+258
-17
@@ -2,11 +2,12 @@
|
|||||||
#define DN_BASE_H
|
#define DN_BASE_H
|
||||||
|
|
||||||
#if defined(_CLANGD)
|
#if defined(_CLANGD)
|
||||||
|
#define DN_STR8_AVX512F 1
|
||||||
#include "../dn.h"
|
#include "../dn.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NOTE: Compiler identification
|
// 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(_MSC_VER)
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#define DN_COMPILER_CLANG_CL
|
#define DN_COMPILER_CLANG_CL
|
||||||
@@ -21,7 +22,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NOTE: __has_feature
|
// 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)
|
#if defined(__has_feature)
|
||||||
#define DN_HAS_FEATURE(expr) __has_feature(expr)
|
#define DN_HAS_FEATURE(expr) __has_feature(expr)
|
||||||
#else
|
#else
|
||||||
@@ -29,7 +31,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NOTE: __has_builtin
|
// 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)
|
#if defined(__has_builtin)
|
||||||
#define DN_HAS_BUILTIN(expr) __has_builtin(expr)
|
#define DN_HAS_BUILTIN(expr) __has_builtin(expr)
|
||||||
#else
|
#else
|
||||||
@@ -244,15 +246,6 @@
|
|||||||
typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \
|
typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \
|
||||||
DN_GCC_WARNING_POP
|
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)
|
#if defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
|
||||||
#define DN_64_BIT
|
#define DN_64_BIT
|
||||||
#else
|
#else
|
||||||
@@ -1760,7 +1753,7 @@ DN_API DN_I16 DN_SaturateCastI64ToI16
|
|||||||
DN_API DN_I32 DN_SaturateCastI64ToI32 (DN_I64 val);
|
DN_API DN_I32 DN_SaturateCastI64ToI32 (DN_I64 val);
|
||||||
|
|
||||||
DN_API DN_UInt DN_SaturateCastI64ToUInt (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_U8 DN_SaturateCastI64ToU8 (DN_I64 val);
|
||||||
DN_API DN_U16 DN_SaturateCastI64ToU16 (DN_I64 val);
|
DN_API DN_U16 DN_SaturateCastI64ToU16 (DN_I64 val);
|
||||||
DN_API DN_U32 DN_SaturateCastI64ToU32 (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_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_Str8PrintFmt(string) (int)((string).size), (string).data
|
||||||
|
|
||||||
#define DN_Str8FromPtr(data, size) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(size)}
|
#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_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->size)
|
||||||
#define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array))
|
#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_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_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_Str8AllocPool (DN_USize size, DN_Pool *pool);
|
||||||
|
|
||||||
DN_API DN_Str8 DN_Str8FromCStr8 (char const *src);
|
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_Str8FromCStr8Arena (char const *src, DN_Arena *arena);
|
||||||
DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize size, 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_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_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_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_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_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||||
DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...);
|
DN_API DN_Str8x32 DN_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_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_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 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 DN_Str8x32 DN_Str8x32FromU64 (DN_U64 val, char separator);
|
||||||
DN_API bool DN_Str8IsAll (DN_Str8 string, DN_Str8IsAllType is_all);
|
DN_API bool DN_Str8IsAll (DN_Str8 string, DN_Str8IsAllType is_all);
|
||||||
DN_API char * DN_Str8End (DN_Str8 string);
|
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_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_Str8EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix);
|
||||||
DN_API bool DN_Str8HasChar (DN_Str8 string, char ch);
|
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_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_Str8TrimHexPrefix (DN_Str8 string);
|
||||||
DN_API DN_Str8 DN_Str8TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
DN_API DN_Str8 DN_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_Str8TrimTailWhitespace (DN_Str8 string);
|
||||||
DN_API DN_Str8 DN_Str8TrimWhitespaceAround (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_Str8TrimByteOrderMark (DN_Str8 string);
|
||||||
|
|
||||||
DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path);
|
DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path);
|
||||||
DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path);
|
DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path);
|
||||||
DN_API DN_Str8 DN_Str8FilePathNoExtension (DN_Str8 path);
|
DN_API DN_Str8 DN_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_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);
|
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_Str8SliceRender (DN_Str8Slice array, DN_Str8 separator, DN_Arena *arena);
|
||||||
DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena);
|
DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena);
|
||||||
DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case);
|
DN_API int DN_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_M4 DN_M4DivF (DN_M4 lhs, DN_F32 rhs);
|
||||||
DN_API DN_Str8x256 DN_M4ColumnMajorString (DN_M4 mat);
|
DN_API DN_Str8x256 DN_M4ColumnMajorString (DN_M4 mat);
|
||||||
|
|
||||||
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 operator!= (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_M2x3Identity ();
|
||||||
DN_API DN_M2x3 DN_M2x3Translate (DN_V2F32 offset);
|
DN_API DN_M2x3 DN_M2x3Translate (DN_V2F32 offset);
|
||||||
DN_API DN_V2F32 DN_M2x3ScaleGet (DN_M2x3 m2x3);
|
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_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)
|
#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 DN_V2F32 DN_RectCenter (DN_Rect rect);
|
||||||
DN_API bool DN_RectContainsPoint (DN_Rect rect, DN_V2F32 p);
|
DN_API bool DN_RectContainsPoint (DN_Rect rect, DN_V2F32 p);
|
||||||
DN_API bool DN_RectContainsRect (DN_Rect a, DN_Rect b);
|
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);
|
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)))
|
#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_DEFAULT_HASH_SEED = 0x8a1ced49;
|
||||||
DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0;
|
DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0;
|
||||||
|
|
||||||
template <typename T> DN_DSMap<T> DN_DSMapInit (DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags);
|
template <typename T> DN_DSMap<T> DN_DSMapInit (DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags);
|
||||||
template <typename T> void DN_DSMapDeinit (DN_DSMap<T> *map, DN_ZMem z_mem);
|
template <typename T> void DN_DSMapDeinit (DN_DSMap<T> *map, DN_ZMem z_mem);
|
||||||
template <typename T> bool DN_DSMapIsValid (DN_DSMap<T> const *map);
|
template <typename T> bool DN_DSMapIsValid (DN_DSMap<T> const *map);
|
||||||
@@ -3069,7 +3077,240 @@ template <typename T> DN_DSMapKey DN_DSMapKeyStr8 (DN_DSMap
|
|||||||
DN_API DN_DSMapKey DN_DSMapKeyU64NoHash (DN_U64 u64);
|
DN_API DN_DSMapKey DN_DSMapKeyU64NoHash (DN_U64 u64);
|
||||||
DN_API bool DN_DSMapKeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs);
|
DN_API bool DN_DSMapKeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs);
|
||||||
DN_API bool operator== (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 <typename T>
|
||||||
|
using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||||
|
|
||||||
|
enum DN_BinarySearchType
|
||||||
|
{
|
||||||
|
// Index of the match. If no match is found, found is set to false and the
|
||||||
|
// index is set to the index where the match should be inserted/exist, if
|
||||||
|
// it were in the array
|
||||||
|
DN_BinarySearchType_Match,
|
||||||
|
|
||||||
|
// Index of the first element in the array that is `element >= find`. If no such
|
||||||
|
// item is found or the array is empty, then, the index is set to the array
|
||||||
|
// size and found is set to `false`.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
// int array[] = {0, 1, 2, 3, 4, 5};
|
||||||
|
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_LowerBound);
|
||||||
|
// printf("%zu\n", result.index); // Prints index '4'
|
||||||
|
|
||||||
|
DN_BinarySearchType_LowerBound,
|
||||||
|
|
||||||
|
// Index of the first element in the array that is `element > find`. If no such
|
||||||
|
// item is found or the array is empty, then, the index is set to the array
|
||||||
|
// size and found is set to `false`.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
// int array[] = {0, 1, 2, 3, 4, 5};
|
||||||
|
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_UpperBound);
|
||||||
|
// printf("%zu\n", result.index); // Prints index '5'
|
||||||
|
|
||||||
|
DN_BinarySearchType_UpperBound,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DN_BinarySearchResult
|
||||||
|
{
|
||||||
|
bool found;
|
||||||
|
DN_USize index;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||||
|
template <typename T> DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc<T> less_than = DN_BinarySearch_DefaultLessThan);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
|
||||||
|
{
|
||||||
|
bool result = lhs < rhs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
DN_BinarySearchResult DN_BinarySearch(T const *array,
|
||||||
|
DN_USize array_size,
|
||||||
|
T const &find,
|
||||||
|
DN_BinarySearchType type,
|
||||||
|
DN_BinarySearchLessThanProc<T> less_than)
|
||||||
|
{
|
||||||
|
DN_BinarySearchResult result = {};
|
||||||
|
if (!array || array_size <= 0 || !less_than)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
T const *end = array + array_size;
|
||||||
|
T const *first = array;
|
||||||
|
T const *last = end;
|
||||||
|
while (first != last) {
|
||||||
|
DN_USize count = last - first;
|
||||||
|
T const *it = first + (count / 2);
|
||||||
|
|
||||||
|
bool advance_first = false;
|
||||||
|
if (type == DN_BinarySearchType_UpperBound)
|
||||||
|
advance_first = !less_than(find, it[0]);
|
||||||
|
else
|
||||||
|
advance_first = less_than(it[0], find);
|
||||||
|
|
||||||
|
if (advance_first)
|
||||||
|
first = it + 1;
|
||||||
|
else
|
||||||
|
last = it;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case DN_BinarySearchType_Match: {
|
||||||
|
result.found = first != end && !less_than(find, *first);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DN_BinarySearchType_LowerBound: /*FALLTHRU*/
|
||||||
|
case DN_BinarySearchType_UpperBound: {
|
||||||
|
result.found = first != end;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.index = first - array;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DN_LeakAllocFlag
|
||||||
|
{
|
||||||
|
DN_LeakAllocFlag_Freed = 1 << 0,
|
||||||
|
DN_LeakAllocFlag_LeakPermitted = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DN_LeakAlloc
|
||||||
|
{
|
||||||
|
void *ptr; // 8 Pointer to the allocation being tracked
|
||||||
|
DN_USize size; // 16 Size of the allocation
|
||||||
|
DN_USize freed_size; // 24 Store the size of the allocation when it is freed
|
||||||
|
DN_Str8 stack_trace; // 40 Stack trace at the point of allocation
|
||||||
|
DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
|
||||||
|
DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag`
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get
|
||||||
|
// expensive. Enforce that there is no unexpected padding.
|
||||||
|
DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively
|
||||||
|
|
||||||
|
struct DN_LeakTracker
|
||||||
|
{
|
||||||
|
DN_DSMap<DN_LeakAlloc> alloc_table;
|
||||||
|
DN_TicketMutex alloc_table_mutex;
|
||||||
|
DN_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)
|
#if defined(__cplusplus)
|
||||||
template <typename T> T *DN_TMemCopyObj(T *dest, T const *src, DN_USize count)
|
template <typename T> T *DN_TMemCopyObj(T *dest, T const *src, DN_USize count)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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 <typename T>
|
|
||||||
DN_DSMap<T> DN_DSMapInit(DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags)
|
|
||||||
{
|
|
||||||
DN_DSMap<T> result = {};
|
|
||||||
if (!DN_CheckF(DN_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size))
|
|
||||||
return result;
|
|
||||||
if (size <= 0)
|
|
||||||
return result;
|
|
||||||
if (!DN_Check(arena))
|
|
||||||
return result;
|
|
||||||
result.arena = arena;
|
|
||||||
result.pool = DN_PoolFromArena(arena, DN_POOL_DEFAULT_ALIGN);
|
|
||||||
result.hash_to_slot = DN_ArenaNewArray(result.arena, DN_U32, size, DN_ZMem_Yes);
|
|
||||||
result.slots = DN_ArenaNewArray(result.arena, DN_DSMapSlot<T>, size, DN_ZMem_Yes);
|
|
||||||
result.occupied = 1; // For sentinel
|
|
||||||
result.size = size;
|
|
||||||
result.initial_size = size;
|
|
||||||
result.flags = flags;
|
|
||||||
DN_AssertF(result.hash_to_slot && result.slots, "We pre-allocated a block of memory sufficient in size for the 2 arrays. Maybe the pointers needed extra space because of natural alignment?");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void DN_DSMapDeinit(DN_DSMap<T> *map, DN_ZMem z_mem)
|
|
||||||
{
|
|
||||||
if (!map)
|
|
||||||
return;
|
|
||||||
// TODO(doyle): Use z_mem
|
|
||||||
(void)z_mem;
|
|
||||||
DN_MemListDeinit(map->arena->mem);
|
|
||||||
*map = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool DN_DSMapIsValid(DN_DSMap<T> const *map)
|
|
||||||
{
|
|
||||||
bool result = map &&
|
|
||||||
map->arena &&
|
|
||||||
map->hash_to_slot && // Hash to slot mapping array must be allocated
|
|
||||||
map->slots && // Slots array must be allocated
|
|
||||||
(map->size & (map->size - 1)) == 0 && // Must be power of two size
|
|
||||||
map->occupied >= 1; // DN_DS_MAP_SENTINEL_SLOT takes up one slot
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_U32 DN_DSMapHash(DN_DSMap<T> const *map, DN_DSMapKey key)
|
|
||||||
{
|
|
||||||
DN_U32 result = 0;
|
|
||||||
if (!map)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
if (key.type == DN_DSMapKeyType_U64NoHash) {
|
|
||||||
result = DN_Cast(DN_U32) key.u64;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.type == DN_DSMapKeyType_BufferAsU64NoHash) {
|
|
||||||
result = key.hash;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
DN_U32 seed = map->hash_seed ? map->hash_seed : DN_DS_MAP_DEFAULT_HASH_SEED;
|
|
||||||
if (map->hash_function) {
|
|
||||||
map->hash_function(key, seed);
|
|
||||||
} else {
|
|
||||||
// NOTE: Courtesy of Demetri Spanos (which this hash table was inspired
|
|
||||||
// from), the following is a hashing function snippet provided for
|
|
||||||
// reliable, quick and simple quality hashing functions for hash table
|
|
||||||
// use.
|
|
||||||
// Source: https://github.com/demetri/scribbles/blob/c475464756c104c91bab83ed4e14badefef12ab5/hashing/ub_aware_hash_functions.c
|
|
||||||
|
|
||||||
char const *key_ptr = nullptr;
|
|
||||||
DN_U32 len = 0;
|
|
||||||
DN_U32 h = seed;
|
|
||||||
switch (key.type) {
|
|
||||||
case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/
|
|
||||||
case DN_DSMapKeyType_U64NoHash: DN_InvalidCodePath; /*FALLTHRU*/
|
|
||||||
case DN_DSMapKeyType_Invalid: break;
|
|
||||||
|
|
||||||
case DN_DSMapKeyType_Buffer:
|
|
||||||
key_ptr = DN_Cast(char const *) key.buffer_data;
|
|
||||||
len = key.buffer_size;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DN_DSMapKeyType_U64:
|
|
||||||
key_ptr = DN_Cast(char const *) & key.u64;
|
|
||||||
len = sizeof(key.u64);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Murmur3 32-bit without UB unaligned accesses
|
|
||||||
// DN_U32 mur3_32_no_UB(const void *key, int len, DN_U32 h)
|
|
||||||
|
|
||||||
// main body, work on 32-bit blocks at a time
|
|
||||||
for (DN_U32 i = 0; i < len / 4; i++) {
|
|
||||||
DN_U32 k;
|
|
||||||
memcpy(&k, &key_ptr[i * 4], sizeof(k));
|
|
||||||
|
|
||||||
k *= 0xcc9e2d51;
|
|
||||||
k = ((k << 15) | (k >> 17)) * 0x1b873593;
|
|
||||||
h = (((h ^ k) << 13) | ((h ^ k) >> 19)) * 5 + 0xe6546b64;
|
|
||||||
}
|
|
||||||
|
|
||||||
// load/mix up to 3 remaining tail bytes into a tail block
|
|
||||||
DN_U32 t = 0;
|
|
||||||
uint8_t *tail = ((uint8_t *)key_ptr) + 4 * (len / 4);
|
|
||||||
switch (len & 3) {
|
|
||||||
case 3: t ^= tail[2] << 16;
|
|
||||||
case 2: t ^= tail[1] << 8;
|
|
||||||
case 1: {
|
|
||||||
t ^= tail[0] << 0;
|
|
||||||
h ^= ((0xcc9e2d51 * t << 15) | (0xcc9e2d51 * t >> 17)) * 0x1b873593;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// finalization mix, including key length
|
|
||||||
h = ((h ^ len) ^ ((h ^ len) >> 16)) * 0x85ebca6b;
|
|
||||||
h = (h ^ (h >> 13)) * 0xc2b2ae35;
|
|
||||||
result = h ^ (h >> 16);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_U32 DN_DSMapHashToSlotIndex(DN_DSMap<T> const *map, DN_DSMapKey key)
|
|
||||||
{
|
|
||||||
DN_Assert(key.type != DN_DSMapKeyType_Invalid);
|
|
||||||
DN_U32 result = DN_DS_MAP_SENTINEL_SLOT;
|
|
||||||
if (!DN_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<T> *slot = map->slots + map->hash_to_slot[result];
|
|
||||||
if (slot->key.type == DN_DSMapKeyType_Invalid || (slot->key.hash == key.hash && slot->key == key))
|
|
||||||
return result;
|
|
||||||
|
|
||||||
result = (result + 1) & (map->size - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_DSMapResult<T> DN_DSMapFind(DN_DSMap<T> const *map, DN_DSMapKey key)
|
|
||||||
{
|
|
||||||
DN_DSMapResult<T> 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 <typename T>
|
|
||||||
DN_DSMapResult<T> DN_DSMapMake(DN_DSMap<T> *map, DN_DSMapKey key)
|
|
||||||
{
|
|
||||||
DN_DSMapResult<T> 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 <typename T>
|
|
||||||
DN_DSMapResult<T> DN_DSMapSet(DN_DSMap<T> *map, DN_DSMapKey key, T const &value)
|
|
||||||
{
|
|
||||||
DN_DSMapResult<T> result = {};
|
|
||||||
if (!DN_DSMapIsValid(map))
|
|
||||||
return result;
|
|
||||||
|
|
||||||
result = DN_DSMapMake(map, key);
|
|
||||||
result.slot->value = value;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_DSMapResult<T> DN_DSMapFindKeyU64(DN_DSMap<T> const *map, DN_U64 key)
|
|
||||||
{
|
|
||||||
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
|
|
||||||
DN_DSMapResult<T> result = DN_DSMapFind(map, map_key);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_DSMapResult<T> DN_DSMapMakeKeyU64(DN_DSMap<T> *map, DN_U64 key)
|
|
||||||
{
|
|
||||||
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
|
|
||||||
DN_DSMapResult<T> result = DN_DSMapMake(map, map_key);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_DSMapResult<T> DN_DSMapSetKeyU64(DN_DSMap<T> *map, DN_U64 key, T const &value)
|
|
||||||
{
|
|
||||||
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
|
|
||||||
DN_DSMapResult<T> result = DN_DSMapSet(map, map_key, value);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_DSMapResult<T> DN_DSMapFindKeyStr8(DN_DSMap<T> const *map, DN_Str8 key)
|
|
||||||
{
|
|
||||||
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
|
|
||||||
DN_DSMapResult<T> result = DN_DSMapFind(map, map_key);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_DSMapResult<T> DN_DSMapMakeKeyStr8(DN_DSMap<T> *map, DN_Str8 key)
|
|
||||||
{
|
|
||||||
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
|
|
||||||
DN_DSMapResult<T> result = DN_DSMapMake(map, map_key);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_DSMapResult<T> DN_DSMapSetKeyStr8(DN_DSMap<T> *map, DN_Str8 key, T const &value)
|
|
||||||
{
|
|
||||||
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
|
|
||||||
DN_DSMapResult<T> result = DN_DSMapSet(map, map_key);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool DN_DSMapResize(DN_DSMap<T> *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<T> new_map = DN_DSMapInit<T>(&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<T> *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 <typename T>
|
|
||||||
bool DN_DSMapErase(DN_DSMap<T> *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<T> *slot = map->slots + slot_index;
|
|
||||||
if (!slot->key.no_copy_buffer)
|
|
||||||
DN_PoolDealloc(&map->pool, DN_Cast(void *) slot->key.buffer_data);
|
|
||||||
*slot = {}; // TODO: Optional?
|
|
||||||
|
|
||||||
if (map->occupied > 1 /*Sentinel*/) {
|
|
||||||
// NOTE: Repair the hash chain, e.g. rehash all the items after the removed
|
|
||||||
// element and reposition them if necessary.
|
|
||||||
for (DN_U32 probe_index = index;;) {
|
|
||||||
probe_index = (probe_index + 1) & (map->size - 1);
|
|
||||||
if (map->hash_to_slot[probe_index] == DN_DS_MAP_SENTINEL_SLOT)
|
|
||||||
break;
|
|
||||||
|
|
||||||
DN_DSMapSlot<T> *probe = map->slots + map->hash_to_slot[probe_index];
|
|
||||||
DN_U32 new_index = probe->key.hash & (map->size - 1);
|
|
||||||
if (index <= probe_index) {
|
|
||||||
if (index < new_index && new_index <= probe_index)
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
if (index < new_index || new_index <= probe_index)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
map->hash_to_slot[index] = map->hash_to_slot[probe_index];
|
|
||||||
map->hash_to_slot[probe_index] = DN_DS_MAP_SENTINEL_SLOT;
|
|
||||||
index = probe_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: We have erased a slot from the hash table, this leaves a gap
|
|
||||||
// in our contiguous array. After repairing the chain, the hash mapping
|
|
||||||
// is correct.
|
|
||||||
// We will now fill in the vacant spot that we erased using the last
|
|
||||||
// element in the slot list.
|
|
||||||
if (map->occupied >= 3 /*Ignoring sentinel, at least 2 other elements to unstable erase*/) {
|
|
||||||
DN_U32 last_index = map->occupied - 1;
|
|
||||||
if (last_index != slot_index) {
|
|
||||||
// NOTE: Copy in last slot to the erase slot
|
|
||||||
DN_DSMapSlot<T> *last_slot = map->slots + last_index;
|
|
||||||
map->slots[slot_index] = *last_slot;
|
|
||||||
|
|
||||||
// NOTE: Update the hash-to-slot mapping for the value that was copied in
|
|
||||||
DN_U32 hash_to_slot_index = DN_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 <typename T>
|
|
||||||
bool DN_DSMapEraseKeyU64(DN_DSMap<T> *map, DN_U64 key)
|
|
||||||
{
|
|
||||||
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
|
|
||||||
bool result = DN_DSMapErase(map, map_key);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool DN_DSMapEraseKeyStr8(DN_DSMap<T> *map, DN_Str8 key)
|
|
||||||
{
|
|
||||||
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
|
|
||||||
bool result = DN_DSMapErase(map, map_key);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_DSMapKey DN_DSMapKeyBuffer(DN_DSMap<T> const *map, void const *data, DN_USize size)
|
|
||||||
{
|
|
||||||
DN_Assert(size > 0 && size <= UINT32_MAX);
|
|
||||||
DN_DSMapKey result = {};
|
|
||||||
result.type = DN_DSMapKeyType_Buffer;
|
|
||||||
result.buffer_data = data;
|
|
||||||
result.buffer_size = DN_Cast(DN_U32) size;
|
|
||||||
result.hash = DN_DSMapHash(map, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_DSMapKey DN_DSMapKeyBufferAsU64NoHash(DN_DSMap<T> 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 <typename T>
|
|
||||||
DN_DSMapKey DN_DSMapKeyU64(DN_DSMap<T> 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 <typename T>
|
|
||||||
DN_DSMapKey DN_DSMapKeyStr8(DN_DSMap<T> 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;
|
|
||||||
}
|
|
||||||
@@ -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<DN_LeakAlloc> *alloc_table = &leak->alloc_table;
|
|
||||||
DN_DSMapResult<DN_LeakAlloc> 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<DN_LeakAlloc> *alloc_table = &leak->alloc_table;
|
|
||||||
DN_DSMapResult<DN_LeakAlloc> 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<DN_LeakAlloc> *slot = leak->alloc_table.slots + index;
|
|
||||||
DN_LeakAlloc *alloc = &slot->value;
|
|
||||||
bool alloc_leaked = (alloc->flags & DN_LeakAllocFlag_Freed) == 0;
|
|
||||||
bool leak_permitted = (alloc->flags & DN_LeakAllocFlag_LeakPermitted);
|
|
||||||
if (alloc_leaked && !leak_permitted) {
|
|
||||||
leaked_bytes += alloc->size;
|
|
||||||
leak_count++;
|
|
||||||
DN_Str8x32 alloc_size = DN_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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<DN_LeakAlloc> 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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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 <stdint.h>
|
|
||||||
#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 <Windows.h> replacement header must be disabled with DN_NO_WINDOWS_H_REPLACEMENT_HEADER since Metadesk includes <Windows.h>
|
|
||||||
#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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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 "%.<decimal_places>f" i.e. %.1f
|
|
||||||
DN_SNPrintF(float_fmt, sizeof(float_fmt), "%%.%df", decimal_places);
|
|
||||||
} else {
|
|
||||||
// NOTE: Emit the format string "%f"
|
|
||||||
DN_SNPrintF(float_fmt, sizeof(float_fmt), "%%f");
|
|
||||||
}
|
|
||||||
DN_JSONBuilder_KeyValueF(builder, key, float_fmt, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
DN_API void DN_JSONBuilder_BoolNamed(DN_JSONBuilder *builder, DN_Str8 key, bool value)
|
|
||||||
{
|
|
||||||
DN_Str8 value_string = value ? DN_Str8Lit("true") : DN_Str8Lit("false");
|
|
||||||
DN_JSONBuilder_KeyValueF(builder, key, "%.*s", value_string.size, value_string.data);
|
|
||||||
}
|
|
||||||
@@ -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 <typename T>
|
|
||||||
using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
|
||||||
|
|
||||||
enum DN_BinarySearchType
|
|
||||||
{
|
|
||||||
// Index of the match. If no match is found, found is set to false and the
|
|
||||||
// index is set to the index where the match should be inserted/exist, if
|
|
||||||
// it were in the array
|
|
||||||
DN_BinarySearchType_Match,
|
|
||||||
|
|
||||||
// Index of the first element in the array that is `element >= find`. If no such
|
|
||||||
// item is found or the array is empty, then, the index is set to the array
|
|
||||||
// size and found is set to `false`.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// int array[] = {0, 1, 2, 3, 4, 5};
|
|
||||||
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_LowerBound);
|
|
||||||
// printf("%zu\n", result.index); // Prints index '4'
|
|
||||||
|
|
||||||
DN_BinarySearchType_LowerBound,
|
|
||||||
|
|
||||||
// Index of the first element in the array that is `element > find`. If no such
|
|
||||||
// item is found or the array is empty, then, the index is set to the array
|
|
||||||
// size and found is set to `false`.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
// int array[] = {0, 1, 2, 3, 4, 5};
|
|
||||||
// DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_UpperBound);
|
|
||||||
// printf("%zu\n", result.index); // Prints index '5'
|
|
||||||
|
|
||||||
DN_BinarySearchType_UpperBound,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DN_BinarySearchResult
|
|
||||||
{
|
|
||||||
bool found;
|
|
||||||
DN_USize index;
|
|
||||||
};
|
|
||||||
|
|
||||||
#if !defined(DN_NO_JSON_BUILDER)
|
|
||||||
// NOTE: DN_JSONBuilder
|
|
||||||
#define DN_JSONBuilder_Object(builder) \
|
|
||||||
DN_DeferLoop(DN_JSONBuilder_ObjectBegin(builder), \
|
|
||||||
DN_JSONBuilder_ObjectEnd(builder))
|
|
||||||
|
|
||||||
#define DN_JSONBuilder_ObjectNamed(builder, name) \
|
|
||||||
DN_DeferLoop(DN_JSONBuilder_ObjectBeginNamed(builder, name), \
|
|
||||||
DN_JSONBuilder_ObjectEnd(builder))
|
|
||||||
|
|
||||||
#define DN_JSONBuilder_Array(builder) \
|
|
||||||
DN_DeferLoop(DN_JSONBuilder_ArrayBegin(builder), \
|
|
||||||
DN_JSONBuilder_ArrayEnd(builder))
|
|
||||||
|
|
||||||
#define DN_JSONBuilder_ArrayNamed(builder, name) \
|
|
||||||
DN_DeferLoop(DN_JSONBuilder_ArrayBeginNamed(builder, name), \
|
|
||||||
DN_JSONBuilder_ArrayEnd(builder))
|
|
||||||
|
|
||||||
DN_API DN_JSONBuilder DN_JSONBuilder_Init (DN_Arena *arena, int spaces_per_indent);
|
|
||||||
DN_API DN_Str8 DN_JSONBuilder_Build (DN_JSONBuilder const *builder, DN_Arena *arena);
|
|
||||||
DN_API void DN_JSONBuilder_KeyValue (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
|
||||||
DN_API void DN_JSONBuilder_KeyValueF (DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...);
|
|
||||||
DN_API void DN_JSONBuilder_ObjectBeginNamed (DN_JSONBuilder *builder, DN_Str8 name);
|
|
||||||
DN_API void DN_JSONBuilder_ObjectEnd (DN_JSONBuilder *builder);
|
|
||||||
DN_API void DN_JSONBuilder_ArrayBeginNamed (DN_JSONBuilder *builder, DN_Str8 name);
|
|
||||||
DN_API void DN_JSONBuilder_ArrayEnd (DN_JSONBuilder *builder);
|
|
||||||
DN_API void DN_JSONBuilder_Str8Named (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
|
||||||
DN_API void DN_JSONBuilder_LiteralNamed (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value);
|
|
||||||
DN_API void DN_JSONBuilder_U64Named (DN_JSONBuilder *builder, DN_Str8 key, uint64_t value);
|
|
||||||
DN_API void DN_JSONBuilder_I64Named (DN_JSONBuilder *builder, DN_Str8 key, int64_t value);
|
|
||||||
DN_API void DN_JSONBuilder_F64Named (DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places);
|
|
||||||
DN_API void DN_JSONBuilder_BoolNamed (DN_JSONBuilder *builder, DN_Str8 key, bool value);
|
|
||||||
|
|
||||||
#define DN_JSONBuilder_ObjectBegin(builder) DN_JSONBuilder_ObjectBeginNamed(builder, DN_Str8Lit(""))
|
|
||||||
#define DN_JSONBuilder_ArrayBegin(builder) DN_JSONBuilder_ArrayBeginNamed(builder, DN_Str8Lit(""))
|
|
||||||
#define DN_JSONBuilder_Str8(builder, value) DN_JSONBuilder_Str8Named(builder, DN_Str8Lit(""), value)
|
|
||||||
#define DN_JSONBuilder_Literal(builder, value) DN_JSONBuilder_LiteralNamed(builder, DN_Str8Lit(""), value)
|
|
||||||
#define DN_JSONBuilder_U64(builder, value) DN_JSONBuilder_U64Named(builder, DN_Str8Lit(""), value)
|
|
||||||
#define DN_JSONBuilder_I64(builder, value) DN_JSONBuilder_I64Named(builder, DN_Str8Lit(""), value)
|
|
||||||
#define DN_JSONBuilder_F64(builder, value) DN_JSONBuilder_F64Named(builder, DN_Str8Lit(""), value)
|
|
||||||
#define DN_JSONBuilder_Bool(builder, value) DN_JSONBuilder_BoolNamed(builder, DN_Str8Lit(""), value)
|
|
||||||
#endif // !defined(DN_NO_JSON_BUILDER)
|
|
||||||
|
|
||||||
// NOTE: DN_BinarySearch
|
|
||||||
template <typename T> bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
|
||||||
template <typename T> DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc<T> less_than = DN_BinarySearch_DefaultLessThan);
|
|
||||||
|
|
||||||
// NOTE: DN_BinarySearch
|
|
||||||
template <typename T>
|
|
||||||
bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
|
|
||||||
{
|
|
||||||
bool result = lhs < rhs;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_BinarySearchResult DN_BinarySearch(T const *array,
|
|
||||||
DN_USize array_size,
|
|
||||||
T const &find,
|
|
||||||
DN_BinarySearchType type,
|
|
||||||
DN_BinarySearchLessThanProc<T> less_than)
|
|
||||||
{
|
|
||||||
DN_BinarySearchResult result = {};
|
|
||||||
if (!array || array_size <= 0 || !less_than)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
T const *end = array + array_size;
|
|
||||||
T const *first = array;
|
|
||||||
T const *last = end;
|
|
||||||
while (first != last) {
|
|
||||||
DN_USize count = last - first;
|
|
||||||
T const *it = first + (count / 2);
|
|
||||||
|
|
||||||
bool advance_first = false;
|
|
||||||
if (type == DN_BinarySearchType_UpperBound)
|
|
||||||
advance_first = !less_than(find, it[0]);
|
|
||||||
else
|
|
||||||
advance_first = less_than(it[0], find);
|
|
||||||
|
|
||||||
if (advance_first)
|
|
||||||
first = it + 1;
|
|
||||||
else
|
|
||||||
last = it;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case DN_BinarySearchType_Match: {
|
|
||||||
result.found = first != end && !less_than(find, *first);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case DN_BinarySearchType_LowerBound: /*FALLTHRU*/
|
|
||||||
case DN_BinarySearchType_UpperBound: {
|
|
||||||
result.found = first != end;
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.index = first - array;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endif // !defined(DN_HELPERS_H)
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
@@ -1342,90 +1342,6 @@ static DN_UTCore DN_TST_BaseArray()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DN_UTCore DN_TST_BaseVArray()
|
|
||||||
{
|
|
||||||
DN_UTCore result = DN_UT_Init();
|
|
||||||
DN_UT_LogF(&result, "DN_VArray\n");
|
|
||||||
{
|
|
||||||
{
|
|
||||||
DN_VArray<DN_U32> array = DN_OS_VArrayInitByteSize<DN_U32>(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<DN_U32>(&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<DN_U32>(&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<UnalignedObject> array = DN_OS_VArrayInitByteSize<UnalignedObject>(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)
|
#if defined(DN_UNIT_TESTS_WITH_KECCAK)
|
||||||
DN_GCC_WARNING_PUSH
|
DN_GCC_WARNING_PUSH
|
||||||
DN_GCC_WARNING_DISABLE(-Wunused-parameter)
|
DN_GCC_WARNING_DISABLE(-Wunused-parameter)
|
||||||
@@ -2695,7 +2611,6 @@ DN_TSTResult DN_TST_RunSuite(DN_TSTPrint print)
|
|||||||
DN_TST_BaseDSMap(),
|
DN_TST_BaseDSMap(),
|
||||||
DN_TST_BaseIArray(),
|
DN_TST_BaseIArray(),
|
||||||
DN_TST_BaseArray(),
|
DN_TST_BaseArray(),
|
||||||
DN_TST_BaseVArray(),
|
|
||||||
DN_TST_Keccak(),
|
DN_TST_Keccak(),
|
||||||
DN_TST_M4(),
|
DN_TST_M4(),
|
||||||
DN_TST_OS(),
|
DN_TST_OS(),
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
|
|
||||||
+108
-180
@@ -883,27 +883,122 @@ DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip(DN_OSThreadLane lane)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: DN_OSHttp
|
static DN_I32 DN_OS_AsyncThreadEntryPoint_(DN_OSThread *thread)
|
||||||
DN_API void DN_OS_HttpRequestWait(DN_OSHttpResponse *response)
|
|
||||||
{
|
{
|
||||||
if (response && response->on_complete_semaphore.handle != 0)
|
DN_OS_ThreadSetNameFmt("%.*s", DN_Str8PrintFmt(thread->name));
|
||||||
DN_OS_SemaphoreWait(&response->on_complete_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
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_Assert(async);
|
||||||
DN_OSHttpResponse result = {};
|
async->ring.size = base_size;
|
||||||
DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1);
|
async->ring.base = base;
|
||||||
result.scratch_arena = scratch.arena;
|
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;
|
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_API DN_LogStyle DN_OS_PrintStyleColour(uint8_t r, uint8_t g, uint8_t b, DN_LogBold bold)
|
||||||
{
|
{
|
||||||
DN_LogStyle result = {};
|
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"));
|
DN_OS_Print(dest, DN_Str8Lit("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: DN_VArray
|
|
||||||
template <typename T>
|
|
||||||
DN_VArray<T> DN_OS_VArrayInitByteSize(DN_USize byte_size)
|
|
||||||
{
|
|
||||||
DN_VArray<T> result = {};
|
|
||||||
result.data = DN_Cast(T *) DN_OS_MemReserve(byte_size, DN_MemCommit_No, DN_MemPage_ReadWrite);
|
|
||||||
if (result.data)
|
|
||||||
result.max = byte_size / sizeof(T);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_VArray<T> DN_OS_VArrayInit(DN_USize max)
|
|
||||||
{
|
|
||||||
DN_VArray<T> result = DN_OS_VArrayInitByteSize<T>(max * sizeof(T));
|
|
||||||
DN_Assert(result.max >= max);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, DN_USize N>
|
|
||||||
DN_VArray<T> DN_OS_VArrayInitCArray(T const (&items)[N], DN_USize max)
|
|
||||||
{
|
|
||||||
DN_USize real_max = DN_Max(N, max);
|
|
||||||
DN_VArray<T> result = DN_OS_VArrayInit<T>(real_max);
|
|
||||||
if (DN_OS_VArrayIsValid(&result))
|
|
||||||
DN_OS_VArrayAddArray(&result, items, N);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void DN_OS_VArrayDeinit(DN_VArray<T> *array)
|
|
||||||
{
|
|
||||||
DN_OS_MemRelease(array->data, array->max * sizeof(T));
|
|
||||||
*array = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool DN_OS_VArrayIsValid(DN_VArray<T> const *array)
|
|
||||||
{
|
|
||||||
bool result = array->data && array->size <= array->max;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T *DN_OS_VArrayAddArray(DN_VArray<T> *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 <typename T, DN_USize N>
|
|
||||||
T *DN_OS_VArrayAddCArray(DN_VArray<T> *array, T const (&items)[N])
|
|
||||||
{
|
|
||||||
T *result = DN_OS_VArrayAddArray(array, items, N);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T *DN_OS_VArrayAdd(DN_VArray<T> *array, T const &item)
|
|
||||||
{
|
|
||||||
T *result = DN_OS_VArrayAddArray(array, &item, 1);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T *DN_OS_VArrayMakeArray(DN_VArray<T> *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 <typename T>
|
|
||||||
T *DN_OS_VArrayMake(DN_VArray<T> *array, DN_ZMem z_mem)
|
|
||||||
{
|
|
||||||
T *result = DN_OS_VArrayMakeArray(array, 1, z_mem);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T *DN_OS_VArrayInsertArray(DN_VArray<T> *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 <typename T, DN_USize N>
|
|
||||||
T *DN_OS_VArrayInsertCArray(DN_VArray<T> *array, DN_USize index, T const (&items)[N])
|
|
||||||
{
|
|
||||||
T *result = DN_OS_VArrayInsertArray(array, index, items, N);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T *DN_OS_VArrayInsert(DN_VArray<T> *array, DN_USize index, T const &item)
|
|
||||||
{
|
|
||||||
T *result = DN_OS_VArrayInsertArray(array, index, &item, 1);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T *DN_OS_VArrayPopFront(DN_VArray<T> *array, DN_USize count)
|
|
||||||
{
|
|
||||||
T *result = DN_Cast(T *)DN_ArrayPopFront(array->data, &array->size, sizeof(T), count);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T *DN_OS_VArrayPopBack(DN_VArray<T> *array, DN_USize count)
|
|
||||||
{
|
|
||||||
T *result = DN_Cast(T *)DN_ArrayPopBack(array->data, &array->size, sizeof(T), count);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
DN_ArrayEraseResult DN_OS_VArrayEraseRange(DN_VArray<T> *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 <typename T>
|
|
||||||
void DN_OS_VArrayClear(DN_VArray<T> *array, DN_ZMem z_mem)
|
|
||||||
{
|
|
||||||
if (array) {
|
|
||||||
if (z_mem == DN_ZMem_Yes)
|
|
||||||
DN_Memset(array->data, 0, array->size * sizeof(T));
|
|
||||||
array->size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool DN_OS_VArrayReserve(DN_VArray<T> *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_API DN_StackTrace DN_StackTraceFromAllocator(DN_Allocator allocator, DN_U16 limit)
|
||||||
{
|
{
|
||||||
DN_StackTrace result = {};
|
DN_StackTrace result = {};
|
||||||
|
|||||||
+46
-86
@@ -31,10 +31,6 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(DN_PLATFORM_EMSCRIPTEN)
|
|
||||||
#include <emscripten/fetch.h> // emscripten_fetch (for DN_OSHttpResponse)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
|
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.
|
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;
|
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
|
struct DN_OSCore
|
||||||
{
|
{
|
||||||
DN_CPUReport cpu_report;
|
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 void DN_OS_TCThreadLaneSync (void **ptr_to_share);
|
||||||
DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip (DN_OSThreadLane lane);
|
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);
|
enum DN_OSAsyncPriority
|
||||||
DN_API void DN_OS_HttpRequestWait (DN_OSHttpResponse *response);
|
{
|
||||||
DN_API void DN_OS_HttpRequestFree (DN_OSHttpResponse *response);
|
DN_OSAsyncPriority_Low,
|
||||||
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_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
|
// NOTE: DN_OSPrint
|
||||||
enum DN_OSPrintDest
|
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_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_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);
|
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 <typename T> struct DN_VArray
|
|
||||||
{
|
|
||||||
T *data; // Pointer to the start of the array items in the block of memory
|
|
||||||
DN_USize size; // Number of items currently in the array
|
|
||||||
DN_USize max; // Maximum number of items this array can store
|
|
||||||
DN_USize commit; // Bytes committed
|
|
||||||
|
|
||||||
T *begin() { return data; }
|
|
||||||
T *end () { return data + size; }
|
|
||||||
T const *begin() const { return data; }
|
|
||||||
T const *end () const { return data + size; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T> DN_VArray<T> DN_OS_VArrayInitByteSize (DN_USize byte_size);
|
|
||||||
template <typename T> DN_VArray<T> DN_OS_VArrayInit (DN_USize max);
|
|
||||||
template <typename T, DN_USize N> DN_VArray<T> DN_OS_VArrayInitCArray (T const (&items)[N], DN_USize max);
|
|
||||||
template <typename T> void DN_OS_VArrayDeinit (DN_VArray<T> *array);
|
|
||||||
template <typename T> bool DN_OS_VArrayIsValid (DN_VArray<T> const *array);
|
|
||||||
template <typename T> bool DN_OS_VArrayReserve (DN_VArray<T> *array, DN_USize count);
|
|
||||||
template <typename T> T * DN_OS_VArrayAddArray (DN_VArray<T> *array, T const *items, DN_USize count);
|
|
||||||
template <typename T, DN_USize N> T * DN_OS_VArrayAddCArray (DN_VArray<T> *array, T const (&items)[N]);
|
|
||||||
template <typename T> T * DN_OS_VArrayAdd (DN_VArray<T> *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 <typename T> T * DN_OS_VArrayMakeArray (DN_VArray<T> *array, DN_USize count, DN_ZMem z_mem);
|
|
||||||
template <typename T> T * DN_OS_VArrayMake (DN_VArray<T> *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 <typename T> T * DN_OS_VArrayInsertArray (DN_VArray<T> *array, DN_USize index, T const *items, DN_USize count);
|
|
||||||
template <typename T, DN_USize N> T * DN_OS_VArrayInsertCArray (DN_VArray<T> *array, DN_USize index, T const (&items)[N]);
|
|
||||||
template <typename T> T * DN_OS_VArrayInsert (DN_VArray<T> *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 <typename T> T DN_OS_VArrayPopFront (DN_VArray<T> *array, DN_USize count);
|
|
||||||
template <typename T> T DN_OS_VArrayPopBack (DN_VArray<T> *array, DN_USize count);
|
|
||||||
template <typename T> DN_ArrayEraseResult DN_OS_VArrayEraseRange (DN_VArray<T> *array, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase);
|
|
||||||
template <typename T> void DN_OS_VArrayClear (DN_VArray<T> *array, DN_ZMem z_mem);
|
|
||||||
#endif // !defined(DN_OS_H)
|
#endif // !defined(DN_OS_H)
|
||||||
|
|||||||
@@ -1444,154 +1444,3 @@ DN_API DN_OSPosixProcSelfStatus DN_OS_PosixProcSelfStatus()
|
|||||||
DN_OS_FileClose(&file);
|
DN_OS_FileClose(&file);
|
||||||
return result;
|
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 = {};
|
|
||||||
}
|
|
||||||
|
|||||||
+12
-212
@@ -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)
|
if (!file || !file->handle || file->error || !buffer || size <= 0)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
|
if (size > ULONG_MAX) {
|
||||||
if (!DN_Check(size <= (unsigned long)-1)) {
|
DN_Str8x32 desc = DN_Str8x32FromByteCountU64Auto(size);
|
||||||
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(desc));
|
||||||
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);
|
|
||||||
return result;
|
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,
|
/*LPDWORD lpNumberOfByesRead*/ &bytes_read,
|
||||||
/*LPOVERLAPPED lpOverlapped*/ nullptr);
|
/*LPOVERLAPPED lpOverlapped*/ nullptr);
|
||||||
if (read_result == 0) {
|
if (read_result == 0) {
|
||||||
|
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
|
||||||
DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
|
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_ErrSinkAppendF(err, win_error.code, "Failed to read data from file: (%u) %.*s", win_error.code, DN_Str8PrintFmt(win_error.msg));
|
||||||
DN_TCScratchEnd(&scratch);
|
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) {
|
if (bytes_read != size) {
|
||||||
|
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
|
||||||
DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
|
DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
|
||||||
DN_ErrSinkAppendF(
|
DN_ErrSinkAppendF(
|
||||||
err,
|
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.bytes_read = bytes_read;
|
||||||
result.success = true;
|
result.success = true;
|
||||||
DN_TCScratchEnd(&scratch);
|
|
||||||
return result;
|
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_ErrSinkAppendF(err, result.os_error_code, "Executed command failed to terminate: %.*s", DN_Str8PrintFmt(win_error.msg));
|
||||||
DN_TCScratchEnd(&scratch);
|
DN_TCScratchEnd(&scratch);
|
||||||
return result;
|
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
|
// NOTE: Read stdout from process
|
||||||
// If the pipes are full, the process will block. We periodically
|
// If the pipes are full, the process will block. We periodically
|
||||||
// flush the pipes to make sure this doesn't happen
|
// 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);
|
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)
|
DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code)
|
||||||
{
|
{
|
||||||
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
|
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;
|
return result;
|
||||||
|
|
||||||
int chars_written = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, buffer, required_size);
|
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.data = buffer;
|
||||||
result.size = chars_written;
|
result.size = chars_written;
|
||||||
result.data[result.size] = 0;
|
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);
|
DN_Str8 buffer = DN_Str8AllocArena(required_size, DN_ZMem_No, &temp);
|
||||||
if (buffer.size) {
|
if (buffer.size) {
|
||||||
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr);
|
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 = buffer;
|
||||||
result.data[result.size] = 0;
|
result.data[result.size] = 0;
|
||||||
}
|
}
|
||||||
@@ -1730,7 +1529,8 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap(DN_Str16 src)
|
|||||||
return result;
|
return result;
|
||||||
|
|
||||||
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr);
|
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 = buffer;
|
||||||
result.data[result.size] = 0;
|
result.data[result.size] = 0;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,271 +0,0 @@
|
|||||||
#define DN_SIMD_AVX512F_CPP
|
|
||||||
|
|
||||||
#include <immintrin.h>
|
|
||||||
|
|
||||||
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_Str8> DN_SIMD_Str8SplitAllocAVX512F(DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode)
|
|
||||||
{
|
|
||||||
DN_Slice<DN_Str8> 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;
|
|
||||||
}
|
|
||||||
@@ -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_Str8> DN_Str8SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitIncludeEmptyStrings mode);
|
|
||||||
|
|
||||||
#endif // DN_SIMD_AVX512F_H
|
|
||||||
@@ -4,8 +4,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Base/dn_base.cpp"
|
#include "Base/dn_base.cpp"
|
||||||
#include "Base/dn_base_containers.cpp"
|
|
||||||
#include "Base/dn_base_leak.cpp"
|
|
||||||
|
|
||||||
DN_Core *g_dn_;
|
DN_Core *g_dn_;
|
||||||
|
|
||||||
@@ -224,14 +222,6 @@ DN_API void DN_BeginFrame()
|
|||||||
#endif
|
#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
|
#if DN_H_WITH_NET
|
||||||
#include "Extra/dn_net.cpp"
|
#include "Extra/dn_net.cpp"
|
||||||
#endif
|
#endif
|
||||||
@@ -239,8 +229,3 @@ DN_API void DN_BeginFrame()
|
|||||||
#if DN_CPP_WITH_TESTS
|
#if DN_CPP_WITH_TESTS
|
||||||
#include "Extra/dn_tests.cpp"
|
#include "Extra/dn_tests.cpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DN_CPP_WITH_DEMO
|
|
||||||
#include "Extra/dn_demo.cpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|||||||
+5
-9
@@ -141,9 +141,13 @@
|
|||||||
// Tracing incurs an additional much heavier performance penalty than the UAF guard due to
|
// 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
|
// the stacktrace that is stored per region to report to the user when a UAF guard violation
|
||||||
// occurs.
|
// 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.h"
|
||||||
#include "Base/dn_base_leak.h"
|
|
||||||
|
|
||||||
#if DN_H_WITH_OS
|
#if DN_H_WITH_OS
|
||||||
#if defined(DN_PLATFORM_WIN32)
|
#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 DN_Core *DN_Get ();
|
||||||
DN_API void DN_BeginFrame();
|
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
|
#if DN_H_WITH_NET
|
||||||
#include "Extra/dn_net.h"
|
#include "Extra/dn_net.h"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -109,8 +109,6 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
DN_Str8 const REL_FILE_PATHS[] = {
|
DN_Str8 const REL_FILE_PATHS[] = {
|
||||||
DN_Str8Lit("dn"),
|
DN_Str8Lit("dn"),
|
||||||
DN_Str8Lit("Extra/dn_bin_pack"),
|
|
||||||
DN_Str8Lit("Extra/dn_csv"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (DN_ForIndexU(type, FileType_Count)) {
|
for (DN_ForIndexU(type, FileType_Count)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user