Compare commits

...
2 Commits
Author SHA1 Message Date
doylet ab4eaa5bb3 Simplify, simplify, simplify. Kill code that was unloved and unused 2026-06-18 22:11:30 +10:00
doylet b813543659 Fix build 2026-06-18 18:20:45 +10:00
32 changed files with 3672 additions and 10086 deletions

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1853 -158
View File
File diff suppressed because it is too large. Load diff
+259 -23
View File
@@ -2,11 +2,12 @@
#define DN_BASE_H
#if defined(_CLANGD)
#define DN_STR8_AVX512F 1
#include "../dn.h"
#endif
// NOTE: Compiler identification
// Warning! Order is important here, clang-cl on Windows defines _MSC_VER
// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER
#if defined(_MSC_VER)
#if defined(__clang__)
#define DN_COMPILER_CLANG_CL
@@ -21,7 +22,8 @@
#endif
// NOTE: __has_feature
// MSVC for example does not support the feature detection macro for instance so we compile it out
// NOTE: MSVC for example does not support the feature detection macro for instance so we compile it
// out
#if defined(__has_feature)
#define DN_HAS_FEATURE(expr) __has_feature(expr)
#else
@@ -29,7 +31,7 @@
#endif
// NOTE: __has_builtin
// MSVC for example does not support the feature detection macro for instance so we compile it out
// NOTE: MSVC for example does not support the feature detection macro for instance so we compile it out
#if defined(__has_builtin)
#define DN_HAS_BUILTIN(expr) __has_builtin(expr)
#else
@@ -244,15 +246,6 @@
typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \
DN_GCC_WARNING_POP
#define DN_Check(expr) DN_CheckF(expr, "")
#if defined(DN_NO_CHECK_BREAK)
#define DN_CheckF(expr, fmt, ...) \
((expr) ? true : (DN_LogWarningF(fmt, ##__VA_ARGS__), false))
#else
#define DN_CheckF(expr, fmt, ...) \
((expr) ? true : (DN_LogErrorF(fmt, ##__VA_ARGS__), DN_StackTracePrint(128 /*limit*/), DN_DebugBreak, false))
#endif
#if defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
#define DN_64_BIT
#else
@@ -943,12 +936,6 @@ struct DN_Allocator
void* context;
};
struct DN_ArenaStatsStr8x64
{
DN_Str8x64 info;
DN_Str8x64 hwm;
};
enum DN_ArenaReset
{
DN_ArenaReset_No,
@@ -1106,6 +1093,7 @@ enum DN_Str8FindFlag_
typedef DN_USize DN_Str8SplitFlags;
enum DN_Str8SplitFlags_
{
DN_Str8SplitFlags_Nil = 0,
DN_Str8SplitFlags_ExcludeEmptyStrings = 1 << 0,
DN_Str8SplitFlags_HandleQuotedStrings = 1 << 1,
};
@@ -1765,7 +1753,7 @@ DN_API DN_I16 DN_SaturateCastI64ToI16
DN_API DN_I32 DN_SaturateCastI64ToI32 (DN_I64 val);
DN_API DN_UInt DN_SaturateCastI64ToUInt (DN_I64 val);
DN_API DN_ISize DN_SaturateCastI64ToUSize (DN_I64 val);
DN_API DN_USize DN_SaturateCastI64ToUSize (DN_I64 val);
DN_API DN_U8 DN_SaturateCastI64ToU8 (DN_I64 val);
DN_API DN_U16 DN_SaturateCastI64ToU16 (DN_I64 val);
DN_API DN_U32 DN_SaturateCastI64ToU32 (DN_I64 val);
@@ -1974,12 +1962,14 @@ DN_API DN_USize DN_CStr16Size
#define DN_Str8Lit(c_str) DN_Literal(DN_Str8){(char *)(c_str), sizeof(c_str) - 1}
#define DN_Str8PrintFmt(string) (int)((string).size), (string).data
#define DN_Str8FromPtr(data, size) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(size)}
#define DN_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->size)
#define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array))
DN_API DN_Str8 DN_Str8AllocAllocator (DN_USize size, DN_ZMem z_mem, DN_Allocator allocator);
DN_API DN_Str8 DN_Str8AllocArena (DN_USize size, DN_ZMem z_mem, DN_Arena *arena);
DN_API DN_Str8 DN_Str8AllocPool (DN_USize size, DN_Pool *pool);
DN_API DN_Str8 DN_Str8FromCStr8 (char const *src);
DN_API DN_Str8 DN_Str8FromCStr8Arena (char const *src, DN_Arena *arena);
DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize size, DN_Arena *arena);
@@ -1993,6 +1983,7 @@ DN_API DN_Str8 DN_Str8FromFmtAllocator
DN_API DN_Str8 DN_Str8FromFmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8 DN_Str8FromFmtVPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_Str8 DN_Str8FromFmtPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8x16 DN_Str8x16FromFmt (DN_FMT_ATTRIB char const *fmt, ...);
DN_API DN_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...);
@@ -2021,6 +2012,7 @@ DN_API void DN_Str8x512AppendFmt
DN_API void DN_Str8x512AppendFmtV (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API void DN_Str8x1024AppendFmt (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void DN_Str8x1024AppendFmtV (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, va_list args);
DN_API DN_Str8x32 DN_Str8x32FromU64 (DN_U64 val, char separator);
DN_API bool DN_Str8IsAll (DN_Str8 string, DN_Str8IsAllType is_all);
DN_API char * DN_Str8End (DN_Str8 string);
@@ -2045,6 +2037,7 @@ DN_API bool DN_Str8StartsWithInsensitive
DN_API bool DN_Str8EndsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
DN_API bool DN_Str8EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix);
DN_API bool DN_Str8HasChar (DN_Str8 string, char ch);
DN_API DN_Str8 DN_Str8TrimPrefix (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
DN_API DN_Str8 DN_Str8TrimHexPrefix (DN_Str8 string);
DN_API DN_Str8 DN_Str8TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
@@ -2053,6 +2046,7 @@ DN_API DN_Str8 DN_Str8TrimHeadWhitespace
DN_API DN_Str8 DN_Str8TrimTailWhitespace (DN_Str8 string);
DN_API DN_Str8 DN_Str8TrimWhitespaceAround (DN_Str8 string);
DN_API DN_Str8 DN_Str8TrimByteOrderMark (DN_Str8 string);
DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path);
DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path);
DN_API DN_Str8 DN_Str8FilePathNoExtension (DN_Str8 path);
@@ -2076,6 +2070,15 @@ DN_API DN_Str8 DN_Str8LineBreakAllocator
DN_API DN_Str8 DN_Str8LineBreakArena (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena);
DN_API DN_Str8 DN_Str8Table (DN_Str8 const* rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena);
#if DN_STR8_AVX512F
DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F (DN_Str8 string, DN_Str8 find);
DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F (DN_Str8 string, DN_Str8 find);
DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F (DN_Str8 string, DN_Str8 find);
DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F (DN_Str8 string, DN_Str8 find);
DN_API DN_USize DN_Str8SplitAVX512F (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags flags);
DN_API DN_Str8Slice DN_Str8SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags flags);
#endif
DN_API DN_Str8 DN_Str8SliceRender (DN_Str8Slice array, DN_Str8 separator, DN_Arena *arena);
DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena);
DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case);
@@ -2557,8 +2560,8 @@ DN_API DN_M4 DN_M4MulF
DN_API DN_M4 DN_M4DivF (DN_M4 lhs, DN_F32 rhs);
DN_API DN_Str8x256 DN_M4ColumnMajorString (DN_M4 mat);
DN_API bool operator== (DN_M2x3 const &lhs, DN_M2x3 const &rhs);
DN_API bool operator!= (DN_M2x3 const &lhs, DN_M2x3 const &rhs);
DN_API bool DN_M2x3Eq (DN_M2x3 const *lhs, DN_M2x3 const *rhs);
DN_API bool DN_M2x3NotEq (DN_M2x3 const *lhs, DN_M2x3 const *rhs);
DN_API DN_M2x3 DN_M2x3Identity ();
DN_API DN_M2x3 DN_M2x3Translate (DN_V2F32 offset);
DN_API DN_V2F32 DN_M2x3ScaleGet (DN_M2x3 m2x3);
@@ -2578,7 +2581,6 @@ DN_API DN_Rect DN_M2x3MulRect
#define DN_RectFrom4N(x, y, w, h) DN_Literal(DN_Rect){DN_Literal(DN_V2F32){{x, y}}, DN_Literal(DN_V2F32){{w, h}}}
#define DN_RectZero DN_RectFrom4N(0, 0, 0, 0)
DN_API bool operator== (const DN_Rect& lhs, const DN_Rect& rhs);
DN_API DN_V2F32 DN_RectCenter (DN_Rect rect);
DN_API bool DN_RectContainsPoint (DN_Rect rect, DN_V2F32 p);
DN_API bool DN_RectContainsRect (DN_Rect a, DN_Rect b);
@@ -3045,9 +3047,10 @@ DN_API void DN_RingWrite (DN_Rin
DN_API void DN_RingRead (DN_Ring *ring, void *dest, DN_U64 dest_size);
#define DN_RingReadStruct(ring, dest) DN_RingRead((ring), (dest), sizeof(*(dest)))
// TODO: Replace with a C-style hash table
#if defined(__cplusplus)
DN_U32 const DN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49;
DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0;
template <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> bool DN_DSMapIsValid (DN_DSMap<T> const *map);
@@ -3074,7 +3077,240 @@ template <typename T> DN_DSMapKey DN_DSMapKeyStr8 (DN_DSMap
DN_API DN_DSMapKey DN_DSMapKeyU64NoHash (DN_U64 u64);
DN_API bool DN_DSMapKeyEquals (DN_DSMapKey lhs, DN_DSMapKey rhs);
DN_API bool operator== (DN_DSMapKey lhs, DN_DSMapKey rhs);
#endif
enum DN_BinPackMode
{
DN_BinPackMode_Serialise,
DN_BinPackMode_Deserialise,
};
struct DN_BinPack
{
DN_Str8Builder writer;
DN_Str8 read;
DN_USize read_index;
};
DN_API bool DN_BinPackIsEndOfReadStream(DN_BinPack const *pack);
DN_API void DN_BinPackUSize (DN_BinPack *pack, DN_BinPackMode mode, DN_USize *item);
DN_API void DN_BinPackU64 (DN_BinPack *pack, DN_BinPackMode mode, DN_U64 *item);
DN_API void DN_BinPackU32 (DN_BinPack *pack, DN_BinPackMode mode, DN_U32 *item);
DN_API void DN_BinPackU16 (DN_BinPack *pack, DN_BinPackMode mode, DN_U16 *item);
DN_API void DN_BinPackU8 (DN_BinPack *pack, DN_BinPackMode mode, DN_U8 *item);
DN_API void DN_BinPackI64 (DN_BinPack *pack, DN_BinPackMode mode, DN_I64 *item);
DN_API void DN_BinPackI32 (DN_BinPack *pack, DN_BinPackMode mode, DN_I32 *item);
DN_API void DN_BinPackI16 (DN_BinPack *pack, DN_BinPackMode mode, DN_I16 *item);
DN_API void DN_BinPackI8 (DN_BinPack *pack, DN_BinPackMode mode, DN_I8 *item);
DN_API void DN_BinPackF64 (DN_BinPack *pack, DN_BinPackMode mode, DN_F64 *item);
DN_API void DN_BinPackF32 (DN_BinPack *pack, DN_BinPackMode mode, DN_F32 *item);
DN_API void DN_BinPackV2 (DN_BinPack *pack, DN_BinPackMode mode, DN_V2F32 *item);
DN_API void DN_BinPackV4 (DN_BinPack *pack, DN_BinPackMode mode, DN_V4F32 *item);
DN_API void DN_BinPackBool (DN_BinPack *pack, DN_BinPackMode mode, bool *item);
DN_API void DN_BinPackStr8FromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, DN_Str8 *string);
DN_API void DN_BinPackStr8FromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, DN_Str8 *string);
DN_API DN_Str8 DN_BinPackStr8FromBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max);
DN_API void DN_BinPackBytesFromArena (DN_BinPack *pack, DN_Arena *arena, DN_BinPackMode mode, void **ptr, DN_USize *size);
DN_API void DN_BinPackBytesFromPool (DN_BinPack *pack, DN_Pool *pool, DN_BinPackMode mode, void **ptr, DN_USize *size);
DN_API void DN_BinPackCArray (DN_BinPack *pack, DN_BinPackMode mode, void *ptr, DN_USize size);
DN_API void DN_BinPackCBuffer (DN_BinPack *pack, DN_BinPackMode mode, char *ptr, DN_USize *size, DN_USize max);
DN_API DN_Str8 DN_BinPackBuild (DN_BinPack const *pack, DN_Arena *arena);
enum DN_CSVSerialise
{
DN_CSVSerialise_Read,
DN_CSVSerialise_Write,
};
struct DN_CSVTokeniser
{
bool bad;
DN_Str8 string;
char delimiter;
char const *it;
bool end_of_line;
};
struct DN_CSVPack
{
DN_Str8Builder write_builder;
DN_USize write_column;
DN_CSVTokeniser read_tokeniser;
};
// NOTE: Data structures to create and parse CSV files, supports Python style escaped quotes (e.g.
// Using "" to escape quotes inside a quoted string).
//
// API
// DN_CSVTokeniserNextN: Reads the next N consecutive fields from the parser. If `column_iterator`
// is `false` then the read of the N consecutive fields does not proceed past the end of the
// current CSV row. If `true` then it reads the next N fields even if reading would progress onto
// the next row.
DN_API DN_CSVTokeniser DN_CSVTokeniserInit (DN_Str8 string, char delimiter);
DN_API bool DN_CSVTokeniserValid (DN_CSVTokeniser *tokeniser);
DN_API bool DN_CSVTokeniserNextRow (DN_CSVTokeniser *tokeniser);
DN_API DN_Str8 DN_CSVTokeniserNextField (DN_CSVTokeniser *tokeniser);
DN_API DN_Str8 DN_CSVTokeniserNextColumn (DN_CSVTokeniser *tokeniser);
DN_API void DN_CSVTokeniserSkipLine (DN_CSVTokeniser *tokeniser);
DN_API int DN_CSVTokeniserNextN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size, bool column_iterator);
DN_API int DN_CSVTokeniserNextColumnN(DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size);
DN_API int DN_CSVTokeniserNextFieldN (DN_CSVTokeniser *tokeniser, DN_Str8 *fields, int fields_size);
DN_API void DN_CSVTokeniserSkipLineN (DN_CSVTokeniser *tokeniser, int count);
DN_API void DN_CSVPackU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U64 *value);
DN_API void DN_CSVPackI64 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I64 *value);
DN_API void DN_CSVPackI32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I32 *value);
DN_API void DN_CSVPackI16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I16 *value);
DN_API void DN_CSVPackI8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_I8 *value);
DN_API void DN_CSVPackU32 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U32 *value);
DN_API void DN_CSVPackU16 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_U16 *value);
DN_API void DN_CSVPackBoolAsU64 (DN_CSVPack *pack, DN_CSVSerialise serialise, bool *value);
DN_API void DN_CSVPackStr8 (DN_CSVPack *pack, DN_CSVSerialise serialise, DN_Str8 *str8, DN_Arena *arena);
DN_API void DN_CSVPackBuffer (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size);
DN_API void DN_CSVPackBufferWithMax (DN_CSVPack *pack, DN_CSVSerialise serialise, void *dest, size_t *size, size_t max);
DN_API bool DN_CSVPackNewLine (DN_CSVPack *pack, DN_CSVSerialise serialise);
// TODO: Replace with a C implementation
template <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)
template <typename T> T *DN_TMemCopyObj(T *dest, T const *src, DN_USize count)
{
-814
View File
@@ -1,814 +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, inclusive of begin_index
// Range: [begin_index + count + 1, begin_index + 1)
// Which is: [begin_index - abs(count) + 1, begin_index + 1)
DN_USize abs_count = DN_Abs(count);
start = (begin_index + 1 > abs_count) ? (begin_index + 1 - abs_count) : 0;
end = begin_index + 1;
} else {
start = begin_index;
end = begin_index + count;
}
// Clamp indices to valid bounds
start = DN_Min(start, *size);
end = DN_Min(end, *size);
// Erase the range [start, end)
DN_USize erase_count = end > start ? end - start : 0;
if (erase_count) {
char *dest = (char *)data + (elem_size * start);
char *array_end = (char *)data + (elem_size * *size);
char *src = dest + (elem_size * erase_count);
if (erase == DN_ArrayErase_Stable) {
DN_USize move_size = array_end - src;
DN_Memmove(dest, src, move_size);
} else {
char *unstable_src = array_end - (elem_size * erase_count);
DN_USize move_size = array_end - unstable_src;
DN_Memcpy(dest, unstable_src, move_size);
}
*size -= erase_count;
}
result.items_erased = erase_count;
result.it_index = start ? start - 1 : start;
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;
}
-135
View File
@@ -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));
}
}
-50
View File
@@ -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
-124
View File
@@ -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;
}
-56
View File
@@ -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
-206
View File
@@ -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_Str8BuilderBuild(&pack->writer, arena);
return result;
}
-47
View File
@@ -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
-185
View File
@@ -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
-290
View File
@@ -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;
}
-62
View File
@@ -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
-153
View File
@@ -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);
}
-185
View File
@@ -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)
-428
View File
@@ -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);
}
}
-96
View File
@@ -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)
+16 -167
View File
@@ -1222,7 +1222,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Stable);
int expected[] = {0, 1, 2, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 2);
DN_UT_Assert(&result, erase.it_index == 3);
DN_UT_Assert(&result, erase.it_index == 2);
DN_UT_Assert(&result, size == 8);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1231,9 +1231,9 @@ static DN_UTCore DN_TST_BaseArray()
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_USize size = 10;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Stable);
int expected[] = {0, 1, 2, 6, 7, 8, 9};
int expected[] = {0, 1, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 3);
DN_UT_Assert(&result, erase.it_index == 3);
DN_UT_Assert(&result, erase.it_index == 1);
DN_UT_Assert(&result, size == 7);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1242,9 +1242,9 @@ static DN_UTCore DN_TST_BaseArray()
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_USize size = 10;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -1, DN_ArrayErase_Stable);
int expected[] = {0, 1, 2, 3, 4, 6, 7, 8, 9};
int expected[] = {0, 1, 2, 3, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 1);
DN_UT_Assert(&result, erase.it_index == 5);
DN_UT_AssertF(&result, erase.it_index == 3, "lhs=%zu", erase.it_index);
DN_UT_Assert(&result, size == 9);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1255,7 +1255,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 3, 2, DN_ArrayErase_Unstable);
int expected[] = {0, 1, 2, 8, 9, 5, 6, 7};
DN_UT_Assert(&result, erase.items_erased == 2);
DN_UT_Assert(&result, erase.it_index == 3);
DN_UT_Assert(&result, erase.it_index == 2);
DN_UT_Assert(&result, size == 8);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1264,9 +1264,9 @@ static DN_UTCore DN_TST_BaseArray()
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_USize size = 10;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, -3, DN_ArrayErase_Unstable);
int expected[] = {0, 1, 2, 7, 8, 9, 6};
int expected[] = {0, 1, 7, 8, 9, 5, 6};
DN_UT_Assert(&result, erase.items_erased == 3);
DN_UT_Assert(&result, erase.it_index == 3);
DN_UT_Assert(&result, erase.it_index == 1);
DN_UT_Assert(&result, size == 7);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1275,10 +1275,10 @@ static DN_UTCore DN_TST_BaseArray()
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_USize size = 10;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 0, -2, DN_ArrayErase_Stable);
int expected[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 1);
int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 0);
DN_UT_Assert(&result, size == 9);
DN_UT_Assert(&result, size == 10);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1299,7 +1299,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 0, DN_ArrayErase_Stable);
int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 0);
DN_UT_Assert(&result, erase.it_index == 5);
DN_UT_Assert(&result, size == 10);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1308,7 +1308,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_USize size = 10;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(nullptr, &size, sizeof(int), 5, 2, DN_ArrayErase_Stable);
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 0);
DN_UT_Assert(&result, erase.it_index == 5);
DN_UT_Assert(&result, size == 10);
}
@@ -1316,7 +1316,7 @@ static DN_UTCore DN_TST_BaseArray()
int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, NULL, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable);
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 0);
DN_UT_Assert(&result, erase.it_index == 5);
}
for (DN_UT_Test(&result, "Invalid input - empty array")) {
@@ -1324,7 +1324,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_USize size = 0;
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 5, 2, DN_ArrayErase_Stable);
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 0);
DN_UT_Assert(&result, erase.it_index == 5);
DN_UT_Assert(&result, size == 0);
}
@@ -1334,7 +1334,7 @@ static DN_UTCore DN_TST_BaseArray()
DN_ArrayEraseResult erase = DN_ArrayEraseRange(arr, &size, sizeof(arr[0]), 15, 2, DN_ArrayErase_Stable);
int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
DN_UT_Assert(&result, erase.items_erased == 0);
DN_UT_Assert(&result, erase.it_index == 10);
DN_UT_Assert(&result, erase.it_index == 9);
DN_UT_Assert(&result, size == 10);
DN_UT_Assert(&result, DN_Memcmp(arr, expected, size * sizeof(arr[0])) == 0);
}
@@ -1342,156 +1342,6 @@ static DN_UTCore DN_TST_BaseArray()
return result;
}
static DN_UTCore DN_TST_BaseVArray()
{
DN_UTCore result = DN_UT_Init();
DN_UT_LogF(&result, "DN_VArray\n");
{
{
DN_VArray<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 stable erase, 1 item, the '2' value from the array")) {
DN_OS_VArrayEraseRange(&array, 2 /*begin_index*/, 1 /*count*/, DN_ArrayErase_Stable);
DN_U32 array_literal[] = {0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
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 unstable erase, 1 item, the '1' value from the array")) {
DN_OS_VArrayEraseRange(&array, 1 /*begin_index*/, 1 /*count*/, DN_ArrayErase_Unstable);
DN_U32 array_literal[] = {0, 15, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
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);
}
DN_ArrayErase erase_enums[] = {DN_ArrayErase_Stable, DN_ArrayErase_Unstable};
for (DN_UT_Test(&result, "Test un/stable erase, OOB")) {
for (DN_ArrayErase erase : erase_enums) {
DN_U32 array_literal[] = {0, 15, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
DN_OS_VArrayEraseRange(&array, DN_ArrayCountU(array_literal) /*begin_index*/, DN_ArrayCountU(array_literal) + 100 /*count*/, erase);
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 flipped begin/end index stable erase, 2 items, the '15, 3' value from the array")) {
DN_OS_VArrayEraseRange(&array, 2 /*begin_index*/, -2 /*count*/, DN_ArrayErase_Stable);
DN_U32 array_literal[] = {0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
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 flipped begin/end index unstable erase, 2 items, the '4, 5' value from the array")) {
DN_OS_VArrayEraseRange(&array, 2 /*begin_index*/, -2 /*count*/, DN_ArrayErase_Unstable);
DN_U32 array_literal[] = {0, 13, 14, 6, 7, 8, 9, 10, 11, 12};
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 stable erase range, 2+1 (oob) item, the '13, 14, +1 OOB' value from the array")) {
DN_OS_VArrayEraseRange(&array, 8 /*begin_index*/, 3 /*count*/, DN_ArrayErase_Stable);
DN_U32 array_literal[] = {0, 13, 14, 6, 7, 8, 9, 10};
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 unstable erase range, 3+1 (oob) item, the '11, 12, +1 OOB' value from the array")) {
DN_OS_VArrayEraseRange(&array, 6 /*begin_index*/, 3 /*count*/, DN_ArrayErase_Unstable);
DN_U32 array_literal[] = {0, 13, 14, 6, 7, 8};
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 stable erase -overflow OOB, erasing the '0, 13' value from the array")) {
DN_OS_VArrayEraseRange(&array, 1 /*begin_index*/, -DN_ISIZE_MAX /*count*/, DN_ArrayErase_Stable);
DN_U32 array_literal[] = {14, 6, 7, 8};
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 unstable erase +overflow OOB, erasing the '7, 8' value from the array")) {
DN_OS_VArrayEraseRange(&array, 2 /*begin_index*/, DN_ISIZE_MAX /*count*/, DN_ArrayErase_Unstable);
DN_U32 array_literal[] = {14, 6};
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 after erase")) {
DN_U32 array_literal[] = {0, 1, 2, 3};
DN_OS_VArrayAddArray<DN_U32>(&array, array_literal, DN_ArrayCountU(array_literal));
DN_U32 expected_literal[] = {14, 6, 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)
DN_GCC_WARNING_PUSH
DN_GCC_WARNING_DISABLE(-Wunused-parameter)
@@ -2761,7 +2611,6 @@ DN_TSTResult DN_TST_RunSuite(DN_TSTPrint print)
DN_TST_BaseDSMap(),
DN_TST_BaseIArray(),
DN_TST_BaseArray(),
DN_TST_BaseVArray(),
DN_TST_Keccak(),
DN_TST_M4(),
DN_TST_OS(),
-37
View File
@@ -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;
}
-68
View File
@@ -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)
+109 -181
View File
@@ -883,27 +883,122 @@ DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip(DN_OSThreadLane lane)
return result;
}
// NOTE: DN_OSHttp
DN_API void DN_OS_HttpRequestWait(DN_OSHttpResponse *response)
static DN_I32 DN_OS_AsyncThreadEntryPoint_(DN_OSThread *thread)
{
if (response && response->on_complete_semaphore.handle != 0)
DN_OS_SemaphoreWait(&response->on_complete_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
DN_OS_ThreadSetNameFmt("%.*s", DN_Str8PrintFmt(thread->name));
DN_OSAsyncCore *async = DN_Cast(DN_OSAsyncCore *) thread->user_context;
DN_Ring *ring = &async->ring;
for (;;) {
DN_OS_SemaphoreWait(&async->worker_sem, UINT32_MAX);
if (async->join_threads)
break;
DN_OSAsyncTask task = {};
for (DN_OS_MutexScope(&async->ring_mutex)) {
if (DN_RingHasData(ring, sizeof(task)))
DN_RingRead(ring, &task, sizeof(task));
}
DN_API DN_OSHttpResponse DN_OS_HttpRequest(DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers)
{
// TODO(doyle): Revise the memory allocation and its lifetime
DN_OSHttpResponse result = {};
DN_TCScratch scratch = DN_TCScratchBeginArena(&arena, 1);
result.scratch_arena = scratch.arena;
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 void DN_OS_AsyncInit(DN_OSAsyncCore *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_OS_AsyncThreadEntryPoint_, /*lane=*/ nullptr, DN_TCInitArgsDefault(), async);
}
}
DN_API void DN_OS_AsyncDeinit(DN_OSAsyncCore *async)
{
DN_Assert(async);
DN_AtomicSetValue32(&async->join_threads, true);
DN_OS_SemaphoreIncrement(&async->worker_sem, async->thread_count);
for (DN_ForItSize(it, DN_OSThread, async->threads, async->thread_count))
DN_OS_ThreadJoin(it.data, DN_TCDeinitArenas_Yes);
}
static bool DN_OS_AsyncQueueTask_(DN_OSAsyncCore *async, DN_OSAsyncTask const *task, DN_U64 wait_time_ms) {
DN_U64 end_time_ms = DN_OS_DateUnixTimeMs() + wait_time_ms;
bool result = false;
for (DN_OS_MutexScope(&async->ring_mutex)) {
for (;;) {
if (DN_RingHasSpace(&async->ring, sizeof(*task))) {
DN_RingWriteStruct(&async->ring, task);
result = true;
break;
}
DN_OS_ConditionVariableWaitUntil(&async->ring_write_cv, &async->ring_mutex, end_time_ms);
if (DN_OS_DateUnixTimeMs() >= end_time_ms)
break;
}
}
if (result)
DN_OS_SemaphoreIncrement(&async->worker_sem, 1); // Flag that a job is available
DN_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers);
DN_OS_HttpRequestWait(&result);
DN_TCScratchEnd(&scratch);
return result;
}
// NOTE: DN_OSPrint
DN_API bool DN_OS_AsyncQueueWork(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms)
{
DN_OSAsyncTask task = {};
task.work.func = func;
task.work.input = input;
bool result = DN_OS_AsyncQueueTask_(async, &task, wait_time_ms);
return result;
}
DN_API DN_OSAsyncTask DN_OS_AsyncQueueTask(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms)
{
DN_OSAsyncTask result = {};
result.work.func = func;
result.work.input = input;
result.completion_sem = DN_OS_SemaphoreInit(0);
result.queued = DN_OS_AsyncQueueTask_(async, &result, wait_time_ms);
if (!result.queued)
DN_OS_SemaphoreDeinit(&result.completion_sem);
return result;
}
DN_API bool DN_OS_AsyncWaitTask(DN_OSAsyncTask *task, DN_U32 timeout_ms)
{
bool result = true;
if (!task->queued)
return result;
DN_OSSemaphoreWaitResult wait = DN_OS_SemaphoreWait(&task->completion_sem, timeout_ms);
result = wait == DN_OSSemaphoreWaitResult_Success;
if (result)
DN_OS_SemaphoreDeinit(&task->completion_sem);
return result;
}
DN_API DN_LogStyle DN_OS_PrintStyleColour(uint8_t r, uint8_t g, uint8_t b, DN_LogBold bold)
{
DN_LogStyle result = {};
@@ -1077,173 +1172,6 @@ DN_API void DN_OS_PrintLnFVStyle(DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_
DN_OS_Print(dest, DN_Str8Lit("\n"));
}
// NOTE: DN_VArray
template <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_StackTrace result = {};
+47 -87
View File
@@ -31,10 +31,6 @@
#endif
#endif
#if defined(DN_PLATFORM_EMSCRIPTEN)
#include <emscripten/fetch.h> // emscripten_fetch (for DN_OSHttpResponse)
#endif
extern DN_CPUFeatureDecl g_dn_cpu_feature_decl[DN_CPUFeature_Count];
struct DN_OSTimer /// Record time between two time-points using the OS's performance counter.
@@ -229,42 +225,6 @@ struct DN_OSThread
DN_TCInitArgs tc_init_args;
};
// NOTE: DN_OSHttp
enum DN_OSHttpRequestSecure
{
DN_OSHttpRequestSecure_No,
DN_OSHttpRequestSecure_Yes,
};
struct DN_OSHttpResponse
{
// NOTE: Response data
DN_U32 error_code;
DN_Str8 error_msg;
DN_U16 http_status;
DN_Str8 body;
DN_B32 done;
// NOTE: Book-keeping
DN_Arena *arena; // Allocates memory for the response
// NOTE: Async book-keeping
// Synchronous HTTP response uses the TLS scratch arena whereas async
// calls use their own dedicated arena.
DN_Arena tmp_arena;
DN_Arena scratch_arena;
DN_Str8Builder builder;
DN_OSSemaphore on_complete_semaphore;
#if defined(DN_PLATFORM_EMSCRIPTEN)
emscripten_fetch_t *em_handle;
#elif defined(DN_PLATFORM_WIN32)
HINTERNET w32_request_session;
HINTERNET w32_request_connection;
HINTERNET w32_request_handle;
#endif
};
struct DN_OSCore
{
DN_CPUReport cpu_report;
@@ -368,7 +328,7 @@ DN_API bool DN_OS_FileWriteF (D
DN_API bool DN_OS_FileFlush (DN_OSFile *file, DN_ErrSink *err);
DN_API void DN_OS_FileClose (DN_OSFile *file);
DN_API DN_Str8 DN_OS_FileReadAll (DN_Allocator alloc_type, void *allocator, DN_Str8 path, DN_ErrSink *err);
DN_API DN_Str8 DN_OS_FileReadAll (DN_Allocator allocator, DN_Str8 path, DN_ErrSink *err);
DN_API DN_Str8 DN_OS_FileReadAllArena (DN_Arena *arena, DN_Str8 path, DN_ErrSink *err);
DN_API DN_Str8 DN_OS_FileReadAllPool (DN_Pool *pool, DN_Str8 path, DN_ErrSink *err);
@@ -509,10 +469,52 @@ DN_API DN_OSThreadLane* DN_OS_TCThreadLane ()
DN_API void DN_OS_TCThreadLaneSync (void **ptr_to_share);
DN_API DN_OSThreadLane DN_OS_TCThreadLaneEquip (DN_OSThreadLane lane);
DN_API void DN_OS_HttpRequestAsync (DN_OSHttpResponse *response, DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
DN_API void DN_OS_HttpRequestWait (DN_OSHttpResponse *response);
DN_API void DN_OS_HttpRequestFree (DN_OSHttpResponse *response);
DN_API DN_OSHttpResponse DN_OS_HttpRequest (DN_Arena *arena, DN_Str8 host, DN_Str8 path, DN_OSHttpRequestSecure secure, DN_Str8 method, DN_Str8 body, DN_Str8 headers);
enum DN_OSAsyncPriority
{
DN_OSAsyncPriority_Low,
DN_OSAsyncPriority_High,
DN_OSAsyncPriority_Count,
};
struct DN_OSAsyncCore
{
DN_OSMutex ring_mutex;
DN_OSConditionVariable ring_write_cv;
DN_OSSemaphore worker_sem;
DN_Ring ring;
DN_OSThread *threads;
DN_U32 thread_count;
DN_U32 busy_threads;
DN_U32 join_threads;
};
struct DN_OSAsyncWorkArgs
{
DN_OSThread *thread;
void *input;
};
typedef void(DN_OSAsyncWorkFunc)(DN_OSAsyncWorkArgs work_args);
struct DN_OSAsyncWork
{
DN_OSAsyncWorkFunc *func;
void *input;
void *output;
};
struct DN_OSAsyncTask
{
bool queued;
DN_OSAsyncWork work;
DN_OSSemaphore completion_sem;
};
DN_API void DN_OS_AsyncInit (DN_OSAsyncCore *async, char *base, DN_USize base_size, DN_OSThread *threads, DN_U32 threads_size);
DN_API void DN_OS_AsyncDeinit (DN_OSAsyncCore *async);
DN_API bool DN_OS_AsyncQueueWork(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms);
DN_API DN_OSAsyncTask DN_OS_AsyncQueueTask(DN_OSAsyncCore *async, DN_OSAsyncWorkFunc *func, void *input, DN_U64 wait_time_ms);
DN_API bool DN_OS_AsyncWaitTask (DN_OSAsyncTask *task, DN_U32 timeout_ms);
// NOTE: DN_OSPrint
enum DN_OSPrintDest
@@ -570,46 +572,4 @@ DN_API void DN_OS_PrintLnFV (DN_OSPrintDest dest, DN_FMT_ATTRIB char
DN_API void DN_OS_PrintLnStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_Str8 string);
DN_API void DN_OS_PrintLnFStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, ...);
DN_API void DN_OS_PrintLnFVStyle (DN_OSPrintDest dest, DN_LogStyle style, DN_FMT_ATTRIB char const *fmt, va_list args);
// NOTE: DN_VArray
// TODO(doyle): Add an API for shrinking the array by decomitting pages back to the OS.
template <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)
-151
View File
@@ -1444,154 +1444,3 @@ DN_API DN_OSPosixProcSelfStatus DN_OS_PosixProcSelfStatus()
DN_OS_FileClose(&file);
return result;
}
// NOTE: DN_OSHttp /////////////////////////////////////////////////////////////////////////////////
#if 0 // TODO(doyle): Implement websockets for Windows and Emscripten
static EM_BOOL EMWebSocketOnOpenCallback(int type, const EmscriptenWebSocketOpenEvent *event, void *user_context)
{
(void)user_context;
(void)type;
(void)event;
// EMSCRIPTEN_RESULT result = emscripten_websocket_send_utf8_text(event->socket, R"({"jsonrpc":"2.0","id":1,"method": "eth_subscribe","params":["newHeads"]})");
// if (result)
// DN_LogInfoF("Failed to emscripten_websocket_send_utf8_text(): %d\n", result);
return EM_TRUE;
}
static EM_BOOL EMWebSocketOnMsgCallback(int type, const EmscriptenWebSocketMessageEvent *event __attribute__((nonnull)), void *user_context)
{
(void)type;
(void)user_context;
(void)event;
if (event->isText) {
DN_LogInfoF("Received: %.*s", event->numBytes, event->data);
} else {
DN_LogInfoF("Received: %d bytes", event->numBytes);
}
return EM_TRUE;
}
static EM_BOOL EMWebSocketOnErrorCallback(int type, const EmscriptenWebSocketErrorEvent *event, void *user_context)
{
(void)user_context;
(void)type;
(void)event;
return EM_TRUE;
}
static EM_BOOL EMWebSocketOnCloseCallback(int type, const EmscriptenWebSocketCloseEvent *event, void *user_context)
{
(void)user_context;
(void)type;
(void)event;
return EM_TRUE;
}
#endif
#if defined(DN_PLATFORM_EMSCRIPTEN)
static void DN_OS_HttpRequestEMFetchOnSuccessCallback(emscripten_fetch_t *fetch)
{
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData;
if (!DN_Check(response))
return;
response->http_status = DN_Cast(DN_U32) fetch->status;
response->body = DN_Str8AllocArena(fetch->numBytes, DN_ZMem_No, response->arena);
if (response->body.data)
DN_Memcpy(response->body.data, fetch->data, fetch->numBytes);
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
DN_AtomicAddU32(&response->done, 1);
}
static void DN_OS_HttpRequestEMFetchOnErrorCallback(emscripten_fetch_t *fetch)
{
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) fetch->userData;
if (!DN_Check(response))
return;
response->http_status = DN_Cast(DN_U32) fetch->status;
response->body = DN_Str8AllocArena(fetch->numBytes, DN_ZMem_No, response->arena);
if (response->body.size)
DN_Memcpy(response->body.data, fetch->data, fetch->numBytes);
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
DN_AtomicAddU32(&response->done, 1);
}
#endif
DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response,
DN_Arena *arena,
DN_Str8 host,
DN_Str8 path,
DN_OSHttpRequestSecure secure,
DN_Str8 method,
DN_Str8 body,
DN_Str8 headers)
{
if (!response || !arena)
return;
response->arena = arena;
response->builder.arena = response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena;
DN_Arena *scratch = &response->scratch_arena;
DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1);
DN_DEFER { DN_TCScratchEnd(&scratch_); };
if (!scratch)
scratch = &scratch_.arena;
#if defined(DN_PLATFORM_EMSCRIPTEN)
emscripten_fetch_attr_t fetch_attribs = {};
emscripten_fetch_attr_init(&fetch_attribs);
if (method.size >= sizeof(fetch_attribs.requestMethod)) {
response->error_msg =
DN_Str8FromFmtArena(arena,
"Request method in EM has a size limit of 31 characters, method was "
"'%.*s' which is %zu characters long",
DN_Str8PrintFmt(method),
method.size);
DN_CheckF(method.size < sizeof(fetch_attribs.requestMethod),
"%.*s",
DN_Str8PrintFmt(response->error_msg));
response->error_code = DN_Cast(DN_U32) - 1;
DN_AtomicAddU32(&response->done, 1);
return;
}
DN_Memcpy(fetch_attribs.requestMethod, method.data, method.size);
fetch_attribs.requestData = body.data;
fetch_attribs.requestDataSize = body.size;
fetch_attribs.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
fetch_attribs.onsuccess = DN_OS_HttpRequestEMFetchOnSuccessCallback;
fetch_attribs.onerror = DN_OS_HttpRequestEMFetchOnErrorCallback;
fetch_attribs.userData = response;
DN_Str8 url = DN_Str8FromFmtArena(scratch, "%.*s%.*s", DN_Str8PrintFmt(host), DN_Str8PrintFmt(path));
DN_LogInfoF("Initiating HTTP '%s' request to '%.*s' with payload '%.*s'",
fetch_attribs.requestMethod,
DN_Str8PrintFmt(url),
DN_Str8PrintFmt(body));
response->on_complete_semaphore = DN_OS_SemaphoreInit(0);
response->em_handle = emscripten_fetch(&fetch_attribs, url.data);
#else // #elif defined(DN_OS_WIN32)
DN_InvalidCodePathF("Unimplemented function");
#endif
}
DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response)
{
// NOTE: Cleanup
#if defined(DN_PLATFORM_EMSCRIPTEN)
if (response->em_handle) {
emscripten_fetch_close(response->em_handle);
response->em_handle = nullptr;
}
#endif // #elif defined(DN_OS_WIN32)
DN_MemListDeinit(response->tmp_arena.mem);
DN_OS_SemaphoreDeinit(&response->on_complete_semaphore);
*response = {};
}
+12 -212
View File
@@ -395,15 +395,9 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
if (!file || !file->handle || file->error || !buffer || size <= 0)
return result;
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
if (!DN_Check(size <= (unsigned long)-1)) {
DN_Str8x32 buffer_size_str8 = DN_Str8x32FromByteCountU64Auto(size);
DN_ErrSinkAppendF(
err,
1 /*error_code*/,
"Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO",
DN_Str8PrintFmt(buffer_size_str8));
DN_TCScratchEnd(&scratch);
if (size > ULONG_MAX) {
DN_Str8x32 desc = DN_Str8x32FromByteCountU64Auto(size);
DN_ErrSinkAppendF(err, 1 /*error_code*/, "Current implementation doesn't support reading >4GiB file (requested %.*s), implement Win32 overlapped IO", DN_Str8PrintFmt(desc));
return result;
}
@@ -414,6 +408,7 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
/*LPDWORD lpNumberOfByesRead*/ &bytes_read,
/*LPOVERLAPPED lpOverlapped*/ nullptr);
if (read_result == 0) {
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
DN_ErrSinkAppendF(err, win_error.code, "Failed to read data from file: (%u) %.*s", win_error.code, DN_Str8PrintFmt(win_error.msg));
DN_TCScratchEnd(&scratch);
@@ -421,6 +416,7 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
}
if (bytes_read != size) {
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena);
DN_ErrSinkAppendF(
err,
@@ -436,7 +432,6 @@ DN_API DN_OSFileRead DN_OS_FileRead(DN_OSFile *file, void *buffer, DN_USize size
result.bytes_read = bytes_read;
result.success = true;
DN_TCScratchEnd(&scratch);
return result;
}
@@ -732,7 +727,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle,
DN_ErrSinkAppendF(err, result.os_error_code, "Executed command failed to terminate: %.*s", DN_Str8PrintFmt(win_error.msg));
DN_TCScratchEnd(&scratch);
return result;
} else if (DN_Check(exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0)) {
} else if (exec_result == WAIT_TIMEOUT || exec_result == WAIT_OBJECT_0) {
// NOTE: Read stdout from process
// If the pipes are full, the process will block. We periodically
// flush the pipes to make sure this doesn't happen
@@ -1341,204 +1336,6 @@ DN_API void DN_OS_W32ThreadSetName(DN_Str8 name)
DN_TCScratchEnd(&scratch);
}
void DN_OS_HttpRequestWin32Callback(HINTERNET session, DWORD *dwContext, DWORD dwInternetStatus, VOID *lpvStatusInformation, DWORD dwStatusInformationLength)
{
(void)session;
(void)dwStatusInformationLength;
DN_OSHttpResponse *response = DN_Cast(DN_OSHttpResponse *) dwContext;
HINTERNET request = DN_Cast(HINTERNET) response->w32_request_handle;
DN_OSW32Error error = {};
DWORD const READ_BUFFER_SIZE = DN_Megabytes(1);
if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESOLVING_NAME) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_NAME_RESOLVED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDING_REQUEST) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_SENT) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CREATED) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DETECTING_PROXY) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REDIRECT) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE) {
DWORD status = 0;
DWORD status_size = sizeof(status_size);
if (WinHttpQueryHeaders(request,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&status,
&status_size,
WINHTTP_NO_HEADER_INDEX)) {
response->http_status = DN_Cast(uint16_t) status;
// NOTE: You can normally call into WinHttpQueryDataAvailable which means the kernel
// will buffer the response into a single buffer and return us the full size of the
// request.
//
// or
//
// You may call WinHttpReadData directly to write the memory into our buffer directly.
// This is advantageous to avoid a copy from the kernel buffer into our buffer. If the
// end user application knows the typical payload size then they can optimise for this
// to prevent unnecessary allocation on the user side.
void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No);
if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr))
error = DN_OS_W32LastError(&response->tmp_arena);
} else {
error = DN_OS_W32LastError(&response->tmp_arena);
}
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE) {
DWORD bytes_read = dwStatusInformationLength;
if (bytes_read) {
DN_Str8 prev_buffer = DN_Str8FromPtr(DN_Cast(char *) lpvStatusInformation, bytes_read);
DN_Str8BuilderAppendRef(&response->builder, prev_buffer);
void *buffer = DN_ArenaAlloc(response->builder.arena, READ_BUFFER_SIZE, 1 /*align*/, DN_ZMem_No);
if (!WinHttpReadData(request, buffer, READ_BUFFER_SIZE, nullptr))
error = DN_OS_W32LastError(&response->tmp_arena);
}
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE) {
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR) {
WINHTTP_ASYNC_RESULT *async_result = DN_Cast(WINHTTP_ASYNC_RESULT *) lpvStatusInformation;
error = DN_OS_W32ErrorCodeToMsg(&response->tmp_arena, DN_Cast(DN_U32) async_result->dwError);
} else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE) {
if (!WinHttpReceiveResponse(request, 0))
error = DN_OS_W32LastError(&response->tmp_arena);
}
// NOTE: If the request handle is missing, then, the response has been freed.
// MSDN says that this callback can still be called after closing the handle
// and trigger the WINHTTP_CALLBACK_STATUS_REQUEST_ERROR.
if (request) {
bool read_complete = dwInternetStatus == WINHTTP_CALLBACK_STATUS_READ_COMPLETE && dwStatusInformationLength == 0;
if (read_complete)
response->body = DN_Str8FromStr8BuilderArena(&response->builder, response->arena);
if (read_complete || dwInternetStatus == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR || error.code) {
DN_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
DN_AtomicAddU32(&response->done, 1);
}
if (error.code) {
response->error_code = error.code;
response->error_msg = error.msg;
}
}
}
DN_API void DN_OS_HttpRequestAsync(DN_OSHttpResponse *response,
DN_Arena *arena,
DN_Str8 host,
DN_Str8 path,
DN_OSHttpRequestSecure secure,
DN_Str8 method,
DN_Str8 body,
DN_Str8 headers)
{
if (!response || !arena)
return;
response->arena = arena;
response->builder = DN_Str8BuilderFromArena(response->scratch_arena.mem ? &response->scratch_arena : &response->tmp_arena);
DN_TCScratch scratch_ = DN_TCScratchBeginArena(&arena, 1);
if (!response->scratch_arena.mem)
response->scratch_arena = scratch_.arena;
DN_OSW32Error error = {};
DN_DEFER
{
response->error_msg = error.msg;
response->error_code = error.code;
if (error.code) {
// NOTE: 'Wait' handles failures gracefully, skipping the wait and
// cleans up the request
DN_OS_HttpRequestWait(response);
DN_AtomicAddU32(&response->done, 1);
}
DN_TCScratchEnd(&scratch_);
};
response->w32_request_session = WinHttpOpen(nullptr /*user agent*/, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC);
if (!response->w32_request_session) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DWORD callback_flags = WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE |
WINHTTP_CALLBACK_STATUS_READ_COMPLETE |
WINHTTP_CALLBACK_STATUS_REQUEST_ERROR |
WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE;
if (WinHttpSetStatusCallback(response->w32_request_session,
DN_Cast(WINHTTP_STATUS_CALLBACK) DN_OS_HttpRequestWin32Callback,
callback_flags,
DN_Cast(DWORD_PTR) nullptr /*dwReserved*/) == WINHTTP_INVALID_STATUS_CALLBACK) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DN_Str16 host16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, host);
response->w32_request_connection = WinHttpConnect(response->w32_request_session, host16.data, secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0 /*reserved*/);
if (!response->w32_request_connection) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DN_Str16 method16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, method);
DN_Str16 path16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, path);
response->w32_request_handle = WinHttpOpenRequest(response->w32_request_connection,
method16.data,
path16.data,
nullptr /*version*/,
nullptr /*referrer*/,
nullptr /*accept types*/,
secure ? WINHTTP_FLAG_SECURE : 0);
if (!response->w32_request_handle) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
DN_Str16 headers16 = DN_OS_W32Str8ToStr16(&response->scratch_arena, headers);
response->on_complete_semaphore = DN_OS_SemaphoreInit(0);
if (!WinHttpSendRequest(response->w32_request_handle,
headers16.data,
DN_Cast(DWORD) headers16.size,
body.data /*optional data*/,
DN_Cast(DWORD) body.size /*optional length*/,
DN_Cast(DWORD) body.size /*total content length*/,
DN_Cast(DWORD_PTR) response)) {
error = DN_OS_W32LastError(&response->tmp_arena);
return;
}
}
DN_API void DN_OS_HttpRequestFree(DN_OSHttpResponse *response)
{
// NOTE: Cleanup
// NOTE: These calls are synchronous even when the HTTP request is async.
WinHttpCloseHandle(response->w32_request_handle);
WinHttpCloseHandle(response->w32_request_connection);
WinHttpCloseHandle(response->w32_request_session);
response->w32_request_session = nullptr;
response->w32_request_connection = nullptr;
response->w32_request_handle = nullptr;
DN_MemListDeinit(response->tmp_arena.mem);
DN_OS_SemaphoreDeinit(&response->on_complete_semaphore);
*response = {};
}
// NOTE: DN_OS_W32
DN_API DN_Str16 DN_OS_W32ErrorCodeToMsg16Alloc(DN_U32 error_code)
{
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
@@ -1638,7 +1435,8 @@ DN_API DN_Str16 DN_OS_W32Str8ToStr16(DN_Arena *arena, DN_Str8 src)
return result;
int chars_written = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.data, DN_Cast(int) src.size, buffer, required_size);
if (DN_Check(chars_written == required_size)) {
DN_Assert(chars_written == required_size);
if (chars_written == required_size) {
result.data = buffer;
result.size = chars_written;
result.data[result.size] = 0;
@@ -1700,7 +1498,8 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8(DN_Arena *arena, DN_Str16 src)
DN_Str8 buffer = DN_Str8AllocArena(required_size, DN_ZMem_No, &temp);
if (buffer.size) {
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr);
if (DN_Check(chars_written == required_size)) {
DN_Assert(chars_written == required_size);
if (chars_written == required_size) {
result = buffer;
result.data[result.size] = 0;
}
@@ -1730,7 +1529,8 @@ DN_API DN_Str8 DN_OS_W32Str16ToStr8FromHeap(DN_Str16 src)
return result;
int chars_written = WideCharToMultiByte(CP_UTF8, 0 /*dwFlags*/, src.data, src_size, buffer.data, DN_Cast(int) buffer.size, nullptr, nullptr);
if (DN_Check(chars_written == required_size)) {
DN_Assert(chars_written == required_size);
if (chars_written == required_size) {
result = buffer;
result.data[result.size] = 0;
} else {
-271
View File
@@ -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;
}
-28
View File
@@ -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
-15
View File
@@ -4,8 +4,6 @@
#endif
#include "Base/dn_base.cpp"
#include "Base/dn_base_containers.cpp"
#include "Base/dn_base_leak.cpp"
DN_Core *g_dn_;
@@ -224,14 +222,6 @@ DN_API void DN_BeginFrame()
#endif
}
#if DN_H_WITH_HELPERS
#include "Extra/dn_helpers.cpp"
#endif
#if DN_H_WITH_ASYNC
#include "Extra/dn_async.cpp"
#endif
#if DN_H_WITH_NET
#include "Extra/dn_net.cpp"
#endif
@@ -239,8 +229,3 @@ DN_API void DN_BeginFrame()
#if DN_CPP_WITH_TESTS
#include "Extra/dn_tests.cpp"
#endif
#if DN_CPP_WITH_DEMO
#include "Extra/dn_demo.cpp"
#endif
+5 -9
View File
@@ -141,9 +141,13 @@
// Tracing incurs an additional much heavier performance penalty than the UAF guard due to
// the stacktrace that is stored per region to report to the user when a UAF guard violation
// occurs.
//
// Str8 AVX512F variants
// We have some AVX512 string functions that can be enabled by defining the following
//
// #define DN_STR8_AVX512F 1
#include "Base/dn_base.h"
#include "Base/dn_base_leak.h"
#if DN_H_WITH_OS
#if defined(DN_PLATFORM_WIN32)
@@ -191,14 +195,6 @@ DN_API void DN_Set (DN_Core *dn);
DN_API DN_Core *DN_Get ();
DN_API void DN_BeginFrame();
#if DN_H_WITH_HELPERS
#include "Extra/dn_helpers.h"
#endif
#if DN_H_WITH_ASYNC
#include "Extra/dn_async.h"
#endif
#if DN_H_WITH_NET
#include "Extra/dn_net.h"
#endif
-2
View File
@@ -109,8 +109,6 @@ int main(int argc, char **argv)
DN_Str8 const REL_FILE_PATHS[] = {
DN_Str8Lit("dn"),
DN_Str8Lit("Extra/dn_bin_pack"),
DN_Str8Lit("Extra/dn_csv"),
};
for (DN_ForIndexU(type, FileType_Count)) {