Simplify, simplify, simplify. Kill code that was unloved and unused
This commit is contained in:
+1860
-165
File diff suppressed because it is too large
Load Diff
+258
-17
@@ -2,11 +2,12 @@
|
||||
#define DN_BASE_H
|
||||
|
||||
#if defined(_CLANGD)
|
||||
#define DN_STR8_AVX512F 1
|
||||
#include "../dn.h"
|
||||
#endif
|
||||
|
||||
// NOTE: Compiler identification
|
||||
// Warning! Order is important here, clang-cl on Windows defines _MSC_VER
|
||||
// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(__clang__)
|
||||
#define DN_COMPILER_CLANG_CL
|
||||
@@ -21,7 +22,8 @@
|
||||
#endif
|
||||
|
||||
// NOTE: __has_feature
|
||||
// MSVC for example does not support the feature detection macro for instance so we compile it out
|
||||
// NOTE: MSVC for example does not support the feature detection macro for instance so we compile it
|
||||
// out
|
||||
#if defined(__has_feature)
|
||||
#define DN_HAS_FEATURE(expr) __has_feature(expr)
|
||||
#else
|
||||
@@ -29,7 +31,7 @@
|
||||
#endif
|
||||
|
||||
// NOTE: __has_builtin
|
||||
// MSVC for example does not support the feature detection macro for instance so we compile it out
|
||||
// NOTE: MSVC for example does not support the feature detection macro for instance so we compile it out
|
||||
#if defined(__has_builtin)
|
||||
#define DN_HAS_BUILTIN(expr) __has_builtin(expr)
|
||||
#else
|
||||
@@ -244,15 +246,6 @@
|
||||
typedef char DN_TokenCombine(static_assert_dummy__, __LINE__)[(expr) ? 1 : -1]; \
|
||||
DN_GCC_WARNING_POP
|
||||
|
||||
#define DN_Check(expr) DN_CheckF(expr, "")
|
||||
#if defined(DN_NO_CHECK_BREAK)
|
||||
#define DN_CheckF(expr, fmt, ...) \
|
||||
((expr) ? true : (DN_LogWarningF(fmt, ##__VA_ARGS__), false))
|
||||
#else
|
||||
#define DN_CheckF(expr, fmt, ...) \
|
||||
((expr) ? true : (DN_LogErrorF(fmt, ##__VA_ARGS__), DN_StackTracePrint(128 /*limit*/), DN_DebugBreak, false))
|
||||
#endif
|
||||
|
||||
#if defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
|
||||
#define DN_64_BIT
|
||||
#else
|
||||
@@ -1760,7 +1753,7 @@ DN_API DN_I16 DN_SaturateCastI64ToI16
|
||||
DN_API DN_I32 DN_SaturateCastI64ToI32 (DN_I64 val);
|
||||
|
||||
DN_API DN_UInt DN_SaturateCastI64ToUInt (DN_I64 val);
|
||||
DN_API DN_ISize DN_SaturateCastI64ToUSize (DN_I64 val);
|
||||
DN_API DN_USize DN_SaturateCastI64ToUSize (DN_I64 val);
|
||||
DN_API DN_U8 DN_SaturateCastI64ToU8 (DN_I64 val);
|
||||
DN_API DN_U16 DN_SaturateCastI64ToU16 (DN_I64 val);
|
||||
DN_API DN_U32 DN_SaturateCastI64ToU32 (DN_I64 val);
|
||||
@@ -1969,12 +1962,14 @@ DN_API DN_USize DN_CStr16Size
|
||||
|
||||
#define DN_Str8Lit(c_str) DN_Literal(DN_Str8){(char *)(c_str), sizeof(c_str) - 1}
|
||||
#define DN_Str8PrintFmt(string) (int)((string).size), (string).data
|
||||
|
||||
#define DN_Str8FromPtr(data, size) DN_Literal(DN_Str8){(char *)(data), (DN_USize)(size)}
|
||||
#define DN_Str8FromStruct(ptr) DN_Str8FromPtr((ptr)->data, (ptr)->size)
|
||||
#define DN_Str8FromLitArray(c_array) DN_Str8FromPtr(c_array, DN_ArrayCountU(c_array))
|
||||
DN_API DN_Str8 DN_Str8AllocAllocator (DN_USize size, DN_ZMem z_mem, DN_Allocator allocator);
|
||||
DN_API DN_Str8 DN_Str8AllocArena (DN_USize size, DN_ZMem z_mem, DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_Str8AllocPool (DN_USize size, DN_Pool *pool);
|
||||
|
||||
DN_API DN_Str8 DN_Str8FromCStr8 (char const *src);
|
||||
DN_API DN_Str8 DN_Str8FromCStr8Arena (char const *src, DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_Str8FromPtrArena (void const *data, DN_USize size, DN_Arena *arena);
|
||||
@@ -1988,6 +1983,7 @@ DN_API DN_Str8 DN_Str8FromFmtAllocator
|
||||
DN_API DN_Str8 DN_Str8FromFmtArena (DN_Arena *arena, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API DN_Str8 DN_Str8FromFmtVPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API DN_Str8 DN_Str8FromFmtPool (DN_Pool *pool, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
|
||||
DN_API DN_Str8x16 DN_Str8x16FromFmt (DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API DN_Str8x16 DN_Str8x16FromFmtV (DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API DN_Str8x32 DN_Str8x32FromFmt (DN_FMT_ATTRIB char const *fmt, ...);
|
||||
@@ -2016,6 +2012,7 @@ DN_API void DN_Str8x512AppendFmt
|
||||
DN_API void DN_Str8x512AppendFmtV (DN_Str8x512 *str, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DN_API void DN_Str8x1024AppendFmt (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, ...);
|
||||
DN_API void DN_Str8x1024AppendFmtV (DN_Str8x1024 *str, DN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
DN_API DN_Str8x32 DN_Str8x32FromU64 (DN_U64 val, char separator);
|
||||
DN_API bool DN_Str8IsAll (DN_Str8 string, DN_Str8IsAllType is_all);
|
||||
DN_API char * DN_Str8End (DN_Str8 string);
|
||||
@@ -2040,6 +2037,7 @@ DN_API bool DN_Str8StartsWithInsensitive
|
||||
DN_API bool DN_Str8EndsWith (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
||||
DN_API bool DN_Str8EndsWithInsensitive (DN_Str8 string, DN_Str8 prefix);
|
||||
DN_API bool DN_Str8HasChar (DN_Str8 string, char ch);
|
||||
|
||||
DN_API DN_Str8 DN_Str8TrimPrefix (DN_Str8 string, DN_Str8 prefix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
||||
DN_API DN_Str8 DN_Str8TrimHexPrefix (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8TrimSuffix (DN_Str8 string, DN_Str8 suffix, DN_Str8EqCase eq_case = DN_Str8EqCase_Sensitive);
|
||||
@@ -2048,6 +2046,7 @@ DN_API DN_Str8 DN_Str8TrimHeadWhitespace
|
||||
DN_API DN_Str8 DN_Str8TrimTailWhitespace (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8TrimWhitespaceAround (DN_Str8 string);
|
||||
DN_API DN_Str8 DN_Str8TrimByteOrderMark (DN_Str8 string);
|
||||
|
||||
DN_API DN_Str8 DN_Str8FileNameFromPath (DN_Str8 path);
|
||||
DN_API DN_Str8 DN_Str8FileNameNoExtension (DN_Str8 path);
|
||||
DN_API DN_Str8 DN_Str8FilePathNoExtension (DN_Str8 path);
|
||||
@@ -2071,6 +2070,15 @@ DN_API DN_Str8 DN_Str8LineBreakAllocator
|
||||
DN_API DN_Str8 DN_Str8LineBreakArena (DN_Str8 src, DN_USize desired_width, DN_Str8 delimiter, DN_Str8LineBreakMode mode, DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_Str8Table (DN_Str8 const* rows, DN_USize num_rows, DN_USize num_cols, DN_Str8TableFlags flags, DN_Arena *arena);
|
||||
|
||||
#if DN_STR8_AVX512F
|
||||
DN_API DN_Str8FindResult DN_Str8FindStr8AVX512F (DN_Str8 string, DN_Str8 find);
|
||||
DN_API DN_Str8FindResult DN_Str8FindLastStr8AVX512F (DN_Str8 string, DN_Str8 find);
|
||||
DN_API DN_Str8BSplitResult DN_Str8BSplitAVX512F (DN_Str8 string, DN_Str8 find);
|
||||
DN_API DN_Str8BSplitResult DN_Str8BSplitLastAVX512F (DN_Str8 string, DN_Str8 find);
|
||||
DN_API DN_USize DN_Str8SplitAVX512F (DN_Str8 string, DN_Str8 delimiter, DN_Str8 *splits, DN_USize splits_count, DN_Str8SplitFlags flags);
|
||||
DN_API DN_Str8Slice DN_Str8SplitAllocAVX512F (DN_Arena *arena, DN_Str8 string, DN_Str8 delimiter, DN_Str8SplitFlags flags);
|
||||
#endif
|
||||
|
||||
DN_API DN_Str8 DN_Str8SliceRender (DN_Str8Slice array, DN_Str8 separator, DN_Arena *arena);
|
||||
DN_API DN_Str8 DN_Str8RenderSpaceSep (DN_Str8Slice array, DN_Arena *arena);
|
||||
DN_API int DN_Str8CompareNatural (DN_Str8 lhs, DN_Str8 rhs, DN_Str8EqCase eq_case);
|
||||
@@ -2552,8 +2560,8 @@ DN_API DN_M4 DN_M4MulF
|
||||
DN_API DN_M4 DN_M4DivF (DN_M4 lhs, DN_F32 rhs);
|
||||
DN_API DN_Str8x256 DN_M4ColumnMajorString (DN_M4 mat);
|
||||
|
||||
DN_API bool operator== (DN_M2x3 const &lhs, DN_M2x3 const &rhs);
|
||||
DN_API bool operator!= (DN_M2x3 const &lhs, DN_M2x3 const &rhs);
|
||||
DN_API bool DN_M2x3Eq (DN_M2x3 const *lhs, DN_M2x3 const *rhs);
|
||||
DN_API bool DN_M2x3NotEq (DN_M2x3 const *lhs, DN_M2x3 const *rhs);
|
||||
DN_API DN_M2x3 DN_M2x3Identity ();
|
||||
DN_API DN_M2x3 DN_M2x3Translate (DN_V2F32 offset);
|
||||
DN_API DN_V2F32 DN_M2x3ScaleGet (DN_M2x3 m2x3);
|
||||
@@ -2573,7 +2581,6 @@ DN_API DN_Rect DN_M2x3MulRect
|
||||
#define DN_RectFrom4N(x, y, w, h) DN_Literal(DN_Rect){DN_Literal(DN_V2F32){{x, y}}, DN_Literal(DN_V2F32){{w, h}}}
|
||||
#define DN_RectZero DN_RectFrom4N(0, 0, 0, 0)
|
||||
|
||||
DN_API bool operator== (const DN_Rect& lhs, const DN_Rect& rhs);
|
||||
DN_API DN_V2F32 DN_RectCenter (DN_Rect rect);
|
||||
DN_API bool DN_RectContainsPoint (DN_Rect rect, DN_V2F32 p);
|
||||
DN_API bool DN_RectContainsRect (DN_Rect a, DN_Rect b);
|
||||
@@ -3040,9 +3047,10 @@ DN_API void DN_RingWrite (DN_Rin
|
||||
DN_API void DN_RingRead (DN_Ring *ring, void *dest, DN_U64 dest_size);
|
||||
#define DN_RingReadStruct(ring, dest) DN_RingRead((ring), (dest), sizeof(*(dest)))
|
||||
|
||||
// TODO: Replace with a C-style hash table
|
||||
#if defined(__cplusplus)
|
||||
DN_U32 const DN_DS_MAP_DEFAULT_HASH_SEED = 0x8a1ced49;
|
||||
DN_U32 const DN_DS_MAP_SENTINEL_SLOT = 0;
|
||||
|
||||
template <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);
|
||||
@@ -3069,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)
|
||||
{
|
||||
|
||||
@@ -1,821 +0,0 @@
|
||||
#if defined(_CLANGD)
|
||||
#include "../dn.h"
|
||||
#endif
|
||||
|
||||
struct DN_ArrayFindEqMemcmpContext_
|
||||
{
|
||||
DN_USize elem_size;
|
||||
void const *find;
|
||||
};
|
||||
|
||||
|
||||
DN_API void *DN_SliceAllocArena(void **data, DN_USize *slice_size_field, DN_USize size, DN_USize elem_size, DN_U8 align, DN_ZMem zmem, DN_Arena *arena)
|
||||
{
|
||||
void *result = *data;
|
||||
*data = DN_ArenaAlloc(arena, size * elem_size, align, zmem);
|
||||
if (*data)
|
||||
*slice_size_field = size;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ArrayFindResult DN_ArrayFind(void *data, DN_USize size, DN_USize elem_size, void const *find, DN_ArrayFindEqFunc *eq_func)
|
||||
{
|
||||
DN_ArrayFindResult result = {};
|
||||
DN_Assert(data);
|
||||
DN_Assert(elem_size);
|
||||
if (find) {
|
||||
for (DN_ForIndexU(index, size)) {
|
||||
DN_U8 *it = DN_Cast(DN_U8 *) data + (index * elem_size);
|
||||
if (eq_func(it, find)) {
|
||||
result.index = index;
|
||||
result.value = it;
|
||||
result.success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool DN_ArrayFindEqMemEqUnsafe_(void const *lhs, void const *find)
|
||||
{
|
||||
DN_ArrayFindEqMemcmpContext_ *context = DN_Cast(DN_ArrayFindEqMemcmpContext_ *) find;
|
||||
bool result = DN_MemEqUnsafe(lhs, context->find, context->elem_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API DN_ArrayFindResult DN_ArrayFindMemEq(void *data, DN_USize size, DN_USize elem_size, void const *find)
|
||||
{
|
||||
DN_ArrayFindEqMemcmpContext_ context = {};
|
||||
context.elem_size = elem_size;
|
||||
context.find = find;
|
||||
DN_ArrayFindResult result = DN_ArrayFind(data, size, elem_size, &context, DN_ArrayFindEqMemEqUnsafe_);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_ArrayInsertArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize index, void const *items, DN_USize count)
|
||||
{
|
||||
void *result = nullptr;
|
||||
if (!data || !size || !items || count <= 0 || ((*size + count) > max))
|
||||
return result;
|
||||
|
||||
DN_USize clamped_index = DN_Min(index, *size);
|
||||
if (clamped_index != *size) {
|
||||
char const *src = DN_Cast(char *)data + (clamped_index * elem_size);
|
||||
char const *dest = DN_Cast(char *)data + ((clamped_index + count) * elem_size);
|
||||
char const *end = DN_Cast(char *)data + (size[0] * elem_size);
|
||||
DN_USize bytes_to_move = end - src;
|
||||
DN_Memmove(DN_Cast(void *) dest, src, bytes_to_move);
|
||||
}
|
||||
|
||||
result = DN_Cast(char *)data + (clamped_index * elem_size);
|
||||
DN_Memcpy(result, items, elem_size * count);
|
||||
*size += count;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_ArrayPopFront(void *data, DN_USize *size, DN_USize elem_size, DN_USize count)
|
||||
{
|
||||
if (!data || !size || *size == 0 || count == 0)
|
||||
return nullptr;
|
||||
|
||||
DN_USize pop_count = DN_Min(count, *size);
|
||||
void *result = data;
|
||||
|
||||
if (pop_count < *size) {
|
||||
char *src = DN_Cast(char *)data + (pop_count * elem_size);
|
||||
char *dest = DN_Cast(char *)data;
|
||||
DN_USize bytes_to_move = (*size - pop_count) * elem_size;
|
||||
DN_Memmove(dest, src, bytes_to_move);
|
||||
}
|
||||
|
||||
*size -= pop_count;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_ArrayPopBack(void *data, DN_USize *size, DN_USize elem_size, DN_USize count)
|
||||
{
|
||||
if (!data || !size || *size == 0 || count == 0)
|
||||
return nullptr;
|
||||
|
||||
DN_USize pop_count = DN_Min(count, *size);
|
||||
*size -= pop_count;
|
||||
|
||||
return DN_Cast(char *)data + (*size * elem_size);
|
||||
}
|
||||
|
||||
DN_API DN_ArrayEraseResult DN_ArrayEraseRange(void *data, DN_USize *size, DN_USize elem_size, DN_USize begin_index, DN_ISize count, DN_ArrayErase erase)
|
||||
{
|
||||
DN_ArrayEraseResult result = {};
|
||||
result.it_index = begin_index;
|
||||
if (!data || !size || *size == 0 || count == 0)
|
||||
return result;
|
||||
|
||||
// Compute the range to erase
|
||||
DN_USize start = 0, end = 0;
|
||||
if (count < 0) {
|
||||
// Erase backwards from begin_index, not inclusive of begin_index
|
||||
// Range: [begin_index + count, begin_index)
|
||||
// Which is: [begin_index - abs(count), begin_index)
|
||||
DN_USize abs_count = DN_Abs(count);
|
||||
start = (begin_index > abs_count) ? (begin_index - abs_count) : 0;
|
||||
end = begin_index;
|
||||
} else {
|
||||
start = begin_index;
|
||||
end = begin_index + count;
|
||||
}
|
||||
|
||||
// Clamp indices to valid bounds
|
||||
start = DN_Min(start, *size);
|
||||
end = DN_Min(end, *size);
|
||||
|
||||
// Erase the range [start, end)
|
||||
DN_USize erase_count = end > start ? end - start : 0;
|
||||
if (erase_count) {
|
||||
char *dest = (char *)data + (elem_size * start);
|
||||
char *array_end = (char *)data + (elem_size * *size);
|
||||
char *src = dest + (elem_size * erase_count);
|
||||
if (erase == DN_ArrayErase_Stable) {
|
||||
DN_USize move_size = array_end - src;
|
||||
DN_Memmove(dest, src, move_size);
|
||||
} else {
|
||||
char *unstable_src = array_end - (elem_size * erase_count);
|
||||
DN_USize move_size = array_end - unstable_src;
|
||||
DN_Memcpy(dest, unstable_src, move_size);
|
||||
}
|
||||
*size -= erase_count;
|
||||
}
|
||||
|
||||
result.items_erased = erase_count;
|
||||
// NOTE: If we are erasing from the current index of the iterator to the end of the array then
|
||||
// there's no more elements in the array to iterate. So the returned index should b
|
||||
// one-past-last index
|
||||
if (begin_index == start && end >= *size) {
|
||||
result.it_index = *size;
|
||||
} else {
|
||||
result.it_index = start ? start - 1 : 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_ArrayMakeArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem)
|
||||
{
|
||||
void *result = nullptr;
|
||||
DN_USize new_size = *size + make_count;
|
||||
if (new_size <= max) {
|
||||
result = DN_Cast(char *) data + (elem_size * size[0]);
|
||||
*size = new_size;
|
||||
if (z_mem == DN_ZMem_Yes)
|
||||
DN_Memset(result, 0, elem_size * make_count);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_ArrayMakeArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, DN_USize make_count, DN_ZMem z_mem, DN_CallSite call_site)
|
||||
{
|
||||
void *result = DN_ArrayMakeArray(data, size, max, elem_size, make_count, z_mem);
|
||||
DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_ArrayAddArray(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add)
|
||||
{
|
||||
void *result = DN_ArrayMakeArray(data, size, max, elem_size, elems_count, DN_ZMem_No);
|
||||
if (result) {
|
||||
if (add == DN_ArrayAdd_Append) {
|
||||
DN_Memcpy(result, elems, elems_count * elem_size);
|
||||
} else {
|
||||
char *move_dest = DN_Cast(char *)data + (elems_count * elem_size); // Shift elements forward
|
||||
char *move_src = DN_Cast(char *)data;
|
||||
DN_Memmove(move_dest, move_src, elem_size * size[0]);
|
||||
DN_Memcpy(data, elems, elem_size * elems_count);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_ArrayAddArrayAssert(void *data, DN_USize *size, DN_USize max, DN_USize elem_size, void const *elems, DN_USize elems_count, DN_ArrayAdd add, DN_CallSite call_site)
|
||||
{
|
||||
void *result = DN_ArrayAddArray(data, size, max, elem_size, elems, elems_count, add);
|
||||
DN_AssertArgsF(result, call_site, "array=%p size=%zu max=%zu", data, *size, max);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max)
|
||||
{
|
||||
bool result = true;
|
||||
if (new_max != *max) {
|
||||
DN_USize bytes_to_alloc = elem_size * new_max;
|
||||
void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc);
|
||||
if (buffer) {
|
||||
DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max);
|
||||
DN_Memcpy(buffer, *data, bytes_to_copy);
|
||||
DN_PoolDealloc(pool, *data);
|
||||
*data = buffer;
|
||||
*max = new_max;
|
||||
*size = DN_Min(*size, new_max);
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_ArrayResizeFromPool(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max)
|
||||
{
|
||||
bool result = true;
|
||||
if (new_max != *max) {
|
||||
DN_USize bytes_to_alloc = elem_size * new_max;
|
||||
void *buffer = DN_PoolNewArray(pool, DN_U8, bytes_to_alloc);
|
||||
if (buffer) {
|
||||
DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max);
|
||||
DN_Memcpy(buffer, *data, bytes_to_copy);
|
||||
DN_PoolDealloc(pool, *data);
|
||||
*data = buffer;
|
||||
*max = new_max;
|
||||
*size = DN_Min(*size, new_max);
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_ArrayResizeFromArena(void **data, DN_USize *size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max)
|
||||
{
|
||||
bool result = true;
|
||||
if (new_max != *max) {
|
||||
DN_USize bytes_to_alloc = elem_size * new_max;
|
||||
void *buffer = DN_ArenaNewArray(arena, DN_U8, bytes_to_alloc, DN_ZMem_No);
|
||||
if (buffer) {
|
||||
DN_USize bytes_to_copy = elem_size * DN_Min(*size, new_max);
|
||||
DN_Memcpy(buffer, *data, bytes_to_copy);
|
||||
*data = buffer;
|
||||
*max = new_max;
|
||||
*size = DN_Min(*size, new_max);
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_ArrayGrowFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize new_max)
|
||||
{
|
||||
bool result = true;
|
||||
if (new_max > *max)
|
||||
result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_ArrayGrowFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize new_max)
|
||||
{
|
||||
bool result = true;
|
||||
if (new_max > *max)
|
||||
result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DN_API bool DN_ArrayGrowIfNeededFromPool(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Pool *pool, DN_USize add_count)
|
||||
{
|
||||
bool result = true;
|
||||
DN_USize new_size = size + add_count;
|
||||
if (new_size > *max) {
|
||||
DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8);
|
||||
result = DN_ArrayResizeFromPool(data, &size, max, elem_size, pool, new_max);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_ArrayGrowIfNeededFromArena(void **data, DN_USize size, DN_USize *max, DN_USize elem_size, DN_Arena *arena, DN_USize add_count)
|
||||
{
|
||||
bool result = true;
|
||||
DN_USize new_size = size + add_count;
|
||||
if (new_size > *max) {
|
||||
DN_USize new_max = DN_Max(DN_Max(*max * 2, new_size), 8);
|
||||
result = DN_ArrayResizeFromArena(data, &size, max, elem_size, arena, new_max);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void *DN_SinglyLLDetach(void **link, void **next)
|
||||
{
|
||||
void *result = *link;
|
||||
if (*link) {
|
||||
*link = *next;
|
||||
*next = nullptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_RingHasSpace(DN_Ring const *ring, DN_U64 size)
|
||||
{
|
||||
DN_U64 avail = ring->write_pos - ring->read_pos;
|
||||
DN_U64 space = ring->size - avail;
|
||||
bool result = space >= size;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_RingHasData(DN_Ring const *ring, DN_U64 size)
|
||||
{
|
||||
DN_U64 data = ring->write_pos - ring->read_pos;
|
||||
bool result = data >= size;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API void DN_RingWrite(DN_Ring *ring, void const *src, DN_U64 src_size)
|
||||
{
|
||||
DN_Assert(src_size <= ring->size);
|
||||
DN_U64 offset = ring->write_pos % ring->size;
|
||||
DN_U64 bytes_before_split = ring->size - offset;
|
||||
DN_U64 pre_split_bytes = DN_Min(bytes_before_split, src_size);
|
||||
DN_U64 post_split_bytes = src_size - pre_split_bytes;
|
||||
void const *pre_split_data = src;
|
||||
void const *post_split_data = ((char *)src + pre_split_bytes);
|
||||
DN_Memcpy(ring->base + offset, pre_split_data, pre_split_bytes);
|
||||
DN_Memcpy(ring->base, post_split_data, post_split_bytes);
|
||||
ring->write_pos += src_size;
|
||||
}
|
||||
|
||||
DN_API void DN_RingRead(DN_Ring *ring, void *dest, DN_U64 dest_size)
|
||||
{
|
||||
DN_Assert(dest_size <= ring->size);
|
||||
DN_U64 offset = ring->read_pos % ring->size;
|
||||
DN_U64 bytes_before_split = ring->size - offset;
|
||||
DN_U64 pre_split_bytes = DN_Min(bytes_before_split, dest_size);
|
||||
DN_U64 post_split_bytes = dest_size - pre_split_bytes;
|
||||
DN_Memcpy(dest, ring->base + offset, pre_split_bytes);
|
||||
DN_Memcpy((char *)dest + pre_split_bytes, ring->base, post_split_bytes);
|
||||
ring->read_pos += dest_size;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMap<T> DN_DSMapInit(DN_Arena *arena, DN_U32 size, DN_DSMapFlags flags)
|
||||
{
|
||||
DN_DSMap<T> result = {};
|
||||
if (!DN_CheckF(DN_IsPowerOfTwo(size), "Power-of-two size required, given size was '%u'", size))
|
||||
return result;
|
||||
if (size <= 0)
|
||||
return result;
|
||||
if (!DN_Check(arena))
|
||||
return result;
|
||||
result.arena = arena;
|
||||
result.pool = DN_PoolFromArena(arena, DN_POOL_DEFAULT_ALIGN);
|
||||
result.hash_to_slot = DN_ArenaNewArray(result.arena, DN_U32, size, DN_ZMem_Yes);
|
||||
result.slots = DN_ArenaNewArray(result.arena, DN_DSMapSlot<T>, size, DN_ZMem_Yes);
|
||||
result.occupied = 1; // For sentinel
|
||||
result.size = size;
|
||||
result.initial_size = size;
|
||||
result.flags = flags;
|
||||
DN_AssertF(result.hash_to_slot && result.slots, "We pre-allocated a block of memory sufficient in size for the 2 arrays. Maybe the pointers needed extra space because of natural alignment?");
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DN_DSMapDeinit(DN_DSMap<T> *map, DN_ZMem z_mem)
|
||||
{
|
||||
if (!map)
|
||||
return;
|
||||
// TODO(doyle): Use z_mem
|
||||
(void)z_mem;
|
||||
DN_MemListDeinit(map->arena->mem);
|
||||
*map = {};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DN_DSMapIsValid(DN_DSMap<T> const *map)
|
||||
{
|
||||
bool result = map &&
|
||||
map->arena &&
|
||||
map->hash_to_slot && // Hash to slot mapping array must be allocated
|
||||
map->slots && // Slots array must be allocated
|
||||
(map->size & (map->size - 1)) == 0 && // Must be power of two size
|
||||
map->occupied >= 1; // DN_DS_MAP_SENTINEL_SLOT takes up one slot
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_U32 DN_DSMapHash(DN_DSMap<T> const *map, DN_DSMapKey key)
|
||||
{
|
||||
DN_U32 result = 0;
|
||||
if (!map)
|
||||
return result;
|
||||
|
||||
if (key.type == DN_DSMapKeyType_U64NoHash) {
|
||||
result = DN_Cast(DN_U32) key.u64;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (key.type == DN_DSMapKeyType_BufferAsU64NoHash) {
|
||||
result = key.hash;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_U32 seed = map->hash_seed ? map->hash_seed : DN_DS_MAP_DEFAULT_HASH_SEED;
|
||||
if (map->hash_function) {
|
||||
map->hash_function(key, seed);
|
||||
} else {
|
||||
// NOTE: Courtesy of Demetri Spanos (which this hash table was inspired
|
||||
// from), the following is a hashing function snippet provided for
|
||||
// reliable, quick and simple quality hashing functions for hash table
|
||||
// use.
|
||||
// Source: https://github.com/demetri/scribbles/blob/c475464756c104c91bab83ed4e14badefef12ab5/hashing/ub_aware_hash_functions.c
|
||||
|
||||
char const *key_ptr = nullptr;
|
||||
DN_U32 len = 0;
|
||||
DN_U32 h = seed;
|
||||
switch (key.type) {
|
||||
case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/
|
||||
case DN_DSMapKeyType_U64NoHash: DN_InvalidCodePath; /*FALLTHRU*/
|
||||
case DN_DSMapKeyType_Invalid: break;
|
||||
|
||||
case DN_DSMapKeyType_Buffer:
|
||||
key_ptr = DN_Cast(char const *) key.buffer_data;
|
||||
len = key.buffer_size;
|
||||
break;
|
||||
|
||||
case DN_DSMapKeyType_U64:
|
||||
key_ptr = DN_Cast(char const *) & key.u64;
|
||||
len = sizeof(key.u64);
|
||||
break;
|
||||
}
|
||||
|
||||
// Murmur3 32-bit without UB unaligned accesses
|
||||
// DN_U32 mur3_32_no_UB(const void *key, int len, DN_U32 h)
|
||||
|
||||
// main body, work on 32-bit blocks at a time
|
||||
for (DN_U32 i = 0; i < len / 4; i++) {
|
||||
DN_U32 k;
|
||||
memcpy(&k, &key_ptr[i * 4], sizeof(k));
|
||||
|
||||
k *= 0xcc9e2d51;
|
||||
k = ((k << 15) | (k >> 17)) * 0x1b873593;
|
||||
h = (((h ^ k) << 13) | ((h ^ k) >> 19)) * 5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
// load/mix up to 3 remaining tail bytes into a tail block
|
||||
DN_U32 t = 0;
|
||||
uint8_t *tail = ((uint8_t *)key_ptr) + 4 * (len / 4);
|
||||
switch (len & 3) {
|
||||
case 3: t ^= tail[2] << 16;
|
||||
case 2: t ^= tail[1] << 8;
|
||||
case 1: {
|
||||
t ^= tail[0] << 0;
|
||||
h ^= ((0xcc9e2d51 * t << 15) | (0xcc9e2d51 * t >> 17)) * 0x1b873593;
|
||||
}
|
||||
}
|
||||
|
||||
// finalization mix, including key length
|
||||
h = ((h ^ len) ^ ((h ^ len) >> 16)) * 0x85ebca6b;
|
||||
h = (h ^ (h >> 13)) * 0xc2b2ae35;
|
||||
result = h ^ (h >> 16);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_U32 DN_DSMapHashToSlotIndex(DN_DSMap<T> const *map, DN_DSMapKey key)
|
||||
{
|
||||
DN_Assert(key.type != DN_DSMapKeyType_Invalid);
|
||||
DN_U32 result = DN_DS_MAP_SENTINEL_SLOT;
|
||||
if (!DN_DSMapIsValid(map))
|
||||
return result;
|
||||
|
||||
result = key.hash & (map->size - 1);
|
||||
for (;;) {
|
||||
if (result == DN_DS_MAP_SENTINEL_SLOT) // Sentinel is reserved
|
||||
result++;
|
||||
|
||||
if (map->hash_to_slot[result] == DN_DS_MAP_SENTINEL_SLOT) // Slot is vacant, can use
|
||||
return result;
|
||||
|
||||
DN_DSMapSlot<T> *slot = map->slots + map->hash_to_slot[result];
|
||||
if (slot->key.type == DN_DSMapKeyType_Invalid || (slot->key.hash == key.hash && slot->key == key))
|
||||
return result;
|
||||
|
||||
result = (result + 1) & (map->size - 1);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapResult<T> DN_DSMapFind(DN_DSMap<T> const *map, DN_DSMapKey key)
|
||||
{
|
||||
DN_DSMapResult<T> result = {};
|
||||
if (DN_DSMapIsValid(map)) {
|
||||
DN_U32 index = DN_DSMapHashToSlotIndex(map, key);
|
||||
if (index != DN_DS_MAP_SENTINEL_SLOT && map->hash_to_slot[index] == DN_DS_MAP_SENTINEL_SLOT) {
|
||||
result.slot = map->slots; // NOTE: Set to sentinel value
|
||||
} else {
|
||||
result.slot = map->slots + map->hash_to_slot[index];
|
||||
result.found = true;
|
||||
}
|
||||
result.value = &result.slot->value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapResult<T> DN_DSMapMake(DN_DSMap<T> *map, DN_DSMapKey key)
|
||||
{
|
||||
DN_DSMapResult<T> result = {};
|
||||
if (!DN_DSMapIsValid(map))
|
||||
return result;
|
||||
|
||||
DN_U32 index = DN_DSMapHashToSlotIndex(map, key);
|
||||
if (map->hash_to_slot[index] == DN_DS_MAP_SENTINEL_SLOT) {
|
||||
// NOTE: Create the slot
|
||||
if (index != DN_DS_MAP_SENTINEL_SLOT)
|
||||
map->hash_to_slot[index] = map->occupied++;
|
||||
|
||||
// NOTE: Check if resize is required
|
||||
bool map_is_75pct_full = (map->occupied * 4) > (map->size * 3);
|
||||
if (map_is_75pct_full) {
|
||||
if (!DN_DSMapResize(map, map->size * 2))
|
||||
return result;
|
||||
result = DN_DSMapMake(map, key);
|
||||
} else {
|
||||
result.slot = map->slots + map->hash_to_slot[index];
|
||||
result.slot->key = key; // NOTE: Assign key to new slot
|
||||
if ((key.type == DN_DSMapKeyType_Buffer ||
|
||||
key.type == DN_DSMapKeyType_BufferAsU64NoHash) &&
|
||||
!key.no_copy_buffer)
|
||||
result.slot->key.buffer_data = DN_PoolNewArrayCopy(&map->pool, char, key.buffer_data, key.buffer_size);
|
||||
}
|
||||
} else {
|
||||
result.slot = map->slots + map->hash_to_slot[index];
|
||||
result.found = true;
|
||||
}
|
||||
|
||||
result.value = &result.slot->value;
|
||||
DN_Assert(result.slot->key.type != DN_DSMapKeyType_Invalid);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapResult<T> DN_DSMapSet(DN_DSMap<T> *map, DN_DSMapKey key, T const &value)
|
||||
{
|
||||
DN_DSMapResult<T> result = {};
|
||||
if (!DN_DSMapIsValid(map))
|
||||
return result;
|
||||
|
||||
result = DN_DSMapMake(map, key);
|
||||
result.slot->value = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapResult<T> DN_DSMapFindKeyU64(DN_DSMap<T> const *map, DN_U64 key)
|
||||
{
|
||||
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
|
||||
DN_DSMapResult<T> result = DN_DSMapFind(map, map_key);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapResult<T> DN_DSMapMakeKeyU64(DN_DSMap<T> *map, DN_U64 key)
|
||||
{
|
||||
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
|
||||
DN_DSMapResult<T> result = DN_DSMapMake(map, map_key);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapResult<T> DN_DSMapSetKeyU64(DN_DSMap<T> *map, DN_U64 key, T const &value)
|
||||
{
|
||||
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
|
||||
DN_DSMapResult<T> result = DN_DSMapSet(map, map_key, value);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapResult<T> DN_DSMapFindKeyStr8(DN_DSMap<T> const *map, DN_Str8 key)
|
||||
{
|
||||
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
|
||||
DN_DSMapResult<T> result = DN_DSMapFind(map, map_key);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapResult<T> DN_DSMapMakeKeyStr8(DN_DSMap<T> *map, DN_Str8 key)
|
||||
{
|
||||
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
|
||||
DN_DSMapResult<T> result = DN_DSMapMake(map, map_key);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapResult<T> DN_DSMapSetKeyStr8(DN_DSMap<T> *map, DN_Str8 key, T const &value)
|
||||
{
|
||||
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
|
||||
DN_DSMapResult<T> result = DN_DSMapSet(map, map_key);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DN_DSMapResize(DN_DSMap<T> *map, DN_U32 size)
|
||||
{
|
||||
if (!DN_DSMapIsValid(map) || size < map->occupied || size < map->initial_size)
|
||||
return false;
|
||||
|
||||
DN_Arena *prev_arena = map->arena;
|
||||
DN_MemList *new_mem = prev_arena->mem;
|
||||
DN_MemList prev_mem = *prev_arena->mem;
|
||||
prev_arena->mem = &prev_mem;
|
||||
|
||||
*new_mem = {};
|
||||
new_mem->funcs = prev_mem.funcs;
|
||||
new_mem->flags = prev_mem.flags;
|
||||
|
||||
DN_Arena new_arena = {};
|
||||
new_arena.mem = new_mem;
|
||||
|
||||
DN_DSMap<T> new_map = DN_DSMapInit<T>(&new_arena, size, map->flags);
|
||||
if (!DN_DSMapIsValid(&new_map))
|
||||
return false;
|
||||
|
||||
new_map.initial_size = map->initial_size;
|
||||
for (DN_U32 old_index = 1 /*Sentinel*/; old_index < map->occupied; old_index++) {
|
||||
DN_DSMapSlot<T> *old_slot = map->slots + old_index;
|
||||
DN_DSMapKey old_key = old_slot->key;
|
||||
if (old_key.type == DN_DSMapKeyType_Invalid)
|
||||
continue;
|
||||
DN_DSMapSet(&new_map, old_key, old_slot->value);
|
||||
}
|
||||
|
||||
if ((map->flags & DN_DSMapFlags_DontFreeArenaOnResize) == 0)
|
||||
DN_DSMapDeinit(map, DN_ZMem_No);
|
||||
*map = new_map; // Update the map inplace
|
||||
map->arena = prev_arena; // Restore the previous arena pointer, it's been de-init-ed
|
||||
*map->arena = new_arena; // Re-init the old arena with the new data
|
||||
map->pool.arena = map->arena;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DN_DSMapErase(DN_DSMap<T> *map, DN_DSMapKey key)
|
||||
{
|
||||
if (!DN_DSMapIsValid(map))
|
||||
return false;
|
||||
|
||||
DN_U32 index = DN_DSMapHashToSlotIndex(map, key);
|
||||
if (index == 0)
|
||||
return true;
|
||||
|
||||
DN_U32 slot_index = map->hash_to_slot[index];
|
||||
if (slot_index == DN_DS_MAP_SENTINEL_SLOT)
|
||||
return false;
|
||||
|
||||
// NOTE: Mark the slot as unoccupied
|
||||
map->hash_to_slot[index] = DN_DS_MAP_SENTINEL_SLOT;
|
||||
|
||||
DN_DSMapSlot<T> *slot = map->slots + slot_index;
|
||||
if (!slot->key.no_copy_buffer)
|
||||
DN_PoolDealloc(&map->pool, DN_Cast(void *) slot->key.buffer_data);
|
||||
*slot = {}; // TODO: Optional?
|
||||
|
||||
if (map->occupied > 1 /*Sentinel*/) {
|
||||
// NOTE: Repair the hash chain, e.g. rehash all the items after the removed
|
||||
// element and reposition them if necessary.
|
||||
for (DN_U32 probe_index = index;;) {
|
||||
probe_index = (probe_index + 1) & (map->size - 1);
|
||||
if (map->hash_to_slot[probe_index] == DN_DS_MAP_SENTINEL_SLOT)
|
||||
break;
|
||||
|
||||
DN_DSMapSlot<T> *probe = map->slots + map->hash_to_slot[probe_index];
|
||||
DN_U32 new_index = probe->key.hash & (map->size - 1);
|
||||
if (index <= probe_index) {
|
||||
if (index < new_index && new_index <= probe_index)
|
||||
continue;
|
||||
} else {
|
||||
if (index < new_index || new_index <= probe_index)
|
||||
continue;
|
||||
}
|
||||
|
||||
map->hash_to_slot[index] = map->hash_to_slot[probe_index];
|
||||
map->hash_to_slot[probe_index] = DN_DS_MAP_SENTINEL_SLOT;
|
||||
index = probe_index;
|
||||
}
|
||||
|
||||
// NOTE: We have erased a slot from the hash table, this leaves a gap
|
||||
// in our contiguous array. After repairing the chain, the hash mapping
|
||||
// is correct.
|
||||
// We will now fill in the vacant spot that we erased using the last
|
||||
// element in the slot list.
|
||||
if (map->occupied >= 3 /*Ignoring sentinel, at least 2 other elements to unstable erase*/) {
|
||||
DN_U32 last_index = map->occupied - 1;
|
||||
if (last_index != slot_index) {
|
||||
// NOTE: Copy in last slot to the erase slot
|
||||
DN_DSMapSlot<T> *last_slot = map->slots + last_index;
|
||||
map->slots[slot_index] = *last_slot;
|
||||
|
||||
// NOTE: Update the hash-to-slot mapping for the value that was copied in
|
||||
DN_U32 hash_to_slot_index = DN_DSMapHashToSlotIndex(map, last_slot->key);
|
||||
map->hash_to_slot[hash_to_slot_index] = slot_index;
|
||||
*last_slot = {}; // TODO: Optional?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
map->occupied--;
|
||||
bool map_is_below_25pct_full = (map->occupied * 4) < (map->size * 1);
|
||||
if (map_is_below_25pct_full && (map->size / 2) >= map->initial_size)
|
||||
DN_DSMapResize(map, map->size / 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DN_DSMapEraseKeyU64(DN_DSMap<T> *map, DN_U64 key)
|
||||
{
|
||||
DN_DSMapKey map_key = DN_DSMapKeyU64(map, key);
|
||||
bool result = DN_DSMapErase(map, map_key);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool DN_DSMapEraseKeyStr8(DN_DSMap<T> *map, DN_Str8 key)
|
||||
{
|
||||
DN_DSMapKey map_key = DN_DSMapKeyStr8(map, key);
|
||||
bool result = DN_DSMapErase(map, map_key);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapKey DN_DSMapKeyBuffer(DN_DSMap<T> const *map, void const *data, DN_USize size)
|
||||
{
|
||||
DN_Assert(size > 0 && size <= UINT32_MAX);
|
||||
DN_DSMapKey result = {};
|
||||
result.type = DN_DSMapKeyType_Buffer;
|
||||
result.buffer_data = data;
|
||||
result.buffer_size = DN_Cast(DN_U32) size;
|
||||
result.hash = DN_DSMapHash(map, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapKey DN_DSMapKeyBufferAsU64NoHash(DN_DSMap<T> const *map, void const *data, DN_USize size)
|
||||
{
|
||||
DN_DSMapKey result = {};
|
||||
result.type = DN_DSMapKeyType_BufferAsU64NoHash;
|
||||
result.buffer_data = data;
|
||||
result.buffer_size = DN_Cast(DN_U32) size;
|
||||
DN_Assert(size >= sizeof(result.hash));
|
||||
DN_Memcpy(&result.hash, data, sizeof(result.hash));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapKey DN_DSMapKeyU64(DN_DSMap<T> const *map, DN_U64 u64)
|
||||
{
|
||||
DN_DSMapKey result = {};
|
||||
result.type = DN_DSMapKeyType_U64;
|
||||
result.u64 = u64;
|
||||
result.hash = DN_DSMapHash(map, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DN_DSMapKey DN_DSMapKeyStr8(DN_DSMap<T> const *map, DN_Str8 string)
|
||||
{
|
||||
DN_DSMapKey result = DN_DSMapKeyBuffer(map, string.data, string.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_DSMap
|
||||
DN_API DN_DSMapKey DN_DSMapKeyU64NoHash(DN_U64 u64)
|
||||
{
|
||||
DN_DSMapKey result = {};
|
||||
result.type = DN_DSMapKeyType_U64NoHash;
|
||||
result.u64 = u64;
|
||||
result.hash = DN_Cast(DN_U32) u64;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_DSMapKeyEquals(DN_DSMapKey lhs, DN_DSMapKey rhs)
|
||||
{
|
||||
bool result = false;
|
||||
if (lhs.type == rhs.type && lhs.hash == rhs.hash) {
|
||||
switch (lhs.type) {
|
||||
case DN_DSMapKeyType_Invalid: result = true; break;
|
||||
case DN_DSMapKeyType_U64NoHash: result = true; break;
|
||||
case DN_DSMapKeyType_U64: result = lhs.u64 == rhs.u64; break;
|
||||
|
||||
case DN_DSMapKeyType_BufferAsU64NoHash: /*FALLTHRU*/
|
||||
case DN_DSMapKeyType_Buffer: {
|
||||
if (lhs.buffer_size == rhs.buffer_size)
|
||||
result = DN_Memcmp(lhs.buffer_data, rhs.buffer_data, lhs.buffer_size) == 0;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool operator==(DN_DSMapKey lhs, DN_DSMapKey rhs)
|
||||
{
|
||||
bool result = DN_DSMapKeyEquals(lhs, rhs);
|
||||
return result;
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
#define DN_BASE_LEAK_CPP
|
||||
|
||||
#if defined(_CLANGD)
|
||||
#include "../dn.h"
|
||||
#endif
|
||||
|
||||
DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, bool leak_permitted)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
DN_TicketMutex_Begin(&leak->alloc_table_mutex);
|
||||
|
||||
DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/);
|
||||
DN_DSMap<DN_LeakAlloc> *alloc_table = &leak->alloc_table;
|
||||
DN_DSMapResult<DN_LeakAlloc> alloc_entry = DN_DSMapMakeKeyU64(alloc_table, DN_Cast(DN_U64) ptr);
|
||||
DN_LeakAlloc *alloc = alloc_entry.value;
|
||||
if (alloc_entry.found) {
|
||||
if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) {
|
||||
DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size);
|
||||
DN_Str8x32 new_alloc_size = DN_Str8x32FromByteCountU64Auto(size);
|
||||
DN_HardAssertF(
|
||||
alloc->flags & DN_LeakAllocFlag_Freed,
|
||||
"This pointer is already in the leak tracker, however it has not been freed yet. This "
|
||||
"same pointer is being ask to be tracked twice in the allocation table, e.g. one if its "
|
||||
"previous free calls has not being marked freed with an equivalent call to "
|
||||
"DN_LeakTrackDealloc()\n"
|
||||
"\n"
|
||||
"The pointer (0x%p) originally allocated %.*s at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer is allocating %.*s again at:\n"
|
||||
"\n"
|
||||
"%.*s\n",
|
||||
ptr,
|
||||
DN_Str8PrintFmt(alloc_size),
|
||||
DN_Str8PrintFmt(alloc->stack_trace),
|
||||
DN_Str8PrintFmt(new_alloc_size),
|
||||
DN_Str8PrintFmt(stack_trace));
|
||||
}
|
||||
|
||||
// NOTE: Pointer was reused, clean up the prior entry
|
||||
leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.size;
|
||||
leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->freed_stack_trace.size;
|
||||
|
||||
DN_OS_MemDealloc(alloc->stack_trace.data);
|
||||
DN_OS_MemDealloc(alloc->freed_stack_trace.data);
|
||||
*alloc = {};
|
||||
}
|
||||
|
||||
alloc->ptr = ptr;
|
||||
alloc->size = size;
|
||||
alloc->stack_trace = stack_trace;
|
||||
alloc->flags |= leak_permitted ? DN_LeakAllocFlag_LeakPermitted : 0;
|
||||
leak->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.size;
|
||||
DN_TicketMutex_End(&leak->alloc_table_mutex);
|
||||
}
|
||||
|
||||
DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
DN_TicketMutex_Begin(&leak->alloc_table_mutex);
|
||||
|
||||
DN_Str8 stack_trace = DN_Str8FromStackTraceNowHeap(128, 3 /*skip*/);
|
||||
DN_DSMap<DN_LeakAlloc> *alloc_table = &leak->alloc_table;
|
||||
DN_DSMapResult<DN_LeakAlloc> alloc_entry = DN_DSMapFindKeyU64(alloc_table, DN_Cast(uintptr_t) ptr);
|
||||
DN_HardAssertF(alloc_entry.found,
|
||||
"Allocated pointer can not be removed as it does not exist in the "
|
||||
"allocation table. When this memory was allocated, the pointer was "
|
||||
"not added to the allocation table [ptr=%p]",
|
||||
ptr);
|
||||
|
||||
DN_LeakAlloc *alloc = alloc_entry.value;
|
||||
if (alloc->flags & DN_LeakAllocFlag_Freed) {
|
||||
DN_Str8x32 freed_size = DN_Str8x32FromByteCountU64Auto(alloc->freed_size);
|
||||
DN_HardAssertF((alloc->flags & DN_LeakAllocFlag_Freed) == 0,
|
||||
"Double free detected, pointer to free was already marked "
|
||||
"as freed. Either the pointer was reallocated but not "
|
||||
"traced, or, the pointer was freed twice.\n"
|
||||
"\n"
|
||||
"The pointer (0x%p) originally allocated %.*s at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer was freed at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer is being freed again at:\n"
|
||||
"\n"
|
||||
"%.*s\n",
|
||||
ptr,
|
||||
DN_Str8PrintFmt(freed_size),
|
||||
DN_Str8PrintFmt(alloc->stack_trace),
|
||||
DN_Str8PrintFmt(alloc->freed_stack_trace),
|
||||
DN_Str8PrintFmt(stack_trace));
|
||||
}
|
||||
|
||||
DN_Assert(alloc->freed_stack_trace.size == 0);
|
||||
alloc->flags |= DN_LeakAllocFlag_Freed;
|
||||
alloc->freed_stack_trace = stack_trace;
|
||||
leak->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.size;
|
||||
DN_TicketMutex_End(&leak->alloc_table_mutex);
|
||||
}
|
||||
|
||||
DN_API void DN_LeakDump_(DN_LeakTracker *leak)
|
||||
{
|
||||
DN_U64 leak_count = 0;
|
||||
DN_U64 leaked_bytes = 0;
|
||||
for (DN_USize index = 1; index < leak->alloc_table.occupied; index++) {
|
||||
DN_DSMapSlot<DN_LeakAlloc> *slot = leak->alloc_table.slots + index;
|
||||
DN_LeakAlloc *alloc = &slot->value;
|
||||
bool alloc_leaked = (alloc->flags & DN_LeakAllocFlag_Freed) == 0;
|
||||
bool leak_permitted = (alloc->flags & DN_LeakAllocFlag_LeakPermitted);
|
||||
if (alloc_leaked && !leak_permitted) {
|
||||
leaked_bytes += alloc->size;
|
||||
leak_count++;
|
||||
DN_Str8x32 alloc_size = DN_Str8x32FromByteCountU64Auto(alloc->size);
|
||||
DN_LogWarningF(
|
||||
"Pointer (0x%p) leaked %.*s at:\n"
|
||||
"%.*s",
|
||||
alloc->ptr,
|
||||
DN_Str8PrintFmt(alloc_size),
|
||||
DN_Str8PrintFmt(alloc->stack_trace));
|
||||
}
|
||||
}
|
||||
|
||||
if (leak_count) {
|
||||
DN_Str8x32 leak_size = DN_Str8x32FromByteCountU64Auto(leaked_bytes);
|
||||
DN_LogWarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DN_Str8PrintFmt(leak_size));
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#if !defined(DN_BASE_LEAK_H)
|
||||
#define DN_BASE_LEAK_H
|
||||
|
||||
#if defined(_CLANGD)
|
||||
#include "../dn.h"
|
||||
#endif
|
||||
|
||||
enum DN_LeakAllocFlag
|
||||
{
|
||||
DN_LeakAllocFlag_Freed = 1 << 0,
|
||||
DN_LeakAllocFlag_LeakPermitted = 1 << 1,
|
||||
};
|
||||
|
||||
struct DN_LeakAlloc
|
||||
{
|
||||
void *ptr; // 8 Pointer to the allocation being tracked
|
||||
DN_USize size; // 16 Size of the allocation
|
||||
DN_USize freed_size; // 24 Store the size of the allocation when it is freed
|
||||
DN_Str8 stack_trace; // 40 Stack trace at the point of allocation
|
||||
DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
|
||||
DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag`
|
||||
};
|
||||
|
||||
// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get
|
||||
// expensive. Enforce that there is no unexpected padding.
|
||||
DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively
|
||||
|
||||
struct DN_LeakTracker
|
||||
{
|
||||
DN_DSMap<DN_LeakAlloc> alloc_table;
|
||||
DN_TicketMutex alloc_table_mutex;
|
||||
DN_MemList alloc_table_mem;
|
||||
DN_Arena alloc_table_arena;
|
||||
DN_U64 alloc_table_bytes_allocated_for_stack_traces;
|
||||
};
|
||||
|
||||
DN_API void DN_LeakTrackAlloc_ (DN_LeakTracker *leak, void *ptr, DN_USize size, bool alloc_can_leak);
|
||||
DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr);
|
||||
DN_API void DN_LeakDump_ (DN_LeakTracker *leak);
|
||||
|
||||
#if defined(DN_LEAK_TRACKING)
|
||||
#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) DN_LeakTrackAlloc_(leak, ptr, size, alloc_can_leak)
|
||||
#define DN_LeakTrackDealloc(leak, ptr) DN_LeakTrackDealloc_(leak, ptr)
|
||||
#define DN_LeakDump(leak) DN_LeakDump_(leak)
|
||||
#else
|
||||
#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
|
||||
#define DN_LeakTrackDealloc(leak, ptr) do { (void)ptr; } while (0)
|
||||
#define DN_LeakDump(leak) do { } while (0)
|
||||
#endif
|
||||
#endif // DN_BASE_LEAK_H
|
||||
Reference in New Issue
Block a user