Import latest changes from CSIGHT project
This commit is contained in:
parent
bf413d7e57
commit
022c309e3a
@ -2,7 +2,7 @@
|
||||
#define DQN_CPP_FILE_H
|
||||
|
||||
// NOTE: Dqn_CppFile: Helper functions to generate C++ files
|
||||
// =============================================================================
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
#include <stdio.h> /// printf, fputc
|
||||
#include <stdarg.h> /// va_list...
|
||||
#include <assert.h> /// assert
|
||||
@ -51,21 +51,21 @@ void Dqn_CppPrint(Dqn_CppFile *cpp, char const *fmt, ...);
|
||||
|
||||
#define Dqn_CppEnumBlock(cpp, fmt, ...) \
|
||||
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__), true); \
|
||||
(Dqn_CppBeginEnumBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndEnumBlock(cpp), false))
|
||||
|
||||
#define Dqn_CppForBlock(cpp, fmt, ...) \
|
||||
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(Dqn_CppBeginBlock(cpp, false /*append*/, "for (" fmt ")", ##__VA_ARGS__), true); \
|
||||
(Dqn_CppBeginForBLock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndForBlock(cpp), false))
|
||||
|
||||
#define Dqn_CppWhileBlock(cpp, fmt, ...) \
|
||||
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(Dqn_CppBeginBlock(cpp, false /*append*/, "while (" fmt ")", ##__VA_ARGS__), true); \
|
||||
(Dqn_CppBeginWhileBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndForBlock(cpp), false))
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndWhileBlock(cpp), false))
|
||||
|
||||
#define Dqn_CppIfOrElseIfBlock(cpp, fmt, ...) \
|
||||
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
@ -81,22 +81,28 @@ void Dqn_CppPrint(Dqn_CppFile *cpp, char const *fmt, ...);
|
||||
|
||||
#define Dqn_CppFuncBlock(cpp, fmt, ...) \
|
||||
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__), true); \
|
||||
(Dqn_CppBeginFuncBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndFuncBlock(cpp), false))
|
||||
|
||||
#define Dqn_CppStructBlock(cpp, fmt, ...) \
|
||||
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(Dqn_CppBeginBlock(cpp, false /*append*/, "struct " fmt, ##__VA_ARGS__), true); \
|
||||
(Dqn_CppBeginStructBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndStructBlock(cpp), false))
|
||||
|
||||
#define Dqn_CppSwitchBlock(cpp, fmt, ...) \
|
||||
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(Dqn_CppBeginBlock(cpp, false /*append*/, "switch (" fmt ")", ##__VA_ARGS__), true); \
|
||||
(Dqn_CppBeginSwitchBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndSwitchBlock(cpp), false))
|
||||
|
||||
#define Dqn_CppBlock(cpp, ending, fmt, ...) \
|
||||
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__), true); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndBlock(cpp, ending), false))
|
||||
|
||||
#define Dqn_CppIfChain(cpp) \
|
||||
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppBeginIfChain(cpp), true); \
|
||||
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
@ -106,29 +112,32 @@ void Dqn_CppPrint(Dqn_CppFile *cpp, char const *fmt, ...);
|
||||
/// increasing the indent level after the brace.
|
||||
void Dqn_CppBeginBlock (Dqn_CppFile *cpp, bool append, char const *fmt, ...);
|
||||
void Dqn_CppBeginBlockV(Dqn_CppFile *cpp, bool append, char const *fmt, va_list args);
|
||||
void Dqn_CppEndBlock (Dqn_CppFile *cpp);
|
||||
void Dqn_CppEndBlock (Dqn_CppFile *cpp, char const *ending);
|
||||
|
||||
/// Begin/End a block, specifically for the following language constructs.
|
||||
#define Dqn_CppBeginEnumBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
|
||||
#define Dqn_CppEndEnumBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, ";\n")
|
||||
#define Dqn_CppBeginEnumBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "enum " fmt, ##__VA_ARGS__)
|
||||
#define Dqn_CppEndEnumBlock(cpp) Dqn_CppEndBlock(cpp, ";\n")
|
||||
|
||||
#define Dqn_CppBeginForBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
|
||||
#define Dqn_CppEndForBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, "\n")
|
||||
#define Dqn_CppBeginWhileBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "while (" fmt ")", ##__VA_ARGS__)
|
||||
#define Dqn_CppEndWhileBlock(cpp) Dqn_CppEndBlock(cpp, "\n")
|
||||
|
||||
#define Dqn_CppBeginForBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "for (" fmt ")", ##__VA_ARGS__)
|
||||
#define Dqn_CppEndForBlock(cpp) Dqn_CppEndBlock(cpp, "\n")
|
||||
|
||||
#define Dqn_CppBeginFuncBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
|
||||
#define Dqn_CppEndFuncBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, "\n")
|
||||
#define Dqn_CppEndFuncBlock(cpp) Dqn_CppEndBlock(cpp, "\n")
|
||||
|
||||
#define Dqn_CppBeginStructBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
|
||||
#define Dqn_CppEndStructBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, ";\n")
|
||||
#define Dqn_CppBeginStructBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "struct " fmt, ##__VA_ARGS__)
|
||||
#define Dqn_CppEndStructBlock(cpp) Dqn_CppEndBlock(cpp, ";\n")
|
||||
|
||||
#define Dqn_CppBeginSwitchBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
|
||||
#define Dqn_CppEndSwitchBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, "\n")
|
||||
#define Dqn_CppBeginSwitchBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "switch (" fmt ")", ##__VA_ARGS__)
|
||||
#define Dqn_CppEndSwitchBlock(cpp) Dqn_CppEndBlock(cpp, "\n")
|
||||
|
||||
void Dqn_CppBeginIfOrElseIfBlock (Dqn_CppFile *cpp, char const *fmt, ...);
|
||||
#define Dqn_CppEndIfOrElseIfBlock(cpp) Dqn_CppEndBlock(cpp)
|
||||
#define Dqn_CppEndIfOrElseIfBlock(cpp) Dqn_CppEndBlock(cpp, "")
|
||||
|
||||
void Dqn_CppBeginElseBlock (Dqn_CppFile *cpp);
|
||||
#define Dqn_CppEndElseBlock(cpp) Dqn_CppEndBlock(cpp)
|
||||
void Dqn_CppEndElseBlock (Dqn_CppFile *cpp);
|
||||
|
||||
#define DQN_CPP_TOKEN_PASTE2_(x, y) x ## y
|
||||
#define DQN_CPP_TOKEN_PASTE_(x, y) DQN_CPP_TOKEN_PASTE2_(x, y)
|
||||
@ -179,20 +188,23 @@ void Dqn_CppBeginBlockV(Dqn_CppFile *cpp, bool append, char const *fmt, va_list
|
||||
Dqn_CppAppendV(cpp, fmt, args);
|
||||
else
|
||||
Dqn_CppPrintV(cpp, fmt, args);
|
||||
Dqn_CppAppend(cpp, " {\n");
|
||||
|
||||
bool empty_fmt = fmt == nullptr || strlen(fmt) == 0;
|
||||
Dqn_CppAppend(cpp, "%s{\n", empty_fmt ? "" : " ");
|
||||
Dqn_CppIndent(cpp);
|
||||
}
|
||||
|
||||
void Dqn_CppEndBlock(Dqn_CppFile *cpp)
|
||||
void Dqn_CppEndBlock(Dqn_CppFile *cpp, char const *ending)
|
||||
{
|
||||
Dqn_CppUnindent(cpp);
|
||||
Dqn_CppPrint(cpp, "}");
|
||||
Dqn_CppPrint(cpp, "}%s", ending);
|
||||
}
|
||||
|
||||
void Dqn_CppBeginIfOrElseIfBlock(Dqn_CppFile *cpp, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
assert(cpp->if_chain_size);
|
||||
if (cpp->if_chain[cpp->if_chain_size - 1] == 0)
|
||||
Dqn_CppPrint(cpp, "if");
|
||||
else
|
||||
@ -208,10 +220,17 @@ void Dqn_CppBeginIfOrElseIfBlock(Dqn_CppFile *cpp, char const *fmt, ...)
|
||||
|
||||
void Dqn_CppBeginElseBlock(Dqn_CppFile *cpp)
|
||||
{
|
||||
assert(cpp->if_chain_size);
|
||||
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1)
|
||||
Dqn_CppBeginBlock(cpp, true /*append*/, " else");
|
||||
}
|
||||
|
||||
void Dqn_CppEndElseBlock(Dqn_CppFile *cpp)
|
||||
{
|
||||
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1)
|
||||
Dqn_CppEndBlock(cpp, "");
|
||||
}
|
||||
|
||||
void Dqn_CppBeginIfChain(Dqn_CppFile *cpp)
|
||||
{
|
||||
assert(cpp->if_chain_size < sizeof(cpp->if_chain)/sizeof(cpp->if_chain[0]));
|
||||
@ -220,8 +239,12 @@ void Dqn_CppBeginIfChain(Dqn_CppFile *cpp)
|
||||
|
||||
void Dqn_CppEndIfChain(Dqn_CppFile *cpp)
|
||||
{
|
||||
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1)
|
||||
assert(cpp->if_chain_size);
|
||||
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1) {
|
||||
Dqn_CppNewLine(cpp);
|
||||
}
|
||||
cpp->if_chain[cpp->if_chain_size - 1] = 0;
|
||||
cpp->if_chain_size--;
|
||||
}
|
||||
|
||||
#endif // DQN_CPP_FILE_IMPLEMENTATION
|
||||
|
367
Misc/dqn_json.h
367
Misc/dqn_json.h
@ -4,103 +4,93 @@
|
||||
|
||||
#if !defined(DQN_JSON_H)
|
||||
#define DQN_JSON_H
|
||||
// NOTE: Dqn_JSON //////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: Dqn_JSON
|
||||
// -----------------------------------------------------------------------------
|
||||
void *Dqn_JSON_ArenaAllocFunc (void *user_data, size_t count);
|
||||
char const *Dqn_JSON_TypeEnumCString(json_type_e type, size_t *size);
|
||||
bool Dqn_JSON_String8Cmp(json_string_s const *lhs, Dqn_String8 rhs);
|
||||
bool Dqn_JSON_String8Cmp (json_string_s const *lhs, Dqn_Str8 rhs);
|
||||
|
||||
// NOTE: Dqn_JSON_Iterator
|
||||
// -----------------------------------------------------------------------------
|
||||
enum Dqn_JSONIteratorEntryType
|
||||
// NOTE: Dqn_JSON_It /////////////////////////////////////////////////////////////////////////
|
||||
enum Dqn_JSONItEntryType
|
||||
{
|
||||
Dqn_JSON_IteratorEntryTypeObjElement,
|
||||
Dqn_JSON_IteratorEntryTypeObj,
|
||||
Dqn_JSON_IteratorEntryTypeArrayElement,
|
||||
Dqn_JSON_IteratorEntryTypeArray,
|
||||
Dqn_JSON_IteratorEntryTypeString,
|
||||
Dqn_JSON_IteratorEntryTypeNumber,
|
||||
Dqn_JSON_ItEntryTypeObjElement,
|
||||
Dqn_JSON_ItEntryTypeObj,
|
||||
Dqn_JSON_ItEntryTypeArrayElement,
|
||||
Dqn_JSON_ItEntryTypeArray,
|
||||
Dqn_JSON_ItEntryTypeString,
|
||||
Dqn_JSON_ItEntryTypeNumber,
|
||||
};
|
||||
|
||||
struct Dqn_JSONIteratorEntry
|
||||
struct Dqn_JSONItEntry
|
||||
{
|
||||
Dqn_JSONIteratorEntryType type;
|
||||
Dqn_JSONItEntryType type;
|
||||
void *value;
|
||||
};
|
||||
|
||||
struct Dqn_JSONIterator
|
||||
struct Dqn_JSONIt
|
||||
{
|
||||
Dqn_JSONIteratorEntry stack[128];
|
||||
Dqn_JSONItEntry stack[128];
|
||||
int stack_count;
|
||||
size_t flags;
|
||||
};
|
||||
|
||||
// NOTE: Dqn_JSON_IteratorPush/Pop
|
||||
// -----------------------------------------------------------------------------
|
||||
bool Dqn_JSON_IteratorPushObjElement (Dqn_JSONIterator *it, json_object_element_s *element);
|
||||
bool Dqn_JSON_IteratorPushObj (Dqn_JSONIterator *it, json_object_s *obj);
|
||||
bool Dqn_JSON_IteratorPushArrayElement(Dqn_JSONIterator *it, json_array_element_s *element);
|
||||
bool Dqn_JSON_IteratorPushArray (Dqn_JSONIterator *it, json_value_s *value);
|
||||
bool Dqn_JSON_IteratorPushValue (Dqn_JSONIterator *it, json_value_s *value);
|
||||
void Dqn_JSON_IteratorPop (Dqn_JSONIterator *it);
|
||||
Dqn_JSONIt Dqn_JSON_LoadFileToIt(Dqn_Arena *arena, Dqn_Str8 json);
|
||||
|
||||
// NOTE: Dqn_JSON_Iterator tree navigation
|
||||
// -----------------------------------------------------------------------------
|
||||
json_value_s *Dqn_JSON_IteratorPushCurrValue(Dqn_JSONIterator *it);
|
||||
bool Dqn_JSON_IteratorNext(Dqn_JSONIterator *it);
|
||||
// NOTE: Dqn_JSON_ItPush/Pop /////////////////////////////////////////////////////////////////
|
||||
bool Dqn_JSON_ItPushObjElement (Dqn_JSONIt *it, json_object_element_s *element);
|
||||
bool Dqn_JSON_ItPushObj (Dqn_JSONIt *it, json_object_s *obj);
|
||||
bool Dqn_JSON_ItPushArrayElement(Dqn_JSONIt *it, json_array_element_s *element);
|
||||
bool Dqn_JSON_ItPushArray (Dqn_JSONIt *it, json_value_s *value);
|
||||
bool Dqn_JSON_ItPushValue (Dqn_JSONIt *it, json_value_s *value);
|
||||
void Dqn_JSON_ItPop (Dqn_JSONIt *it);
|
||||
|
||||
#define Dqn_JSON_IteratorErrorUnrecognisedKey(it) Dqn_JSON_IteratorErrorUnrecognisedKey_(it, DQN_STRING8(__FILE__), DQN_STRING8(__func__), __LINE__)
|
||||
void Dqn_JSON_IteratorErrorUnrecognisedKey_(Dqn_JSONIterator *it, Dqn_String8 file, Dqn_String8 func, Dqn_uint line);
|
||||
// NOTE: Dqn_JSON_It tree navigation /////////////////////////////////////////////////////////
|
||||
json_value_s *Dqn_JSON_ItPushCurrValue(Dqn_JSONIt *it);
|
||||
bool Dqn_JSON_ItNext(Dqn_JSONIt *it);
|
||||
|
||||
#define Dqn_JSON_IteratorPushCurrValueIterateThenPop(it) \
|
||||
for(void *DQN_UNIQUE_NAME(ptr) = Dqn_JSON_IteratorPushCurrValue(it); DQN_UNIQUE_NAME(ptr); Dqn_JSON_IteratorPop(it), DQN_UNIQUE_NAME(ptr) = nullptr) \
|
||||
while (Dqn_JSON_IteratorNext(it))
|
||||
#define Dqn_JSON_ItPushCurrValueIterateThenPop(it) \
|
||||
for(void *DQN_UNIQUE_NAME(ptr) = Dqn_JSON_ItPushCurrValue(it); DQN_UNIQUE_NAME(ptr); Dqn_JSON_ItPop(it), DQN_UNIQUE_NAME(ptr) = nullptr) \
|
||||
while (Dqn_JSON_ItNext(it))
|
||||
|
||||
// NOTE: Dqn_JSON_IteratorCurr
|
||||
// -----------------------------------------------------------------------------
|
||||
Dqn_JSONIteratorEntry *Dqn_JSON_IteratorCurr(Dqn_JSONIterator *it);
|
||||
json_value_s *Dqn_JSON_IteratorCurrValue(Dqn_JSONIterator *it);
|
||||
json_object_element_s *Dqn_JSON_IteratorCurrObjElement(Dqn_JSONIterator *it);
|
||||
// NOTE: Dqn_JSON_ItCurr /////////////////////////////////////////////////////////////////////
|
||||
Dqn_JSONItEntry *Dqn_JSON_ItCurr(Dqn_JSONIt *it);
|
||||
json_value_s *Dqn_JSON_ItCurrValue(Dqn_JSONIt *it);
|
||||
json_object_element_s *Dqn_JSON_ItCurrObjElement(Dqn_JSONIt *it);
|
||||
|
||||
// NOTE: Dqn_JSON_IteratorValueIs
|
||||
// -----------------------------------------------------------------------------
|
||||
json_value_s *Dqn_JSON_IteratorValueIs(Dqn_JSONIterator *it, json_type_e type);
|
||||
json_object_s *Dqn_JSON_IteratorValueIsObj(Dqn_JSONIterator *it);
|
||||
json_array_s *Dqn_JSON_IteratorValueIsArray(Dqn_JSONIterator *it);
|
||||
json_string_s *Dqn_JSON_IteratorValueIsString(Dqn_JSONIterator *it);
|
||||
json_number_s *Dqn_JSON_IteratorValueIsNumber(Dqn_JSONIterator *it);
|
||||
json_value_s *Dqn_JSON_IteratorValueIsBool(Dqn_JSONIterator *it);
|
||||
// NOTE: Dqn_JSON_ItValueIs //////////////////////////////////////////////////////////////////
|
||||
json_value_s *Dqn_JSON_ItValueIs(Dqn_JSONIt *it, json_type_e type);
|
||||
json_object_s *Dqn_JSON_ItValueIsObj(Dqn_JSONIt *it);
|
||||
json_array_s *Dqn_JSON_ItValueIsArray(Dqn_JSONIt *it);
|
||||
json_string_s *Dqn_JSON_ItValueIsString(Dqn_JSONIt *it);
|
||||
json_number_s *Dqn_JSON_ItValueIsNumber(Dqn_JSONIt *it);
|
||||
json_value_s *Dqn_JSON_ItValueIsBool(Dqn_JSONIt *it);
|
||||
json_value_s *Dqn_JSON_ItValueIsNull(Dqn_JSONIt *it);
|
||||
|
||||
size_t Dqn_JSON_IteratorValueArraySize(Dqn_JSONIterator *it);
|
||||
size_t Dqn_JSON_ItValueArraySize(Dqn_JSONIt *it);
|
||||
|
||||
// NOTE: Dqn_JSON_IteratorKeyValueIs
|
||||
// -----------------------------------------------------------------------------
|
||||
Dqn_String8 Dqn_JSON_IteratorKey(Dqn_JSONIterator *it);
|
||||
bool Dqn_JSON_IteratorKeyIs(Dqn_JSONIterator *it, Dqn_String8 key);
|
||||
json_object_s *Dqn_JSON_IteratorKeyValueIsObj(Dqn_JSONIterator *it, Dqn_String8 key);
|
||||
json_array_s *Dqn_JSON_IteratorKeyValueIsArray(Dqn_JSONIterator *it, Dqn_String8 key);
|
||||
json_string_s *Dqn_JSON_IteratorKeyValueIsString(Dqn_JSONIterator *it, Dqn_String8 key);
|
||||
json_number_s *Dqn_JSON_IteratorKeyValueIsNumber(Dqn_JSONIterator *it, Dqn_String8 key);
|
||||
json_value_s *Dqn_JSON_IteratorKeyValueIsBool(Dqn_JSONIterator *it, Dqn_String8 key);
|
||||
// NOTE: Dqn_JSON_ItKeyValueIs ///////////////////////////////////////////////////////////////
|
||||
Dqn_Str8 Dqn_JSON_ItKey(Dqn_JSONIt *it);
|
||||
bool Dqn_JSON_ItKeyIs(Dqn_JSONIt *it, Dqn_Str8 key);
|
||||
json_object_s *Dqn_JSON_ItKeyValueIsObj(Dqn_JSONIt *it, Dqn_Str8 key);
|
||||
json_array_s *Dqn_JSON_ItKeyValueIsArray(Dqn_JSONIt *it, Dqn_Str8 key);
|
||||
json_string_s *Dqn_JSON_ItKeyValueIsString(Dqn_JSONIt *it, Dqn_Str8 key);
|
||||
json_number_s *Dqn_JSON_ItKeyValueIsNumber(Dqn_JSONIt *it, Dqn_Str8 key);
|
||||
json_value_s *Dqn_JSON_ItKeyValueIsBool(Dqn_JSONIt *it, Dqn_Str8 key);
|
||||
json_value_s *Dqn_JSON_ItKeyValueIsNull(Dqn_JSONIt *it, Dqn_Str8 key);
|
||||
|
||||
// NOTE: Dqn_JSON_IteratorValueTo
|
||||
// -----------------------------------------------------------------------------
|
||||
Dqn_String8 Dqn_JSON_IteratorValueToString(Dqn_JSONIterator *it);
|
||||
int64_t Dqn_JSON_IteratorValueToI64(Dqn_JSONIterator *it);
|
||||
uint64_t Dqn_JSON_IteratorValueToU64(Dqn_JSONIterator *it);
|
||||
bool Dqn_JSON_IteratorValueToBool(Dqn_JSONIterator *it);
|
||||
// NOTE: Dqn_JSON_ItValueTo //////////////////////////////////////////////////////////////////
|
||||
Dqn_Str8 Dqn_JSON_ItValueToString(Dqn_JSONIt *it);
|
||||
int64_t Dqn_JSON_ItValueToI64(Dqn_JSONIt *it);
|
||||
uint64_t Dqn_JSON_ItValueToU64(Dqn_JSONIt *it);
|
||||
bool Dqn_JSON_ItValueToBool(Dqn_JSONIt *it);
|
||||
|
||||
#define Dqn_JSON_IteratorErrorUnknownKeyValue(it) \
|
||||
Dqn_JSON_IteratorErrorUnknownKeyValue_(it, DQN_CALL_SITE)
|
||||
|
||||
void Dqn_JSON_IteratorErrorUnknownKeyValue_(Dqn_JSONIterator *it, Dqn_String8 file, Dqn_String8 func, int line);
|
||||
#define Dqn_JSON_ItErrorUnknownKeyValue(it) Dqn_JSON_ItErrorUnknownKeyValue_(it, DQN_CALL_SITE)
|
||||
void Dqn_JSON_ItErrorUnknownKeyValue_(Dqn_JSONIt *it, Dqn_CallSite call_site);
|
||||
|
||||
#endif // DQN_JSON_H
|
||||
|
||||
#if defined(DQN_JSON_IMPLEMENTATION)
|
||||
// NOTE: Dqn_JSON
|
||||
// -----------------------------------------------------------------------------
|
||||
// NOTE: Dqn_JSON //////////////////////////////////////////////////////////////////////////////////
|
||||
void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count)
|
||||
{
|
||||
void *result = NULL;
|
||||
@ -108,7 +98,7 @@ void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count)
|
||||
return result;
|
||||
|
||||
Dqn_Arena *arena = DQN_CAST(Dqn_Arena*)user_data;
|
||||
result = Dqn_Arena_Allocate(arena, count, alignof(json_value_s), Dqn_ZeroMem_No);
|
||||
result = Dqn_Arena_Alloc(arena, count, alignof(json_value_s), Dqn_ZeroMem_No);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -127,70 +117,86 @@ char const *Dqn_JSON_TypeEnumCString(json_type_e type, size_t *size)
|
||||
}
|
||||
}
|
||||
|
||||
bool Dqn_JSON_String8Cmp(json_string_s const *lhs, Dqn_String8 key)
|
||||
bool Dqn_JSON_String8Cmp(json_string_s const *lhs, Dqn_Str8 key)
|
||||
{
|
||||
bool result = false;
|
||||
if (lhs && Dqn_String8_IsValid(key)) {
|
||||
Dqn_String8 lhs_string = Dqn_String8_Init(lhs->string, lhs->string_size);
|
||||
result = Dqn_String8_Eq(lhs_string, key);
|
||||
if (lhs && Dqn_Str8_HasData(key)) {
|
||||
Dqn_Str8 lhs_string = Dqn_Str8_Init(lhs->string, lhs->string_size);
|
||||
result = Dqn_Str8_Eq(lhs_string, key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSON_Iterator_push/pop
|
||||
// -----------------------------------------------------------------------------
|
||||
bool Dqn_JSON_IteratorPushObjElement(Dqn_JSONIterator *it, json_object_element_s *element)
|
||||
// NOTE: Dqn_JSON_It ///////////////////////////////////////////////////////////////////////////////
|
||||
Dqn_JSONIt Dqn_JSON_LoadFileToIt(Dqn_Arena *arena, Dqn_Str8 json)
|
||||
{
|
||||
json_parse_result_s parse_result = {};
|
||||
json_value_ex_s *ex_value =
|
||||
DQN_CAST(json_value_ex_s *) json_parse_ex(json.data,
|
||||
json.size,
|
||||
json_parse_flags_allow_location_information,
|
||||
Dqn_JSON_ArenaAllocFunc,
|
||||
arena,
|
||||
&parse_result);
|
||||
|
||||
Dqn_JSONIt result = {};
|
||||
Dqn_JSON_ItPushValue(&result, &ex_value->value);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSON_ItPush/Pop ///////////////////////////////////////////////////////////////////////
|
||||
bool Dqn_JSON_ItPushObjElement(Dqn_JSONIt *it, json_object_element_s *element)
|
||||
{
|
||||
if (!it || !element)
|
||||
return false;
|
||||
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_IteratorEntryTypeObjElement, element};
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeObjElement, element};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_IteratorPushObj(Dqn_JSONIterator *it, json_object_s *obj)
|
||||
bool Dqn_JSON_ItPushObj(Dqn_JSONIt *it, json_object_s *obj)
|
||||
{
|
||||
if (!it || !obj)
|
||||
return false;
|
||||
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_IteratorEntryTypeObj, obj};
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeObj, obj};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_IteratorPushArrayElement(Dqn_JSONIterator *it, json_array_element_s *element)
|
||||
bool Dqn_JSON_ItPushArrayElement(Dqn_JSONIt *it, json_array_element_s *element)
|
||||
{
|
||||
if (!it || !element)
|
||||
return false;
|
||||
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_IteratorEntryTypeArrayElement, element};
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeArrayElement, element};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_IteratorPushArray(Dqn_JSONIterator *it, json_value_s *value)
|
||||
bool Dqn_JSON_ItPushArray(Dqn_JSONIt *it, json_value_s *value)
|
||||
{
|
||||
if (!it || !value || json_value_as_array(value) == nullptr)
|
||||
return false;
|
||||
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack));
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_IteratorEntryTypeArray, value};
|
||||
it->stack[it->stack_count++] = {Dqn_JSON_ItEntryTypeArray, value};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_IteratorPushValue(Dqn_JSONIterator *it, json_value_s *value)
|
||||
bool Dqn_JSON_ItPushValue(Dqn_JSONIt *it, json_value_s *value)
|
||||
{
|
||||
bool result = false;
|
||||
if (!it || !value)
|
||||
return result;
|
||||
|
||||
if (value->type == json_type_object) {
|
||||
result = Dqn_JSON_IteratorPushObj(it, json_value_as_object(value));
|
||||
result = Dqn_JSON_ItPushObj(it, json_value_as_object(value));
|
||||
} else if (value->type == json_type_array) {
|
||||
result = Dqn_JSON_IteratorPushArray(it, value);
|
||||
result = Dqn_JSON_ItPushArray(it, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Dqn_JSON_IteratorPop(Dqn_JSONIterator *it)
|
||||
void Dqn_JSON_ItPop(Dqn_JSONIt *it)
|
||||
{
|
||||
if (!it)
|
||||
return;
|
||||
@ -199,19 +205,18 @@ void Dqn_JSON_IteratorPop(Dqn_JSONIterator *it)
|
||||
it->stack_count--;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSON_Iterator json tree navigation
|
||||
// -----------------------------------------------------------------------------
|
||||
json_value_s *Dqn_JSON_IteratorPushCurrValue(Dqn_JSONIterator *it)
|
||||
// NOTE: Dqn_JSON_It JSON tree navigation //////////////////////////////////////////////////////////
|
||||
json_value_s *Dqn_JSON_ItPushCurrValue(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *result = nullptr;
|
||||
Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it);
|
||||
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
|
||||
if (!curr)
|
||||
return result;
|
||||
|
||||
if (curr->type == Dqn_JSON_IteratorEntryTypeObjElement) {
|
||||
if (curr->type == Dqn_JSON_ItEntryTypeObjElement) {
|
||||
json_object_element_s *element = DQN_CAST(json_object_element_s *) curr->value;
|
||||
result = element->value;
|
||||
} else if (curr->type == Dqn_JSON_IteratorEntryTypeArrayElement) {
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) {
|
||||
json_array_element_s *element = DQN_CAST(json_array_element_s *) curr->value;
|
||||
result = element->value;
|
||||
} else {
|
||||
@ -221,57 +226,56 @@ json_value_s *Dqn_JSON_IteratorPushCurrValue(Dqn_JSONIterator *it)
|
||||
if (result->type == json_type_array) {
|
||||
json_array_s *array = json_value_as_array(result);
|
||||
DQN_ASSERT(array);
|
||||
Dqn_JSON_IteratorPushArray(it, result);
|
||||
Dqn_JSON_ItPushArray(it, result);
|
||||
} else if (result->type == json_type_object) {
|
||||
json_object_s *obj = json_value_as_object(result);
|
||||
DQN_ASSERT(obj);
|
||||
Dqn_JSON_IteratorPushObj(it, obj);
|
||||
Dqn_JSON_ItPushObj(it, obj);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_IteratorNext(Dqn_JSONIterator *it)
|
||||
bool Dqn_JSON_ItNext(Dqn_JSONIt *it)
|
||||
{
|
||||
Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it);
|
||||
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
|
||||
if (!curr)
|
||||
return false;
|
||||
|
||||
json_object_element_s *obj_element = nullptr;
|
||||
json_array_element_s *array_element = nullptr;
|
||||
if (curr->type == Dqn_JSON_IteratorEntryTypeObj) {
|
||||
if (curr->type == Dqn_JSON_ItEntryTypeObj) {
|
||||
auto *obj = DQN_CAST(json_object_s *) curr->value;
|
||||
obj_element = obj->start;
|
||||
} else if (curr->type == Dqn_JSON_IteratorEntryTypeObjElement) {
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeObjElement) {
|
||||
auto *element = DQN_CAST(json_object_element_s *) curr->value;
|
||||
obj_element = element->next;
|
||||
Dqn_JSON_IteratorPop(it);
|
||||
} else if (curr->type == Dqn_JSON_IteratorEntryTypeArray) {
|
||||
Dqn_JSON_ItPop(it);
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeArray) {
|
||||
auto *value = DQN_CAST(json_value_s *) curr->value;
|
||||
auto *array = json_value_as_array(value);
|
||||
array_element = array->start;
|
||||
} else if (curr->type == Dqn_JSON_IteratorEntryTypeArrayElement) {
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) {
|
||||
auto *element = DQN_CAST(json_array_element_s *) curr->value;
|
||||
array_element = element->next;
|
||||
Dqn_JSON_IteratorPop(it);
|
||||
Dqn_JSON_ItPop(it);
|
||||
} else {
|
||||
Dqn_JSON_IteratorPop(it);
|
||||
Dqn_JSON_ItPop(it);
|
||||
}
|
||||
|
||||
if (obj_element)
|
||||
Dqn_JSON_IteratorPushObjElement(it, obj_element);
|
||||
Dqn_JSON_ItPushObjElement(it, obj_element);
|
||||
else if (array_element)
|
||||
Dqn_JSON_IteratorPushArrayElement(it, array_element);
|
||||
Dqn_JSON_ItPushArrayElement(it, array_element);
|
||||
|
||||
bool result = obj_element || array_element;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSON_IteratorCurr
|
||||
// -----------------------------------------------------------------------------
|
||||
Dqn_JSONIteratorEntry *Dqn_JSON_IteratorCurr(Dqn_JSONIterator *it)
|
||||
// NOTE: Dqn_JSON_ItCurr ///////////////////////////////////////////////////////////////////////////
|
||||
Dqn_JSONItEntry *Dqn_JSON_ItCurr(Dqn_JSONIt *it)
|
||||
{
|
||||
Dqn_JSONIteratorEntry *result = nullptr;
|
||||
Dqn_JSONItEntry *result = nullptr;
|
||||
if (!it || it->stack_count <= 0)
|
||||
return result;
|
||||
|
||||
@ -279,23 +283,23 @@ Dqn_JSONIteratorEntry *Dqn_JSON_IteratorCurr(Dqn_JSONIterator *it)
|
||||
return result;
|
||||
}
|
||||
|
||||
json_value_s *Dqn_JSON_IteratorCurrValue(Dqn_JSONIterator *it)
|
||||
json_value_s *Dqn_JSON_ItCurrValue(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *result = nullptr;
|
||||
Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it);
|
||||
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
|
||||
if (!curr)
|
||||
return result;
|
||||
|
||||
if (curr->type == Dqn_JSON_IteratorEntryTypeObjElement) {
|
||||
if (curr->type == Dqn_JSON_ItEntryTypeObjElement) {
|
||||
auto *element = DQN_CAST(json_object_element_s *)curr->value;
|
||||
result = element->value;
|
||||
} else if (curr->type == Dqn_JSON_IteratorEntryTypeArrayElement) {
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeArrayElement) {
|
||||
auto *element = DQN_CAST(json_array_element_s *)curr->value;
|
||||
result = element->value;
|
||||
} else if (curr->type == Dqn_JSON_IteratorEntryTypeString ||
|
||||
curr->type == Dqn_JSON_IteratorEntryTypeNumber ||
|
||||
curr->type == Dqn_JSON_IteratorEntryTypeObj ||
|
||||
curr->type == Dqn_JSON_IteratorEntryTypeArray)
|
||||
} else if (curr->type == Dqn_JSON_ItEntryTypeString ||
|
||||
curr->type == Dqn_JSON_ItEntryTypeNumber ||
|
||||
curr->type == Dqn_JSON_ItEntryTypeObj ||
|
||||
curr->type == Dqn_JSON_ItEntryTypeArray)
|
||||
{
|
||||
result = DQN_CAST(json_value_s *)curr->value;
|
||||
}
|
||||
@ -303,74 +307,79 @@ json_value_s *Dqn_JSON_IteratorCurrValue(Dqn_JSONIterator *it)
|
||||
return result;
|
||||
}
|
||||
|
||||
json_object_element_s *Dqn_JSON_IteratorCurrObjElement(Dqn_JSONIterator *it)
|
||||
json_object_element_s *Dqn_JSON_ItCurrObjElement(Dqn_JSONIt *it)
|
||||
{
|
||||
Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it);
|
||||
auto *result = (curr && curr->type == Dqn_JSON_IteratorEntryTypeObjElement)
|
||||
Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
|
||||
auto *result = (curr && curr->type == Dqn_JSON_ItEntryTypeObjElement)
|
||||
? DQN_CAST(json_object_element_s *) curr->value
|
||||
: nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSON_IteratorValueIs
|
||||
// -----------------------------------------------------------------------------
|
||||
json_value_s *Dqn_JSON_IteratorValueIs(Dqn_JSONIterator *it, json_type_e type)
|
||||
// NOTE: Dqn_JSON_ItValueIs ////////////////////////////////////////////////////////////////////////
|
||||
json_value_s *Dqn_JSON_ItValueIs(Dqn_JSONIt *it, json_type_e type)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_IteratorCurrValue(it);
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_value_s *result = (curr && type == curr->type) ? curr : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_object_s *Dqn_JSON_IteratorValueIsObj(Dqn_JSONIterator *it)
|
||||
json_object_s *Dqn_JSON_ItValueIsObj(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_IteratorCurrValue(it);
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_object_s *result = curr ? json_value_as_object(curr) : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_array_s *Dqn_JSON_IteratorValueIsArray(Dqn_JSONIterator *it)
|
||||
json_array_s *Dqn_JSON_ItValueIsArray(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_IteratorCurrValue(it);
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_array_s *result = curr ? json_value_as_array(curr) : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_string_s *Dqn_JSON_IteratorValueIsString(Dqn_JSONIterator *it)
|
||||
json_string_s *Dqn_JSON_ItValueIsString(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_IteratorCurrValue(it);
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_string_s *result = curr ? json_value_as_string(curr) : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_number_s *Dqn_JSON_IteratorValueIsNumber(Dqn_JSONIterator *it)
|
||||
json_number_s *Dqn_JSON_ItValueIsNumber(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_IteratorCurrValue(it);
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_number_s *result = curr ? json_value_as_number(curr) : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
json_value_s *Dqn_JSON_IteratorValueIsBool(Dqn_JSONIterator *it)
|
||||
json_value_s *Dqn_JSON_ItValueIsBool(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_IteratorCurrValue(it);
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_value_s *result = (curr && (curr->type == json_type_true || curr->type == json_type_false)) ? curr : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t Dqn_JSON_IteratorValueArraySize(Dqn_JSONIterator *it)
|
||||
json_value_s *Dqn_JSON_ItValueIsNull(Dqn_JSONIt *it)
|
||||
{
|
||||
json_value_s *curr = Dqn_JSON_ItCurrValue(it);
|
||||
json_value_s *result = (curr && (curr->type == json_type_null)) ? curr : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t Dqn_JSON_ItValueArraySize(Dqn_JSONIt *it)
|
||||
{
|
||||
size_t result = 0;
|
||||
if (json_array_s *curr = Dqn_JSON_IteratorValueIsArray(it))
|
||||
if (json_array_s *curr = Dqn_JSON_ItValueIsArray(it))
|
||||
result = curr->length;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Dqn_JSON_IteratorKeyValueIs
|
||||
// -----------------------------------------------------------------------------
|
||||
Dqn_String8 Dqn_JSON_IteratorKey(Dqn_JSONIterator *it)
|
||||
// NOTE: Dqn_JSON_ItKeyValueIs /////////////////////////////////////////////////////////////////////
|
||||
Dqn_Str8 Dqn_JSON_ItKey(Dqn_JSONIt *it)
|
||||
{
|
||||
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it);
|
||||
Dqn_String8 result = {};
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
Dqn_Str8 result = {};
|
||||
if (curr) {
|
||||
result.data = DQN_CAST(char *)curr->name->string;
|
||||
result.size = curr->name->string_size;
|
||||
@ -378,98 +387,107 @@ Dqn_String8 Dqn_JSON_IteratorKey(Dqn_JSONIterator *it)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_IteratorKeyIs(Dqn_JSONIterator *it, Dqn_String8 key)
|
||||
bool Dqn_JSON_ItKeyIs(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it);
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
bool result = Dqn_JSON_String8Cmp(curr->name, key);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_object_s *Dqn_JSON_IteratorKeyValueIsObj(Dqn_JSONIterator *it, Dqn_String8 key)
|
||||
json_object_s *Dqn_JSON_ItKeyValueIsObj(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_object_s *result = nullptr;
|
||||
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it);
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = json_value_as_object(curr->value);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_array_s *Dqn_JSON_IteratorKeyValueIsArray(Dqn_JSONIterator *it, Dqn_String8 key)
|
||||
json_array_s *Dqn_JSON_ItKeyValueIsArray(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_array_s *result = nullptr;
|
||||
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it);
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = json_value_as_array(curr->value);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_string_s *Dqn_JSON_IteratorKeyValueIsString(Dqn_JSONIterator *it, Dqn_String8 key)
|
||||
json_string_s *Dqn_JSON_ItKeyValueIsString(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it);
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
json_string_s *result = nullptr;
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = json_value_as_string(curr->value);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_number_s *Dqn_JSON_IteratorKeyValueIsNumber(Dqn_JSONIterator *it, Dqn_String8 key)
|
||||
json_number_s *Dqn_JSON_ItKeyValueIsNumber(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it);
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
json_number_s *result = nullptr;
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = json_value_as_number(curr->value);
|
||||
return result;
|
||||
}
|
||||
|
||||
json_value_s *Dqn_JSON_IteratorKeyValueIsBool(Dqn_JSONIterator *it, Dqn_String8 key)
|
||||
json_value_s *Dqn_JSON_ItKeyValueIsBool(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it);
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
json_value_s *result = nullptr;
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = curr->value->type == json_type_true || curr->value->type == json_type_false ? curr->value : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_JSON_IteratorValueTo
|
||||
// -----------------------------------------------------------------------------
|
||||
Dqn_String8 Dqn_JSON_IteratorValueToString(Dqn_JSONIterator *it)
|
||||
json_value_s *Dqn_JSON_ItKeyValueIsNull(Dqn_JSONIt *it, Dqn_Str8 key)
|
||||
{
|
||||
Dqn_String8 result = {};
|
||||
if (json_string_s *curr = Dqn_JSON_IteratorValueIsString(it))
|
||||
result = Dqn_String8_Init(curr->string, curr->string_size);
|
||||
json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
json_value_s *result = nullptr;
|
||||
if (curr && Dqn_JSON_String8Cmp(curr->name, key))
|
||||
result = curr->value->type == json_type_null ? curr->value : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t Dqn_JSON_IteratorValueToI64(Dqn_JSONIterator *it)
|
||||
|
||||
// NOTE: Dqn_JSON_ItValueTo ////////////////////////////////////////////////////////////////////////
|
||||
Dqn_Str8 Dqn_JSON_ItValueToString(Dqn_JSONIt *it)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (json_string_s *curr = Dqn_JSON_ItValueIsString(it))
|
||||
result = Dqn_Str8_Init(curr->string, curr->string_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t Dqn_JSON_ItValueToI64(Dqn_JSONIt *it)
|
||||
{
|
||||
int64_t result = {};
|
||||
if (json_number_s *curr = Dqn_JSON_IteratorValueIsNumber(it))
|
||||
result = Dqn_String8_ToI64(Dqn_String8_Init(curr->number, curr->number_size), 0 /*separator*/);
|
||||
if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it))
|
||||
result = Dqn_Str8_ToI64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t Dqn_JSON_IteratorValueToU64(Dqn_JSONIterator *it)
|
||||
uint64_t Dqn_JSON_ItValueToU64(Dqn_JSONIt *it)
|
||||
{
|
||||
uint64_t result = {};
|
||||
if (json_number_s *curr = Dqn_JSON_IteratorValueIsNumber(it))
|
||||
result = Dqn_String8_ToU64(Dqn_String8_Init(curr->number, curr->number_size), 0 /*separator*/);
|
||||
if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it))
|
||||
result = Dqn_Str8_ToU64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Dqn_JSON_IteratorValueToBool(Dqn_JSONIterator *it)
|
||||
bool Dqn_JSON_ItValueToBool(Dqn_JSONIt *it)
|
||||
{
|
||||
bool result = {};
|
||||
if (json_value_s *curr = Dqn_JSON_IteratorValueIsBool(it))
|
||||
if (json_value_s *curr = Dqn_JSON_ItValueIsBool(it))
|
||||
result = curr->type == json_type_true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Dqn_JSON_IteratorErrorUnknownKeyValue_(Dqn_JSONIterator *it, Dqn_CallSite call_site)
|
||||
void Dqn_JSON_ItErrorUnknownKeyValue_(Dqn_JSONIt *it, Dqn_CallSite call_site)
|
||||
{
|
||||
if (!it)
|
||||
return;
|
||||
|
||||
json_object_element_s const *curr = Dqn_JSON_IteratorCurrObjElement(it);
|
||||
json_object_element_s const *curr = Dqn_JSON_ItCurrObjElement(it);
|
||||
if (!curr)
|
||||
return;
|
||||
|
||||
@ -484,19 +502,18 @@ void Dqn_JSON_IteratorErrorUnknownKeyValue_(Dqn_JSONIterator *it, Dqn_CallSite c
|
||||
"Unknown key-value pair in object [loc=%zu:%zu, key=%.*s, value=%.*s]",
|
||||
info->line_no,
|
||||
info->row_no,
|
||||
key->string_size,
|
||||
DQN_CAST(int)key->string_size,
|
||||
key->string,
|
||||
value_type_size,
|
||||
DQN_CAST(int)value_type_size,
|
||||
value_type);
|
||||
} else {
|
||||
Dqn_Log_TypeFCallSite(Dqn_LogType_Warning,
|
||||
call_site,
|
||||
"Unknown key-value pair in object [key=%.*s, value=%.*s]",
|
||||
key->string_size,
|
||||
DQN_CAST(int)key->string_size,
|
||||
key->string,
|
||||
value_type_size,
|
||||
DQN_CAST(int)value_type_size,
|
||||
value_type);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(DQN_JSON_IMPLEMENTATION)
|
||||
|
@ -1,100 +0,0 @@
|
||||
#if defined(DQN_KECCAK_H)
|
||||
// -----------------------------------------------------------------------------
|
||||
// Dqn_Keccak Reference Implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
// A very compact Keccak implementation taken from the reference implementation
|
||||
// repository
|
||||
//
|
||||
// https://github.com/XKCP/XKCP/blob/master/Standalone/CompactFIPS202/C/Keccak-more-compact.c
|
||||
//
|
||||
|
||||
#define FOR(i,n) for(i=0; i<n; ++i)
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned long long int u64;
|
||||
typedef unsigned int ui;
|
||||
|
||||
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen);
|
||||
void FIPS202_SHAKE128(const u8 *in, u64 inLen, u8 *out, u64 outLen) { Keccak(1344, 256, in, inLen, 0x1F, out, outLen); }
|
||||
void FIPS202_SHAKE256(const u8 *in, u64 inLen, u8 *out, u64 outLen) { Keccak(1088, 512, in, inLen, 0x1F, out, outLen); }
|
||||
void FIPS202_SHA3_224(const u8 *in, u64 inLen, u8 *out) { Keccak(1152, 448, in, inLen, 0x06, out, 28); }
|
||||
void FIPS202_SHA3_256(const u8 *in, u64 inLen, u8 *out) { Keccak(1088, 512, in, inLen, 0x06, out, 32); }
|
||||
void FIPS202_SHA3_384(const u8 *in, u64 inLen, u8 *out) { Keccak(832, 768, in, inLen, 0x06, out, 48); }
|
||||
void FIPS202_SHA3_512(const u8 *in, u64 inLen, u8 *out) { Keccak(576, 1024, in, inLen, 0x06, out, 64); }
|
||||
|
||||
int LFSR86540(u8 *R) { (*R)=((*R)<<1)^(((*R)&0x80)?0x71:0); return ((*R)&2)>>1; }
|
||||
#define ROL(a,o) ((((u64)a)<<o)^(((u64)a)>>(64-o)))
|
||||
static u64 load64(const u8 *x) { ui i; u64 u=0; FOR(i,8) { u<<=8; u|=x[7-i]; } return u; }
|
||||
static void store64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]=u; u>>=8; } }
|
||||
static void xor64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]^=u; u>>=8; } }
|
||||
#define rL(x,y) load64((u8*)s+8*(x+5*y))
|
||||
#define wL(x,y,l) store64((u8*)s+8*(x+5*y),l)
|
||||
#define XL(x,y,l) xor64((u8*)s+8*(x+5*y),l)
|
||||
void KeccakF1600(void *s)
|
||||
{
|
||||
ui r,x,y,i,j,Y; u8 R=0x01; u64 C[5],D;
|
||||
for(i=0; i<24; i++) {
|
||||
/*θ*/ FOR(x,5) C[x]=rL(x,0)^rL(x,1)^rL(x,2)^rL(x,3)^rL(x,4); FOR(x,5) { D=C[(x+4)%5]^ROL(C[(x+1)%5],1); FOR(y,5) XL(x,y,D); }
|
||||
/*ρπ*/ x=1; y=r=0; D=rL(x,y); FOR(j,24) { r+=j+1; Y=(2*x+3*y)%5; x=y; y=Y; C[0]=rL(x,y); wL(x,y,ROL(D,r%64)); D=C[0]; }
|
||||
/*χ*/ FOR(y,5) { FOR(x,5) C[x]=rL(x,y); FOR(x,5) wL(x,y,C[x]^((~C[(x+1)%5])&C[(x+2)%5])); }
|
||||
/*ι*/ FOR(j,7) if (LFSR86540(&R)) XL(0,0,(u64)1<<((1<<j)-1));
|
||||
}
|
||||
}
|
||||
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen)
|
||||
{
|
||||
/*initialize*/ u8 s[200]; ui R=r/8; ui i,b=0; FOR(i,200) s[i]=0;
|
||||
/*absorb*/ while(inLen>0) { b=(inLen<R)?inLen:R; FOR(i,b) s[i]^=in[i]; in+=b; inLen-=b; if (b==R) { KeccakF1600(s); b=0; } }
|
||||
/*pad*/ s[b]^=sfx; if((sfx&0x80)&&(b==(R-1))) KeccakF1600(s); s[R-1]^=0x80; KeccakF1600(s);
|
||||
/*squeeze*/ while(outLen>0) { b=(outLen<R)?outLen:R; FOR(i,b) out[i]=s[i]; out+=b; outLen-=b; if(outLen>0) KeccakF1600(s); }
|
||||
}
|
||||
|
||||
|
||||
// PCG32 Random Number Generator
|
||||
// -----------------------------------------------------------------------------
|
||||
// NOTE: https://github.com/imneme/pcg-c-basic
|
||||
|
||||
struct pcg_state_setseq_64
|
||||
{ // Internals are *Private*.
|
||||
uint64_t state; // RNG state. All values are possible.
|
||||
uint64_t inc; // Controls which RNG sequence (stream) is
|
||||
// selected. Must *always* be odd.
|
||||
};
|
||||
typedef struct pcg_state_setseq_64 pcg32_random_t;
|
||||
|
||||
// pcg32_random_r(rng)
|
||||
// Generate a uniformly distributed 32-bit random number
|
||||
|
||||
uint32_t pcg32_random_r(pcg32_random_t* rng)
|
||||
{
|
||||
uint64_t oldstate = rng->state;
|
||||
rng->state = oldstate * 6364136223846793005ULL + rng->inc;
|
||||
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
|
||||
uint32_t rot = oldstate >> 59u;
|
||||
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
|
||||
}
|
||||
|
||||
// pcg32_srandom_r(rng, initstate, initseq):
|
||||
// Seed the rng. Specified in two parts, state initializer and a
|
||||
// sequence selection constant (a.k.a. stream id)
|
||||
|
||||
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
|
||||
{
|
||||
rng->state = 0U;
|
||||
rng->inc = (initseq << 1u) | 1u;
|
||||
pcg32_random_r(rng);
|
||||
rng->state += initstate;
|
||||
pcg32_random_r(rng);
|
||||
}
|
||||
|
||||
// pcg32_boundedrand_r(rng, bound):
|
||||
// Generate a uniformly distributed number, r, where 0 <= r < bound
|
||||
|
||||
uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound)
|
||||
{
|
||||
uint32_t threshold = -bound % bound;
|
||||
for (;;) {
|
||||
uint32_t r = pcg32_random_r(rng);
|
||||
if (r >= threshold)
|
||||
return r % bound;
|
||||
}
|
||||
}
|
||||
#endif // DQN_KECCAK_H
|
@ -13,7 +13,7 @@ pushd Build
|
||||
REM O2 Optimisation Level 2
|
||||
REM Oi Use CPU Intrinsics
|
||||
REM Z7 Combine multi-debug files to one debug file
|
||||
set common_flags=-D DQN_TEST_WITH_MAIN -I %script_dir% %script_dir%\Misc\dqn_unit_tests.cpp
|
||||
set common_flags=-D DQN_TEST_WITH_MAIN -D DQN_IMPLEMENTATION -D DQN_USE_STD_PRINTF /Tp %script_dir%\dqn.h
|
||||
|
||||
set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo
|
||||
|
||||
|
387
dqn.h
387
dqn.h
@ -1,11 +1,28 @@
|
||||
// dqn.h "Personal standard library" | MIT licensed | git.doylet.dev/dqn
|
||||
#if !defined(DQN_H)
|
||||
#define DQN_H
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\ $$$$$$\ $$\ $$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |
|
||||
// $$ | $$ |$$ / $$ |$$$$\ $$ |
|
||||
// $$ | $$ |$$ | $$ |$$ $$\$$ |
|
||||
// $$ | $$ |$$ | $$ |$$ \$$$$ |
|
||||
// $$ | $$ |$$ $$\$$ |$$ |\$$$ |
|
||||
// $$$$$$$ |\$$$$$$ / $$ | \$$ |
|
||||
// \_______/ \___$$$\ \__| \__|
|
||||
// \___|
|
||||
//
|
||||
// dqn.h -- Personal standard library -- MIT License -- git.doylet.dev/dqn
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// This library is a single-header file-esque library with inspiration taken
|
||||
// from STB libraries for ease of integration and use. It defines a bunch of
|
||||
// primitives and standard library functions that are missing and or more
|
||||
// appropriate for development in modern day computing (e.g. cache friendly
|
||||
// memory management, 64bit MMU, non-pessimized APIs that aren't constrained by
|
||||
// the language specification and operate closer to the OS).
|
||||
// appropriate for development in modern day computing (e.g. allocator
|
||||
// first-class APIs, a 64bit MMU and in general non-pessimized APIs that aren't
|
||||
// constrained by the language specification and operate closer to the OS).
|
||||
//
|
||||
// Define DQN_IMPLEMENTATION macro in one and only one translation unit to
|
||||
// enable the implementation of this library, for example:
|
||||
@ -33,18 +50,128 @@
|
||||
// Below is a table of contents that describes what you can find in each header
|
||||
// of this library and additional macros that can be defined to customise the
|
||||
// behaviour of this library.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\\__$$ __|\_$$ _|$$ __$$\ $$$\ $$ |$$ __$$\
|
||||
// $$ / $$ |$$ | $$ | $$ | $$ | $$ / $$ |$$$$\ $$ |$$ / \__|
|
||||
// $$ | $$ |$$$$$$$ | $$ | $$ | $$ | $$ |$$ $$\$$ |\$$$$$$\
|
||||
// $$ | $$ |$$ ____/ $$ | $$ | $$ | $$ |$$ \$$$$ | \____$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ | $$ |$$ |\$$$ |$$\ $$ |
|
||||
// $$$$$$ |$$ | $$ | $$$$$$\ $$$$$$ |$$ | \$$ |\$$$$$$ |
|
||||
// \______/ \__| \__| \______| \______/ \__| \__| \______/
|
||||
//
|
||||
// Options -- Compile time build customisation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// - Override these routines from the CRT by redefining them. By default we wrap
|
||||
// the CRT functions from <strings.h> and <math.h>, e.g:
|
||||
//
|
||||
// #define DQN_MEMCPY(dest, src, count) memcpy(dest, src, value)
|
||||
// #define DQN_MEMSET(dest, value, count) memset(dest, value, count)
|
||||
// #define DQN_MEMCMP(lhs, rhs, count) memcpy(lhs, rhs, count)
|
||||
// #define DQN_MEMMOVE(dest, src, count) memmove(dest, src, count)
|
||||
// #define DQN_SQRTF(val) sqrtf(val)
|
||||
// #define DQN_SINF(val) sinf(val)
|
||||
// #define DQN_COSF(val) cosf(val)
|
||||
// #define DQN_TANF(val) tanf(val)
|
||||
//
|
||||
// - Redefine 'DQN_API' to change the prefix of all declared functions in the library
|
||||
//
|
||||
// #define DQN_API
|
||||
//
|
||||
// - Define 'DQN_STATIC_API' to apply 'static' to all function definitions and
|
||||
// disable external linkage to other translation units by redefining 'DQN_API' to
|
||||
// 'static'.
|
||||
//
|
||||
// #define DQN_STATIC_API
|
||||
//
|
||||
// - Turn all assertion macros to no-ops except for hard asserts (which are
|
||||
// always enabled and represent unrecoverable errors in the library).
|
||||
//
|
||||
// #define DQN_NO_ASSERT
|
||||
//
|
||||
// - Augment DQN_CHECK(expr) macro's behaviour. By default when the check fails a
|
||||
// debug break is emitted. If this macro is defined, the check will not trigger
|
||||
// a debug break.
|
||||
//
|
||||
// #define DQN_NO_CHECK_BREAK
|
||||
//
|
||||
// - Define this macro to enable memory leak tracking on arena's that are
|
||||
// configured to track allocations.
|
||||
//
|
||||
// Allocations are stored in a global hash-table and their respective stack
|
||||
// traces for the allocation location. Memory leaks can be dumped at the end
|
||||
// of the program or some epoch by calling Dqn_Library_DumpLeaks()
|
||||
//
|
||||
// #define DQN_LEAK_TRACKING
|
||||
//
|
||||
// - Define this to revert to the family of printf functions from <stdio.h>
|
||||
// instead of using stb_sprintf in this library. stb_sprintf is 5-6x faster
|
||||
// than printf with a smaller binary footprint and has deterministic behaviour
|
||||
// across all supported platforms.
|
||||
//
|
||||
// #define DQN_USE_STD_PRINTF
|
||||
//
|
||||
// However, if you are compiling with ASAN on MSVC, MSVC's implementation of
|
||||
// __declspec(no_sanitize_address) is unable to suppress warnings in some
|
||||
// individual functions in stb's implementation causing ASAN to trigger. This
|
||||
// library will error on compilation if it detects this is the case and is
|
||||
// being compiled with STB sprintf.
|
||||
//
|
||||
// - Define this to stop this library from defining a minimal subset of Win32
|
||||
// prototypes and definitions in this file. You should use this macro if you
|
||||
// intend to #include <Windows.h> yourself to avoid symbol conflicts with
|
||||
// the redefined declarations in this library.
|
||||
//
|
||||
// #define DQN_NO_WIN32_MIN_HEADER
|
||||
//
|
||||
// - Define this to stop this library from defining STB_SPRINTF_IMPLEMENTATION.
|
||||
// Useful if another library uses and includes "stb_sprintf.h"
|
||||
//
|
||||
// #define DQN_STB_SPRINTF_HEADER_ONLY
|
||||
//
|
||||
// - Override the default break into the active debugger function. By default
|
||||
// we use __debugbreak() on Windows and raise(SIGTRAP) on other platforms.
|
||||
//
|
||||
// #define DQN_DEBUG_BREAK
|
||||
//
|
||||
// - Define this macro to 1 to enable poisoning of memory from arenas when ASAN
|
||||
// `-fsanitize=address` is enabled. Enabling this will detect memory overwrite
|
||||
// by padding allocated before and after with poisoned memory which will raise
|
||||
// a use-after-poison in ASAN on read/write. This is a no-op if the library is
|
||||
// not compiled with ASAN.
|
||||
//
|
||||
// #define DQN_ASAN_POISON 1
|
||||
//
|
||||
// - Define this macro 1 to enable sanity checks for manually poisoned memory in
|
||||
// this library when ASAN `-fsanitize=address` is enabled. These sanity checks
|
||||
// ensure that memory from arenas are correctly un/poisoned when pointers are
|
||||
// allocated and returned to the memory arena's. This is a no-op if we are not
|
||||
// compiled with ASAN or `DQN_ASAN_POISON` is not set to `1`.
|
||||
//
|
||||
// #define DQN_ASAN_VET_POISON 1
|
||||
//
|
||||
// - Define this macro to the size of the guard memory reserved before and after
|
||||
// allocations made that are poisoned to protect against out-of-bounds memory
|
||||
// accesses. By default the library sets the guard to 128 bytes.
|
||||
//
|
||||
// #define DQN_ASAN_POISON_GUARD_SIZE 128
|
||||
//
|
||||
|
||||
#if !defined(DQN_H)
|
||||
#if defined(DQN_ONLY_VARRAY) || \
|
||||
defined(DQN_ONLY_SARRAY) || \
|
||||
defined(DQN_ONLY_FARRAY) || \
|
||||
defined(DQN_ONLY_SLICE) || \
|
||||
defined(DQN_ONLY_DSMAP) || \
|
||||
defined(DQN_ONLY_LIST) || \
|
||||
defined(DQN_ONLY_FSTR8) || \
|
||||
defined(DQN_ONLY_FS) || \
|
||||
defined(DQN_ONLY_WINNET) || \
|
||||
defined(DQN_ONLY_WIN) || \
|
||||
defined(DQN_ONLY_SEMAPHORE) || \
|
||||
defined(DQN_ONLY_THREAD) || \
|
||||
defined(DQN_ONLY_V2) || \
|
||||
defined(DQN_ONLY_V3) || \
|
||||
defined(DQN_ONLY_V4) || \
|
||||
@ -63,9 +190,6 @@
|
||||
#if !defined(DQN_ONLY_SARRAY)
|
||||
#define DQN_NO_SARRAY
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_SLICE)
|
||||
#define DQN_NO_SLICE
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_DSMAP)
|
||||
#define DQN_NO_DSMAP
|
||||
#endif
|
||||
@ -84,6 +208,12 @@
|
||||
#if !defined(DQN_ONLY_WIN)
|
||||
#define DQN_NO_WIN
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_SEMAPHORE)
|
||||
#define DQN_NO_SEMAPHORE
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_THREAD)
|
||||
#define DQN_NO_THREAD
|
||||
#endif
|
||||
#if !defined(DQN_ONLY_V2)
|
||||
#define DQN_NO_V2
|
||||
#endif
|
||||
@ -110,222 +240,65 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: Table of Contents =========================================================================
|
||||
// Index | #define Label | Description
|
||||
// NOTE: C Headers =================================================================================
|
||||
#include <stdarg.h> // | | va_list
|
||||
#include <stdio.h> // | | fprintf, FILE, stdout, stderr
|
||||
#include <stdint.h> // | | [u]int_*, ...
|
||||
#include <limits.h> // | | [U]INT_MAX, ...
|
||||
|
||||
// NOTE: Dqn_Base ==================================================================================
|
||||
// [$CMAC] Compiler macros | | Macros for the compiler
|
||||
// [$MACR] Macros | | Define macros used in the library
|
||||
// [$TYPE] Types | | Basic types and typedefs
|
||||
// [$INTR] Intrinsics | | Atomics, cpuid, ticket mutex
|
||||
// [$CALL] Dqn_CallSite | | Source code location/tracing
|
||||
// [$TMUT] Dqn_TicketMutex | | Userland mutex via spinlocking atomics
|
||||
// [$ALLO] Dqn_Allocator | | Generic allocator interface
|
||||
// [$PRIN] Dqn_Print | | Console printing
|
||||
// [$LLOG] Dqn_Log | | Console logging macros
|
||||
|
||||
// NOTE: Additional Configuration
|
||||
// - Override the default heap-allocation routine that is called when the
|
||||
// default Dqn_Allocator is used by #define-ing. By default we use the OS's
|
||||
// virtual memory allocators (e.g. VirtualAlloc on Windows and mmap on Linux).
|
||||
//
|
||||
// DQN_ALLOC(size)
|
||||
// DQN_DEALLOC(ptr, size)
|
||||
//
|
||||
// - Override the byte-manipulation routines by #define-ing. By default we use
|
||||
// <strings.h>
|
||||
//
|
||||
// DQN_MEMCPY(dest, src, count)
|
||||
// DQN_MEMSET(dest, value, count)
|
||||
// DQN_MEMCMP(lhs, rhs, count)
|
||||
// DQN_MEMMOVE(dest, src, count)
|
||||
//
|
||||
// - Override these math functions. By default we use <math.h>
|
||||
//
|
||||
// DQN_SQRTF(val)
|
||||
// DQN_SINF(val)
|
||||
// DQN_COSF(val)
|
||||
// DQN_TANF(val)
|
||||
//
|
||||
// - Change the prefix to all declared functions in the library by #define-ing.
|
||||
//
|
||||
// DQN_API
|
||||
//
|
||||
// - Apply static to all function definitions and disable external linkage to
|
||||
// other translation units by #define-ing. This macro is only used if DQN_API
|
||||
// is not overriden.
|
||||
//
|
||||
// DQN_STATIC_API
|
||||
//
|
||||
// - Turn all assertion macros to no-ops except for hard asserts (which are
|
||||
// always enabled and represent unrecoverable errors in the library).
|
||||
//
|
||||
// DQN_NO_ASSERT
|
||||
//
|
||||
// - Augment DQN_CHECK(expr) macro's behaviour. By default it will trigger a
|
||||
// debugger break when when the expression evalutes false otherwise by
|
||||
// #define-ing this macro it will evaluate to false and DQN_CHECK is usually
|
||||
// used in a if branch to recover gracefully from the failed condition.
|
||||
//
|
||||
// DQN_NO_CHECK_BREAK
|
||||
|
||||
#include "dqn_base.h"
|
||||
|
||||
// NOTE: Dqn_External ==============================================================================
|
||||
// [$OS_H] OS Headers | | Headers from the operating system
|
||||
// [$STBS] stb_sprintf | | Portable sprintf
|
||||
#include "dqn_external.h"
|
||||
#if defined(DQN_PLATFORM_WIN32)
|
||||
#include "dqn_win32.h"
|
||||
|
||||
// NOTE: Additional Configuration
|
||||
// - Define this to stop this library from defining a minimal subset of Win32
|
||||
// prototypes and definitions in this file. You should use this macro if you
|
||||
// intend to #include <Windows.h> yourself to avoid symbol conflicts with
|
||||
// the redefined declarations in this library.
|
||||
//
|
||||
// DQN_NO_WIN32_MIN_HEADER
|
||||
//
|
||||
// - Define this to stop this library from defining STB_SPRINTF_IMPLEMENTATION.
|
||||
// Useful if another library uses and includes "stb_sprintf.h"
|
||||
//
|
||||
// DQN_STB_SPRINTF_HEADER_ONLY
|
||||
|
||||
// NOTE: Dqn_Memory ================================================================================
|
||||
// [$VMEM] Dqn_VMem | | Virtual memory allocation
|
||||
// [$MEMB] Dqn_MemBlock | | Virtual memory blocks
|
||||
// [$AREN] Dqn_Arena | | Growing bump allocator
|
||||
// [$ACAT] Dqn_ArenaCatalog | | Collate, create & manage arenas in a catalog
|
||||
#include "dqn_memory.h"
|
||||
|
||||
// NOTE: Dqn_Debug =================================================================================
|
||||
// [$DEBM] Debug Macros | |
|
||||
// [$ASAN] Dqn_Asan | | Helpers to manually poison memory using ASAN
|
||||
// [$STKT] Dqn_StackTrace | | Create stack traces
|
||||
// [$DEBG] Dqn_Debug | | Debugging tools/helpers
|
||||
#endif
|
||||
#include "dqn_allocator.h"
|
||||
#include "dqn_thread_context.h"
|
||||
#include "dqn_debug.h"
|
||||
|
||||
// NOTE: Dqn_Strings ===============================================================================
|
||||
// [$CSTR] Dqn_CStr8 | | C-string helpers
|
||||
// [$STR8] Dqn_Str8 | | Pointer and length strings
|
||||
// [$FSTR] Dqn_FStr8 | DQN_FSTr8 | Fixed-size strings
|
||||
// [$STRB] Dqn_Str8Builder | |
|
||||
// [$CHAR] Dqn_Char | | Character ascii/digit.. helpers
|
||||
// [$UTFX] Dqn_UTF | | Unicode helpers
|
||||
#include "dqn_strings.h"
|
||||
|
||||
// NOTE: Dqn_Containers ============================================================================
|
||||
// [$CARR] Dqn_CArray | | Basic operations on C arrays for VArray/SArray/FArray to reuse
|
||||
// [$VARR] Dqn_VArray | DQN_VARRAY | Array backed by virtual memory arena
|
||||
// [$SARR] Dqn_SArray | DQN_SARRAY | Array that are allocated but cannot resize
|
||||
// [$FARR] Dqn_FArray | DQN_FARRAY | Fixed-size arrays
|
||||
// [$SLIC] Dqn_Slice | DQN_SLICE | Pointe and length container of data
|
||||
// [$DMAP] Dqn_DSMap | DQN_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair
|
||||
// [$LIST] Dqn_List | DQN_LIST | Chunked linked lists, append only
|
||||
#include "dqn_string.h"
|
||||
#include "dqn_containers.h"
|
||||
|
||||
// NOTE: Additional Configuration
|
||||
// - Override the default break into the active debugger function. By default
|
||||
// we use __debugbreak() on Windows and raise(SIGTRAP) on other platforms.
|
||||
//
|
||||
// DQN_DEBUG_BREAK
|
||||
//
|
||||
// - Change the byte that DQN_MEMSET will clear memory with. By default this
|
||||
// is set to 0. Some of the API's in this library accept a Dqn_ZeroMem enum
|
||||
// which scrubs memory with this #define-d value.
|
||||
//
|
||||
// DQN_MEMSET_BYTE
|
||||
//
|
||||
// - Define this macro to enable emory leak tracking when requesting memory
|
||||
// from the OS via this library. For example calls to Dqn_VMem_Reserve or
|
||||
// DQN_ALLOC are recorded to the leak table.
|
||||
//
|
||||
// Allocations are stored in a global hash-table and their respective stack
|
||||
// traces for the allocation location. Memory leaks can be dumped at the end
|
||||
// of the program or some epoch by calling Dqn_Library_DumpLeaks()
|
||||
//
|
||||
// You may mark sections of your program as allowed to leak memory by setting
|
||||
// the arena's or Dqn_Library's runtime struct `allocs_are_allowed_to_leak`
|
||||
// flag.
|
||||
//
|
||||
// DQN_LEAK_TRACING
|
||||
//
|
||||
// - Define this macro to 1 to enable poisoning of memory from arenas when ASAN
|
||||
// `-fsanitize=address` is enabled. Enabling this will detect memory overwrite
|
||||
// by padding allocated before and after with poisoned memory which will raise
|
||||
// a use-after-poison in ASAN on read/write. This is a no-op if the library is
|
||||
// not compiled with ASAN.
|
||||
//
|
||||
// DQN_ASAN_POISON 1
|
||||
//
|
||||
// - Define this macro 1 to enable sanity checks for manually poisoned memory in
|
||||
// this library when ASAN `-fsanitize=address` is enabled. These sanity checks
|
||||
// ensure that memory from arenas are correctly un/poisoned when pointers are
|
||||
// allocated and returned to the memory arena's. This is a no-op if we are not
|
||||
// compiled with ASAN or `DQN_ASAN_POISON` is not set to `1`.
|
||||
//
|
||||
// DQN_ASAN_VET_POISON 1
|
||||
//
|
||||
// - Define this macro to the size of the guard memory reserved before and after
|
||||
// allocations made that are poisoned to protect against out-of-bounds memory
|
||||
// accesses. By default the library sets the guard to 128 bytes.
|
||||
//
|
||||
// DQN_ASAN_POISON_GUARD_SIZE 128
|
||||
|
||||
// NOTE: Dqn_Platform ==============================================================================
|
||||
// [$FSYS] Dqn_Fs | DQN_FS | Filesystem helpers
|
||||
// [$DATE] Dqn_Date | | Date-time helpers
|
||||
// [$WIND] Dqn_Win | | Windows OS helpers
|
||||
// [$WINN] Dqn_WinNet | DQN_WINNET | Windows internet download/query helpers
|
||||
// [$OSYS] Dqn_OS | DQN_WIN | Operating-system APIs
|
||||
// [$TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas
|
||||
#include "dqn_platform.h"
|
||||
|
||||
// NOTE: Dqn_OS ====================================================================================
|
||||
// [$EXEC] Dqn_OSExec | | Execute programs programatically
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64)
|
||||
#elif defined(DQN_PLATFORM_WIN32)
|
||||
#include "dqn_os_win32.h"
|
||||
#else
|
||||
#error Please define a platform e.g. 'DQN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
|
||||
#endif
|
||||
#include "dqn_os.h"
|
||||
|
||||
// NOTE: Dqn_Math ==================================================================================
|
||||
// [$VEC2] Dqn_V2, V2i | DQN_V2 |
|
||||
// [$VEC3] Dqn_V3, V3i | DQN_V3 |
|
||||
// [$VEC4] Dqn_V4, V4i | DQN_V4 |
|
||||
// [$MAT4] Dqn_M4 | DQN_M4 |
|
||||
// [$RECT] Dqn_Rect | DQN_RECT |
|
||||
// [$MATH] Other | |
|
||||
#include "dqn_math.h"
|
||||
|
||||
// NOTE: Dqn_Hash ==================================================================================
|
||||
// [$FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a
|
||||
// [$MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3
|
||||
#include "dqn_hash.h"
|
||||
|
||||
// NOTE: Dqn_Helpers ===============================================================================
|
||||
// [$PCG3] Dqn_PCG32 | | RNG from the PCG family
|
||||
// [$JSON] Dqn_JSONBuilder | DQN_JSON_BUILDER | Construct json output
|
||||
// [$BHEX] Dqn_Bin | DQN_BIN | Binary <-> hex helpers
|
||||
// [$BSEA] Dqn_BinarySearch | | Binary search
|
||||
// [$BITS] Dqn_Bit | | Bitset manipulation
|
||||
// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts
|
||||
// [$MISC] Misc | | Uncategorised helper functions
|
||||
// [$DLIB] Dqn_Library | | Globally shared runtime data for this library
|
||||
// [$PROF] Dqn_Profiler | DQN_PROFILER | Profiler that measures using a timestamp counter
|
||||
#include "dqn_helpers.h"
|
||||
#include "dqn_type_info.h"
|
||||
#endif // DQN_H
|
||||
|
||||
#if defined(DQN_IMPLEMENTATION)
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// /$$$$$$\ $$\ $$\ $$$$$$$\ $$\
|
||||
// \_$$ _|$$$\ $$$ |$$ __$$\ $$ |
|
||||
// $$ | $$$$\ $$$$ |$$ | $$ |$$ |
|
||||
// $$ | $$\$$\$$ $$ |$$$$$$$ |$$ |
|
||||
// $$ | $$ \$$$ $$ |$$ ____/ $$ |
|
||||
// $$ | $$ |\$ /$$ |$$ | $$ |
|
||||
// $$$$$$\ $$ | \_/ $$ |$$ | $$$$$$$$\
|
||||
// \______|\__| \__|\__| \________|
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "dqn_base.cpp"
|
||||
#include "dqn_thread_context.cpp"
|
||||
#include "dqn_external.cpp"
|
||||
#include "dqn_memory.cpp"
|
||||
#include "dqn_allocator.cpp"
|
||||
#include "dqn_debug.cpp"
|
||||
#include "dqn_strings.cpp"
|
||||
#include "dqn_string.cpp"
|
||||
#include "dqn_containers.cpp"
|
||||
#include "dqn_platform.cpp"
|
||||
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64)
|
||||
#include "dqn_os_posix.cpp"
|
||||
#elif defined(DQN_PLATFORM_WIN32)
|
||||
#include "dqn_os_win32.cpp"
|
||||
#else
|
||||
#error Please define a platform e.g. 'DQN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
|
||||
#endif
|
||||
|
||||
#include "dqn_os.cpp"
|
||||
#include "dqn_math.cpp"
|
||||
#include "dqn_hash.cpp"
|
||||
#include "dqn_helpers.cpp"
|
||||
#include "dqn_unit_tests.cpp"
|
||||
#include "dqn_docs.cpp"
|
||||
#endif // DQN_IMPLEMENTATION
|
||||
|
497
dqn_allocator.cpp
Normal file
497
dqn_allocator.cpp
Normal file
@ -0,0 +1,497 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
// $$ __$$\ $$ | $$ | $$ __$$\ $$ __$$\ $$ __$$\\__$$ __|$$ __$$\ $$ __$$\
|
||||
// $$ / $$ |$$ | $$ | $$ / $$ |$$ / \__|$$ / $$ | $$ | $$ / $$ |$$ | $$ |
|
||||
// $$$$$$$$ |$$ | $$ | $$ | $$ |$$ | $$$$$$$$ | $$ | $$ | $$ |$$$$$$$ |
|
||||
// $$ __$$ |$$ | $$ | $$ | $$ |$$ | $$ __$$ | $$ | $$ | $$ |$$ __$$<
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$\ $$ | $$ | $$ | $$ | $$ |$$ | $$ |
|
||||
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$$$$$ |\$$$$$$ |$$ | $$ | $$ | $$$$$$ |$$ | $$ |
|
||||
// \__| \__|\________|\________|\______/ \______/ \__| \__| \__| \______/ \__| \__|
|
||||
//
|
||||
// dqn_allocator.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$AREN] Dqn_Arena /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_ArenaBlock *Dqn_Arena_BlockInit(uint64_t reserve, uint64_t commit, bool track_alloc, bool alloc_can_leak)
|
||||
{
|
||||
Dqn_usize const page_size = g_dqn_library->os_page_size;
|
||||
uint64_t real_reserve = reserve ? reserve : DQN_ARENA_RESERVE_SIZE;
|
||||
uint64_t real_commit = commit ? commit : DQN_ARENA_COMMIT_SIZE;
|
||||
real_reserve = Dqn_AlignUpPowerOfTwo(real_reserve, page_size);
|
||||
real_commit = DQN_MIN(Dqn_AlignUpPowerOfTwo(real_commit, page_size), real_reserve);
|
||||
|
||||
DQN_ASSERTF(DQN_ARENA_HEADER_SIZE < real_commit && real_commit <= real_reserve, "%I64u < %I64u <= %I64u", DQN_ARENA_HEADER_SIZE, real_commit, real_reserve);
|
||||
DQN_ASSERTF(page_size, "Call Dqn_Library_Init() to initialise the known page size");
|
||||
|
||||
Dqn_OSMemCommit mem_commit = real_reserve == real_commit ? Dqn_OSMemCommit_Yes : Dqn_OSMemCommit_No;
|
||||
Dqn_ArenaBlock *result = DQN_CAST(Dqn_ArenaBlock *)Dqn_OS_MemReserve(real_reserve, mem_commit, Dqn_OSMemPage_ReadWrite);
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
if (mem_commit == Dqn_OSMemCommit_No && !Dqn_OS_MemCommit(result, real_commit, Dqn_OSMemPage_ReadWrite)) {
|
||||
Dqn_OS_MemRelease(result, real_reserve);
|
||||
return result;
|
||||
}
|
||||
|
||||
result->used = DQN_ARENA_HEADER_SIZE;
|
||||
result->commit = real_commit;
|
||||
result->reserve = real_reserve;
|
||||
|
||||
if (track_alloc)
|
||||
Dqn_Debug_TrackAlloc(result, result->reserve, alloc_can_leak);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ArenaBlock *Dqn_Arena_BlockInitFlags(uint64_t reserve, uint64_t commit, uint8_t arena_flags)
|
||||
{
|
||||
bool track_alloc = (arena_flags & Dqn_ArenaFlag_NoAllocTrack) == 0;
|
||||
bool alloc_can_leak = arena_flags & Dqn_ArenaFlag_AllocCanLeak;
|
||||
Dqn_ArenaBlock *result = Dqn_Arena_BlockInit(reserve, commit, track_alloc, alloc_can_leak);
|
||||
if (result && ((arena_flags & Dqn_ArenaFlag_NoPoison) == 0))
|
||||
Dqn_ASAN_PoisonMemoryRegion(DQN_CAST(char *)result + DQN_ARENA_HEADER_SIZE, result->commit - DQN_ARENA_HEADER_SIZE);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena Dqn_Arena_InitSize(uint64_t reserve, uint64_t commit, uint8_t flags)
|
||||
{
|
||||
Dqn_Arena result = {};
|
||||
result.flags = flags;
|
||||
result.curr = Dqn_Arena_BlockInitFlags(reserve, commit, flags);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Dqn_Arena_BlockDeinit_(Dqn_Arena const *arena, Dqn_ArenaBlock *block)
|
||||
{
|
||||
Dqn_usize release_size = block->reserve;
|
||||
if (Dqn_Bit_IsNotSet(arena->flags, Dqn_ArenaFlag_NoAllocTrack))
|
||||
Dqn_Debug_TrackDealloc(block);
|
||||
Dqn_ASAN_UnpoisonMemoryRegion(block, block->commit);
|
||||
Dqn_OS_MemRelease(block, release_size);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_Deinit(Dqn_Arena *arena)
|
||||
{
|
||||
for (Dqn_ArenaBlock *block = arena ? arena->curr : nullptr; block; ) {
|
||||
Dqn_ArenaBlock *block_to_free = block;
|
||||
block = block->prev;
|
||||
Dqn_Arena_BlockDeinit_(arena, block_to_free);
|
||||
}
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Arena_CommitTo(Dqn_Arena *arena, uint64_t pos)
|
||||
{
|
||||
if (!arena || !arena->curr)
|
||||
return false;
|
||||
|
||||
Dqn_ArenaBlock *curr = arena->curr;
|
||||
if (pos <= curr->commit)
|
||||
return true;
|
||||
|
||||
uint64_t real_pos = pos;
|
||||
if (!DQN_CHECK(pos <= curr->reserve))
|
||||
real_pos = curr->reserve;
|
||||
|
||||
Dqn_usize end_commit = Dqn_AlignUpPowerOfTwo(real_pos, g_dqn_library->os_page_size);
|
||||
Dqn_usize commit_size = end_commit - curr->commit;
|
||||
char *commit_ptr = DQN_CAST(char *) curr + curr->commit;
|
||||
if (!Dqn_OS_MemCommit(commit_ptr, commit_size, Dqn_OSMemPage_ReadWrite))
|
||||
return false;
|
||||
|
||||
bool poison = DQN_ASAN_POISON && ((arena->flags & Dqn_ArenaFlag_NoPoison) == 0);
|
||||
if (poison)
|
||||
Dqn_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
|
||||
|
||||
curr->commit = end_commit;
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Arena_Commit(Dqn_Arena *arena, uint64_t size)
|
||||
{
|
||||
if (!arena || !arena->curr)
|
||||
return false;
|
||||
uint64_t pos = arena->curr->commit + size;
|
||||
bool result = Dqn_Arena_CommitTo(arena, pos);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, uint64_t size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||
{
|
||||
if (!arena)
|
||||
return nullptr;
|
||||
|
||||
if (!arena->curr)
|
||||
arena->curr = Dqn_Arena_BlockInitFlags(DQN_ARENA_RESERVE_SIZE, DQN_ARENA_COMMIT_SIZE, arena->flags);
|
||||
|
||||
if (!arena->curr)
|
||||
return nullptr;
|
||||
|
||||
try_alloc_again:
|
||||
Dqn_ArenaBlock *curr = arena->curr;
|
||||
bool poison = DQN_ASAN_POISON && ((arena->flags & Dqn_ArenaFlag_NoPoison) == 0);
|
||||
uint8_t real_align = poison ? DQN_MAX(align, DQN_ASAN_POISON_ALIGNMENT) : align;
|
||||
uint64_t offset_pos = Dqn_AlignUpPowerOfTwo(curr->used, real_align) + (poison ? DQN_ASAN_POISON_GUARD_SIZE : 0);
|
||||
uint64_t end_pos = offset_pos + size;
|
||||
|
||||
if (end_pos > curr->reserve) {
|
||||
if (arena->flags & Dqn_ArenaFlag_NoGrow)
|
||||
return nullptr;
|
||||
Dqn_usize new_reserve = DQN_MAX(DQN_ARENA_HEADER_SIZE + size, DQN_ARENA_RESERVE_SIZE);
|
||||
Dqn_usize new_commit = DQN_MAX(DQN_ARENA_HEADER_SIZE + size, DQN_ARENA_COMMIT_SIZE);
|
||||
Dqn_ArenaBlock *new_block = Dqn_Arena_BlockInitFlags(new_reserve, new_commit, arena->flags);
|
||||
if (!new_block)
|
||||
return nullptr;
|
||||
new_block->prev = arena->curr;
|
||||
arena->curr = new_block;
|
||||
new_block->reserve_sum = new_block->prev->reserve_sum + new_block->prev->reserve;
|
||||
goto try_alloc_again;
|
||||
}
|
||||
|
||||
Dqn_usize prev_arena_commit = curr->commit;
|
||||
if (end_pos > curr->commit) {
|
||||
Dqn_usize end_commit = Dqn_AlignUpPowerOfTwo(end_pos, g_dqn_library->os_page_size);
|
||||
Dqn_usize commit_size = end_commit - curr->commit;
|
||||
char *commit_ptr = DQN_CAST(char *)curr + curr->commit;
|
||||
if (!Dqn_OS_MemCommit(commit_ptr, commit_size, Dqn_OSMemPage_ReadWrite))
|
||||
return nullptr;
|
||||
if (poison)
|
||||
Dqn_ASAN_PoisonMemoryRegion(commit_ptr, commit_size);
|
||||
curr->commit = end_commit;
|
||||
}
|
||||
|
||||
void *result = DQN_CAST(char *) curr + offset_pos;
|
||||
curr->used = end_pos;
|
||||
Dqn_ASAN_UnpoisonMemoryRegion(result, size);
|
||||
|
||||
if (zero_mem == Dqn_ZeroMem_Yes) {
|
||||
Dqn_usize reused_bytes = DQN_MIN(prev_arena_commit - offset_pos, size);
|
||||
DQN_MEMSET(result, 0, reused_bytes);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_AllocContiguous(Dqn_Arena *arena, uint64_t size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||
{
|
||||
uint8_t prev_flags = arena->flags;
|
||||
arena->flags |= (Dqn_ArenaFlag_NoGrow | Dqn_ArenaFlag_NoPoison);
|
||||
void *memory = Dqn_Arena_Alloc(arena, size, align, zero_mem);
|
||||
arena->flags = prev_flags;
|
||||
return memory;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_Copy(Dqn_Arena *arena, void const *data, uint64_t size, uint8_t align)
|
||||
{
|
||||
if (!arena || !data || size == 0)
|
||||
return nullptr;
|
||||
void *result = Dqn_Arena_Alloc(arena, size, align, Dqn_ZeroMem_No);
|
||||
if (result)
|
||||
DQN_MEMCPY(result, data, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_PopTo(Dqn_Arena *arena, uint64_t init_used)
|
||||
{
|
||||
if (!arena)
|
||||
return;
|
||||
uint64_t used = DQN_MAX(DQN_ARENA_HEADER_SIZE, init_used);
|
||||
Dqn_ArenaBlock *curr = arena->curr;
|
||||
while (curr->reserve_sum >= used) {
|
||||
Dqn_ArenaBlock *block_to_free = curr;
|
||||
curr = curr->prev;
|
||||
Dqn_Arena_BlockDeinit_(arena, block_to_free);
|
||||
}
|
||||
|
||||
arena->curr = curr;
|
||||
curr->used = used - curr->reserve_sum;
|
||||
char *poison_ptr = (char *)curr + Dqn_AlignUpPowerOfTwo(curr->used, DQN_ASAN_POISON_ALIGNMENT);
|
||||
Dqn_usize poison_size = ((char *)curr + curr->commit) - poison_ptr;
|
||||
Dqn_ASAN_PoisonMemoryRegion(poison_ptr, poison_size);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_Pop(Dqn_Arena *arena, uint64_t amount)
|
||||
{
|
||||
Dqn_ArenaBlock *curr = arena->curr;
|
||||
Dqn_usize used_sum = curr->reserve_sum + curr->used;
|
||||
if (!DQN_CHECK(amount <= used_sum))
|
||||
amount = used_sum;
|
||||
Dqn_usize pop_to = used_sum - amount;
|
||||
Dqn_Arena_PopTo(arena, pop_to);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_Clear(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Arena_PopTo(arena, 0);
|
||||
}
|
||||
|
||||
DQN_API Dqn_ArenaTempMem Dqn_Arena_TempMemBegin(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_ArenaTempMem result = {};
|
||||
if (arena) {
|
||||
Dqn_ArenaBlock *curr = arena->curr;
|
||||
result = {arena, curr ? curr->reserve_sum + curr->used : 0};
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
DQN_API void Dqn_Arena_TempMemEnd(Dqn_ArenaTempMem mem)
|
||||
{
|
||||
Dqn_Arena_PopTo(mem.arena, mem.used_sum);
|
||||
};
|
||||
|
||||
Dqn_ArenaTempMemScope::Dqn_ArenaTempMemScope(Dqn_Arena *arena)
|
||||
{
|
||||
mem = Dqn_Arena_TempMemBegin(arena);
|
||||
}
|
||||
|
||||
Dqn_ArenaTempMemScope::~Dqn_ArenaTempMemScope()
|
||||
{
|
||||
Dqn_Arena_TempMemEnd(mem);
|
||||
}
|
||||
|
||||
// NOTE: [$CHUN] Dqn_ChunkPool /////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_ChunkPool Dqn_ChunkPool_Init(Dqn_Arena *arena, uint8_t align)
|
||||
{
|
||||
Dqn_ChunkPool result = {};
|
||||
if (arena) {
|
||||
result.arena = arena;
|
||||
result.align = align;
|
||||
if (result.align == 0)
|
||||
result.align = DQN_CHUNK_POOL_DEFAULT_ALIGN;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_ChunkPool_IsValid(Dqn_ChunkPool const *pool)
|
||||
{
|
||||
bool result = pool && pool->arena && pool->align;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_ChunkPool_Alloc(Dqn_ChunkPool *pool, Dqn_usize size)
|
||||
{
|
||||
void *result = nullptr;
|
||||
if (!Dqn_ChunkPool_IsValid(pool))
|
||||
return result;
|
||||
|
||||
Dqn_usize const required_size = sizeof(Dqn_ChunkPoolSlot) + pool->align + size;
|
||||
Dqn_usize const size_to_slot_offset = 5; // __lzcnt64(32) e.g. Dqn_ChunkPoolSlotSize_32B
|
||||
Dqn_usize slot_index = 0;
|
||||
if (required_size > 32) {
|
||||
#if defined(DQN_OS_WIN32)
|
||||
Dqn_usize dist_to_next_msb = __lzcnt64(required_size) + 1;
|
||||
#else
|
||||
Dqn_usize dist_to_next_msb = __builtin_clz(required_size) + 1;
|
||||
#endif
|
||||
|
||||
// NOTE: Round up if not PoT as the low bits are set.
|
||||
dist_to_next_msb -= DQN_CAST(Dqn_usize)(!Dqn_IsPowerOfTwo(required_size));
|
||||
|
||||
Dqn_usize const register_size = sizeof(Dqn_usize) * 8;
|
||||
DQN_ASSERT(register_size >= dist_to_next_msb + size_to_slot_offset);
|
||||
slot_index = register_size - dist_to_next_msb - size_to_slot_offset;
|
||||
}
|
||||
|
||||
if (!DQN_CHECKF(slot_index < Dqn_ChunkPoolSlotSize_Count, "Chunk pool does not support the requested allocation size"))
|
||||
return result;
|
||||
|
||||
Dqn_usize slot_size_in_bytes = 1ULL << (slot_index + size_to_slot_offset);
|
||||
DQN_ASSERT(required_size <= (slot_size_in_bytes << 0));
|
||||
DQN_ASSERT(required_size >= (slot_size_in_bytes >> 1));
|
||||
|
||||
Dqn_ChunkPoolSlot *slot = nullptr;
|
||||
if (pool->slots[slot_index]) {
|
||||
slot = pool->slots[slot_index];
|
||||
pool->slots[slot_index] = slot->next;
|
||||
DQN_MEMSET(slot->data, 0, size);
|
||||
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(slot->data, pool->align));
|
||||
} else {
|
||||
void *bytes = Dqn_Arena_Alloc(pool->arena, slot_size_in_bytes, alignof(Dqn_ChunkPoolSlot), Dqn_ZeroMem_Yes);
|
||||
slot = DQN_CAST(Dqn_ChunkPoolSlot *) bytes;
|
||||
|
||||
// NOTE: The raw pointer is round up to the next 'pool->align'-ed
|
||||
// address ensuring at least 1 byte of padding between the raw pointer
|
||||
// and the pointer given to the user and that the user pointer is
|
||||
// aligned to the pool's alignment.
|
||||
//
|
||||
// This allows us to smuggle 1 byte behind the user pointer that has
|
||||
// the offset to the original pointer.
|
||||
slot->data = DQN_CAST(void *)Dqn_AlignDownPowerOfTwo(DQN_CAST(uintptr_t)slot + sizeof(Dqn_ChunkPoolSlot) + pool->align, pool->align);
|
||||
|
||||
uintptr_t offset_to_original_ptr = DQN_CAST(uintptr_t)slot->data - DQN_CAST(uintptr_t)bytes;
|
||||
DQN_ASSERT(slot->data > bytes);
|
||||
DQN_ASSERT(offset_to_original_ptr <= sizeof(Dqn_ChunkPoolSlot) + pool->align);
|
||||
|
||||
// NOTE: Store the offset to the original pointer behind the user's
|
||||
// pointer.
|
||||
DQN_MEMCPY(&(DQN_CAST(char *)slot->data)[-1], &offset_to_original_ptr, 1);
|
||||
}
|
||||
|
||||
// NOTE: Smuggle the slot type in the next pointer so that we know, when the
|
||||
// pointer gets returned which free list to return the pointer to.
|
||||
result = slot->data;
|
||||
slot->next = DQN_CAST(Dqn_ChunkPoolSlot *)slot_index;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8FV(Dqn_ChunkPool *pool, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!Dqn_ChunkPool_IsValid(pool))
|
||||
return result;
|
||||
|
||||
Dqn_usize size_required = Dqn_CStr8_FVSize(fmt, args);
|
||||
result.data = DQN_CAST(char *) Dqn_ChunkPool_Alloc(pool, size_required + 1);
|
||||
if (result.data) {
|
||||
result.size = size_required;
|
||||
DQN_VSNPRINTF(result.data, DQN_CAST(int)(result.size + 1), fmt, args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8F(Dqn_ChunkPool *pool, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Str8 result = Dqn_ChunkPool_AllocStr8FV(pool, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8Copy(Dqn_ChunkPool *pool, Dqn_Str8 string)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!Dqn_ChunkPool_IsValid(pool))
|
||||
return result;
|
||||
|
||||
if (!Dqn_Str8_HasData(string))
|
||||
return result;
|
||||
|
||||
char *data = DQN_CAST(char *)Dqn_ChunkPool_Alloc(pool, string.size + 1);
|
||||
if (!data)
|
||||
return result;
|
||||
|
||||
DQN_MEMCPY(data, string.data, string.size);
|
||||
data[string.size] = 0;
|
||||
result = Dqn_Str8_Init(data, string.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_ChunkPool_Dealloc(Dqn_ChunkPool *pool, void *ptr)
|
||||
{
|
||||
if (!Dqn_ChunkPool_IsValid(pool))
|
||||
return;
|
||||
|
||||
Dqn_usize offset_to_original_ptr = 0;
|
||||
DQN_MEMCPY(&offset_to_original_ptr, &(DQN_CAST(char *)ptr)[-1], 1);
|
||||
DQN_ASSERT(offset_to_original_ptr <= sizeof(Dqn_ChunkPoolSlot) + pool->align);
|
||||
|
||||
char *original_ptr = DQN_CAST(char *)ptr - offset_to_original_ptr;
|
||||
Dqn_ChunkPoolSlot *slot = DQN_CAST(Dqn_ChunkPoolSlot *)original_ptr;
|
||||
Dqn_ChunkPoolSlotSize slot_index = DQN_CAST(Dqn_ChunkPoolSlotSize)(DQN_CAST(uintptr_t)slot->next);
|
||||
DQN_ASSERT(slot_index < Dqn_ChunkPoolSlotSize_Count);
|
||||
|
||||
slot->next = pool->slots[slot_index];
|
||||
pool->slots[slot_index] = slot;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: [$ACAT] Dqn_ArenaCatalog //////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_ArenaCatalog_Init(Dqn_ArenaCatalog *catalog, Dqn_ChunkPool *pool)
|
||||
{
|
||||
catalog->pool = pool;
|
||||
catalog->sentinel.next = &catalog->sentinel;
|
||||
catalog->sentinel.prev = &catalog->sentinel;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find(Dqn_ArenaCatalog *catalog, Dqn_Str8 label)
|
||||
{
|
||||
Dqn_ArenaCatalogItem *result = &catalog->sentinel;
|
||||
for (Dqn_ArenaCatalogItem *item = catalog->sentinel.next; item != &catalog->sentinel; item = item->next) {
|
||||
if (item->label == label) {
|
||||
result = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_AddLabelRef(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label)
|
||||
{
|
||||
// NOTE: We could use an atomic for appending to the sentinel but it is such
|
||||
// a rare operation to append to the catalog that we don't bother.
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
|
||||
// NOTE: Create item in the catalog
|
||||
Dqn_ArenaCatalogItem *result = Dqn_ChunkPool_New(catalog->pool, Dqn_ArenaCatalogItem);
|
||||
if (result) {
|
||||
result->arena = arena;
|
||||
result->label = label;
|
||||
|
||||
// NOTE: Add to the catalog (linked list)
|
||||
Dqn_ArenaCatalogItem *sentinel = &catalog->sentinel;
|
||||
result->next = sentinel;
|
||||
result->prev = sentinel->prev;
|
||||
result->next->prev = result;
|
||||
result->prev->next = result;
|
||||
Dqn_Atomic_AddU32(&catalog->arena_count, 1);
|
||||
}
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_AddLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label)
|
||||
{
|
||||
Dqn_Str8 label_copy = Dqn_ChunkPool_AllocStr8Copy(catalog->pool, label);
|
||||
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label_copy);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_AddF(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
va_end(args);
|
||||
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_AddFV(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
Dqn_ArenaCatalog_AddLabelRef(catalog, arena, label);
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocLabelRef(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Arena *result = Dqn_ChunkPool_New(catalog->pool, Dqn_Arena);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
|
||||
*result = Dqn_Arena_InitSize(reserve, commit, arena_flags);
|
||||
Dqn_ArenaCatalog_AddLabelRef(catalog, result, label);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label)
|
||||
{
|
||||
Dqn_Str8 label_copy = Dqn_ChunkPool_AllocStr8Copy(catalog->pool, label);
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label_copy);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Str8 label = Dqn_ChunkPool_AllocStr8FV(catalog->pool, fmt, args);
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_AllocLabelRef(catalog, reserve, commit, arena_flags, label);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
176
dqn_allocator.h
Normal file
176
dqn_allocator.h
Normal file
@ -0,0 +1,176 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
// $$ __$$\ $$ | $$ | $$ __$$\ $$ __$$\ $$ __$$\\__$$ __|$$ __$$\ $$ __$$\
|
||||
// $$ / $$ |$$ | $$ | $$ / $$ |$$ / \__|$$ / $$ | $$ | $$ / $$ |$$ | $$ |
|
||||
// $$$$$$$$ |$$ | $$ | $$ | $$ |$$ | $$$$$$$$ | $$ | $$ | $$ |$$$$$$$ |
|
||||
// $$ __$$ |$$ | $$ | $$ | $$ |$$ | $$ __$$ | $$ | $$ | $$ |$$ __$$<
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$\ $$ | $$ | $$ | $$ | $$ |$$ | $$ |
|
||||
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$$$$$ |\$$$$$$ |$$ | $$ | $$ | $$$$$$ |$$ | $$ |
|
||||
// \__| \__|\________|\________|\______/ \______/ \__| \__| \__| \______/ \__| \__|
|
||||
//
|
||||
// dqn_allocator.h -- Custom memory allocators
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$AREN] Dqn_Arena -- Growing bump allocator
|
||||
// [$CHUN] Dqn_ChunkPool -- Allocates reusable, free-able memory in PoT chunks
|
||||
// [$ACAT] Dqn_ArenaCatalog -- Collate, create & manage arenas in a catalog
|
||||
//
|
||||
// NOTE: [$AREN] Dqn_Arena /////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_ARENA_RESERVE_SIZE)
|
||||
#define DQN_ARENA_RESERVE_SIZE DQN_MEGABYTES(64)
|
||||
#endif
|
||||
#if !defined(DQN_ARENA_COMMIT_SIZE)
|
||||
#define DQN_ARENA_COMMIT_SIZE DQN_KILOBYTES(64)
|
||||
#endif
|
||||
|
||||
struct Dqn_ArenaBlock
|
||||
{
|
||||
Dqn_ArenaBlock *prev;
|
||||
uint64_t used;
|
||||
uint64_t commit;
|
||||
uint64_t reserve;
|
||||
uint64_t reserve_sum;
|
||||
};
|
||||
|
||||
enum Dqn_ArenaFlag
|
||||
{
|
||||
Dqn_ArenaFlag_Nil = 0,
|
||||
Dqn_ArenaFlag_NoGrow = 1 << 0,
|
||||
Dqn_ArenaFlag_NoPoison = 1 << 1,
|
||||
Dqn_ArenaFlag_NoAllocTrack = 1 << 2,
|
||||
Dqn_ArenaFlag_AllocCanLeak = 1 << 3,
|
||||
};
|
||||
|
||||
struct Dqn_Arena
|
||||
{
|
||||
Dqn_ArenaBlock *curr;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
struct Dqn_ArenaTempMem
|
||||
{
|
||||
Dqn_Arena *arena;
|
||||
uint64_t used_sum;
|
||||
};
|
||||
|
||||
struct Dqn_ArenaTempMemScope
|
||||
{
|
||||
Dqn_ArenaTempMemScope(Dqn_Arena *arena);
|
||||
~Dqn_ArenaTempMemScope();
|
||||
Dqn_ArenaTempMem mem;
|
||||
};
|
||||
|
||||
Dqn_usize const DQN_ARENA_HEADER_SIZE = Dqn_AlignUpPowerOfTwo(sizeof(Dqn_Arena), 64);
|
||||
|
||||
// NOTE: [$CHUN] Dqn_ChunkPool /////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_CHUNK_POOL_DEFAULT_ALIGN)
|
||||
#define DQN_CHUNK_POOL_DEFAULT_ALIGN 16
|
||||
#endif
|
||||
|
||||
struct Dqn_ChunkPoolSlot
|
||||
{
|
||||
void *data;
|
||||
Dqn_ChunkPoolSlot *next;
|
||||
};
|
||||
|
||||
enum Dqn_ChunkPoolSlotSize
|
||||
{
|
||||
Dqn_ChunkPoolSlotSize_32B,
|
||||
Dqn_ChunkPoolSlotSize_64B,
|
||||
Dqn_ChunkPoolSlotSize_128B,
|
||||
Dqn_ChunkPoolSlotSize_256B,
|
||||
Dqn_ChunkPoolSlotSize_512B,
|
||||
Dqn_ChunkPoolSlotSize_1KiB,
|
||||
Dqn_ChunkPoolSlotSize_2KiB,
|
||||
Dqn_ChunkPoolSlotSize_4KiB,
|
||||
Dqn_ChunkPoolSlotSize_8KiB,
|
||||
Dqn_ChunkPoolSlotSize_16KiB,
|
||||
Dqn_ChunkPoolSlotSize_32KiB,
|
||||
Dqn_ChunkPoolSlotSize_64KiB,
|
||||
Dqn_ChunkPoolSlotSize_128KiB,
|
||||
Dqn_ChunkPoolSlotSize_256KiB,
|
||||
Dqn_ChunkPoolSlotSize_512KiB,
|
||||
Dqn_ChunkPoolSlotSize_1MiB,
|
||||
Dqn_ChunkPoolSlotSize_2MiB,
|
||||
Dqn_ChunkPoolSlotSize_4MiB,
|
||||
Dqn_ChunkPoolSlotSize_8MiB,
|
||||
Dqn_ChunkPoolSlotSize_16MiB,
|
||||
Dqn_ChunkPoolSlotSize_32MiB,
|
||||
Dqn_ChunkPoolSlotSize_64MiB,
|
||||
Dqn_ChunkPoolSlotSize_128MiB,
|
||||
Dqn_ChunkPoolSlotSize_256MiB,
|
||||
Dqn_ChunkPoolSlotSize_512MiB,
|
||||
Dqn_ChunkPoolSlotSize_1GiB,
|
||||
Dqn_ChunkPoolSlotSize_2GiB,
|
||||
Dqn_ChunkPoolSlotSize_4GiB,
|
||||
Dqn_ChunkPoolSlotSize_8GiB,
|
||||
Dqn_ChunkPoolSlotSize_16GiB,
|
||||
Dqn_ChunkPoolSlotSize_32GiB,
|
||||
Dqn_ChunkPoolSlotSize_Count,
|
||||
};
|
||||
|
||||
struct Dqn_ChunkPool
|
||||
{
|
||||
Dqn_Arena *arena;
|
||||
Dqn_ChunkPoolSlot *slots[Dqn_ChunkPoolSlotSize_Count];
|
||||
uint8_t align;
|
||||
};
|
||||
|
||||
// NOTE: [$ACAT] Dqn_ArenaCatalog //////////////////////////////////////////////////////////////////
|
||||
struct Dqn_ArenaCatalogItem
|
||||
{
|
||||
Dqn_Arena *arena;
|
||||
Dqn_Str8 label;
|
||||
Dqn_ArenaCatalogItem *next;
|
||||
Dqn_ArenaCatalogItem *prev;
|
||||
};
|
||||
|
||||
struct Dqn_ArenaCatalog
|
||||
{
|
||||
Dqn_TicketMutex ticket_mutex; // Mutex for adding to the linked list of arenas
|
||||
struct Dqn_ChunkPool *pool;
|
||||
Dqn_ArenaCatalogItem sentinel;
|
||||
uint16_t arena_count;
|
||||
};
|
||||
|
||||
// NOTE: [$AREN] Dqn_Arena /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Arena Dqn_Arena_InitSize (uint64_t reserve, uint64_t commit, uint8_t flags);
|
||||
DQN_API void Dqn_Arena_Deinit (Dqn_Arena *arena);
|
||||
DQN_API bool Dqn_Arena_Commit (Dqn_Arena *arena, uint64_t size);
|
||||
DQN_API bool Dqn_Arena_CommitTo (Dqn_Arena *arena, uint64_t pos);
|
||||
DQN_API void * Dqn_Arena_Alloc (Dqn_Arena *arena, uint64_t size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||
DQN_API void * Dqn_Arena_AllocContiguous (Dqn_Arena *arena, uint64_t size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||
DQN_API void * Dqn_Arena_Copy (Dqn_Arena *arena, void const *data, uint64_t size, uint8_t align);
|
||||
DQN_API void Dqn_Arena_PopTo (Dqn_Arena *arena, uint64_t init_used);
|
||||
DQN_API void Dqn_Arena_Pop (Dqn_Arena *arena, uint64_t amount);
|
||||
DQN_API void Dqn_Arena_Clear (Dqn_Arena *arena);
|
||||
DQN_API Dqn_ArenaTempMem Dqn_Arena_TempMemBegin (Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Arena_TempMemEnd (Dqn_ArenaTempMem mem);
|
||||
#define Dqn_Arena_New(arena, T, zero_mem) (T *)Dqn_Arena_Alloc(arena, sizeof(T), alignof(T), zero_mem)
|
||||
#define Dqn_Arena_NewArray(arena, T, count, zero_mem) (T *)Dqn_Arena_Alloc(arena, sizeof(T) * (count), alignof(T), zero_mem)
|
||||
#define Dqn_Arena_NewCopy(arena, T, src) (T *)Dqn_Arena_Copy (arena, (src), sizeof(*src), alignof(T))
|
||||
#define Dqn_Arena_NewArrayCopy(arena, T, src, count) (T *)Dqn_Arena_Copy (arena, (src), sizeof(*src) * (count), alignof(T))
|
||||
|
||||
// NOTE: [$CHUN] Dqn_ChunkPool /////////////////////////////////////////////////////////////////////
|
||||
#define Dqn_ChunkPool_New(pool, T) (T *)Dqn_ChunkPool_Alloc(pool, sizeof(T))
|
||||
DQN_API Dqn_ChunkPool Dqn_ChunkPool_Init (Dqn_Arena *arena, uint8_t align);
|
||||
DQN_API bool Dqn_ChunkPool_IsValid (Dqn_ChunkPool const *pool);
|
||||
DQN_API void * Dqn_ChunkPool_Alloc (Dqn_ChunkPool *pool, Dqn_usize size);
|
||||
DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8FV (Dqn_ChunkPool *pool, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8F (Dqn_ChunkPool *pool, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_Str8 Dqn_ChunkPool_AllocStr8Copy (Dqn_ChunkPool *pool, Dqn_Str8 string);
|
||||
DQN_API void Dqn_ChunkPool_Dealloc (Dqn_ChunkPool *pool, void *ptr);
|
||||
|
||||
// NOTE: [$ACAT] Dqn_ArenaCatalog //////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_ArenaCatalog_Init (Dqn_ArenaCatalog *catalog, Dqn_ChunkPool *pool);
|
||||
DQN_API Dqn_ArenaCatalogItem *Dqn_ArenaCatalog_Find (Dqn_ArenaCatalog *catalog, Dqn_Str8 label);
|
||||
DQN_API void Dqn_ArenaCatalog_AddLabelRef (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label);
|
||||
DQN_API void Dqn_ArenaCatalog_AddLabelCopy (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, Dqn_Str8 label);
|
||||
DQN_API void Dqn_ArenaCatalog_AddF (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API void Dqn_ArenaCatalog_AddFV (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocLabelRef (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label);
|
||||
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocLabelCopy(Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, Dqn_Str8 label);
|
||||
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocFV (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Arena * Dqn_ArenaCatalog_AllocF (Dqn_ArenaCatalog *catalog, Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, DQN_FMT_ATTRIB char const *fmt, ...);
|
146
dqn_base.cpp
146
dqn_base.cpp
@ -1,10 +1,25 @@
|
||||
// NOTE: [$INTR] Intrinsics ========================================================================
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\
|
||||
// $$ __$$\
|
||||
// $$ | $$ | $$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$$$$$$\ | \____$$\ $$ _____|$$ __$$\
|
||||
// $$ __$$\ $$$$$$$ |\$$$$$$\ $$$$$$$$ |
|
||||
// $$ | $$ |$$ __$$ | \____$$\ $$ ____|
|
||||
// $$$$$$$ |\$$$$$$$ |$$$$$$$ |\$$$$$$$\
|
||||
// \_______/ \_______|\_______/ \_______|
|
||||
//
|
||||
// dqn_base.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
#if defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
Dqn_CPUIDRegisters Dqn_CPUID(int function_id)
|
||||
DQN_API Dqn_CPUIDRegisters Dqn_CPUID(int function_id)
|
||||
{
|
||||
Dqn_CPUIDRegisters result = {};
|
||||
#if defined(DQN_COMPILER_MSVC)
|
||||
@ -18,28 +33,7 @@ Dqn_CPUIDRegisters Dqn_CPUID(int function_id)
|
||||
}
|
||||
#endif // !defined(DQN_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
|
||||
// NOTE: [$ALLO] Dqn_Allocator =====================================================================
|
||||
DQN_API void *Dqn_Allocator_Alloc(Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||
{
|
||||
void *result = NULL;
|
||||
if (allocator.alloc) {
|
||||
result = allocator.alloc(size, align, zero_mem, allocator.user_context);
|
||||
} else {
|
||||
result = DQN_ALLOC(size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Allocator_Dealloc(Dqn_Allocator allocator, void *ptr, size_t size)
|
||||
{
|
||||
if (allocator.dealloc) {
|
||||
allocator.dealloc(ptr, size, allocator.user_context);
|
||||
} else {
|
||||
DQN_DEALLOC(ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: [$TMUT] Dqn_TicketMutex ===================================================================
|
||||
// NOTE: [$TMUT] Dqn_TicketMutex ///////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_TicketMutex_Begin(Dqn_TicketMutex *mutex)
|
||||
{
|
||||
unsigned int ticket = Dqn_Atomic_AddU32(&mutex->ticket, 1);
|
||||
@ -82,7 +76,7 @@ DQN_API bool Dqn_TicketMutex_CanLock(Dqn_TicketMutex const *mutex, Dqn_uint tick
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: [$PRIN] Dqn_Print =========================================================================
|
||||
// NOTE: [$PRIN] Dqn_Print /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour(uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold)
|
||||
{
|
||||
Dqn_PrintStyle result = {};
|
||||
@ -115,7 +109,7 @@ DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_Str8 string)
|
||||
DQN_ASSERT(std_handle == Dqn_PrintStd_Out || std_handle == Dqn_PrintStd_Err);
|
||||
|
||||
#if defined(DQN_OS_WIN32)
|
||||
// NOTE: Get the output handles from kernel ====================================================
|
||||
// NOTE: Get the output handles from kernel ////////////////////////////////////////////////////
|
||||
DQN_THREAD_LOCAL void *std_out_print_handle = nullptr;
|
||||
DQN_THREAD_LOCAL void *std_err_print_handle = nullptr;
|
||||
DQN_THREAD_LOCAL bool std_out_print_to_console = false;
|
||||
@ -130,7 +124,7 @@ DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_Str8 string)
|
||||
std_err_print_to_console = GetConsoleMode(std_err_print_handle, &mode) != 0;
|
||||
}
|
||||
|
||||
// NOTE: Select the output handle ==============================================================
|
||||
// NOTE: Select the output handle //////////////////////////////////////////////////////////////
|
||||
void *print_handle = std_out_print_handle;
|
||||
bool print_to_console = std_out_print_to_console;
|
||||
if (std_handle == Dqn_PrintStd_Err) {
|
||||
@ -138,7 +132,7 @@ DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_Str8 string)
|
||||
print_to_console = std_err_print_to_console;
|
||||
}
|
||||
|
||||
// NOTE: Write the string ======================================================================
|
||||
// NOTE: Write the string //////////////////////////////////////////////////////////////////////
|
||||
DQN_ASSERT(string.size < DQN_CAST(unsigned long)-1);
|
||||
unsigned long bytes_written = 0; (void)bytes_written;
|
||||
if (print_to_console) {
|
||||
@ -164,17 +158,6 @@ DQN_API void Dqn_Print_StdStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, D
|
||||
}
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int len)
|
||||
{
|
||||
Dqn_Str8 string = {};
|
||||
string.data = DQN_CAST(char *)buf;
|
||||
string.size = len;
|
||||
|
||||
Dqn_PrintStd std_handle = DQN_CAST(Dqn_PrintStd)DQN_CAST(uintptr_t)user;
|
||||
Dqn_Print_Std(std_handle, string);
|
||||
return (char *)buf;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
@ -191,10 +174,27 @@ DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style,
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#if !defined(DQN_USE_STD_PRINTF)
|
||||
DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int len)
|
||||
{
|
||||
Dqn_Str8 string = {};
|
||||
string.data = DQN_CAST(char *)buf;
|
||||
string.size = len;
|
||||
|
||||
Dqn_PrintStd std_handle = DQN_CAST(Dqn_PrintStd)DQN_CAST(uintptr_t)user;
|
||||
Dqn_Print_Std(std_handle, string);
|
||||
return (char *)buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
DQN_API void Dqn_Print_StdFV(Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
#if defined(DQN_USE_STD_PRINTF)
|
||||
vfprintf(std_handle == Dqn_PrintStd_Out ? stdout : stderr, fmt, args);
|
||||
#else
|
||||
char buffer[STB_SPRINTF_MIN];
|
||||
STB_SPRINTF_DECORATE(vsprintfcb)(Dqn_Print_VSPrintfChunker_, DQN_CAST(void *)DQN_CAST(uintptr_t)std_handle, buffer, fmt, args);
|
||||
#endif
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Print_StdFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
@ -255,7 +255,7 @@ DQN_API Dqn_Str8 Dqn_Print_ESCColourStr8(Dqn_PrintESCColour colour, uint8_t r, u
|
||||
DQN_THREAD_LOCAL char buffer[32];
|
||||
buffer[0] = 0;
|
||||
Dqn_Str8 result = {};
|
||||
result.size = STB_SPRINTF_DECORATE(snprintf)(buffer,
|
||||
result.size = DQN_SNPRINTF(buffer,
|
||||
DQN_ARRAY_UCOUNT(buffer),
|
||||
"\x1b[%d;2;%u;%u;%um",
|
||||
colour == Dqn_PrintESCColour_Fg ? 38 : 48,
|
||||
@ -273,17 +273,14 @@ DQN_API Dqn_Str8 Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour colour, uint32_t
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$LLOG] Dqn_Log ==========================================================================
|
||||
DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Allocator allocator,
|
||||
// NOTE: [$LLOG] Dqn_Log ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Arena *arena,
|
||||
bool colour,
|
||||
Dqn_Str8 type,
|
||||
int log_type,
|
||||
Dqn_CallSite call_site,
|
||||
DQN_FMT_ATTRIB char const *fmt,
|
||||
va_list args)
|
||||
{
|
||||
Dqn_usize header_size_no_ansi_codes = 0;
|
||||
Dqn_Str8 header = {};
|
||||
{
|
||||
DQN_LOCAL_PERSIST Dqn_usize max_type_length = 0;
|
||||
max_type_length = DQN_MAX(max_type_length, type.size);
|
||||
@ -304,8 +301,8 @@ DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Allocator allocator,
|
||||
}
|
||||
|
||||
Dqn_Str8 file_name = Dqn_Str8_FileNameFromPath(call_site.file);
|
||||
Dqn_DateHMSTimeStr8 const time = Dqn_Date_LocalTimeHMSStr8Now();
|
||||
header = Dqn_Str8_InitF(allocator,
|
||||
Dqn_OSDateTimeStr8 const time = Dqn_OS_DateLocalTimeStr8Now();
|
||||
Dqn_Str8 header = Dqn_Str8_InitF(arena,
|
||||
"%.*s " // date
|
||||
"%.*s " // hms
|
||||
"%.*s" // colour
|
||||
@ -314,30 +311,27 @@ DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Allocator allocator,
|
||||
"%*s" // type padding
|
||||
"%.*s" // reset
|
||||
" %.*s" // file name
|
||||
":%05u ", // line number
|
||||
DQN_CAST(uint32_t)time.date_size - 2, time.date + 2, // date
|
||||
DQN_CAST(uint32_t)time.hms_size, time.hms, // hms
|
||||
DQN_CAST(uint32_t)colour_esc.size, colour_esc.data, // colour
|
||||
DQN_CAST(uint32_t)bold_esc.size, bold_esc.data, // bold
|
||||
DQN_CAST(uint32_t)type.size, type.data, // type
|
||||
DQN_CAST(uint32_t)type_padding, "", // type padding
|
||||
DQN_CAST(uint32_t)reset_esc.size, reset_esc.data, // reset
|
||||
DQN_CAST(uint32_t)file_name.size, file_name.data, // file name
|
||||
":%05I32u " // line number
|
||||
,
|
||||
DQN_CAST(int)time.date_size - 2, time.date + 2, // date
|
||||
DQN_CAST(int)time.hms_size, time.hms, // hms
|
||||
DQN_STR_FMT(colour_esc), // colour
|
||||
DQN_STR_FMT(bold_esc), // bold
|
||||
DQN_STR_FMT(type), // type
|
||||
DQN_CAST(int)type_padding, "", // type padding
|
||||
DQN_STR_FMT(reset_esc), // reset
|
||||
DQN_STR_FMT(file_name), // file name
|
||||
call_site.line); // line number
|
||||
header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetStr8.size;
|
||||
}
|
||||
Dqn_usize header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetStr8.size;
|
||||
|
||||
// NOTE: Header padding ========================================================================
|
||||
Dqn_usize header_padding = 0;
|
||||
{
|
||||
// NOTE: Header padding ////////////////////////////////////////////////////////////////////////
|
||||
DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0;
|
||||
max_header_length = DQN_MAX(max_header_length, header_size_no_ansi_codes);
|
||||
header_padding = max_header_length - header_size_no_ansi_codes;
|
||||
}
|
||||
Dqn_usize header_padding = max_header_length - header_size_no_ansi_codes;
|
||||
|
||||
// NOTE: Construct final log ===================================================================
|
||||
Dqn_Str8 user_msg = Dqn_Str8_InitFV(allocator, fmt, args);
|
||||
Dqn_Str8 result = Dqn_Str8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No);
|
||||
// NOTE: Construct final log ///////////////////////////////////////////////////////////////////
|
||||
Dqn_Str8 user_msg = Dqn_Str8_InitFV(arena, fmt, args);
|
||||
Dqn_Str8 result = Dqn_Str8_Alloc(arena, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No);
|
||||
DQN_MEMCPY(result.data, header.data, header.size);
|
||||
DQN_MEMSET(result.data + header.size, ' ', header_padding);
|
||||
DQN_MEMCPY(result.data + header.size + header_padding, user_msg.data, user_msg.size);
|
||||
@ -350,25 +344,25 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_Str8 type, int log_type, void *user_d
|
||||
(void)log_type;
|
||||
(void)user_data;
|
||||
|
||||
// NOTE: Open log file for appending if requested ==========================
|
||||
// NOTE: Open log file for appending if requested //////////////////////////
|
||||
Dqn_TicketMutex_Begin(&lib->log_file_mutex);
|
||||
if (lib->log_to_file && !lib->log_file.handle && lib->log_file.error_size == 0) {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str8 log_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir));
|
||||
lib->log_file = Dqn_Fs_OpenFile(log_path, Dqn_FsFileOpen_CreateAlways, Dqn_FsFileAccess_AppendOnly);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 log_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir));
|
||||
lib->log_file = Dqn_OS_OpenFile(log_path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_AppendOnly);
|
||||
}
|
||||
Dqn_TicketMutex_End(&lib->log_file_mutex);
|
||||
|
||||
// NOTE: Generate the log header ===========================================
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str8 log_line = Dqn_Log_MakeStr8(scratch.allocator, !lib->log_no_colour, type, log_type, call_site, fmt, args);
|
||||
// NOTE: Generate the log header ///////////////////////////////////////////
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 log_line = Dqn_Log_MakeStr8(scratch.arena, !lib->log_no_colour, type, log_type, call_site, fmt, args);
|
||||
|
||||
// NOTE: Print log =========================================================
|
||||
// NOTE: Print log /////////////////////////////////////////////////////////
|
||||
Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line);
|
||||
|
||||
Dqn_TicketMutex_Begin(&lib->log_file_mutex);
|
||||
Dqn_Fs_WriteFile(&lib->log_file, log_line);
|
||||
Dqn_Fs_WriteFile(&lib->log_file, DQN_STR8("\n"));
|
||||
Dqn_OS_WriteFile(&lib->log_file, log_line);
|
||||
Dqn_OS_WriteFile(&lib->log_file, DQN_STR8("\n"));
|
||||
Dqn_TicketMutex_End(&lib->log_file_mutex);
|
||||
}
|
||||
|
||||
|
417
dqn_base.h
417
dqn_base.h
@ -1,9 +1,31 @@
|
||||
// NOTE: Preprocessor Token Tricks =================================================================
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\
|
||||
// $$ __$$\
|
||||
// $$ | $$ | $$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$$$$$$\ | \____$$\ $$ _____|$$ __$$\
|
||||
// $$ __$$\ $$$$$$$ |\$$$$$$\ $$$$$$$$ |
|
||||
// $$ | $$ |$$ __$$ | \____$$\ $$ ____|
|
||||
// $$$$$$$ |\$$$$$$$ |$$$$$$$ |\$$$$$$$\
|
||||
// \_______/ \_______|\_______/ \_______|
|
||||
//
|
||||
// dqn_base.h -- Base primitives for the library
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$MACR] Macros -- General macros
|
||||
// [$TYPE] Types -- Basic types and typedefs
|
||||
// [$INTR] Intrinsics -- Platform agnostic functions for CPU instructions (e.g. atomics, cpuid, ...)
|
||||
// [$CALL] Dqn_CallSite -- Source code location/tracing
|
||||
// [$TMUT] Dqn_TicketMutex -- Userland mutex via spinlocking atomics
|
||||
// [$PRIN] Dqn_Print -- Console printing
|
||||
// [$LLOG] Dqn_Log -- Console logging macros
|
||||
//
|
||||
// NOTE: [$MACR] Macros ////////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_STRINGIFY(x) #x
|
||||
#define DQN_TOKEN_COMBINE2(x, y) x ## y
|
||||
#define DQN_TOKEN_COMBINE(x, y) DQN_TOKEN_COMBINE2(x, y)
|
||||
|
||||
// NOTE: [$CMAC] Compiler macros ===================================================================
|
||||
// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(__clang__)
|
||||
@ -39,14 +61,31 @@
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define DQN_OS_WIN32
|
||||
#elif defined(__linux__)
|
||||
#elif defined(__gnu_linux__) || defined(__linux__)
|
||||
#define DQN_OS_UNIX
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
#if !defined(DQN_OS_WIN32)
|
||||
#include <stdlib.h> // exit()
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_PLATFORM_EMSCRIPTEN) && \
|
||||
!defined(DQN_PLATFORM_POSIX) && \
|
||||
!defined(DQN_PLATFORM_WIN32)
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define DQN_PLATFORM_ARM64
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#define DQN_PLATFORM_EMSCRIPTEN
|
||||
#elif defined(DQN_OS_WIN32)
|
||||
#define DQN_PLATFORM_WIN32
|
||||
#else
|
||||
#define DQN_PLATFORM_POSIX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
||||
@ -81,33 +120,23 @@
|
||||
#define DQN_GCC_WARNING_POP
|
||||
#endif
|
||||
|
||||
// NOTE: [$MACR] Macros ============================================================================
|
||||
// NOTE: MSVC does not support the feature detection macro for instance so we
|
||||
// compile it out
|
||||
#if defined(__has_feature)
|
||||
#define DQN_HAS_FEATURE(expr) __has_feature(expr)
|
||||
#else
|
||||
#define DQN_HAS_FEATURE(expr) 0
|
||||
#endif
|
||||
|
||||
#define DQN_FOR_UINDEX(index, size) for (Dqn_usize index = 0; index < size; index++)
|
||||
#define DQN_FOR_IINDEX(index, size) for (Dqn_isize index = 0; index < size; index++)
|
||||
|
||||
#define Dqn_AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1))
|
||||
#define Dqn_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ((uintptr_t)(pot) - 1))
|
||||
#define Dqn_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ~((uintptr_t)(pot) - 1))
|
||||
#define Dqn_IsPowerOfTwo(value) ((((uintptr_t)(value)) & (((uintptr_t)(value)) - 1)) == 0)
|
||||
#define Dqn_IsPowerOfTwoAligned(value, pot) ((((uintptr_t)value) & (((uintptr_t)pot) - 1)) == 0)
|
||||
|
||||
// NOTE: Alloc Macros ==============================================================================
|
||||
#if !defined(DQN_ALLOC)
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
#define DQN_ALLOC(size) malloc(size)
|
||||
#else
|
||||
#define DQN_ALLOC(size) Dqn_VMem_Reserve(size, Dqn_VMemCommit_Yes, Dqn_VMemPage_ReadWrite)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_DEALLOC)
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
#define DQN_DEALLOC(ptr, size) free(ptr)
|
||||
#else
|
||||
#define DQN_DEALLOC(ptr, size) Dqn_VMem_Release(ptr, size)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: String.h Dependencies =====================================================================
|
||||
// NOTE: String.h Dependencies /////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_MEMCPY) || !defined(DQN_MEMSET) || !defined(DQN_MEMCMP) || !defined(DQN_MEMMOVE)
|
||||
#include <string.h>
|
||||
#if !defined(DQN_MEMCPY)
|
||||
@ -124,10 +153,12 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: Math.h Dependencies =======================================================================
|
||||
// NOTE: Math.h Dependencies ///////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_SQRTF) || !defined(DQN_SINF) || !defined(DQN_COSF) || !defined(DQN_TANF)
|
||||
#include <math.h>
|
||||
#if !defined(DQN_SQRTF)
|
||||
#define DQN_SQRTF(val) sqrtf(val)
|
||||
#endif
|
||||
#if !defined(DQN_SINF)
|
||||
#define DQN_SINF(val) sinf(val)
|
||||
#endif
|
||||
@ -139,11 +170,7 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_OS_WIN32)
|
||||
#include <stdlib.h> // exit()
|
||||
#endif
|
||||
|
||||
// NOTE: Math Macros ===============================================================================
|
||||
// NOTE: Math //////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_PI 3.14159265359f
|
||||
|
||||
#define DQN_DEGREE_TO_RADIAN(degrees) ((degrees) * (DQN_PI / 180.0f))
|
||||
@ -163,7 +190,7 @@
|
||||
b = temp; \
|
||||
} while (0)
|
||||
|
||||
// NOTE: Function/Variable Annotations =============================================================
|
||||
// NOTE: Function/Variable Annotations /////////////////////////////////////////////////////////////
|
||||
#if defined(DQN_STATIC_API)
|
||||
#define DQN_API static
|
||||
#else
|
||||
@ -180,86 +207,88 @@
|
||||
#define DQN_FORCE_INLINE inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
// NOTE: Size Macros ===============================================================================
|
||||
// NOTE: Size //////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_ISIZEOF(val) DQN_CAST(ptrdiff_t)sizeof(val)
|
||||
#define DQN_ARRAY_UCOUNT(array) (sizeof(array)/(sizeof((array)[0])))
|
||||
#define DQN_ARRAY_ICOUNT(array) (Dqn_isize)DQN_ARRAY_UCOUNT(array)
|
||||
#define DQN_CHAR_COUNT(string) (sizeof(string) - 1)
|
||||
|
||||
// NOTE: SI Byte Macros ============================================================================
|
||||
// NOTE: SI Byte ///////////////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_BYTES(val) (val)
|
||||
#define DQN_KILOBYTES(val) (1024ULL * DQN_BYTES(val))
|
||||
#define DQN_MEGABYTES(val) (1024ULL * DQN_KILOBYTES(val))
|
||||
#define DQN_GIGABYTES(val) (1024ULL * DQN_MEGABYTES(val))
|
||||
|
||||
// NOTE: Time Macros ===============================================================================
|
||||
// NOTE: Time //////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_SECONDS_TO_MS(val) ((val) * 1000)
|
||||
#define DQN_MINS_TO_S(val) ((val) * 60ULL)
|
||||
#define DQN_HOURS_TO_S(val) (DQN_MINS_TO_S(val) * 60ULL)
|
||||
#define DQN_DAYS_TO_S(val) (DQN_HOURS_TO_S(val) * 24ULL)
|
||||
#define DQN_YEARS_TO_S(val) (DQN_DAYS_TO_S(val) * 365ULL)
|
||||
#define DQN_WEEKS_TO_S(val) (DQN_DAYS_TO_S(val) * 7ULL)
|
||||
#define DQN_YEARS_TO_S(val) (DQN_WEEKS_TO_S(val) * 52ULL)
|
||||
|
||||
// NOTE: Debug Break ===============================================================================
|
||||
#if defined(__has_builtin)
|
||||
#define DQN_HAS_BUILTIN(expr) __has_builtin(expr)
|
||||
#else
|
||||
#define DQN_HAS_BUILTIN(expr) 0
|
||||
#endif
|
||||
|
||||
// NOTE: Debug Break ///////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_DEBUG_BREAK)
|
||||
#if defined(NDEBUG)
|
||||
#define DQN_DEBUG_BREAK
|
||||
#else
|
||||
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
||||
#define DQN_DEBUG_BREAK __debugbreak()
|
||||
#elif defined(DQN_COMPILER_CLANG)
|
||||
#elif DQN_HAS_BUILTIN(__builtin_debugtrap)
|
||||
#define DQN_DEBUG_BREAK __builtin_debugtrap()
|
||||
#elif defined(DQN_COMPILER_CLANG) || defined(DQN_COMPILER_GCC)
|
||||
#elif DQN_HAS_BUILTIN(__builtin_trap) || defined(DQN_COMPILER_GCC)
|
||||
#define DQN_DEBUG_BREAK __builtin_trap()
|
||||
#else
|
||||
#include <signal.h>
|
||||
#if defined(SIGTRAP)
|
||||
#define DQN_DEBUG_BREAK raise(SIGTRAP)
|
||||
#elif
|
||||
#error "Unhandled compiler"
|
||||
#else
|
||||
#define DQN_DEBUG_BREAK raise(SIGABRT)
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_MEMSET_BYTE)
|
||||
#define DQN_MEMSET_BYTE 0
|
||||
#endif
|
||||
|
||||
// NOTE: Assert Macros =============================================================================
|
||||
// NOTE: Assert Macros /////////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_HARD_ASSERT(expr) DQN_HARD_ASSERTF(expr, "")
|
||||
#define DQN_HARD_ASSERTF(expr, fmt, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
Dqn_Log_ErrorF("Hard assert triggered [" #expr "]. " fmt, ##__VA_ARGS__); \
|
||||
Dqn_StackTrace_Print(128 /*limit*/); \
|
||||
Dqn_Str8 stack_trace_ = Dqn_StackTrace_WalkStr8CRT(128 /*limit*/, 2 /*skip*/); \
|
||||
Dqn_Log_ErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||
DQN_STR_FMT(stack_trace_), \
|
||||
##__VA_ARGS__); \
|
||||
DQN_DEBUG_BREAK; \
|
||||
}
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#if defined(DQN_NO_ASSERT)
|
||||
#define DQN_ASSERTF(...)
|
||||
#define DQN_ASSERT(...)
|
||||
#else
|
||||
#define DQN_ASSERT(expr) DQN_ASSERTF(expr, "")
|
||||
#define DQN_ASSERT(expr) DQN_ASSERTF((expr), "")
|
||||
#define DQN_ASSERTF(expr, fmt, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
Dqn_Log_ErrorF("Assert triggered [" #expr "]. " fmt, ##__VA_ARGS__); \
|
||||
Dqn_StackTrace_Print(128 /*limit*/); \
|
||||
Dqn_Str8 stack_trace_ = Dqn_StackTrace_WalkStr8CRT(128 /*limit*/, 2 /*skip*/); \
|
||||
Dqn_Log_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||
DQN_STR_FMT(stack_trace_), \
|
||||
##__VA_ARGS__); \
|
||||
DQN_DEBUG_BREAK; \
|
||||
}
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define DQN_INVALID_CODE_PATHF(fmt, ...) DQN_ASSERTF(0, fmt, ##__VA_ARGS__)
|
||||
#define DQN_INVALID_CODE_PATH DQN_INVALID_CODE_PATHF("Invalid code path triggered")
|
||||
|
||||
// NOTE: Check macro ===============================================================================
|
||||
// Check the expression trapping in debug, whilst in release- trapping is
|
||||
// removed and the expression is evaluated as if it were a normal 'if' branch.
|
||||
//
|
||||
// This allows handling of the condition gracefully when compiled out but traps
|
||||
// to notify the developer in builds when it's compiled in.
|
||||
#if 0
|
||||
bool flag = true;
|
||||
if (DQN_CHECKF(flag, "Flag was false!")) {
|
||||
// This branch will execute!
|
||||
} else {
|
||||
// Prints "Flag was false!"
|
||||
}
|
||||
#endif
|
||||
// NOTE: Check macro ///////////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_CHECK(expr) DQN_CHECKF(expr, "")
|
||||
|
||||
#if defined(DQN_NO_CHECK_BREAK)
|
||||
@ -267,32 +296,18 @@
|
||||
((expr) ? true : (Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, ## __VA_ARGS__), false))
|
||||
#else
|
||||
#define DQN_CHECKF(expr, fmt, ...) \
|
||||
((expr) ? true : (Dqn_Log_TypeFCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, ## __VA_ARGS__), DQN_DEBUG_BREAK, false))
|
||||
((expr) ? true : (Dqn_Log_TypeFCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, ## __VA_ARGS__), Dqn_StackTrace_Print(128 /*limit*/), DQN_DEBUG_BREAK, false))
|
||||
#endif
|
||||
|
||||
// NOTE: Zero initialisation macro =================================================================
|
||||
// NOTE: Zero initialisation macro /////////////////////////////////////////////////////////////////
|
||||
#if defined(__cplusplus)
|
||||
#define DQN_ZERO_INIT {}
|
||||
#else
|
||||
#define DQN_ZERO_INIT {0}
|
||||
#endif
|
||||
|
||||
// NOTE: Defer Macro ===============================================================================
|
||||
// NOTE: Defer Macro ///////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(__cplusplus)
|
||||
#if 0
|
||||
#include <stdio.h>
|
||||
int main()
|
||||
{
|
||||
DQN_DEFER { printf("Three ..\n"); };
|
||||
printf("One ..\n");
|
||||
printf("Two ..\n");
|
||||
// One ..
|
||||
// Two ..
|
||||
// Three ..
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename Procedure>
|
||||
struct Dqn_Defer
|
||||
{
|
||||
@ -316,7 +331,7 @@ struct Dqn_DeferHelper
|
||||
DQN_UNIQUE_NAME(once); \
|
||||
end, DQN_UNIQUE_NAME(once) = false)
|
||||
|
||||
// NOTE: [$TYPE] Types =============================================================================
|
||||
// NOTE: [$TYPE] Types /////////////////////////////////////////////////////////////////////////////
|
||||
typedef intptr_t Dqn_isize;
|
||||
typedef uintptr_t Dqn_usize;
|
||||
typedef intptr_t Dqn_isize;
|
||||
@ -350,7 +365,6 @@ struct Dqn_Str8
|
||||
char *end () { return data + size; }
|
||||
};
|
||||
|
||||
#if !defined(DQN_NO_SLICE)
|
||||
template <typename T> struct Dqn_Slice // A pointer and length container of data
|
||||
{
|
||||
T *data;
|
||||
@ -361,32 +375,17 @@ template <typename T> struct Dqn_Slice // A pointer and length container of data
|
||||
T const *begin() const { return data; }
|
||||
T const *end () const { return data + size; }
|
||||
};
|
||||
#endif
|
||||
|
||||
// NOTE: [$CALL] Dqn_CallSite ======================================================================
|
||||
// NOTE: [$CALL] Dqn_CallSite //////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_CallSite
|
||||
{
|
||||
Dqn_Str8 file;
|
||||
Dqn_Str8 function;
|
||||
unsigned int line;
|
||||
uint32_t line;
|
||||
};
|
||||
#define DQN_CALL_SITE Dqn_CallSite{DQN_STR8(__FILE__), DQN_STR8(__func__), __LINE__}
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ========================================================================
|
||||
// Platform agnostic functions for CPU level instructions like atomics, barriers
|
||||
// and timestamp counters.
|
||||
//
|
||||
// NOTE: API
|
||||
// @proc Dqn_Atomic_SetValue64, Dqn_Atomic_SetValue32
|
||||
// @desc Atomically set the value into the target using an atomic compare and
|
||||
// swap.
|
||||
// @param[in,out] target The target pointer to set atomically
|
||||
// @param[in] value The value to set atomically into the target
|
||||
// @return The value that was last stored in the target
|
||||
|
||||
// @proc Dqn_CPUID
|
||||
// Execute 'CPUID' instruction to query the capabilities of the current CPU.
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: Dqn_Atomic_Add/Exchange return the previous value store in the target
|
||||
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
||||
#include <intrin.h>
|
||||
@ -422,118 +421,21 @@ struct Dqn_CallSite
|
||||
#error "Compiler not supported"
|
||||
#endif
|
||||
|
||||
DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64(uint64_t volatile *target, uint64_t value)
|
||||
{
|
||||
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
||||
__int64 result;
|
||||
do {
|
||||
result = *target;
|
||||
} while (Dqn_Atomic_CompareExchange64(target, value, result) != result);
|
||||
return DQN_CAST(uint64_t)result;
|
||||
#elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||
uint64_t result = __sync_lock_test_and_set(target, value);
|
||||
return result;
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
|
||||
DQN_FORCE_INLINE long Dqn_Atomic_SetValue32(long volatile *target, long value)
|
||||
{
|
||||
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
||||
long result;
|
||||
do {
|
||||
result = *target;
|
||||
} while (Dqn_Atomic_CompareExchange32(target, value, result) != result);
|
||||
return result;
|
||||
#elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||
long result = __sync_lock_test_and_set(target, value);
|
||||
return result;
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(DQN_PLATFORM_ARM64)
|
||||
struct Dqn_CPUIDRegisters
|
||||
{
|
||||
Dqn_uint array[4]; ///< Values from 'CPUID' instruction for each register (EAX, EBX, ECX, EDX)
|
||||
Dqn_uint array[4]; // Values from 'CPUID' instruction for each register (EAX, EBX, ECX, EDX)
|
||||
};
|
||||
|
||||
Dqn_CPUIDRegisters Dqn_CPUID(int function_id);
|
||||
#endif // DQN_PLATFORM_ARM64
|
||||
|
||||
// NOTE: [$TMUT] Dqn_TicketMutex ===================================================================
|
||||
//
|
||||
// A mutex implemented using an atomic compare and swap on tickets handed out
|
||||
// for each critical section.
|
||||
//
|
||||
// This mutex serves ticket in order and will block all other threads until the
|
||||
// tickets are returned in order. The thread with the oldest ticket that has
|
||||
// not been returned has right of way to execute, all other threads will be
|
||||
// blocked in an atomic compare and swap loop. block execution by going into an
|
||||
// atomic
|
||||
//
|
||||
// When a thread is blocked by this mutex, a spinlock intrinsic `_mm_pause` is
|
||||
// used to yield the CPU and reduce spinlock on the thread. This mutex is not
|
||||
// ideal for long blocking operations. This mutex does not issue any syscalls
|
||||
// and relies entirely on atomic instructions.
|
||||
//
|
||||
// NOTE: API
|
||||
//
|
||||
// @proc Dqn_TicketMutex_Begin, End
|
||||
// @desc Lock and unlock the mutex respectively
|
||||
|
||||
// @proc Dqn_TicketMutex_MakeTicket
|
||||
// @desc Allocate the next available ticket from the mutex for locking using
|
||||
// Dqn_TicketMutex_BeginTicket().
|
||||
// @param[in] mutex The mutex
|
||||
#if 0
|
||||
Dqn_TicketMutex mutex = {};
|
||||
unsigned int ticket = Dqn_TicketMutex_MakeTicket(&mutex);
|
||||
Dqn_TicketMutex_BeginTicket(&mutex, ticket); // Blocking call until we attain the lock
|
||||
Dqn_TicketMutex_End(&mutex);
|
||||
#endif
|
||||
|
||||
// @proc Dqn_TicketMutex_BeginTicket
|
||||
// @desc Lock the mutex using the given ticket if possible, otherwise block
|
||||
// waiting until the mutex can be locked.
|
||||
|
||||
// @proc Dqn_TicketMutex_CanLock
|
||||
// @desc Determine if the mutex can be locked using the given ticket number
|
||||
|
||||
// NOTE: [$TMUT] Dqn_TicketMutex ///////////////////////////////////////////////////////////////////
|
||||
struct Dqn_TicketMutex
|
||||
{
|
||||
unsigned int volatile ticket; // The next ticket to give out to the thread taking the mutex
|
||||
unsigned int volatile serving; // The ticket ID to block the mutex on until it is returned
|
||||
};
|
||||
|
||||
void Dqn_TicketMutex_Begin (Dqn_TicketMutex *mutex);
|
||||
void Dqn_TicketMutex_End (Dqn_TicketMutex *mutex);
|
||||
Dqn_uint Dqn_TicketMutex_MakeTicket (Dqn_TicketMutex *mutex);
|
||||
void Dqn_TicketMutex_BeginTicket(Dqn_TicketMutex const *mutex, Dqn_uint ticket);
|
||||
bool Dqn_TicketMutex_CanLock (Dqn_TicketMutex const *mutex, Dqn_uint ticket);
|
||||
|
||||
// NOTE: [$ALLO] Dqn_Allocator =====================================================================
|
||||
typedef void *Dqn_Allocator_AllocProc(size_t size, uint8_t align, Dqn_ZeroMem zero_mem, void *user_context);
|
||||
typedef void Dqn_Allocator_DeallocProc(void *ptr, size_t size, void *user_context);
|
||||
|
||||
struct Dqn_Allocator
|
||||
{
|
||||
void *user_context; // User assigned pointer that is passed into the allocator functions
|
||||
Dqn_Allocator_AllocProc *alloc; // Memory allocating routine
|
||||
Dqn_Allocator_DeallocProc *dealloc; // Memory deallocating routine
|
||||
};
|
||||
|
||||
// NOTE: Macros ====================================================================================
|
||||
#define Dqn_Allocator_NewArray(allocator, Type, count, zero_mem) (Type *)Dqn_Allocator_Alloc(allocator, sizeof(Type) * count, alignof(Type), zero_mem)
|
||||
#define Dqn_Allocator_New(allocator, Type, zero_mem) (Type *)Dqn_Allocator_Alloc(allocator, sizeof(Type), alignof(Type), zero_mem)
|
||||
|
||||
// NOTE: API =======================================================================================
|
||||
void *Dqn_Allocator_Alloc (Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||
void Dqn_Allocator_Dealloc(Dqn_Allocator allocator, void *ptr, size_t size);
|
||||
|
||||
// NOTE: [$PRIN] Dqn_Print =========================================================================
|
||||
// NOTE: [$PRIN] Dqn_Print /////////////////////////////////////////////////////////////////////////
|
||||
enum Dqn_PrintStd
|
||||
{
|
||||
Dqn_PrintStd_Out,
|
||||
@ -559,12 +461,45 @@ enum Dqn_PrintESCColour
|
||||
Dqn_PrintESCColour_Bg,
|
||||
};
|
||||
|
||||
// NOTE: Print Style ===============================================================================
|
||||
|
||||
// NOTE: [$LLOG] Dqn_Log ///////////////////////////////////////////////////////////////////////////
|
||||
enum Dqn_LogType
|
||||
{
|
||||
Dqn_LogType_Debug,
|
||||
Dqn_LogType_Info,
|
||||
Dqn_LogType_Warning,
|
||||
Dqn_LogType_Error,
|
||||
Dqn_LogType_Count,
|
||||
};
|
||||
|
||||
typedef void Dqn_LogProc(Dqn_Str8 type,
|
||||
int log_type,
|
||||
void *user_data,
|
||||
Dqn_CallSite call_site,
|
||||
DQN_FMT_ATTRIB char const *fmt,
|
||||
va_list va);
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||
DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64 (uint64_t volatile *target, uint64_t value);
|
||||
DQN_FORCE_INLINE long Dqn_Atomic_SetValue32 (long volatile *target, long value);
|
||||
#if !defined(DQN_PLATFORM_ARM64)
|
||||
DQN_API Dqn_CPUIDRegisters Dqn_CPUID (int function_id);
|
||||
#endif
|
||||
|
||||
// NOTE: [$TMUT] Dqn_TicketMutex ///////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_TicketMutex_Begin (Dqn_TicketMutex *mutex);
|
||||
DQN_API void Dqn_TicketMutex_End (Dqn_TicketMutex *mutex);
|
||||
DQN_API Dqn_uint Dqn_TicketMutex_MakeTicket (Dqn_TicketMutex *mutex);
|
||||
DQN_API void Dqn_TicketMutex_BeginTicket (Dqn_TicketMutex const *mutex, Dqn_uint ticket);
|
||||
DQN_API bool Dqn_TicketMutex_CanLock (Dqn_TicketMutex const *mutex, Dqn_uint ticket);
|
||||
|
||||
// NOTE: [$PRIN] Dqn_Print /////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: Print Style ///////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour (uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold);
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32 (uint32_t rgb, Dqn_PrintBold bold);
|
||||
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold ();
|
||||
|
||||
// NOTE: Print Standard Out ========================================================================
|
||||
// NOTE: Print Macros //////////////////////////////////////////////////////////////////////////////
|
||||
#define Dqn_Print(string) Dqn_Print_Std(Dqn_PrintStd_Out, string)
|
||||
#define Dqn_Print_F(fmt, ...) Dqn_Print_StdF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Print_FV(fmt, args) Dqn_Print_StdFV(Dqn_PrintStd_Out, fmt, args)
|
||||
@ -596,9 +531,7 @@ DQN_API Dqn_PrintStyle Dqn_Print_StyleBold ();
|
||||
#define Dqn_Print_ErrLnStyle(style, string) Dqn_Print_StdLnStyle(Dqn_PrintStd_Err, style, string);
|
||||
#define Dqn_Print_ErrLnFStyle(style, fmt, ...) Dqn_Print_StdLnFStyle(Dqn_PrintStd_Err, style, fmt, ## __VA_ARGS__);
|
||||
#define Dqn_Print_ErrLnFVStyle(style, fmt, args) Dqn_Print_StdLnFVStyle(Dqn_PrintStd_Err, style, fmt, args);
|
||||
|
||||
|
||||
// NOTE: Print =====================================================================================
|
||||
// NOTE: Print /////////////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_Str8 string);
|
||||
DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
@ -615,9 +548,9 @@ DQN_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn
|
||||
DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
|
||||
// NOTE: ANSI Formatting Codes =====================================================================
|
||||
Dqn_Str8 Dqn_Print_ESCColourStr8 (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b);
|
||||
Dqn_Str8 Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour colour, uint32_t value);
|
||||
// NOTE: ANSI Formatting Codes /////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str8 Dqn_Print_ESCColourStr8 (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b);
|
||||
DQN_API Dqn_Str8 Dqn_Print_ESCColourU32Str8 (Dqn_PrintESCColour colour, uint32_t value);
|
||||
|
||||
#define Dqn_Print_ESCColourFgStr8(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Fg, r, g, b)
|
||||
#define Dqn_Print_ESCColourBgStr8(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Bg, r, g, b)
|
||||
@ -633,53 +566,59 @@ Dqn_Str8 Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour colour, uin
|
||||
#define Dqn_Print_ESCBold "\x1b[1m"
|
||||
#define Dqn_Print_ESCResetStr8 DQN_STR8(Dqn_Print_ESCReset)
|
||||
#define Dqn_Print_ESCBoldStr8 DQN_STR8(Dqn_Print_ESCBold)
|
||||
|
||||
// NOTE: [$LLOG] Dqn_Log ==========================================================================
|
||||
// NOTE: API
|
||||
// @proc Dqn_LogProc
|
||||
// @desc The logging procedure of the library. Users can override the default
|
||||
// logging function by setting the logging function pointer in Dqn_Library.
|
||||
// This function will be invoked every time a log is recorded using the
|
||||
// following functions.
|
||||
//
|
||||
// @param[in] log_type This value is one of the Dqn_LogType values if the log
|
||||
// was generated from one of the default categories. -1 if the log is not from
|
||||
// one of the default categories.
|
||||
|
||||
enum Dqn_LogType
|
||||
{
|
||||
Dqn_LogType_Debug,
|
||||
Dqn_LogType_Info,
|
||||
Dqn_LogType_Warning,
|
||||
Dqn_LogType_Error,
|
||||
Dqn_LogType_Count,
|
||||
};
|
||||
|
||||
/// RGBA
|
||||
// NOTE: [$LLOG] Dqn_Log ///////////////////////////////////////////////////////////////////////////
|
||||
#define Dqn_LogTypeColourU32_Info 0x00'87'ff'ff // Blue
|
||||
#define Dqn_LogTypeColourU32_Warning 0xff'ff'00'ff // Yellow
|
||||
#define Dqn_LogTypeColourU32_Error 0xff'00'00'ff // Red
|
||||
|
||||
typedef void Dqn_LogProc(Dqn_Str8 type, int log_type, void *user_data, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va);
|
||||
|
||||
#define Dqn_Log_DebugF(fmt, ...) Dqn_Log_TypeFCallSite (Dqn_LogType_Debug, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Log_InfoF(fmt, ...) Dqn_Log_TypeFCallSite (Dqn_LogType_Info, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Log_WarningF(fmt, ...) Dqn_Log_TypeFCallSite (Dqn_LogType_Warning, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
#define Dqn_Log_ErrorF(fmt, ...) Dqn_Log_TypeFCallSite (Dqn_LogType_Error, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define Dqn_Log_DebugFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Debug, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_InfoFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Info, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_WarningFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_ErrorFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, args)
|
||||
|
||||
#define Dqn_Log_TypeFV(type, fmt, args) Dqn_Log_TypeFVCallSite(type, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_TypeF(type, fmt, ...) Dqn_Log_TypeFCallSite (type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define Dqn_Log_FV(type, fmt, args) Dqn_Log_FVCallSite (type, DQN_CALL_SITE, fmt, args)
|
||||
#define Dqn_Log_F(type, fmt, ...) Dqn_Log_FCallSite (type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Log_MakeStr8 (Dqn_Allocator allocator, bool colour, Dqn_Str8 type, int log_type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Str8 Dqn_Log_MakeStr8 (struct Dqn_Arena *arena, bool colour, Dqn_Str8 type, int log_type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API void Dqn_Log_TypeFVCallSite (Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va);
|
||||
DQN_API void Dqn_Log_TypeFCallSite (Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API void Dqn_Log_FVCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va);
|
||||
DQN_API void Dqn_Log_FCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||
DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64(uint64_t volatile *target, uint64_t value)
|
||||
{
|
||||
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
||||
__int64 result;
|
||||
do {
|
||||
result = *target;
|
||||
} while (Dqn_Atomic_CompareExchange64(target, value, result) != result);
|
||||
return DQN_CAST(uint64_t)result;
|
||||
#elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||
uint64_t result = __sync_lock_test_and_set(target, value);
|
||||
return result;
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
|
||||
DQN_FORCE_INLINE long Dqn_Atomic_SetValue32(long volatile *target, long value)
|
||||
{
|
||||
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
||||
long result;
|
||||
do {
|
||||
result = *target;
|
||||
} while (Dqn_Atomic_CompareExchange32(target, value, result) != result);
|
||||
return result;
|
||||
#elif defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
|
||||
long result = __sync_lock_test_and_set(target, value);
|
||||
return result;
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
}
|
||||
|
@ -1,5 +1,58 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ __$$\ \_$$ _|$$$\ $$ |$$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ / $$ | $$ | $$$$\ $$ |$$ | $$ | $$ |$$ / \__|
|
||||
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$$$$ | $$ | $$ $$\$$ |$$$$$\ $$$$$$$ |\$$$$$$\
|
||||
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __$$ | $$ | $$ \$$$$ |$$ __| $$ __$$< \____$$\
|
||||
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ | $$ |$$\ $$ |
|
||||
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$ | $$ |$$$$$$\ $$ | \$$ |$$$$$$$$\ $$ | $$ |\$$$$$$ |
|
||||
// \______/ \______/ \__| \__| \__| \__| \__|\______|\__| \__|\________|\__| \__| \______/
|
||||
//
|
||||
// dqn_containers.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$SLIC] Dqn_Slice /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str8 Dqn_Slice_Str8Render(Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array, Dqn_Str8 separator)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
Dqn_usize total_size = 0;
|
||||
for (Dqn_usize index = 0; index < array.size; index++) {
|
||||
if (index)
|
||||
total_size += separator.size;
|
||||
Dqn_Str8 item = array.data[index];
|
||||
total_size += item.size;
|
||||
}
|
||||
|
||||
result = Dqn_Str8_Alloc(arena, total_size, Dqn_ZeroMem_No);
|
||||
if (result.data) {
|
||||
Dqn_usize write_index = 0;
|
||||
for (Dqn_usize index = 0; index < array.size; index++) {
|
||||
if (index) {
|
||||
DQN_MEMCPY(result.data + write_index, separator.data, separator.size);
|
||||
write_index += separator.size;
|
||||
}
|
||||
Dqn_Str8 item = array.data[index];
|
||||
DQN_MEMCPY(result.data + write_index, item.data, item.size);
|
||||
write_index += item.size;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Slice_Str8RenderSpaceSeparated(Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_Slice_Str8Render(arena, array, DQN_STR8(" "));
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_DSMAP)
|
||||
// NOTE: [$DMAP] Dqn_DSMap =========================================================================
|
||||
// NOTE: [$DMAP] Dqn_DSMap /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash(uint64_t u64)
|
||||
{
|
||||
Dqn_DSMapKey result = {};
|
||||
|
834
dqn_containers.h
834
dqn_containers.h
File diff suppressed because it is too large
Load Diff
134
dqn_cppbuild.h
134
dqn_cppbuild.h
@ -1,9 +1,27 @@
|
||||
#if !defined(DQN_CPP_BUILD_H)
|
||||
#define DQN_CPP_BUILD_H
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$$\ $$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$\ $$$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ $$ | $$ |\_$$ _|$$ | $$ __$$\
|
||||
// $$ / \__|$$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |
|
||||
// $$ | $$$$$$$ |$$$$$$$ | $$$$$$$\ |$$ | $$ | $$ | $$ | $$ | $$ |
|
||||
// $$ | $$ ____/ $$ ____/ $$ __$$\ $$ | $$ | $$ | $$ | $$ | $$ |
|
||||
// $$ | $$\ $$ | $$ | $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |
|
||||
// \$$$$$$ |$$ | $$ | $$$$$$$ |\$$$$$$ |$$$$$$\ $$$$$$$$\ $$$$$$$ |
|
||||
// \______/ \__| \__| \_______/ \______/ \______|\________|\_______/
|
||||
//
|
||||
// dqn_cppbuild.h -- Helper functions to make build scripts in C++
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdlib.h> // exit
|
||||
|
||||
struct Dqn_CPPBuildCompileFile
|
||||
{
|
||||
Dqn_Slice<Dqn_Str8> flags;
|
||||
Dqn_Slice<Dqn_Str8> prefix_flags;
|
||||
Dqn_Slice<Dqn_Str8> suffix_flags;
|
||||
Dqn_Str8 input_file_path;
|
||||
Dqn_Str8 output_file_path;
|
||||
};
|
||||
@ -11,15 +29,25 @@ struct Dqn_CPPBuildCompileFile
|
||||
Dqn_Str8 const DQN_CPP_BUILD_OBJ_SUFFIX_OBJ = DQN_STR8(".obj");
|
||||
Dqn_Str8 const DQN_CPP_BUILD_OBJ_SUFFIX_O = DQN_STR8(".o");
|
||||
|
||||
enum Dqn_CPPBuildCompiler
|
||||
enum Dqn_CPPBuildFlagsStyle
|
||||
{
|
||||
Dqn_CPPBuildCompiler_MSVC,
|
||||
Dqn_CPPBuildCompiler_GCC,
|
||||
Dqn_CPPBuildFlagsStyle_MSVC,
|
||||
Dqn_CPPBuildFlagsStyle_GCC,
|
||||
Dqn_CPPBuildFlagsStyle_CLANG,
|
||||
};
|
||||
|
||||
enum Dqn_CPPBuildAppendCompilerToCommand
|
||||
{
|
||||
Dqn_CPPBuildAppendCompilerToCommand_No,
|
||||
Dqn_CPPBuildAppendCompilerToCommand_Yes,
|
||||
};
|
||||
|
||||
struct Dqn_CPPBuildContext
|
||||
{
|
||||
Dqn_CPPBuildCompiler compiler;
|
||||
// Dictates the type of compiler flags the functions may append to the
|
||||
// build command line
|
||||
Dqn_CPPBuildFlagsStyle flags_style;
|
||||
|
||||
Dqn_Str8 compile_file_obj_suffix;
|
||||
Dqn_Slice<Dqn_CPPBuildCompileFile> compile_files;
|
||||
Dqn_Slice<Dqn_Str8> compile_flags;
|
||||
@ -46,17 +74,18 @@ enum Dqn_CPPBuildMode
|
||||
Dqn_CPPBuildMode_CacheBuild,
|
||||
};
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Allocator allocator);
|
||||
DQN_API Dqn_Slice<Dqn_Str8> Dqn_CPPBuild_ToCommandLine (Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Arena *arena);
|
||||
DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLineStr8(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Arena *arena);
|
||||
DQN_API Dqn_CPPBuildAsyncResult Dqn_CPPBuild_Async (Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode);
|
||||
DQN_API void Dqn_CPPBuild_ExecOrAbort (Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode);
|
||||
#endif // DQN_CPP_BUILD_H
|
||||
|
||||
#if defined(DQN_CPP_BUILD_IMPLEMENTATION)
|
||||
DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Allocator allocator)
|
||||
DQN_API Dqn_Slice<Dqn_Str8> Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Arena *arena)
|
||||
{
|
||||
// NOTE: Check if object files are newer than the source files =================================
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context);
|
||||
Dqn_Str8 result = {};
|
||||
// NOTE: Check if object files are newer than the source files /////////////////////////////////
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_Slice<Dqn_Str8> result = {};
|
||||
|
||||
Dqn_Slice<Dqn_CPPBuildCompileFile> dirtied_compile_files = build_context.compile_files;
|
||||
if (mode == Dqn_CPPBuildMode_CacheBuild) {
|
||||
@ -77,15 +106,15 @@ DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, D
|
||||
|
||||
// NOTE: Create the object file path
|
||||
Dqn_Str8 file_stem = Dqn_Str8_FileNameNoExtension(file.input_file_path);
|
||||
obj_file_name = Dqn_Str8_InitF(scratch.allocator, "%.*s%.*s", DQN_STR_FMT(file_stem), DQN_STR_FMT(compile_file_obj_suffix));
|
||||
obj_file_name = Dqn_Str8_InitF(scratch.arena, "%.*s%.*s", DQN_STR_FMT(file_stem), DQN_STR_FMT(compile_file_obj_suffix));
|
||||
}
|
||||
|
||||
Dqn_Str8 obj_file_path = obj_file_name;
|
||||
if (build_context.build_dir.size)
|
||||
obj_file_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s", DQN_STR_FMT(build_context.build_dir), DQN_STR_FMT(obj_file_name));
|
||||
obj_file_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s/%.*s", DQN_STR_FMT(build_context.build_dir), DQN_STR_FMT(obj_file_name));
|
||||
|
||||
Dqn_FsInfo file_info = Dqn_Fs_GetInfo(file.input_file_path);
|
||||
Dqn_FsInfo obj_file_info = Dqn_Fs_GetInfo(obj_file_path);
|
||||
Dqn_OSPathInfo file_info = Dqn_OS_PathInfo(file.input_file_path);
|
||||
Dqn_OSPathInfo obj_file_info = Dqn_OS_PathInfo(obj_file_path);
|
||||
if (obj_file_info.last_write_time_in_s >= file_info.last_write_time_in_s)
|
||||
continue;
|
||||
|
||||
@ -96,88 +125,77 @@ DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, D
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Build the command line invocation =====================================================
|
||||
// NOTE: Build the command line invocation /////////////////////////////////////////////////////
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.allocator = allocator;
|
||||
DQN_FOR_UINDEX (index, build_context.compile_flags.size) {
|
||||
Dqn_Str8 flag = build_context.compile_flags.data[index];
|
||||
if (index)
|
||||
Dqn_Str8Builder_AppendF(&builder, " ");
|
||||
Dqn_Str8Builder_AppendRef(&builder, flag);
|
||||
}
|
||||
builder.arena = scratch.arena;
|
||||
Dqn_Str8Builder_AppendRefArray(&builder, build_context.compile_flags);
|
||||
|
||||
DQN_FOR_UINDEX(index, build_context.include_dirs.size) {
|
||||
Dqn_Str8 include_dir = build_context.include_dirs.data[index];
|
||||
if (builder.count)
|
||||
Dqn_Str8Builder_AppendF(&builder, " ");
|
||||
Dqn_Str8Builder_AppendF(&builder, "/I %.*s", DQN_STR_FMT(include_dir));
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8("-I"));
|
||||
Dqn_Str8Builder_AppendRef(&builder, include_dir);
|
||||
}
|
||||
|
||||
DQN_FOR_UINDEX(index, dirtied_compile_files.size) {
|
||||
Dqn_CPPBuildCompileFile file = dirtied_compile_files.data[index];
|
||||
Dqn_Str8 obj_file = {};
|
||||
if (builder.count)
|
||||
Dqn_Str8Builder_AppendF(&builder, " ");
|
||||
|
||||
if (file.output_file_path.size) {
|
||||
switch (build_context.compiler) {
|
||||
case Dqn_CPPBuildCompiler_MSVC: {
|
||||
Dqn_Str8Builder_AppendF(&builder, "/Fo%.*s ", DQN_STR_FMT(file.output_file_path));
|
||||
if (Dqn_Str8_HasData(file.output_file_path)) {
|
||||
switch (build_context.flags_style) {
|
||||
case Dqn_CPPBuildFlagsStyle_MSVC: {
|
||||
Dqn_Str8Builder_AppendF(&builder, "-Fo%.*s", DQN_STR_FMT(file.output_file_path));
|
||||
} break;
|
||||
|
||||
case Dqn_CPPBuildCompiler_GCC: {
|
||||
Dqn_Str8Builder_AppendF(&builder, "-o %.*s ", DQN_STR_FMT(file.output_file_path));
|
||||
case Dqn_CPPBuildFlagsStyle_GCC: /*FALLTHRU*/
|
||||
case Dqn_CPPBuildFlagsStyle_CLANG: {
|
||||
Dqn_Str8Builder_AppendF (&builder, "-o");
|
||||
Dqn_Str8Builder_AppendRef(&builder, file.output_file_path);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
DQN_FOR_UINDEX (flag_index, file.flags.size) {
|
||||
Dqn_Str8 flag = file.flags.data[flag_index];
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%.*s", flag_index ? " " : "", DQN_STR_FMT(flag));
|
||||
}
|
||||
// TODO(doyle): Check if the file exists, error if it doesn't
|
||||
|
||||
if (file.flags.size)
|
||||
Dqn_Str8Builder_AppendF(&builder, " ");
|
||||
Dqn_Str8Builder_AppendRefArray(&builder, file.prefix_flags);
|
||||
Dqn_Str8Builder_AppendRef(&builder, file.input_file_path);
|
||||
Dqn_Str8Builder_AppendRefArray(&builder, file.suffix_flags);
|
||||
}
|
||||
|
||||
DQN_FOR_UINDEX (index, build_context.link_flags.size) {
|
||||
Dqn_Str8 file = build_context.link_flags.data[index];
|
||||
if (builder.count)
|
||||
Dqn_Str8Builder_AppendF(&builder, " ");
|
||||
Dqn_Str8Builder_AppendRef(&builder, file);
|
||||
}
|
||||
|
||||
result = Dqn_Str8Builder_Build(&builder, allocator);
|
||||
Dqn_Str8Builder_AppendRefArray(&builder, build_context.link_flags);
|
||||
result = Dqn_Str8Builder_BuildSlice(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLineStr8(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Slice<Dqn_Str8> cmd_line = Dqn_CPPBuild_ToCommandLine(build_context, mode, arena);
|
||||
Dqn_Str8 result = Dqn_Slice_Str8Render(arena, cmd_line, DQN_STR8(" ") /*separator*/);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_CPPBuildAsyncResult Dqn_CPPBuild_Async(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode)
|
||||
{
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.allocator);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Slice<Dqn_Str8> cmd_line = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.arena);
|
||||
Dqn_CPPBuildAsyncResult result = {};
|
||||
if (!cmd.size)
|
||||
if (!cmd_line.size)
|
||||
return result;
|
||||
|
||||
if (!Dqn_Fs_MakeDir(build_context.build_dir)) {
|
||||
if (!Dqn_OS_DirMake(build_context.build_dir)) {
|
||||
result.status = Dqn_CPPBuildStatus_BuildDirectoryFailedToBeMade;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.async_handle = Dqn_OS_ExecAsync(cmd, build_context.build_dir);
|
||||
result.async_handle = Dqn_OS_ExecAsync(cmd_line, build_context.build_dir);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Dqn_CPPBuild_ExecOrAbort(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode)
|
||||
{
|
||||
if (!Dqn_Fs_MakeDir(build_context.build_dir)) {
|
||||
if (!Dqn_OS_DirMake(build_context.build_dir)) {
|
||||
Dqn_Log_ErrorF("Failed to make build dir '%.*s'", DQN_STR_FMT(build_context.build_dir));
|
||||
exit(-1);
|
||||
}
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.allocator);
|
||||
Dqn_OS_ExecOrAbort(cmd, build_context.build_dir);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Slice<Dqn_Str8> cmd_line = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.arena);
|
||||
Dqn_OS_ExecOrAbort(cmd_line, build_context.build_dir);
|
||||
}
|
||||
#endif // DQN_CPP_BUILD_IMPLEMENTATION
|
||||
|
355
dqn_debug.cpp
355
dqn_debug.cpp
@ -1,7 +1,25 @@
|
||||
// NOTE: [$ASAN] Dqn_Asan ========================================================================== ===
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\ $$ _____|$$ __$$\ $$ | $$ |$$ __$$\
|
||||
// $$ | $$ |$$ | $$ | $$ |$$ | $$ |$$ / \__|
|
||||
// $$ | $$ |$$$$$\ $$$$$$$\ |$$ | $$ |$$ |$$$$\
|
||||
// $$ | $$ |$$ __| $$ __$$\ $$ | $$ |$$ |\_$$ |
|
||||
// $$ | $$ |$$ | $$ | $$ |$$ | $$ |$$ | $$ |
|
||||
// $$$$$$$ |$$$$$$$$\ $$$$$$$ |\$$$$$$ |\$$$$$$ |
|
||||
// \_______/ \________|\_______/ \______/ \______/
|
||||
//
|
||||
// dqn_debug.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$ASAN] Dqn_Asan ////////////////////////////////////////////////////////////////////////// ///
|
||||
DQN_API void Dqn_ASAN_PoisonMemoryRegion(void const volatile *ptr, Dqn_usize size)
|
||||
{
|
||||
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
if (!ptr || !size)
|
||||
return;
|
||||
|
||||
#if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
__asan_poison_memory_region(ptr, size);
|
||||
if (DQN_ASAN_VET_POISON) {
|
||||
DQN_HARD_ASSERT(__asan_address_is_poisoned(ptr));
|
||||
@ -14,7 +32,10 @@ DQN_API void Dqn_ASAN_PoisonMemoryRegion(void const volatile *ptr, Dqn_usize siz
|
||||
|
||||
DQN_API void Dqn_ASAN_UnpoisonMemoryRegion(void const volatile *ptr, Dqn_usize size)
|
||||
{
|
||||
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
if (!ptr || !size)
|
||||
return;
|
||||
|
||||
#if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
__asan_unpoison_memory_region(ptr, size);
|
||||
if (DQN_ASAN_VET_POISON) {
|
||||
DQN_HARD_ASSERT(__asan_region_is_poisoned((void *)ptr, size) == 0);
|
||||
@ -37,10 +58,11 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t
|
||||
HANDLE thread = GetCurrentThread();
|
||||
result.process = GetCurrentProcess();
|
||||
|
||||
for (static bool init = false; !init; init = true) {
|
||||
if (!g_dqn_library->win32_sym_initialised) {
|
||||
g_dqn_library->win32_sym_initialised = true;
|
||||
SymSetOptions(SYMOPT_LOAD_LINES);
|
||||
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
|
||||
Dqn_Log_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DQN_STR_FMT(error.msg));
|
||||
}
|
||||
@ -57,10 +79,8 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena);
|
||||
Dqn_List<uint64_t> raw_frames = Dqn_List_Init<uint64_t>(scratch.arena, 32 /*chunk size*/);
|
||||
|
||||
while (raw_frames.count < limit) {
|
||||
Dqn_FArray<uint64_t, 256> raw_frames = {};
|
||||
while (raw_frames.size < limit) {
|
||||
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64,
|
||||
result.process,
|
||||
thread,
|
||||
@ -75,22 +95,53 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t
|
||||
|
||||
// NOTE: It might be useful one day to use frame.AddrReturn.Offset.
|
||||
// If AddrPC.Offset == AddrReturn.Offset then we can detect recursion.
|
||||
Dqn_List_Add(&raw_frames, frame.AddrPC.Offset);
|
||||
Dqn_FArray_Add(&raw_frames, frame.AddrPC.Offset);
|
||||
}
|
||||
Dqn_TicketMutex_End(&mutex);
|
||||
|
||||
result.base_addr = Dqn_Arena_NewArray(arena, uint64_t, raw_frames.count, Dqn_ZeroMem_No);
|
||||
for (Dqn_ListChunk<uint64_t> *chunk = raw_frames.head; chunk; chunk = chunk->next) {
|
||||
DQN_MEMCPY(result.base_addr + result.size, chunk->data, sizeof(*chunk->data) * chunk->count);
|
||||
result.size += DQN_CAST(uint16_t)chunk->count;
|
||||
}
|
||||
result.base_addr = Dqn_Arena_NewArray(arena, uint64_t, raw_frames.size, Dqn_ZeroMem_No);
|
||||
result.size = DQN_CAST(uint16_t)raw_frames.size;
|
||||
DQN_MEMCPY(result.base_addr, raw_frames.data, raw_frames.size * sizeof(raw_frames.data[0]));
|
||||
#else
|
||||
(void)limit; (void)arena;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *it, Dqn_StackTraceWalkResult *walk)
|
||||
DQN_API Dqn_Str8 Dqn_StackTrace_WalkStr8CRT(uint16_t limit, uint16_t skip)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_StackTraceWalkResult walk_result = Dqn_StackTrace_Walk(scratch.arena, limit);
|
||||
Dqn_Str8 result = Dqn_StackTrace_WalkResultStr8CRT(&walk_result, skip);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Dqn_StackTrace_AddWalkToStr8Builder_(Dqn_StackTraceWalkResult const *walk, Dqn_Str8Builder *builder, Dqn_usize skip)
|
||||
{
|
||||
Dqn_StackTraceRawFrame raw_frame = {};
|
||||
raw_frame.process = walk->process;
|
||||
for (Dqn_usize index = skip; index < walk->size; index++) {
|
||||
raw_frame.base_addr = walk->base_addr[index];
|
||||
Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(builder->arena, raw_frame);
|
||||
Dqn_Str8Builder_AppendF(builder, "%.*s(%I64u): %.*s%s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name), (index == walk->size - 1) ? "" : "\n");
|
||||
}
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_StackTrace_WalkStr8CRTNoScratch(uint16_t limit, uint16_t skip)
|
||||
{
|
||||
Dqn_Arena arena = {};
|
||||
arena.flags |= Dqn_ArenaFlag_NoAllocTrack;
|
||||
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(&arena, limit);
|
||||
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = &arena;
|
||||
Dqn_StackTrace_AddWalkToStr8Builder_(&walk, &builder, skip);
|
||||
Dqn_Str8 result = Dqn_Str8Builder_BuildCRT(&builder);
|
||||
Dqn_Arena_Deinit(&arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *it, Dqn_StackTraceWalkResult const *walk)
|
||||
{
|
||||
bool result = false;
|
||||
if (!it || !walk || !walk->base_addr || !walk->process)
|
||||
@ -105,53 +156,43 @@ DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame(Dqn_Arena *arena, Dqn_StackTraceRawFrame raw_frame)
|
||||
DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8(Dqn_Arena *arena, Dqn_StackTraceWalkResult const *walk, uint16_t skip)
|
||||
{
|
||||
#if defined(DQN_OS_WIN32)
|
||||
// NOTE: Get line+filename =====================================================================
|
||||
Dqn_Str8 result {};
|
||||
if (!walk || !arena)
|
||||
return result;
|
||||
|
||||
// TODO: Why does zero-initialising this with `line = {};` cause
|
||||
// SymGetLineFromAddr64 function to fail once we are at
|
||||
// __scrt_commain_main_seh and hit BaseThreadInitThunk frame? The
|
||||
// line and file number are still valid in the result which we use, so,
|
||||
// we silently ignore this error.
|
||||
IMAGEHLP_LINEW64 line;
|
||||
line.SizeOfStruct = sizeof(line);
|
||||
DWORD line_displacement = 0;
|
||||
SymGetLineFromAddrW64(raw_frame.process, raw_frame.base_addr, &line_displacement, &line);
|
||||
|
||||
// NOTE: Get function name =====================================================================
|
||||
|
||||
alignas(SYMBOL_INFOW) char buffer[sizeof(SYMBOL_INFOW) + (MAX_SYM_NAME * sizeof(wchar_t))] = {};
|
||||
SYMBOL_INFOW *symbol = DQN_CAST(SYMBOL_INFOW *)buffer;
|
||||
symbol->SizeOfStruct = sizeof(*symbol);
|
||||
symbol->MaxNameLen = sizeof(buffer) - sizeof(*symbol);
|
||||
|
||||
uint64_t symbol_displacement = 0; // Offset to the beginning of the symbol to the address
|
||||
SymFromAddrW(raw_frame.process, raw_frame.base_addr, &symbol_displacement, symbol);
|
||||
|
||||
// NOTE: Construct result ======================================================================
|
||||
|
||||
Dqn_Str16 file_name16 = Dqn_Str16{line.FileName, Dqn_CStr16_Size(line.FileName)};
|
||||
Dqn_Str16 function_name16 = Dqn_Str16{symbol->Name, symbol->NameLen};
|
||||
|
||||
Dqn_StackTraceFrame result = {};
|
||||
result.address = raw_frame.base_addr;
|
||||
result.line_number = line.LineNumber;
|
||||
result.file_name = Dqn_Win_Str16ToStr8(arena, file_name16);
|
||||
result.function_name = Dqn_Win_Str16ToStr8(arena, function_name16);
|
||||
#else
|
||||
Dqn_StackTraceFrame result = {};
|
||||
#endif
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
|
||||
result = Dqn_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8CRT(Dqn_StackTraceWalkResult const *walk, uint16_t skip)
|
||||
{
|
||||
Dqn_Str8 result {};
|
||||
if (!walk)
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
|
||||
result = Dqn_Str8Builder_BuildCRT(&builder);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames(Dqn_Arena *arena, uint16_t limit)
|
||||
{
|
||||
Dqn_Slice<Dqn_StackTraceFrame> result = {};
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena);
|
||||
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, limit);
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, limit);
|
||||
if (!walk.size)
|
||||
return result;
|
||||
|
||||
@ -163,138 +204,173 @@ DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames(Dqn_Arena *arena
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame(Dqn_Arena *arena, Dqn_StackTraceRawFrame raw_frame)
|
||||
{
|
||||
#if defined(DQN_OS_WIN32)
|
||||
// NOTE: Get line+filename /////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO: Why does zero-initialising this with `line = {};` cause
|
||||
// SymGetLineFromAddr64 function to fail once we are at
|
||||
// __scrt_commain_main_seh and hit BaseThreadInitThunk frame? The
|
||||
// line and file number are still valid in the result which we use, so,
|
||||
// we silently ignore this error.
|
||||
IMAGEHLP_LINEW64 line;
|
||||
line.SizeOfStruct = sizeof(line);
|
||||
DWORD line_displacement = 0;
|
||||
if (!SymGetLineFromAddrW64(raw_frame.process, raw_frame.base_addr, &line_displacement, &line)) {
|
||||
line = {};
|
||||
}
|
||||
|
||||
// NOTE: Get function name /////////////////////////////////////////////////////////////////////
|
||||
|
||||
alignas(SYMBOL_INFOW) char buffer[sizeof(SYMBOL_INFOW) + (MAX_SYM_NAME * sizeof(wchar_t))] = {};
|
||||
SYMBOL_INFOW *symbol = DQN_CAST(SYMBOL_INFOW *)buffer;
|
||||
symbol->SizeOfStruct = sizeof(*symbol);
|
||||
symbol->MaxNameLen = sizeof(buffer) - sizeof(*symbol);
|
||||
|
||||
uint64_t symbol_displacement = 0; // Offset to the beginning of the symbol to the address
|
||||
SymFromAddrW(raw_frame.process, raw_frame.base_addr, &symbol_displacement, symbol);
|
||||
|
||||
// NOTE: Construct result //////////////////////////////////////////////////////////////////////
|
||||
|
||||
Dqn_Str16 file_name16 = Dqn_Str16{line.FileName, Dqn_CStr16_Size(line.FileName)};
|
||||
Dqn_Str16 function_name16 = Dqn_Str16{symbol->Name, symbol->NameLen};
|
||||
|
||||
Dqn_StackTraceFrame result = {};
|
||||
result.address = raw_frame.base_addr;
|
||||
result.line_number = line.LineNumber;
|
||||
result.file_name = Dqn_Win_Str16ToStr8(arena, file_name16);
|
||||
result.function_name = Dqn_Win_Str16ToStr8(arena, function_name16);
|
||||
|
||||
if (!Dqn_Str8_HasData(result.function_name))
|
||||
result.function_name = DQN_STR8("<unknown function>");
|
||||
if (!Dqn_Str8_HasData(result.file_name))
|
||||
result.file_name = DQN_STR8("<unknown file>");
|
||||
#else
|
||||
Dqn_StackTraceFrame result = {};
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_StackTrace_Print(uint16_t limit)
|
||||
{
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Slice<Dqn_StackTraceFrame> stack_trace = Dqn_StackTrace_GetFrames(scratch.arena, limit);
|
||||
for (Dqn_StackTraceFrame &frame : stack_trace)
|
||||
Dqn_Print_ErrLnF("%.*s(%I64u): %.*s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name));
|
||||
}
|
||||
|
||||
// NOTE: [$DEBG] Dqn_Debug =========================================================================
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
DQN_API void Dqn_Debug_TrackAlloc_(Dqn_Str8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted)
|
||||
DQN_API void Dqn_StackTrace_ReloadSymbols()
|
||||
{
|
||||
#if defined(DQN_OS_WIN32)
|
||||
HANDLE process = GetCurrentProcess();
|
||||
SymRefreshModuleList(process);
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: [$DEBG] Dqn_Debug /////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DQN_LEAK_TRACKING)
|
||||
DQN_API void Dqn_Debug_TrackAlloc(void *ptr, Dqn_usize size, bool leak_permitted)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
if (g_dqn_library->alloc_tracking_disabled)
|
||||
return;
|
||||
|
||||
// NOTE: In this function we can create alloc records and hence it's
|
||||
// possible for the alloc table to resize. This can cause a nested call
|
||||
// into the tracking alloc function and dead-lock. We don't
|
||||
// care about tracking these alloc records for the alloc table itself so we
|
||||
// disable alloc tracking for the duration of this function.
|
||||
// TODO(doyle): @robust This is not thread safe.
|
||||
Dqn_TicketMutex_Begin(&g_dqn_library->alloc_table_mutex);
|
||||
g_dqn_library->alloc_tracking_disabled = true;
|
||||
|
||||
DQN_DEFER {
|
||||
g_dqn_library->alloc_tracking_disabled = false;
|
||||
Dqn_TicketMutex_End(&g_dqn_library->alloc_table_mutex);
|
||||
};
|
||||
|
||||
// NOTE: If the entry was not added, we are reusing a pointer that has been freed.
|
||||
// TODO: Add API for always making the item but exposing a var to indicate if the item was newly created or it already existed.
|
||||
Dqn_DSMap<Dqn_AllocRecord> *alloc_table = &g_dqn_library->alloc_table;
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(alloc_table, DQN_CAST(uintptr_t)ptr);
|
||||
Dqn_AllocRecord *alloc = Dqn_DSMap_Find(alloc_table, key);
|
||||
if (alloc) {
|
||||
if ((alloc->flags & Dqn_AllocRecordFlag_Freed) == 0) {
|
||||
Dqn_Str8 alloc_stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size);
|
||||
Dqn_Str8 alloc_clean_stack_trace = Dqn_Str8_Slice(alloc_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_stack_trace.size);
|
||||
Dqn_Str8 clean_stack_trace = Dqn_Str8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size);
|
||||
// TODO: Add API for always making the item but exposing a var to indicate if the item was newly created or it
|
||||
// already existed.
|
||||
Dqn_Str8 stack_trace = Dqn_StackTrace_WalkStr8CRTNoScratch(128, 3 /*skip*/);
|
||||
Dqn_DSMap<Dqn_DebugAlloc> *alloc_table = &g_dqn_library->alloc_table;
|
||||
Dqn_DSMapResult<Dqn_DebugAlloc> alloc_entry = Dqn_DSMap_MakeKeyU64(alloc_table, DQN_CAST(uint64_t) ptr);
|
||||
Dqn_DebugAlloc *alloc = alloc_entry.value;
|
||||
if (alloc_entry.found) {
|
||||
if ((alloc->flags & Dqn_DebugAllocFlag_Freed) == 0) {
|
||||
Dqn_Str8 alloc_size = Dqn_U64ToByteSizeStr8(alloc_table->arena, alloc->size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_Str8 new_alloc_size = Dqn_U64ToByteSizeStr8(alloc_table->arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
DQN_HARD_ASSERTF(
|
||||
alloc->flags & Dqn_AllocRecordFlag_Freed,
|
||||
"\n\nThis pointer is already in the leak tracker, however it has not "
|
||||
alloc->flags & Dqn_DebugAllocFlag_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 "
|
||||
"Dqn_Debug_TrackDealloc()\n"
|
||||
"\n"
|
||||
"The pointer (0x%p) originally allocated %_$$zu at:\n"
|
||||
"The pointer (0x%p) originally allocated %.*s at:\n"
|
||||
"\n"
|
||||
"%.*s"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer is being allocated again at:\n"
|
||||
"%.*s"
|
||||
"The pointer is allocating %.*s again at:\n"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
,
|
||||
ptr, alloc->size,
|
||||
DQN_STR_FMT(alloc_clean_stack_trace),
|
||||
DQN_STR_FMT(clean_stack_trace));
|
||||
ptr, DQN_STR_FMT(alloc_size),
|
||||
DQN_STR_FMT(alloc->stack_trace),
|
||||
DQN_STR_FMT(new_alloc_size),
|
||||
DQN_STR_FMT(stack_trace));
|
||||
}
|
||||
|
||||
// NOTE: Pointer was reused, clean up the prior entry
|
||||
free(alloc->stack_trace);
|
||||
free(alloc->freed_stack_trace);
|
||||
free(alloc->stack_trace.data);
|
||||
free(alloc->freed_stack_trace.data);
|
||||
*alloc = {};
|
||||
} else {
|
||||
alloc = Dqn_DSMap_Make(alloc_table, key, /*found*/ nullptr);
|
||||
}
|
||||
|
||||
alloc->ptr = ptr;
|
||||
alloc->size = size;
|
||||
alloc->stack_trace = stack_trace.data;
|
||||
alloc->stack_trace_size = DQN_CAST(uint16_t)stack_trace.size;
|
||||
|
||||
// TODO(doyle): @robust The global flag is not multi-thread safe
|
||||
if (leak_permitted || g_dqn_library->alloc_is_allowed_to_leak)
|
||||
alloc->flags |= Dqn_AllocRecordFlag_LeakPermitted;
|
||||
alloc->stack_trace = stack_trace;
|
||||
alloc->flags |= leak_permitted ? Dqn_DebugAllocFlag_LeakPermitted : 0;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Debug_TrackDealloc_(Dqn_Str8 stack_trace, void *ptr)
|
||||
DQN_API void Dqn_Debug_TrackDealloc(void *ptr)
|
||||
{
|
||||
if (!ptr || g_dqn_library->alloc_tracking_disabled)
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
Dqn_TicketMutex_Begin(&g_dqn_library->alloc_table_mutex);
|
||||
DQN_DEFER { Dqn_TicketMutex_End(&g_dqn_library->alloc_table_mutex); };
|
||||
|
||||
Dqn_DSMap<Dqn_AllocRecord> *alloc_table = &g_dqn_library->alloc_table;
|
||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(alloc_table, DQN_CAST(uintptr_t)ptr);
|
||||
Dqn_AllocRecord *alloc = Dqn_DSMap_Find(alloc_table, key);
|
||||
|
||||
DQN_HARD_ASSERTF(alloc, "Allocated pointer can not be removed as it does not exist in the "
|
||||
Dqn_Str8 stack_trace = Dqn_StackTrace_WalkStr8CRTNoScratch(128, 3 /*skip*/);
|
||||
Dqn_DSMap<Dqn_DebugAlloc> *alloc_table = &g_dqn_library->alloc_table;
|
||||
Dqn_DSMapResult<Dqn_DebugAlloc> alloc_entry = Dqn_DSMap_FindKeyU64(alloc_table, DQN_CAST(uintptr_t) ptr);
|
||||
DQN_HARD_ASSERTF(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);
|
||||
|
||||
if (alloc->flags & Dqn_AllocRecordFlag_Freed) {
|
||||
Dqn_Str8 alloc_stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size);
|
||||
Dqn_Str8 alloc_clean_stack_trace = Dqn_Str8_Slice(alloc_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_stack_trace.size);
|
||||
|
||||
Dqn_Str8 alloc_freed_stack_trace = Dqn_Str8_Init(alloc->freed_stack_trace, alloc->freed_stack_trace_size);
|
||||
Dqn_Str8 alloc_freed_clean_stack_trace = Dqn_Str8_Slice(alloc_freed_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_freed_stack_trace.size);
|
||||
|
||||
Dqn_Str8 dealloc_stack_trace = Dqn_Str8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size);
|
||||
|
||||
DQN_HARD_ASSERTF((alloc->flags & Dqn_AllocRecordFlag_Freed) == 0,
|
||||
"\n\nDouble free detected, pointer to free was already marked "
|
||||
Dqn_DebugAlloc *alloc = alloc_entry.value;
|
||||
if (alloc->flags & Dqn_DebugAllocFlag_Freed) {
|
||||
Dqn_Str8 freed_size = Dqn_U64ToByteSizeStr8(alloc_table->arena, alloc->freed_size, Dqn_U64ByteSizeType_Auto);
|
||||
DQN_HARD_ASSERTF((alloc->flags & Dqn_DebugAllocFlag_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 %_$$zu at:\n"
|
||||
"The pointer (0x%p) originally allocated %.*s at:\n"
|
||||
"\n"
|
||||
"%.*s"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer was freed at:\n"
|
||||
"\n"
|
||||
"%.*s"
|
||||
"%.*s\n"
|
||||
"\n"
|
||||
"The pointer is being freed again at:\n"
|
||||
"%.*s"
|
||||
"\n"
|
||||
"%.*s\n"
|
||||
,
|
||||
ptr, alloc->freed_size,
|
||||
DQN_STR_FMT(alloc_clean_stack_trace),
|
||||
DQN_STR_FMT(alloc_freed_clean_stack_trace),
|
||||
DQN_STR_FMT(dealloc_stack_trace));
|
||||
ptr, DQN_STR_FMT(freed_size),
|
||||
DQN_STR_FMT(alloc->stack_trace),
|
||||
DQN_STR_FMT(alloc->freed_stack_trace),
|
||||
DQN_STR_FMT(stack_trace));
|
||||
}
|
||||
|
||||
alloc->flags |= Dqn_AllocRecordFlag_Freed;
|
||||
alloc->freed_size = alloc->size;
|
||||
alloc->freed_stack_trace = stack_trace.data;
|
||||
alloc->freed_stack_trace_size = DQN_CAST(uint16_t)stack_trace.size;
|
||||
DQN_ASSERT(!Dqn_Str8_HasData(alloc->freed_stack_trace));
|
||||
alloc->flags |= Dqn_DebugAllocFlag_Freed;
|
||||
alloc->freed_stack_trace = stack_trace;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Debug_DumpLeaks()
|
||||
@ -302,25 +378,24 @@ DQN_API void Dqn_Debug_DumpLeaks()
|
||||
uint64_t leak_count = 0;
|
||||
uint64_t leaked_bytes = 0;
|
||||
for (Dqn_usize index = 1; index < g_dqn_library->alloc_table.occupied; index++) {
|
||||
Dqn_DSMapSlot<Dqn_AllocRecord> *slot = g_dqn_library->alloc_table.slots + index;
|
||||
Dqn_AllocRecord *alloc = &slot->value;
|
||||
bool alloc_leaked = (alloc->flags & Dqn_AllocRecordFlag_Freed) == 0;
|
||||
bool leak_permitted = (alloc->flags & Dqn_AllocRecordFlag_LeakPermitted);
|
||||
Dqn_DSMapSlot<Dqn_DebugAlloc> *slot = g_dqn_library->alloc_table.slots + index;
|
||||
Dqn_DebugAlloc *alloc = &slot->value;
|
||||
bool alloc_leaked = (alloc->flags & Dqn_DebugAllocFlag_Freed) == 0;
|
||||
bool leak_permitted = (alloc->flags & Dqn_DebugAllocFlag_LeakPermitted);
|
||||
if (alloc_leaked && !leak_permitted) {
|
||||
leaked_bytes += alloc->size;
|
||||
leak_count++;
|
||||
|
||||
Dqn_Str8 stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size);
|
||||
Dqn_Str8 clean_stack_trace = Dqn_Str8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size);
|
||||
Dqn_Log_WarningF("Pointer (0x%p) leaked %_$$zu at:\n"
|
||||
Dqn_Str8 alloc_size = Dqn_U64ToByteSizeStr8(g_dqn_library->alloc_table.arena, alloc->size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_Log_WarningF("Pointer (0x%p) leaked %.*s at:\n"
|
||||
"%.*s",
|
||||
alloc->ptr, alloc->size,
|
||||
DQN_STR_FMT(clean_stack_trace));
|
||||
alloc->ptr, DQN_STR_FMT(alloc_size),
|
||||
DQN_STR_FMT(alloc->stack_trace));
|
||||
}
|
||||
}
|
||||
|
||||
if (leak_count) {
|
||||
Dqn_Log_WarningF("There were %I64u leaked allocations totalling %_$$I64u", leak_count, leaked_bytes);
|
||||
Dqn_Str8 leak_size = Dqn_U64ToByteSizeStr8(&g_dqn_library->arena, leaked_bytes, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_Log_WarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DQN_STR_FMT(leak_size));
|
||||
}
|
||||
}
|
||||
#endif // defined(DQN_LEAK_TRACING)
|
||||
#endif // DQN_LEAK_TRACKING
|
||||
|
134
dqn_debug.h
134
dqn_debug.h
@ -1,3 +1,25 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\ $$ _____|$$ __$$\ $$ | $$ |$$ __$$\
|
||||
// $$ | $$ |$$ | $$ | $$ |$$ | $$ |$$ / \__|
|
||||
// $$ | $$ |$$$$$\ $$$$$$$\ |$$ | $$ |$$ |$$$$\
|
||||
// $$ | $$ |$$ __| $$ __$$\ $$ | $$ |$$ |\_$$ |
|
||||
// $$ | $$ |$$ | $$ | $$ |$$ | $$ |$$ | $$ |
|
||||
// $$$$$$$ |$$$$$$$$\ $$$$$$$ |\$$$$$$ |\$$$$$$ |
|
||||
// \_______/ \________|\_______/ \______/ \______/
|
||||
//
|
||||
// dqn_debug.h -- Tools for debugging
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$ASAN] Dqn_Asan -- Helpers to manually poison memory using ASAN
|
||||
// [$STKT] Dqn_StackTrace -- Create stack traces
|
||||
// [$DEBG] Dqn_Debug -- Allocation leak tracking API
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$ASAN] Dqn_Asan //////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_ASAN_POISON)
|
||||
#define DQN_ASAN_POISON 0
|
||||
#endif
|
||||
@ -7,6 +29,7 @@
|
||||
#endif
|
||||
|
||||
#define DQN_ASAN_POISON_ALIGNMENT 8
|
||||
|
||||
#if !defined(DQN_ASAN_POISON_GUARD_SIZE)
|
||||
#define DQN_ASAN_POISON_GUARD_SIZE 128
|
||||
#endif
|
||||
@ -14,53 +37,11 @@ static_assert(Dqn_IsPowerOfTwoAligned(DQN_ASAN_POISON_GUARD_SIZE, DQN_ASAN_POISO
|
||||
"ASAN poison guard size must be a power-of-two and aligned to ASAN's alignment"
|
||||
"requirement (8 bytes)");
|
||||
|
||||
// NOTE: MSVC does not support the feature detection macro for instance so we
|
||||
// compile it out
|
||||
#if !defined(__has_feature)
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
// NOTE: [$ASAN] Dqn_Asan ==========================================================================
|
||||
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
#if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
|
||||
DQN_API void Dqn_ASAN_PoisonMemoryRegion(void const volatile *ptr, Dqn_usize size);
|
||||
DQN_API void Dqn_ASAN_UnpoisonMemoryRegion(void const volatile *ptr, Dqn_usize size);
|
||||
|
||||
// NOTE: [$STKT] Dqn_StackTrace ====================================================================
|
||||
// Create a stack trace at the calling site that these functions are invoked
|
||||
// from.
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_StackTrace_Walk
|
||||
// @desc This functions generates the stack trace as a series of U64's that
|
||||
// represent the base address of the functions on the call-stack at the point
|
||||
// of execution. These functions are stored in order from the current
|
||||
// executing function first and the most ancestor function last in the walk.
|
||||
|
||||
// @proc Dqn_StackTrace_WalkResultIterate
|
||||
// @desc Create an iterator to iterate the walk result and produce
|
||||
// `Dqn_StackTraceRawFrame` from each level of the call-stack. These frames
|
||||
// can then be converted into `Dqn_StackTraceFrame` which is a human readable
|
||||
// representation of the frame.
|
||||
#if 0
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, 128 /*limit*/);
|
||||
for (Dqn_StackTraceWalkResultIterator it = {}; Dqn_StackTrace_WalkResultIterate(&it, &walk); ) {
|
||||
Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(scratch.arena, it.raw_frame);
|
||||
Dqn_Print_LnF("%.*s(%I64u): %.*s", DQN_STRING_FMT(frame.file_name), frame.line_number, DQN_STRING_FMT(frame.function_name));
|
||||
}
|
||||
#endif
|
||||
|
||||
// @proc Dqn_StackTrace_GetFrames
|
||||
// @desc Create a stack trace at the point of execution and return the frames
|
||||
// converted into its human readable format.
|
||||
|
||||
// @proc Dqn_StackTrace_RawFrameToFrame
|
||||
// @desc Convert a raw frame from a stack trace walk into the human readable
|
||||
// format (e.g. with function names, line number and file name).
|
||||
|
||||
// NOTE: [$STKT] Dqn_StackTrace ////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_StackTraceFrame
|
||||
{
|
||||
uint64_t address;
|
||||
@ -88,45 +69,52 @@ struct Dqn_StackTraceWalkResultIterator
|
||||
uint16_t index;
|
||||
};
|
||||
|
||||
DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk (Dqn_Arena *arena, uint16_t limit);
|
||||
DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *it, Dqn_StackTraceWalkResult *walk);
|
||||
DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames (Dqn_Arena *arena, uint16_t limit);
|
||||
DQN_API void Dqn_StackTrace_Print (uint16_t limit);
|
||||
DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame (Dqn_Arena *arena, Dqn_StackTraceRawFrame raw_frame);
|
||||
|
||||
// NOTE: [$DEBG] Dqn_Debug =========================================================================
|
||||
enum Dqn_AllocRecordFlag
|
||||
// NOTE: [$DEBG] Dqn_Debug /////////////////////////////////////////////////////////////////////////
|
||||
enum Dqn_DebugAllocFlag
|
||||
{
|
||||
Dqn_AllocRecordFlag_Freed = 1 << 0,
|
||||
Dqn_AllocRecordFlag_LeakPermitted = 1 << 1,
|
||||
Dqn_DebugAllocFlag_Freed = 1 << 0,
|
||||
Dqn_DebugAllocFlag_LeakPermitted = 1 << 1,
|
||||
};
|
||||
|
||||
struct Dqn_AllocRecord
|
||||
struct Dqn_DebugAlloc
|
||||
{
|
||||
void *ptr; // Pointer to the allocation being tracked
|
||||
Dqn_usize size; // Size of the allocation
|
||||
Dqn_usize freed_size; // Store the size of the allocation when it is freed
|
||||
char *stack_trace; // Stack trace at the point of allocation
|
||||
char *freed_stack_trace; // Stack trace of where the allocation was freed
|
||||
uint16_t stack_trace_size; // Size of the `stack_trace`
|
||||
uint16_t freed_stack_trace_size; // Size of `freed_stack_trace`
|
||||
uint16_t flags; // Bit flags from `Dqn_AllocRecordFlag`
|
||||
char padding[2];
|
||||
void *ptr; // 8 Pointer to the allocation being tracked
|
||||
Dqn_usize size; // 16 Size of the allocation
|
||||
Dqn_usize freed_size; // 24 Store the size of the allocation when it is freed
|
||||
Dqn_Str8 stack_trace; // 40 Stack trace at the point of allocation
|
||||
Dqn_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
|
||||
uint16_t flags; // 72 Bit flags from `Dqn_DebugAllocFlag`
|
||||
};
|
||||
static_assert(sizeof(Dqn_AllocRecord) == 48 || sizeof(Dqn_AllocRecord) == 28, // NOTE: 64 bit vs 32 bit pointers respectively
|
||||
|
||||
static_assert(sizeof(Dqn_DebugAlloc) == 64 || sizeof(Dqn_DebugAlloc) == 32, // NOTE: 64 bit vs 32 bit pointers respectively
|
||||
"We aim to keep the allocation record as light as possible as "
|
||||
"memory tracking can get expensive. Enforce that there is no "
|
||||
"unexpected padding.");
|
||||
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
#define Dqn_Debug_TrackAlloc(ptr, size, leak_permitted) Dqn_Debug_TrackAlloc_ (Dqn_Str8_InitCString8(b_stacktrace_get_string()), ptr, size, leak_permitted)
|
||||
#define Dqn_Debug_TrackDealloc(ptr) Dqn_Debug_TrackDealloc_(Dqn_Str8_InitCString8(b_stacktrace_get_string()), ptr)
|
||||
// NOTE: [$ASAN] Dqn_Asan //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_ASAN_PoisonMemoryRegion (void const volatile *ptr, Dqn_usize size);
|
||||
DQN_API void Dqn_ASAN_UnpoisonMemoryRegion (void const volatile *ptr, Dqn_usize size);
|
||||
|
||||
DQN_API void Dqn_Debug_TrackAlloc_(Dqn_Str8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted);
|
||||
DQN_API void Dqn_Debug_TrackDealloc_(Dqn_Str8 stack_trace, void *ptr);
|
||||
// NOTE: [$STKT] Dqn_StackTrace ////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk (Dqn_Arena *arena, uint16_t limit);
|
||||
DQN_API Dqn_Str8 Dqn_StackTrace_WalkStr8CRT (uint16_t limit, uint16_t skip);
|
||||
DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *it, Dqn_StackTraceWalkResult const *walk);
|
||||
DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8 (Dqn_Arena *arena, Dqn_StackTraceWalkResult const *walk, uint16_t skip);
|
||||
DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8CRT(Dqn_StackTraceWalkResult const *walk, uint16_t skip);
|
||||
DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames (Dqn_Arena *arena, uint16_t limit);
|
||||
DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame (Dqn_Arena *arena, Dqn_StackTraceRawFrame raw_frame);
|
||||
DQN_API void Dqn_StackTrace_Print (uint16_t limit);
|
||||
DQN_API void Dqn_StackTrace_ReloadSymbols ();
|
||||
|
||||
// NOTE: [$DEBG] Dqn_Debug /////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DQN_LEAK_TRACKING)
|
||||
DQN_API void Dqn_Debug_TrackAlloc (void *ptr, Dqn_usize size, bool alloc_can_leak);
|
||||
DQN_API void Dqn_Debug_TrackDealloc (void *ptr);
|
||||
DQN_API void Dqn_Debug_DumpLeaks ();
|
||||
#else
|
||||
#define Dqn_Debug_TrackAlloc(...)
|
||||
#define Dqn_Debug_TrackDealloc(...)
|
||||
#define Dqn_Debug_DumpLeaks(...)
|
||||
#define Dqn_Debug_TrackAlloc(ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
|
||||
#define Dqn_Debug_TrackDealloc(ptr) do { (void)ptr; } while (0)
|
||||
#define Dqn_Debug_DumpLeaks() do { } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
|
1091
dqn_docs.cpp
Normal file
1091
dqn_docs.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,20 @@
|
||||
// NOTE: [$STBS] stb_sprintf =======================================================================
|
||||
#if !defined(DQN_STB_SPRINTF_HEADER_ONLY)
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$\
|
||||
// $$ _____|$$ | $$ |\__$$ __|$$ _____|$$ __$$\ $$$\ $$ |$$ __$$\ $$ |
|
||||
// $$ | \$$\ $$ | $$ | $$ | $$ | $$ |$$$$\ $$ |$$ / $$ |$$ |
|
||||
// $$$$$\ \$$$$ / $$ | $$$$$\ $$$$$$$ |$$ $$\$$ |$$$$$$$$ |$$ |
|
||||
// $$ __| $$ $$< $$ | $$ __| $$ __$$< $$ \$$$$ |$$ __$$ |$$ |
|
||||
// $$ | $$ /\$$\ $$ | $$ | $$ | $$ |$$ |\$$$ |$$ | $$ |$$ |
|
||||
// $$$$$$$$\ $$ / $$ | $$ | $$$$$$$$\ $$ | $$ |$$ | \$$ |$$ | $$ |$$$$$$$$\
|
||||
// \________|\__| \__| \__| \________|\__| \__|\__| \__|\__| \__|\________|
|
||||
//
|
||||
// dqn_external.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if !defined(DQN_USE_STD_PRINTF) && !defined(DQN_STB_SPRINTF_HEADER_ONLY)
|
||||
// NOTE: [$STBS] stb_sprintf ///////////////////////////////////////////////////////////////////////
|
||||
#define STB_SPRINTF_IMPLEMENTATION
|
||||
#ifdef STB_SPRINTF_IMPLEMENTATION
|
||||
|
||||
@ -132,7 +147,6 @@ static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uin
|
||||
return (stbsp__uint32)(sn - s);
|
||||
}
|
||||
|
||||
|
||||
#if defined(__clang__)
|
||||
__attribute__((no_sanitize("undefined")))
|
||||
#endif
|
||||
@ -1160,7 +1174,7 @@ done:
|
||||
#undef stbsp__flush_cb
|
||||
#undef stbsp__cb_buf_clamp
|
||||
|
||||
// ============================================================================
|
||||
// ////////////////////////////////////////////////////////////////////////////
|
||||
// wrapper functions
|
||||
|
||||
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...)
|
||||
@ -1264,7 +1278,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt,
|
||||
return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// ///////////////////////////////////////////////////////////////////////
|
||||
// low level float utility functions
|
||||
|
||||
#ifndef STB_SPRINTF_NOFLOAT
|
||||
@ -1694,4 +1708,4 @@ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
#endif // DQN_STB_SPRINTF_HEADER_ONLY
|
||||
#endif // !defined(DQN_USE_STD_PRINTF) && !defined(DQN_STB_SPRINTF_HEADER_ONLY)
|
||||
|
@ -1,4 +1,24 @@
|
||||
// NOTE: [$OS_H] OS Headers ========================================================================
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$\
|
||||
// $$ _____|$$ | $$ |\__$$ __|$$ _____|$$ __$$\ $$$\ $$ |$$ __$$\ $$ |
|
||||
// $$ | \$$\ $$ | $$ | $$ | $$ | $$ |$$$$\ $$ |$$ / $$ |$$ |
|
||||
// $$$$$\ \$$$$ / $$ | $$$$$\ $$$$$$$ |$$ $$\$$ |$$$$$$$$ |$$ |
|
||||
// $$ __| $$ $$< $$ | $$ __| $$ __$$< $$ \$$$$ |$$ __$$ |$$ |
|
||||
// $$ | $$ /\$$\ $$ | $$ | $$ | $$ |$$ |\$$$ |$$ | $$ |$$ |
|
||||
// $$$$$$$$\ $$ / $$ | $$ | $$$$$$$$\ $$ | $$ |$$ | \$$ |$$ | $$ |$$$$$$$$\
|
||||
// \________|\__| \__| \__| \________|\__| \__|\__| \__|\__| \__|\________|
|
||||
//
|
||||
// dqn_external.h -- Third party dependencies
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$OS_H] OS Headers ////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_OS_WIN32) || defined(DQN_OS_WIN32_USE_PTHREADS)
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#if defined(DQN_OS_UNIX) || defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
#include <errno.h> // errno
|
||||
#include <fcntl.h> // O_RDONLY ... etc
|
||||
@ -18,9 +38,26 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: [$STBS] stb_sprintf =======================================================================
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
#include <emscripten/fetch.h> // emscripten_fetch (for Dqn_OSHttpResponse)
|
||||
#endif
|
||||
|
||||
// NOTE: [$STBS] stb_sprintf ///////////////////////////////////////////////////////////////////////
|
||||
#if defined(DQN_USE_STD_PRINTF)
|
||||
#include <stdio.h>
|
||||
#define DQN_SPRINTF(...) sprintf(__VA_ARGS__)
|
||||
#define DQN_SNPRINTF(...) snprintf(__VA_ARGS__)
|
||||
#define DQN_VSPRINTF(...) vsprintf(__VA_ARGS__)
|
||||
#define DQN_VSNPRINTF(...) vsnprintf(__VA_ARGS__)
|
||||
#else
|
||||
#define DQN_SPRINTF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
|
||||
#define DQN_SNPRINTF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
|
||||
#define DQN_VSPRINTF(...) STB_SPRINTF_DECORATE(vsprintf)(__VA_ARGS__)
|
||||
#define DQN_VSNPRINTF(...) STB_SPRINTF_DECORATE(vsnprintf)(__VA_ARGS__)
|
||||
|
||||
#if (DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)) && defined(DQN_COMPILER_MSVC)
|
||||
#error The STB implementation of sprintf triggers MSVC's implementation of ASAN. Compiling ASAN with STB sprintf is not supported.
|
||||
|
||||
#if defined(DQN_COMPILER_MSVC)
|
||||
// NOTE: stb_sprintf assumes c-string literals are 4 byte aligned which is
|
||||
// always true, however, reading past the end of a string whose size is not
|
||||
// a multiple of 4 is UB causing ASAN to complain. This is practically safe
|
||||
@ -96,7 +133,7 @@ As a comparison, when using MSVC static libs, calling sprintf drags
|
||||
in 16K.
|
||||
|
||||
API:
|
||||
====
|
||||
////
|
||||
int stbsp_sprintf( char * buf, char const * fmt, ... )
|
||||
int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
|
||||
Convert an arg list into a buffer. stbsp_snprintf always returns
|
||||
@ -119,7 +156,7 @@ void stbsp_set_separators( char comma, char period )
|
||||
Set the comma and period characters to use.
|
||||
|
||||
FLOATS/DOUBLES:
|
||||
===============
|
||||
///////////////
|
||||
This code uses a internal float->ascii conversion method that uses
|
||||
doubles with error correction (double-doubles, for ~105 bits of
|
||||
precision). This conversion is round-trip perfect - that is, an atof
|
||||
@ -134,13 +171,13 @@ If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
|
||||
and you'll save 4K of code space.
|
||||
|
||||
64-BIT INTS:
|
||||
============
|
||||
////////////
|
||||
This library also supports 64-bit integers and you can use MSVC style or
|
||||
GCC style indicators (%I64d or %lld). It supports the C99 specifiers
|
||||
for size_t and ptr_diff_t (%jd %zd) as well.
|
||||
|
||||
EXTRAS:
|
||||
=======
|
||||
///////
|
||||
Like some GCCs, for integers and floats, you can use a ' (single quote)
|
||||
specifier and commas will be inserted on the thousands: "%'d" on 12345
|
||||
would print 12,345.
|
||||
@ -157,7 +194,7 @@ In addition to octal and hexadecimal conversions, you can print
|
||||
integers in binary: "%b" for 256 would print 100.
|
||||
|
||||
PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
|
||||
===================================================================
|
||||
///////////////////////////////////////////////////////////////////
|
||||
"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
|
||||
"%24d" across all 32-bit ints (4.5x/4.2x faster)
|
||||
"%x" across all 32-bit ints (4.5x/3.8x faster)
|
||||
@ -248,5 +285,4 @@ STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char c
|
||||
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
|
||||
STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);
|
||||
#endif // STB_SPRINTF_H_INCLUDE
|
||||
|
||||
|
||||
#endif // !defined(DQN_USE_STD_PRINTF)
|
||||
|
20
dqn_hash.cpp
20
dqn_hash.cpp
@ -1,4 +1,19 @@
|
||||
// NOTE: [$FNV1] Dqn_FNV1A =========================================================================
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$$$$$\ $$\ $$\
|
||||
// $$ | $$ |$$ __$$\ $$ __$$\ $$ | $$ |
|
||||
// $$ | $$ |$$ / $$ |$$ / \__|$$ | $$ |
|
||||
// $$$$$$$$ |$$$$$$$$ |\$$$$$$\ $$$$$$$$ |
|
||||
// $$ __$$ |$$ __$$ | \____$$\ $$ __$$ |
|
||||
// $$ | $$ |$$ | $$ |$$\ $$ |$$ | $$ |
|
||||
// $$ | $$ |$$ | $$ |\$$$$$$ |$$ | $$ |
|
||||
// \__| \__|\__| \__| \______/ \__| \__|
|
||||
//
|
||||
// dqn_hash.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$FNV1] Dqn_FNV1A /////////////////////////////////////////////////////////////////////////
|
||||
// Default values recommended by: http://isthe.com/chongo/tech/comp/fnv/
|
||||
DQN_API uint32_t Dqn_FNV1A32_Iterate(void const *bytes, Dqn_usize size, uint32_t hash)
|
||||
{
|
||||
@ -28,7 +43,7 @@ DQN_API uint64_t Dqn_FNV1A64_Hash(void const *bytes, Dqn_usize size)
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$MMUR] Dqn_MurmurHash3 ===================================================================
|
||||
// NOTE: [$MMUR] Dqn_MurmurHash3 ///////////////////////////////////////////////////////////////////
|
||||
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
||||
#define DQN_MMH3_ROTL32(x, y) _rotl(x, y)
|
||||
#define DQN_MMH3_ROTL64(x, y) _rotl64(x, y)
|
||||
@ -244,4 +259,3 @@ DQN_API Dqn_MurmurHash3 Dqn_MurmurHash3_x64U128(void const *key, int len, uint32
|
||||
result.e[1] = h2;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
44
dqn_hash.h
44
dqn_hash.h
@ -1,12 +1,24 @@
|
||||
// NOTE: [$FNV1] Dqn_FNV1A =========================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
#if 0
|
||||
char buffer1[128] = {random bytes};
|
||||
char buffer2[128] = {random bytes};
|
||||
uint64_t hash = Dqn_FNV1A64_Hash(buffer1, sizeof(buffer1));
|
||||
hash = Dqn_FNV1A64_Iterate(buffer2, sizeof(buffer2), hash); // subsequent hashing
|
||||
#endif
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$$$$$\ $$\ $$\
|
||||
// $$ | $$ |$$ __$$\ $$ __$$\ $$ | $$ |
|
||||
// $$ | $$ |$$ / $$ |$$ / \__|$$ | $$ |
|
||||
// $$$$$$$$ |$$$$$$$$ |\$$$$$$\ $$$$$$$$ |
|
||||
// $$ __$$ |$$ __$$ | \____$$\ $$ __$$ |
|
||||
// $$ | $$ |$$ | $$ |$$\ $$ |$$ | $$ |
|
||||
// $$ | $$ |$$ | $$ |\$$$$$$ |$$ | $$ |
|
||||
// \__| \__|\__| \__| \______/ \__| \__|
|
||||
//
|
||||
// dqn_hash.h -- Hashing functions
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$FNV1] Dqn_FNV1A -- Hash(x) -> 32/64bit via FNV1a
|
||||
// [$MMUR] Dqn_MurmurHash3 -- Hash(x) -> 32/128bit via MurmurHash3
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$FNV1] Dqn_FNV1A /////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_FNV1A32_SEED)
|
||||
#define DQN_FNV1A32_SEED 2166136261U
|
||||
#endif
|
||||
@ -15,22 +27,16 @@
|
||||
#define DQN_FNV1A64_SEED 14695981039346656037ULL
|
||||
#endif
|
||||
|
||||
// NOTE: [$MMUR] Dqn_MurmurHash3 ///////////////////////////////////////////////////////////////////
|
||||
struct Dqn_MurmurHash3 { uint64_t e[2]; };
|
||||
|
||||
// NOTE: [$FNV1] Dqn_FNV1A /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API uint32_t Dqn_FNV1A32_Hash (void const *bytes, Dqn_usize size);
|
||||
DQN_API uint64_t Dqn_FNV1A64_Hash (void const *bytes, Dqn_usize size);
|
||||
DQN_API uint32_t Dqn_FNV1A32_Iterate (void const *bytes, Dqn_usize size, uint32_t hash);
|
||||
DQN_API uint64_t Dqn_FNV1A64_Iterate (void const *bytes, Dqn_usize size, uint64_t hash);
|
||||
|
||||
// NOTE: [$MMUR] Dqn_MurmurHash3 ===================================================================
|
||||
// MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||
// domain. The author (Austin Appleby) hereby disclaims copyright to this source
|
||||
// code.
|
||||
//
|
||||
// Note - The x86 and x64 versions do _not_ produce the same results, as the
|
||||
// algorithms are optimized for their respective platforms. You can still
|
||||
// compile and run any of them on any platform, but your performance with the
|
||||
// non-native version will be less than optimal.
|
||||
struct Dqn_MurmurHash3 { uint64_t e[2]; };
|
||||
|
||||
// NOTE: [$MMUR] Dqn_MurmurHash3 ///////////////////////////////////////////////////////////////////
|
||||
DQN_API uint32_t Dqn_MurmurHash3_x86U32 (void const *key, int len, uint32_t seed);
|
||||
DQN_API Dqn_MurmurHash3 Dqn_MurmurHash3_x64U128 (void const *key, int len, uint32_t seed);
|
||||
#define Dqn_MurmurHash3_x64U128AsU64(key, len, seed) (Dqn_MurmurHash3_x64U128(key, len, seed).e[0])
|
||||
|
494
dqn_helpers.cpp
494
dqn_helpers.cpp
@ -1,7 +1,31 @@
|
||||
// NOTE: [$PCGX] Dqn_PCG32 =========================================================================
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$ | $$ |$$ _____|$$ | $$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ |$$ / \__|
|
||||
// $$$$$$$$ |$$$$$\ $$ | $$$$$$$ |$$$$$\ $$$$$$$ |\$$$$$$\
|
||||
// $$ __$$ |$$ __| $$ | $$ ____/ $$ __| $$ __$$< \____$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |$$\ $$ |
|
||||
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$ | $$$$$$$$\ $$ | $$ |\$$$$$$ |
|
||||
// \__| \__|\________|\________|\__| \________|\__| \__| \______/
|
||||
//
|
||||
// dqn_helpers.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$PCGX] Dqn_PCG32 /////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL
|
||||
#define DQN_PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL
|
||||
|
||||
DQN_API Dqn_PCG32 Dqn_PCG32_Init(uint64_t seed)
|
||||
{
|
||||
Dqn_PCG32 result = {};
|
||||
Dqn_PCG32_Next(&result);
|
||||
result.state += seed;
|
||||
Dqn_PCG32_Next(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_PCG32_Next(Dqn_PCG32 *rng)
|
||||
{
|
||||
uint64_t state = rng->state;
|
||||
@ -45,14 +69,6 @@ DQN_API double Dqn_PCG32_NextF64(Dqn_PCG32 *rng)
|
||||
return (double)(int64_t)(x >> 11) * 0x1.0p-53;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_PCG32_Seed(Dqn_PCG32 *rng, uint64_t seed)
|
||||
{
|
||||
rng->state = 0ULL;
|
||||
Dqn_PCG32_Next(rng);
|
||||
rng->state += seed;
|
||||
Dqn_PCG32_Next(rng);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_PCG32_Advance(Dqn_PCG32 *rng, uint64_t delta)
|
||||
{
|
||||
uint64_t cur_mult = DQN_PCG_DEFAULT_MULTIPLIER_64;
|
||||
@ -75,18 +91,18 @@ DQN_API void Dqn_PCG32_Advance(Dqn_PCG32 *rng, uint64_t delta)
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_JSON_BUILDER)
|
||||
// NOTE: [$JSON] Dqn_JSONBuilder ===================================================================
|
||||
DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init(Dqn_Allocator allocator, int spaces_per_indent)
|
||||
// NOTE: [$JSON] Dqn_JSONBuilder ///////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init(Dqn_Arena *arena, int spaces_per_indent)
|
||||
{
|
||||
Dqn_JSONBuilder result = {};
|
||||
result.spaces_per_indent = spaces_per_indent;
|
||||
result.string_builder.allocator = allocator;
|
||||
result.string_builder.arena = arena;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_JSONBuilder_Build(Dqn_JSONBuilder const *builder, Dqn_Allocator allocator)
|
||||
DQN_API Dqn_Str8 Dqn_JSONBuilder_Build(Dqn_JSONBuilder const *builder, Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_Str8Builder_Build(&builder->string_builder, allocator);
|
||||
Dqn_Str8 result = Dqn_Str8Builder_Build(&builder->string_builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -97,15 +113,14 @@ DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_Str8 key, Dq
|
||||
|
||||
Dqn_JSONBuilderItem item = Dqn_JSONBuilderItem_KeyValue;
|
||||
if (value.size == 1) {
|
||||
if (value.data[0] == '{' || value.data[0] == '[') {
|
||||
if (value.data[0] == '{' || value.data[0] == '[')
|
||||
item = Dqn_JSONBuilderItem_OpenContainer;
|
||||
} else if (value.data[0] == '}' || value.data[0] == ']') {
|
||||
else if (value.data[0] == '}' || value.data[0] == ']')
|
||||
item = Dqn_JSONBuilderItem_CloseContainer;
|
||||
}
|
||||
}
|
||||
|
||||
bool adding_to_container_with_items = item != Dqn_JSONBuilderItem_CloseContainer &&
|
||||
(builder->last_item == Dqn_JSONBuilderItem_KeyValue ||
|
||||
bool adding_to_container_with_items =
|
||||
item != Dqn_JSONBuilderItem_CloseContainer && (builder->last_item == Dqn_JSONBuilderItem_KeyValue ||
|
||||
builder->last_item == Dqn_JSONBuilderItem_CloseContainer);
|
||||
|
||||
uint8_t prefix_size = 0;
|
||||
@ -125,16 +140,15 @@ DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_Str8 key, Dq
|
||||
if (key.size) {
|
||||
Dqn_Str8Builder_AppendF(&builder->string_builder,
|
||||
"%.*s%*c\"%.*s\": %.*s",
|
||||
prefix_size, prefix,
|
||||
spaces, ' ',
|
||||
prefix_size,
|
||||
prefix,
|
||||
spaces,
|
||||
' ',
|
||||
DQN_STR_FMT(key),
|
||||
DQN_STR_FMT(value));
|
||||
} else {
|
||||
Dqn_Str8Builder_AppendF(&builder->string_builder,
|
||||
"%.*s%*c%.*s",
|
||||
prefix_size, prefix,
|
||||
spaces, ' ',
|
||||
DQN_STR_FMT(value));
|
||||
Dqn_Str8Builder_AppendF(
|
||||
&builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DQN_STR_FMT(value));
|
||||
}
|
||||
|
||||
if (item == Dqn_JSONBuilderItem_OpenContainer)
|
||||
@ -145,8 +159,8 @@ DQN_API void Dqn_JSONBuilder_KeyValue(Dqn_JSONBuilder *builder, Dqn_Str8 key, Dq
|
||||
|
||||
DQN_API void Dqn_JSONBuilder_KeyValueFV(Dqn_JSONBuilder *builder, Dqn_Str8 key, char const *value_fmt, va_list args)
|
||||
{
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(builder->string_builder.allocator.user_context);
|
||||
Dqn_Str8 value = Dqn_Str8_InitFV(scratch.allocator, value_fmt, args);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(builder->string_builder.arena);
|
||||
Dqn_Str8 value = Dqn_Str8_InitFV(scratch.arena, value_fmt, args);
|
||||
Dqn_JSONBuilder_KeyValue(builder, key, value);
|
||||
}
|
||||
|
||||
@ -211,17 +225,17 @@ DQN_API void Dqn_JSONBuilder_F64Named(Dqn_JSONBuilder *builder, Dqn_Str8 key, do
|
||||
char float_fmt[16];
|
||||
if (decimal_places > 0) {
|
||||
// NOTE: Emit the format string "%.<decimal_places>f" i.e. %.1f
|
||||
STB_SPRINTF_DECORATE(snprintf)(float_fmt, sizeof(float_fmt), "%%.%df", decimal_places);
|
||||
DQN_SNPRINTF(float_fmt, sizeof(float_fmt), "%%.%df", decimal_places);
|
||||
} else {
|
||||
// NOTE: Emit the format string "%f"
|
||||
STB_SPRINTF_DECORATE(snprintf)(float_fmt, sizeof(float_fmt), "%%f");
|
||||
DQN_SNPRINTF(float_fmt, sizeof(float_fmt), "%%f");
|
||||
}
|
||||
|
||||
char fmt[32];
|
||||
if (key.size)
|
||||
STB_SPRINTF_DECORATE(snprintf)(fmt, sizeof(fmt), "\"%%.*s\": %s", float_fmt);
|
||||
DQN_SNPRINTF(fmt, sizeof(fmt), "\"%%.*s\": %s", float_fmt);
|
||||
else
|
||||
STB_SPRINTF_DECORATE(snprintf)(fmt, sizeof(fmt), "%s", float_fmt);
|
||||
DQN_SNPRINTF(fmt, sizeof(fmt), "%s", float_fmt);
|
||||
|
||||
Dqn_JSONBuilder_KeyValueF(builder, key, fmt, value);
|
||||
}
|
||||
@ -234,7 +248,7 @@ DQN_API void Dqn_JSONBuilder_BoolNamed(Dqn_JSONBuilder *builder, Dqn_Str8 key, b
|
||||
#endif // !defined(DQN_NO_JSON_BUILDER)
|
||||
|
||||
#if !defined(DQN_NO_BIN)
|
||||
// NOTE: [$BHEX] Dqn_Bin ===========================================================================
|
||||
// NOTE: [$BHEX] Dqn_Bin ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API char const *Dqn_Bin_HexBufferTrim0x(char const *hex, Dqn_usize size, Dqn_usize *real_size)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_Str8_TrimWhitespaceAround(Dqn_Str8_Init(hex, size));
|
||||
@ -263,7 +277,7 @@ DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8(uint64_t number, uint32_t flag
|
||||
result.size += DQN_CAST(int8_t) prefix.size;
|
||||
|
||||
char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
int size = STB_SPRINTF_DECORATE(snprintf)(result.data + result.size, DQN_ARRAY_UCOUNT(result.data) - result.size, fmt, number);
|
||||
int size = DQN_SNPRINTF(result.data + result.size, DQN_ARRAY_UCOUNT(result.data) - result.size, fmt, number);
|
||||
result.size += DQN_CAST(uint8_t) size;
|
||||
DQN_ASSERT(result.size < DQN_ARRAY_UCOUNT(result.data));
|
||||
|
||||
@ -274,7 +288,7 @@ DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8(uint64_t number, uint32_t flag
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Bin_U64ToHex(Dqn_Allocator allocator, uint64_t number, uint32_t flags)
|
||||
DQN_API Dqn_Str8 Dqn_Bin_U64ToHex(Dqn_Arena *arena, uint64_t number, uint32_t flags)
|
||||
{
|
||||
Dqn_Str8 prefix = {};
|
||||
if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix))
|
||||
@ -282,33 +296,26 @@ DQN_API Dqn_Str8 Dqn_Bin_U64ToHex(Dqn_Allocator allocator, uint64_t number, uint
|
||||
|
||||
char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
|
||||
Dqn_usize required_size = Dqn_CStr8_FSize(fmt, number) + prefix.size;
|
||||
Dqn_Str8 result = Dqn_Str8_Allocate(allocator, required_size, Dqn_ZeroMem_No);
|
||||
Dqn_Str8 result = Dqn_Str8_Alloc(arena, required_size, Dqn_ZeroMem_No);
|
||||
|
||||
if (Dqn_Str8_IsValid(result)) {
|
||||
if (Dqn_Str8_HasData(result)) {
|
||||
DQN_MEMCPY(result.data, prefix.data, prefix.size);
|
||||
int space = DQN_CAST(int) DQN_MAX((result.size - prefix.size) + 1, 0); /*null-terminator*/
|
||||
STB_SPRINTF_DECORATE(snprintf)(result.data + prefix.size, space, fmt, number);
|
||||
DQN_SNPRINTF(result.data + prefix.size, space, fmt, number);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Bin_HexBufferToU64(char const *hex, Dqn_usize size)
|
||||
DQN_API uint64_t Dqn_Bin_HexToU64(Dqn_Str8 hex)
|
||||
{
|
||||
Dqn_usize trim_size = size;
|
||||
char const *trim_hex = hex;
|
||||
if (trim_size >= 2) {
|
||||
if (trim_hex[0] == '0' && (trim_hex[1] == 'x' || trim_hex[1] == 'X')) {
|
||||
trim_size -= 2;
|
||||
trim_hex += 2;
|
||||
}
|
||||
}
|
||||
|
||||
DQN_ASSERT(DQN_CAST(Dqn_usize)(trim_size * 4 / 8) /*maximum amount of bytes represented in the hex string*/ <= sizeof(uint64_t));
|
||||
Dqn_Str8 real_hex = Dqn_Str8_TrimPrefix(Dqn_Str8_TrimPrefix(hex, DQN_STR8("0x")), DQN_STR8("0X"));
|
||||
Dqn_usize max_hex_size = sizeof(uint64_t) * 2 /*hex chars per byte*/;
|
||||
DQN_ASSERT(real_hex.size <= max_hex_size);
|
||||
|
||||
Dqn_usize size = DQN_MIN(max_hex_size, real_hex.size);
|
||||
uint64_t result = 0;
|
||||
Dqn_usize max_size = DQN_MIN(size, 8 /*bytes*/ * 2 /*hex chars per byte*/);
|
||||
for (Dqn_usize hex_index = 0; hex_index < max_size; hex_index++) {
|
||||
char ch = trim_hex[hex_index];
|
||||
for (Dqn_usize index = 0; index < size; index++) {
|
||||
char ch = real_hex.data[index];
|
||||
if (!Dqn_Char_IsHex(ch))
|
||||
break;
|
||||
uint8_t val = Dqn_Char_HexToU8(ch);
|
||||
@ -317,9 +324,9 @@ DQN_API uint64_t Dqn_Bin_HexBufferToU64(char const *hex, Dqn_usize size)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_Bin_HexToU64(Dqn_Str8 hex)
|
||||
DQN_API uint64_t Dqn_Bin_HexPtrToU64(char const *hex, Dqn_usize size)
|
||||
{
|
||||
uint64_t result = Dqn_Bin_HexBufferToU64(hex.data, hex.size);
|
||||
uint64_t result = Dqn_Bin_HexToU64(Dqn_Str8_Init(hex, size));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -346,7 +353,8 @@ DQN_API bool Dqn_Bin_BytesToHexBuffer(void const *src, Dqn_usize src_size, char
|
||||
|
||||
DQN_API char *Dqn_Bin_BytesToHexBufferArena(Dqn_Arena *arena, void const *src, Dqn_usize size)
|
||||
{
|
||||
char *result = size > 0 ? Dqn_Arena_NewArray(arena, char, (size * 2) + 1 /*null terminate*/, Dqn_ZeroMem_No) : nullptr;
|
||||
char *result =
|
||||
size > 0 ? Dqn_Arena_NewArray(arena, char, (size * 2) + 1 /*null terminate*/, Dqn_ZeroMem_No) : nullptr;
|
||||
if (result) {
|
||||
bool converted = Dqn_Bin_BytesToHexBuffer(src, size, result, size * 2);
|
||||
DQN_ASSERT(converted);
|
||||
@ -371,9 +379,7 @@ DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes(char const *hex, Dqn_usize hex_size,
|
||||
return result;
|
||||
|
||||
Dqn_usize trim_size = 0;
|
||||
char const *trim_hex = Dqn_Bin_HexBufferTrim0x(hex,
|
||||
hex_size,
|
||||
&trim_size);
|
||||
char const *trim_hex = Dqn_Bin_HexBufferTrim0x(hex, hex_size, &trim_size);
|
||||
|
||||
// NOTE: Trimmed hex can be "0xf" -> "f" or "0xAB" -> "AB"
|
||||
// Either way, the size can be odd or even, hence we round up to the nearest
|
||||
@ -385,22 +391,19 @@ DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes(char const *hex, Dqn_usize hex_size,
|
||||
return result;
|
||||
}
|
||||
|
||||
result = Dqn_Bin_HexBufferToBytesUnchecked(trim_hex,
|
||||
trim_size,
|
||||
dest,
|
||||
dest_size);
|
||||
result = Dqn_Bin_HexBufferToBytesUnchecked(trim_hex, trim_size, dest, dest_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked(char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size)
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked(char const *hex,
|
||||
Dqn_usize hex_size,
|
||||
void *dest,
|
||||
Dqn_usize dest_size)
|
||||
{
|
||||
Dqn_usize result = 0;
|
||||
unsigned char *dest_u8 = DQN_CAST(unsigned char *) dest;
|
||||
|
||||
for (Dqn_usize hex_index = 0;
|
||||
hex_index < hex_size;
|
||||
hex_index += 2, result += 1)
|
||||
{
|
||||
for (Dqn_usize hex_index = 0; hex_index < hex_size; hex_index += 2, result += 1) {
|
||||
char hex01 = hex[hex_index];
|
||||
char hex02 = (hex_index + 1 < hex_size) ? hex[hex_index + 1] : 0;
|
||||
|
||||
@ -460,30 +463,30 @@ DQN_API Dqn_Str8 Dqn_Bin_HexToBytesArena(Dqn_Arena *arena, Dqn_Str8 hex)
|
||||
}
|
||||
#endif // !defined(DQN_NO_BIN)
|
||||
|
||||
// NOTE: [$BITS] Dqn_Bit ===========================================================================
|
||||
DQN_API void Dqn_Bit_UnsetInplace(uint64_t *flags, uint64_t bitfield)
|
||||
// NOTE: [$BITS] Dqn_Bit ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_Bit_UnsetInplace(Dqn_usize *flags, Dqn_usize bitfield)
|
||||
{
|
||||
*flags = (*flags & ~bitfield);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Bit_SetInplace(uint64_t *flags, uint64_t bitfield)
|
||||
DQN_API void Dqn_Bit_SetInplace(Dqn_usize *flags, Dqn_usize bitfield)
|
||||
{
|
||||
*flags = (*flags | bitfield);
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Bit_IsSet(uint64_t bits, uint64_t bits_to_set)
|
||||
DQN_API bool Dqn_Bit_IsSet(Dqn_usize bits, Dqn_usize bits_to_set)
|
||||
{
|
||||
auto result = DQN_CAST(bool)((bits & bits_to_set) == bits_to_set);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Bit_IsNotSet(uint64_t bits, uint64_t bits_to_check)
|
||||
DQN_API bool Dqn_Bit_IsNotSet(Dqn_usize bits, Dqn_usize bits_to_check)
|
||||
{
|
||||
auto result = !Dqn_Bit_IsSet(bits, bits_to_check);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$SAFE] Dqn_Safe ==========================================================================
|
||||
// NOTE: [$SAFE] Dqn_Safe //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API int64_t Dqn_Safe_AddI64(int64_t a, int64_t b)
|
||||
{
|
||||
int64_t result = DQN_CHECKF(a <= INT64_MAX - b, "a=%zd, b=%zd", a, b) ? (a + b) : INT64_MAX;
|
||||
@ -582,8 +585,16 @@ DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64(Dqn_usize val)
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastU64ToU*
|
||||
// NOTE: Dqn_Safe_SaturateCastU64To*
|
||||
// -----------------------------------------------------------------------------
|
||||
// INT*_MAX literals will be promoted to the type of val as val is
|
||||
// the highest possible rank (unsigned > signed).
|
||||
DQN_API int Dqn_Safe_SaturateCastU64ToInt(uint64_t val)
|
||||
{
|
||||
int result = DQN_CHECK(val <= INT_MAX) ? DQN_CAST(int)val : INT_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Both operands are unsigned and the lowest rank operand will be promoted to
|
||||
// match the highest rank operand.
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt(uint64_t val)
|
||||
@ -610,7 +621,6 @@ DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32(uint64_t val)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: Dqn_Safe_SaturateCastISizeToI*
|
||||
// -----------------------------------------------------------------------------
|
||||
// Both operands are signed so the lowest rank operand will be promoted to
|
||||
@ -805,12 +815,12 @@ DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64(int val)
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$MISC] Misc ==============================================================================
|
||||
DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
// NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API int Dqn_FmtBuffer3DotTruncate(char *buffer, int size, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int size_required = STB_SPRINTF_DECORATE(vsnprintf)(buffer, size, fmt, args);
|
||||
int size_required = DQN_VSNPRINTF(buffer, size, fmt, args);
|
||||
int result = DQN_MAX(DQN_MIN(size_required, size - 1), 0);
|
||||
if (result == size - 1) {
|
||||
buffer[size - 2] = '.';
|
||||
@ -842,7 +852,8 @@ DQN_API Dqn_U64Str8 Dqn_U64ToStr8(uint64_t val, char separator)
|
||||
// NOTE: Reverse the string
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(6293) // Ill-defined for-loop
|
||||
DQN_MSVC_WARNING_DISABLE(6385) // Reading invalid data from 'temp.data' NOTE(doyle): Unsigned overflow is valid for loop termination
|
||||
DQN_MSVC_WARNING_DISABLE(
|
||||
6385) // Reading invalid data from 'temp.data' NOTE(doyle): Unsigned overflow is valid for loop termination
|
||||
for (Dqn_usize temp_index = temp.size - 1; temp_index < temp.size; temp_index--) {
|
||||
char ch = temp.data[temp_index];
|
||||
result.data[result.size++] = ch;
|
||||
@ -853,7 +864,105 @@ DQN_API Dqn_U64Str8 Dqn_U64ToStr8(uint64_t val, char separator)
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$DLIB] Dqn_Library =======================================================================
|
||||
DQN_API Dqn_U64ByteSize Dqn_U64ToByteSize(uint64_t bytes, Dqn_U64ByteSizeType desired_type)
|
||||
{
|
||||
Dqn_U64ByteSize result = {};
|
||||
result.bytes = DQN_CAST(Dqn_f64)bytes;
|
||||
if (!DQN_CHECK(desired_type != Dqn_U64ByteSizeType_Count)) {
|
||||
result.suffix = Dqn_U64ByteSizeTypeString(result.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (desired_type == Dqn_U64ByteSizeType_Auto) {
|
||||
for (; result.type < Dqn_U64ByteSizeType_Count && result.bytes >= 1024.0; result.type = DQN_CAST(Dqn_U64ByteSizeType)(DQN_CAST(Dqn_usize)result.type + 1))
|
||||
result.bytes /= 1024.0;
|
||||
} else {
|
||||
for (; result.type < desired_type; result.type = DQN_CAST(Dqn_U64ByteSizeType)(DQN_CAST(Dqn_usize)result.type + 1))
|
||||
result.bytes /= 1024.0;
|
||||
}
|
||||
|
||||
result.suffix = Dqn_U64ByteSizeTypeString(result.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_U64ToByteSizeStr8(Dqn_Arena *arena, uint64_t bytes, Dqn_U64ByteSizeType desired_type)
|
||||
{
|
||||
Dqn_U64ByteSize byte_size = Dqn_U64ToByteSize(bytes, desired_type);
|
||||
Dqn_Str8 result = Dqn_Str8_InitF(arena, "%.2f%.*s", byte_size.bytes, DQN_STR_FMT(byte_size.suffix));
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_U64ByteSizeTypeString(Dqn_U64ByteSizeType type)
|
||||
{
|
||||
Dqn_Str8 result = DQN_STR8("");
|
||||
switch (type) {
|
||||
case Dqn_U64ByteSizeType_B: result = DQN_STR8("B"); break;
|
||||
case Dqn_U64ByteSizeType_KiB: result = DQN_STR8("KiB"); break;
|
||||
case Dqn_U64ByteSizeType_MiB: result = DQN_STR8("MiB"); break;
|
||||
case Dqn_U64ByteSizeType_GiB: result = DQN_STR8("GiB"); break;
|
||||
case Dqn_U64ByteSizeType_TiB: result = DQN_STR8("TiB"); break;
|
||||
case Dqn_U64ByteSizeType_Count: result = DQN_STR8(""); break;
|
||||
case Dqn_U64ByteSizeType_Auto: result = DQN_STR8(""); break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_U64ToAge(Dqn_Arena *arena, uint64_t age_s, Dqn_usize type)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = arena;
|
||||
uint64_t remainder = age_s;
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Year) {
|
||||
Dqn_usize unit = remainder / DQN_YEARS_TO_S(1);
|
||||
remainder -= DQN_YEARS_TO_S(unit);
|
||||
if (unit)
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64uyr", builder.string_size ? " " : "", unit);
|
||||
}
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Week) {
|
||||
Dqn_usize unit = remainder / DQN_WEEKS_TO_S(1);
|
||||
remainder -= DQN_WEEKS_TO_S(unit);
|
||||
if (unit)
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64uw", builder.string_size ? " " : "", unit);
|
||||
}
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Day) {
|
||||
Dqn_usize unit = remainder / DQN_DAYS_TO_S(1);
|
||||
remainder -= DQN_DAYS_TO_S(unit);
|
||||
if (unit)
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64ud", builder.string_size ? " " : "", unit);
|
||||
}
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Hr) {
|
||||
Dqn_usize unit = remainder / DQN_HOURS_TO_S(1);
|
||||
remainder -= DQN_HOURS_TO_S(unit);
|
||||
if (unit)
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64uh", builder.string_size ? " " : "", unit);
|
||||
}
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Min) {
|
||||
Dqn_usize unit = remainder / DQN_MINS_TO_S(1);
|
||||
remainder -= DQN_MINS_TO_S(unit);
|
||||
if (unit)
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64um", builder.string_size ? " " : "", unit);
|
||||
}
|
||||
|
||||
if (type & Dqn_U64AgeUnit_Sec) {
|
||||
Dqn_usize unit = remainder;
|
||||
Dqn_Str8Builder_AppendF(&builder, "%s%I64us", builder.string_size ? " " : "", unit);
|
||||
}
|
||||
|
||||
result = Dqn_Str8Builder_Build(&builder, arena);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$DLIB] Dqn_Library ///////////////////////////////////////////////////////////////////////
|
||||
Dqn_Library *g_dqn_library;
|
||||
|
||||
DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
@ -863,22 +972,26 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
g_dqn_library = &default_instance;
|
||||
}
|
||||
|
||||
// NOTE: Init check ===========================================================================
|
||||
|
||||
// NOTE: Init check ///////////////////////////////////////////////////////////////////////////
|
||||
Dqn_Library *result = g_dqn_library;
|
||||
Dqn_TicketMutex_Begin(&result->lib_mutex);
|
||||
DQN_DEFER { Dqn_TicketMutex_End(&result->lib_mutex); };
|
||||
DQN_DEFER {
|
||||
Dqn_TicketMutex_End(&result->lib_mutex);
|
||||
};
|
||||
|
||||
if (result->lib_init)
|
||||
return result;
|
||||
result->lib_init = true;
|
||||
|
||||
// NOTE: Query OS page size ====================================================================
|
||||
|
||||
// NOTE: Query OS info /////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
#if defined(DQN_OS_WIN32)
|
||||
SYSTEM_INFO system_info = {};
|
||||
GetSystemInfo(&system_info);
|
||||
result->os_page_size = system_info.dwPageSize;
|
||||
result->os_alloc_granularity = system_info.dwAllocationGranularity;
|
||||
|
||||
QueryPerformanceFrequency(&result->win32_qpc_frequency);
|
||||
#else
|
||||
// TODO(doyle): Get the proper page size from the OS.
|
||||
result->os_page_size = DQN_KILOBYTES(4);
|
||||
@ -886,60 +999,77 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE Initialise fields ======================================================================
|
||||
|
||||
// NOTE Initialise fields //////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
result->profiler = &result->profiler_default_instance;
|
||||
#endif
|
||||
|
||||
result->lib_init = true;
|
||||
Dqn_ArenaCatalog_Init(&result->arena_catalog, &result->arena);
|
||||
result->exe_dir = Dqn_OS_EXEDir(&result->arena);
|
||||
|
||||
// NOTE: Leak tracing ==========================================================================
|
||||
|
||||
#if defined(DQN_LEAK_TRACING) // NOTE: Initialise the allocation leak tracker
|
||||
{
|
||||
result->alloc_tracking_disabled = true; // TODO(doyle): @robust Does this need to be atomic?
|
||||
|
||||
Dqn_Str8 sample_backtrace = Dqn_Str8_InitCStr8(b_stacktrace_get_string());
|
||||
Dqn_Str8 clean_backtrace = Dqn_Debug_CleanStackTrace(sample_backtrace);
|
||||
result->stack_trace_offset_to_our_call_stack = DQN_CAST(uint16_t)(sample_backtrace.size - clean_backtrace.size);
|
||||
free(sample_backtrace.data);
|
||||
|
||||
result->alloc_table = Dqn_DSMap_Init<Dqn_AllocRecord>(4096);
|
||||
result->alloc_tracking_disabled = false;
|
||||
}
|
||||
// NOTE: BEGIN IMPORTANT ORDER OF STATEMENTS ///////////////////////////////////////////////////
|
||||
#if defined(DQN_LEAK_TRACKING)
|
||||
// NOTE: Setup the allocation table with allocation tracking turned off on
|
||||
// the arena we're using to initialise the table.
|
||||
result->alloc_table_arena.flags |= Dqn_ArenaFlag_NoAllocTrack;
|
||||
result->alloc_table = Dqn_DSMap_Init<Dqn_DebugAlloc>(&result->alloc_table_arena, 4096);
|
||||
#endif
|
||||
|
||||
// NOTE: Print out init features ===============================================================
|
||||
if (on_init == Dqn_LibraryOnInit_LogFeatures) {
|
||||
Dqn_Log_DebugF("Dqn Library initialised:\n");
|
||||
|
||||
// NOTE: %$$_I32u is a stb_sprintf format specifier, non-standard
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(6271) // Extra argument passed to 'Dqn_Print_StdLnF'.
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " OS Page Size/Alloc Granularity: %$$_I32u/%$$_I32u", result->os_page_size, result->os_alloc_granularity);
|
||||
DQN_MSVC_WARNING_POP
|
||||
|
||||
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
if (DQN_ASAN_POISON) {
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " ASAN manual poisoning%s", DQN_ASAN_VET_POISON ? " (+vet sanity checks)" : "");
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " ASAN poison guard size: %$$_I32u", DQN_ASAN_POISON_GUARD_SIZE);
|
||||
}
|
||||
#endif
|
||||
result->arena = Dqn_Arena_InitSize(0, 0, Dqn_ArenaFlag_AllocCanLeak);
|
||||
result->pool = Dqn_ChunkPool_Init(&result->arena, /*align*/ 0);
|
||||
Dqn_ArenaCatalog_Init(&result->arena_catalog, &result->pool);
|
||||
Dqn_ArenaCatalog_AddF(&result->arena_catalog, &result->arena, "Dqn Library");
|
||||
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " Allocation leak tracing");
|
||||
Dqn_ArenaCatalog_AddF(&result->arena_catalog, &result->alloc_table_arena, "Dqn Allocation Table");
|
||||
#endif
|
||||
|
||||
// NOTE: Initialise scratch arenas which allocate memory and will be
|
||||
// recorded to the now initialised allocation table. The initialisation
|
||||
// of scratch memory may request scratch memory itself in leak tracing mode.
|
||||
// This is supported as the scratch arenas defer allocation tracking until
|
||||
// initialisation is done.
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
// NOTE: END IMPORTANT ORDER OF STATEMENTS /////////////////////////////////////////////////////
|
||||
|
||||
result->exe_dir = Dqn_OS_EXEDir(&result->arena);
|
||||
|
||||
// NOTE: Print out init features ///////////////////////////////////////////////////////////////
|
||||
if (on_init == Dqn_LibraryOnInit_LogFeatures) {
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8("Dqn Library initialised:\n"));
|
||||
|
||||
Dqn_f64 page_size_kib = result->os_page_size / 1024.0;
|
||||
Dqn_f64 alloc_granularity_kib = result->os_alloc_granularity / 1024.0;
|
||||
Dqn_Str8Builder_AppendF(
|
||||
&builder, " OS Page Size/Alloc Granularity: %.1f/%.1fKiB\n", page_size_kib, alloc_granularity_kib);
|
||||
|
||||
#if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
if (DQN_ASAN_POISON) {
|
||||
Dqn_Str8Builder_AppendF(
|
||||
&builder, " ASAN manual poisoning%s\n", DQN_ASAN_VET_POISON ? " (+vet sanity checks)" : "");
|
||||
Dqn_Str8Builder_AppendF(&builder, " ASAN poison guard size: %u\n", DQN_ASAN_POISON_GUARD_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DQN_LEAK_TRACKING)
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" Allocation leak tracing\n"));
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Err, " TSC profiler available");
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" TSC profiler available\n"));
|
||||
#endif
|
||||
|
||||
#if defined(DQN_USE_STD_PRINTF)
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" Using stdio's printf functions\n"));
|
||||
#else
|
||||
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8(" Using stb_sprintf functions\n"));
|
||||
#endif
|
||||
|
||||
// TODO(doyle): Add stacktrace feature log
|
||||
|
||||
Dqn_Print_StdLnF(Dqn_PrintStd_Err, "");
|
||||
Dqn_Str8 info_log = Dqn_Str8Builder_Build(&builder, scratch.arena);
|
||||
Dqn_Log_DebugF("%.*s", DQN_STR_FMT(info_log));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -990,8 +1120,10 @@ DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_Str8 file_path)
|
||||
Dqn_DateHMSTimeStr now = Dqn_Date_HMSLocalTimeStrNow();
|
||||
fprintf(file,
|
||||
"Time=%.*s %.*s | Thread Context Arenas | Count=%d\n",
|
||||
now.date_size, now.date,
|
||||
now.hms_size, now.hms,
|
||||
now.date_size,
|
||||
now.date,
|
||||
now.hms_size,
|
||||
now.hms,
|
||||
g_dqn_library->thread_context_arena_stats_count);
|
||||
|
||||
// NOTE: Write the cumulative thread arena data
|
||||
@ -1028,9 +1160,19 @@ DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_Str8 file_path)
|
||||
#endif // #if defined(DQN_DEBUG_THREAD_CONTEXT)
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_Library_AllocArenaF(Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, char const *fmt, ...)
|
||||
{
|
||||
DQN_ASSERT(g_dqn_library->lib_init);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog;
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_AllocFV(catalog, reserve, commit, arena_flags, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
// NOTE: [$PROF] Dqn_Profiler ======================================================================
|
||||
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
|
||||
Dqn_ProfilerZoneScope::Dqn_ProfilerZoneScope(Dqn_Str8 name, uint16_t anchor_index)
|
||||
{
|
||||
zone = Dqn_Profiler_BeginZoneWithIndex(name, anchor_index);
|
||||
@ -1072,7 +1214,8 @@ void Dqn_Profiler_EndZone(Dqn_ProfilerZone zone)
|
||||
Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer buffer)
|
||||
{
|
||||
uint8_t offset = buffer == Dqn_ProfilerAnchorBuffer_Back ? 0 : 1;
|
||||
uint8_t anchor_buffer = (g_dqn_library->profiler->active_anchor_buffer + offset) % DQN_ARRAY_UCOUNT(g_dqn_library->profiler->anchors);
|
||||
uint8_t anchor_buffer =
|
||||
(g_dqn_library->profiler->active_anchor_buffer + offset) % DQN_ARRAY_UCOUNT(g_dqn_library->profiler->anchors);
|
||||
Dqn_ProfilerAnchor *result = g_dqn_library->profiler->anchors[anchor_buffer];
|
||||
return result;
|
||||
}
|
||||
@ -1081,7 +1224,9 @@ void Dqn_Profiler_SwapAnchorBuffer()
|
||||
{
|
||||
g_dqn_library->profiler->active_anchor_buffer++;
|
||||
Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back);
|
||||
DQN_MEMSET(anchors, 0, DQN_ARRAY_UCOUNT(g_dqn_library->profiler->anchors[0]) * sizeof(g_dqn_library->profiler->anchors[0][0]));
|
||||
DQN_MEMSET(anchors,
|
||||
0,
|
||||
DQN_ARRAY_UCOUNT(g_dqn_library->profiler->anchors[0]) * sizeof(g_dqn_library->profiler->anchors[0][0]));
|
||||
}
|
||||
|
||||
void Dqn_Profiler_Dump(uint64_t tsc_per_second)
|
||||
@ -1096,10 +1241,7 @@ void Dqn_Profiler_Dump(uint64_t tsc_per_second)
|
||||
uint64_t tsc_inclusive = anchor->tsc_inclusive;
|
||||
Dqn_f64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DQN_CAST(Dqn_f64) tsc_per_second;
|
||||
if (tsc_exclusive == tsc_inclusive) {
|
||||
Dqn_Print_LnF("%.*s[%u]: %.1fms",
|
||||
DQN_STR_FMT(anchor->name),
|
||||
anchor->hit_count,
|
||||
tsc_exclusive_milliseconds);
|
||||
Dqn_Print_LnF("%.*s[%u]: %.1fms", DQN_STR_FMT(anchor->name), anchor->hit_count, tsc_exclusive_milliseconds);
|
||||
} else {
|
||||
Dqn_f64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DQN_CAST(Dqn_f64) tsc_per_second;
|
||||
Dqn_Print_LnF("%.*s[%u]: %.1f/%.1fms",
|
||||
@ -1111,3 +1253,91 @@ void Dqn_Profiler_Dump(uint64_t tsc_per_second)
|
||||
}
|
||||
}
|
||||
#endif // !defined(DQN_NO_PROFILER)
|
||||
|
||||
// NOTE: [$JOBQ] Dqn_JobQueue ///////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_JobQueueSPMC Dqn_OS_JobQueueSPMCInit()
|
||||
{
|
||||
Dqn_JobQueueSPMC result = {};
|
||||
result.thread_wait_for_job_semaphore = Dqn_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
result.wait_for_completion_semaphore = Dqn_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
result.mutex = Dqn_OS_MutexInit();
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_JobQueueSPMCAddArray(Dqn_JobQueueSPMC *job_queue, Dqn_Job *jobs, uint32_t count)
|
||||
{
|
||||
if (!job_queue)
|
||||
return false;
|
||||
|
||||
uint32_t const pot_mask = DQN_ARRAY_UCOUNT(job_queue->jobs) - 1;
|
||||
uint32_t read_index = job_queue->read_index;
|
||||
uint32_t write_index = job_queue->write_index;
|
||||
uint32_t size = write_index - read_index;
|
||||
|
||||
if ((size + count) > DQN_ARRAY_UCOUNT(job_queue->jobs))
|
||||
return false;
|
||||
|
||||
for (size_t offset = 0; offset < count; offset++) {
|
||||
uint32_t wrapped_write_index = (write_index + offset) & pot_mask;
|
||||
job_queue->jobs[wrapped_write_index] = jobs[offset];
|
||||
}
|
||||
|
||||
Dqn_OS_MutexLock(&job_queue->mutex);
|
||||
job_queue->write_index += count;
|
||||
Dqn_OS_SemaphoreIncrement(&job_queue->thread_wait_for_job_semaphore, count);
|
||||
Dqn_OS_MutexUnlock(&job_queue->mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_JobQueueSPMCAdd(Dqn_JobQueueSPMC *job_queue, Dqn_Job job)
|
||||
{
|
||||
bool result = Dqn_OS_JobQueueSPMCAddArray(job_queue, &job, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API int32_t Dqn_OS_JobQueueSPMCThread(Dqn_OSThread *thread)
|
||||
{
|
||||
Dqn_JobQueueSPMC *job_queue = DQN_CAST(Dqn_JobQueueSPMC *) thread->user_context;
|
||||
uint32_t const pot_mask = DQN_ARRAY_UCOUNT(job_queue->jobs) - 1;
|
||||
|
||||
for (;;) {
|
||||
Dqn_OS_SemaphoreWait(&job_queue->thread_wait_for_job_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
if (job_queue->quit)
|
||||
break;
|
||||
|
||||
DQN_ASSERT(job_queue->read_index != job_queue->write_index);
|
||||
|
||||
Dqn_OS_MutexLock(&job_queue->mutex);
|
||||
uint32_t wrapped_read_index = job_queue->read_index & pot_mask;
|
||||
Dqn_Job job = job_queue->jobs[wrapped_read_index];
|
||||
job_queue->read_index += 1;
|
||||
Dqn_OS_MutexUnlock(&job_queue->mutex);
|
||||
|
||||
job.func(job.user_context);
|
||||
Dqn_Arena_Deinit(job.arena);
|
||||
|
||||
Dqn_OS_MutexLock(&job_queue->mutex);
|
||||
job_queue->complete_index += 1;
|
||||
if (job_queue->complete_index == job_queue->write_index && job_queue->threads_waiting_for_completion) {
|
||||
Dqn_OS_SemaphoreIncrement(&job_queue->wait_for_completion_semaphore,
|
||||
job_queue->threads_waiting_for_completion);
|
||||
job_queue->threads_waiting_for_completion = 0;
|
||||
}
|
||||
Dqn_OS_MutexUnlock(&job_queue->mutex);
|
||||
}
|
||||
|
||||
return job_queue->quit_exit_code;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_JobQueueSPMCWaitForCompletion(Dqn_JobQueueSPMC *job_queue)
|
||||
{
|
||||
Dqn_OS_MutexLock(&job_queue->mutex);
|
||||
if (job_queue->read_index == job_queue->write_index) {
|
||||
Dqn_OS_MutexUnlock(&job_queue->mutex);
|
||||
return;
|
||||
}
|
||||
job_queue->threads_waiting_for_completion++;
|
||||
Dqn_OS_MutexUnlock(&job_queue->mutex);
|
||||
|
||||
Dqn_OS_SemaphoreWait(&job_queue->wait_for_completion_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
}
|
||||
|
863
dqn_helpers.h
863
dqn_helpers.h
@ -1,60 +1,35 @@
|
||||
// NOTE: [$PCGX] Dqn_PCG32 =========================================================================
|
||||
// Random number generator of the PCG family. Implementation taken from Martins
|
||||
// Mmozeiko from Handmade Network.
|
||||
// https://gist.github.com/mmozeiko/1561361cd4105749f80bb0b9223e9db8
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_PCG32_Range
|
||||
// @desc Returns a value in the [low; high) interval
|
||||
|
||||
// @proc Dqn_PCG32_NextF
|
||||
// @desc Returns a float & double in [0; 1) interval;
|
||||
// $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
// $$ | $$ |$$ _____|$$ | $$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ |$$ / \__|
|
||||
// $$$$$$$$ |$$$$$\ $$ | $$$$$$$ |$$$$$\ $$$$$$$ |\$$$$$$\
|
||||
// $$ __$$ |$$ __| $$ | $$ ____/ $$ __| $$ __$$< \____$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |$$\ $$ |
|
||||
// $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$ | $$$$$$$$\ $$ | $$ |\$$$$$$ |
|
||||
// \__| \__|\________|\________|\__| \________|\__| \__| \______/
|
||||
//
|
||||
// dqn_helpers.h -- Helper functions/data structures
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$PCG3] Dqn_PCG32 -- -- RNG from the PCG family
|
||||
// [$JSON] Dqn_JSONBuilder -- DQN_JSON_BUILDER -- Construct json output
|
||||
// [$BHEX] Dqn_Bin -- DQN_BIN -- Binary <-> hex helpers
|
||||
// [$BSEA] Dqn_BinarySearch -- -- Binary search
|
||||
// [$BITS] Dqn_Bit -- -- Bitset manipulation
|
||||
// [$SAFE] Dqn_Safe -- -- Safe arithmetic, casts, asserts
|
||||
// [$MISC] Misc -- -- Uncategorised helper functions
|
||||
// [$DLIB] Dqn_Library -- -- Globally shared runtime data for this library
|
||||
// [$PROF] Dqn_Profiler -- DQN_PROFILER -- Profiler that measures using a timestamp counter
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$PCGX] Dqn_PCG32 /////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_PCG32 { uint64_t state; };
|
||||
|
||||
DQN_API uint32_t Dqn_PCG32_Next (Dqn_PCG32 *rng);
|
||||
DQN_API uint64_t Dqn_PCG32_Next64 (Dqn_PCG32 *rng);
|
||||
DQN_API uint32_t Dqn_PCG32_Range (Dqn_PCG32 *rng, uint32_t low, uint32_t high);
|
||||
DQN_API Dqn_f32 Dqn_PCG32_NextF32(Dqn_PCG32 *rng);
|
||||
DQN_API Dqn_f64 Dqn_PCG32_NextF64(Dqn_PCG32 *rng);
|
||||
DQN_API void Dqn_PCG32_Seed (Dqn_PCG32 *rng, uint64_t seed);
|
||||
DQN_API void Dqn_PCG32_Advance(Dqn_PCG32 *rng, uint64_t delta);
|
||||
|
||||
#if !defined(DQN_NO_JSON_BUILDER)
|
||||
// NOTE: [$JSON] Dqn_JSONBuilder ===================================================================
|
||||
// Basic helper class to construct JSON output to a string
|
||||
// TODO(dqn): We need to write tests for this
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_JSONBuilder_Build
|
||||
// @desc Convert the internal JSON buffer in the builder into a string.
|
||||
// @param[in] arena The allocator to use to build the string
|
||||
|
||||
// @proc Dqn_JSONBuilder_KeyValue, Dqn_JSONBuilder_KeyValueF
|
||||
// @desc Add a JSON key value pair untyped. The value is emitted directly
|
||||
// without checking the contents of value.
|
||||
//
|
||||
// All other functions internally call into this function which is the main
|
||||
// workhorse of the builder.
|
||||
|
||||
// @proc Dqn_JSON_Builder_ObjectEnd
|
||||
// @desc End a JSON object in the builder, generates internally a '}' string
|
||||
|
||||
// @proc Dqn_JSON_Builder_ArrayEnd
|
||||
// @desc End a JSON array in the builder, generates internally a ']' string
|
||||
|
||||
// @proc Dqn_JSONBuilder_LiteralNamed
|
||||
// @desc Add a named JSON key-value object whose value is directly written to
|
||||
// the following '"<key>": <value>' (e.g. useful for emitting the 'null'
|
||||
// value)
|
||||
|
||||
// @proc Dqn_JSONBuilder_U64Named, Dqn_JSONBuilder_U64,
|
||||
// Dqn_JSONBuilder_I64Named, Dqn_JSONBuilder_I64,
|
||||
// Dqn_JSONBuilder_F64Named, Dqn_JSONBuilder_F64,
|
||||
// Dqn_JSONBuilder_BoolNamed, Dqn_JSONBuilder_Bool,
|
||||
// @desc Add the named JSON data type as a key-value object. Generates
|
||||
// internally the string '"<key>": <value>'
|
||||
|
||||
// NOTE: [$JSON] Dqn_JSONBuilder ///////////////////////////////////////////////////////////////////
|
||||
enum Dqn_JSONBuilderItem
|
||||
{
|
||||
Dqn_JSONBuilderItem_Empty,
|
||||
@ -71,138 +46,10 @@ struct Dqn_JSONBuilder
|
||||
int spaces_per_indent; // The number of spaces per indent level
|
||||
Dqn_JSONBuilderItem last_item;
|
||||
};
|
||||
|
||||
#define Dqn_JSONBuilder_Object(builder) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBegin(builder), \
|
||||
Dqn_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_ObjectNamed(builder, name) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBeginNamed(builder, name), \
|
||||
Dqn_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_Array(builder) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBegin(builder), \
|
||||
Dqn_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_ArrayNamed(builder, name) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBeginNamed(builder, name), \
|
||||
Dqn_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
|
||||
DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init (Dqn_Allocator allocator, int spaces_per_indent);
|
||||
DQN_API Dqn_Str8 Dqn_JSONBuilder_Build (Dqn_JSONBuilder const *builder, Dqn_Allocator allocator);
|
||||
DQN_API void Dqn_JSONBuilder_KeyValue (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
|
||||
DQN_API void Dqn_JSONBuilder_KeyValueF (Dqn_JSONBuilder *builder, Dqn_Str8 key, char const *value_fmt, ...);
|
||||
DQN_API void Dqn_JSONBuilder_ObjectBeginNamed(Dqn_JSONBuilder *builder, Dqn_Str8 name);
|
||||
DQN_API void Dqn_JSONBuilder_ObjectEnd (Dqn_JSONBuilder *builder);
|
||||
DQN_API void Dqn_JSONBuilder_ArrayBeginNamed (Dqn_JSONBuilder *builder, Dqn_Str8 name);
|
||||
DQN_API void Dqn_JSONBuilder_ArrayEnd (Dqn_JSONBuilder *builder);
|
||||
DQN_API void Dqn_JSONBuilder_Str8Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
|
||||
DQN_API void Dqn_JSONBuilder_LiteralNamed (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
|
||||
DQN_API void Dqn_JSONBuilder_U64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, uint64_t value);
|
||||
DQN_API void Dqn_JSONBuilder_I64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, int64_t value);
|
||||
DQN_API void Dqn_JSONBuilder_F64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, double value, int decimal_places);
|
||||
DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBuilder *builder, Dqn_Str8 key, bool value);
|
||||
|
||||
#define Dqn_JSONBuilder_ObjectBegin(builder) Dqn_JSONBuilder_ObjectBeginNamed(builder, DQN_STRING8(""))
|
||||
#define Dqn_JSONBuilder_ArrayBegin(builder) Dqn_JSONBuilder_ArrayBeginNamed(builder, DQN_STRING8(""))
|
||||
#define Dqn_JSONBuilder_Str8(builder, value) Dqn_JSONBuilder_Str8Named(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_Literal(builder, value) Dqn_JSONBuilder_LiteralNamed(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_U64(builder, value) Dqn_JSONBuilder_U64Named(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_I64(builder, value) Dqn_JSONBuilder_I64Named(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_F64(builder, value) Dqn_JSONBuilder_F64Named(builder, DQN_STRING8(""), value)
|
||||
#define Dqn_JSONBuilder_Bool(builder, value) Dqn_JSONBuilder_BoolNamed(builder, DQN_STRING8(""), value)
|
||||
#endif // !defined(DQN_NO_JSON_BUIDLER)
|
||||
|
||||
#if !defined(DQN_NO_BIN)
|
||||
// NOTE: [$BHEX] Dqn_Bin ===========================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Bin_U64ToHexU64Str8
|
||||
// @desc Convert a 64 bit number to a hex string
|
||||
// @param[in] number Number to convert to hexadecimal representation
|
||||
// @param[in] flags Bit flags from Dqn_BinHexU64Str8Flags to customise the
|
||||
// output of the hexadecimal string.
|
||||
// @return The hexadecimal representation of the number. This string is always
|
||||
// null-terminated.
|
||||
|
||||
// @proc Dqn_Bin_U64ToHex
|
||||
// @copybrief Dqn_Bin_U64ToHexU64Str8
|
||||
|
||||
// @param[in] allocator The memory allocator to use for the memory of the
|
||||
// hexadecimal string.
|
||||
// @copyparams Dqn_Bin_U64ToHexU64Str8
|
||||
|
||||
// @proc Dqn_Bin_HexBufferToU64
|
||||
// @desc Convert a hexadecimal string a 64 bit value.
|
||||
// Asserts if the hex string is too big to be converted into a 64 bit number.
|
||||
|
||||
// @proc Dqn_Bin_HexToU64
|
||||
// @copydoc Dqn_Bin_HexToU64
|
||||
|
||||
// @proc Dqn_Bin_BytesToHexBuffer
|
||||
// @desc Convert a binary buffer into its hex representation into dest.
|
||||
//
|
||||
// The dest buffer must be large enough to contain the hex representation, i.e.
|
||||
// atleast (src_size * 2).
|
||||
//
|
||||
// @return True if the conversion into the dest buffer was successful, false
|
||||
// otherwise (e.g. invalid arguments).
|
||||
|
||||
// @proc Dqn_Bin_BytesToHexBufferArena
|
||||
// @desc Convert a series of bytes into a string
|
||||
// @return A null-terminated hex string, null pointer if allocation failed
|
||||
|
||||
// @proc Dqn_Bin_BytesToHexArena
|
||||
// @copydoc Dqn_Bin_BytesToHexBufferArena
|
||||
// @return A hex string, the string is invalid if conversion failed.
|
||||
|
||||
// @proc Dqn_Bin_HexBufferToBytes
|
||||
// @desc Convert a hex string into binary at `dest`.
|
||||
//
|
||||
// The dest buffer must be large enough to contain the binary representation,
|
||||
// i.e. atleast ceil(hex_size / 2). This function will strip whitespace,
|
||||
// leading 0x/0X prefix from the string before conversion.
|
||||
//
|
||||
// @param[in] hex The hexadecimal string to convert
|
||||
// @param[in] hex_size Size of the hex buffer. This function can handle an odd
|
||||
// size hex string i.e. "fff" produces 0xfff0.
|
||||
// @param[out] dest Buffer to write the bytes to
|
||||
// @param[out] dest_size Maximum number of bytes to write to dest
|
||||
//
|
||||
// @return The number of bytes written to `dest_size`, this value will *never*
|
||||
// be greater than `dest_size`.
|
||||
|
||||
// @proc Dqn_Bin_HexToBytes
|
||||
// @desc Str8 variant of @see Dqn_Bin_HexBufferToBytes
|
||||
|
||||
// @proc Dqn_Bin_Str8HexBufferToBytesUnchecked
|
||||
// @desc Unchecked variant of @see Dqn_Bin_HexBufferToBytes
|
||||
//
|
||||
// This function skips some string checks, it assumes the hex is a valid hex
|
||||
// stream and that the arguments are valid e.g. no trimming or 0x prefix
|
||||
// stripping is performed
|
||||
|
||||
// @proc Dqn_Bin_Str8
|
||||
// @desc Str8 variant of @see Dqn_Bin_HexBufferToBytesUnchecked
|
||||
|
||||
// @proc Dqn_Bin_HexBufferToBytesArena
|
||||
// Dynamic allocating variant of @see Dqn_Bin_HexBufferToBytesUnchecked
|
||||
//
|
||||
// @param[in] arena The arena to allocate the bytes from
|
||||
// @param[in] hex Hex string to convert into bytes
|
||||
// @param[in] size Size of the hex string
|
||||
// @param[out] real_size The size of the buffer returned by the function
|
||||
//
|
||||
// @return The byte representation of the hex string.
|
||||
|
||||
// @proc Dqn_Bin_HexToBytesArena
|
||||
// @copybrief Dqn_Bin_HexBufferToBytesArena
|
||||
//
|
||||
// @param[in] arena The arena to allocate the bytes from
|
||||
// @param[in] hex Hex string to convert into bytes
|
||||
//
|
||||
// @return The byte representation of the hex string.
|
||||
|
||||
// NOTE: [$BHEX] Dqn_Bin ///////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_BinHexU64Str8
|
||||
{
|
||||
char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/];
|
||||
@ -214,37 +61,15 @@ enum Dqn_BinHexU64Str8Flags
|
||||
Dqn_BinHexU64Str8Flags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string
|
||||
Dqn_BinHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
|
||||
};
|
||||
|
||||
DQN_API char const * Dqn_Bin_HexBufferTrim0x (char const *hex, Dqn_usize size, Dqn_usize *real_size);
|
||||
DQN_API Dqn_Str8 Dqn_Bin_HexTrim0x (Dqn_Str8 string);
|
||||
|
||||
DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8 (uint64_t number, uint32_t flags);
|
||||
DQN_API Dqn_Str8 Dqn_Bin_U64ToHex (Dqn_Allocator allocator, uint64_t number, uint32_t flags);
|
||||
|
||||
DQN_API uint64_t Dqn_Bin_HexBufferToU64 (char const *hex, Dqn_usize size);
|
||||
DQN_API uint64_t Dqn_Bin_HexToU64 (Dqn_Str8 hex);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Bin_BytesToHexArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
DQN_API char * Dqn_Bin_BytesToHexBufferArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
DQN_API bool Dqn_Bin_BytesToHexBuffer (void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size);
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked(char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
|
||||
DQN_API char * Dqn_Bin_HexBufferToBytesArena (Dqn_Arena *arena, char const *hex, Dqn_usize hex_size, Dqn_usize *real_size);
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytes (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_Str8 Dqn_Bin_HexToBytesArena (Dqn_Arena *arena, Dqn_Str8 hex);
|
||||
#endif // !defined(DQN_NO_BIN)
|
||||
|
||||
// NOTE: [$BSEA] Dqn_BinarySearch ==================================================================
|
||||
// NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
|
||||
|
||||
template <typename T>
|
||||
bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||
|
||||
// TODO(doyle): Implement lower/upper bound searching
|
||||
enum Dqn_BinarySearchType
|
||||
{
|
||||
// Index of the match. If no match is found, found is set to false and the
|
||||
@ -253,14 +78,26 @@ enum Dqn_BinarySearchType
|
||||
// [last index + 1] of the array).
|
||||
Dqn_BinarySearchType_Match,
|
||||
|
||||
// Index of the first element in the array that is `<= find`. If no such
|
||||
// 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};
|
||||
// Dqn_BinarySearchResult result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4, Dqn_BinarySearchType_LowerBound);
|
||||
// printf("%zu\n", result.index); // Prints index '4'
|
||||
|
||||
Dqn_BinarySearchType_LowerBound,
|
||||
|
||||
// Index of the first element in the array that is `> find`. If no such
|
||||
// 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};
|
||||
// Dqn_BinarySearchResult result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4, Dqn_BinarySearchType_UpperBound);
|
||||
// printf("%zu\n", result.index); // Prints index '5'
|
||||
|
||||
Dqn_BinarySearchType_UpperBound,
|
||||
};
|
||||
|
||||
@ -270,287 +107,45 @@ struct Dqn_BinarySearchResult
|
||||
Dqn_usize index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Dqn_BinarySearchResult Dqn_BinarySearch(T const *array,
|
||||
Dqn_usize array_size,
|
||||
T const &find,
|
||||
Dqn_BinarySearchType type = Dqn_BinarySearchType_Match,
|
||||
Dqn_BinarySearchLessThanProc<T> less_than = Dqn_BinarySearch_DefaultLessThan);
|
||||
|
||||
template <typename T>
|
||||
bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
|
||||
{
|
||||
bool result = lhs < rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Dqn_BinarySearchResult
|
||||
Dqn_BinarySearch(T const *array,
|
||||
Dqn_usize array_size,
|
||||
T const &find,
|
||||
Dqn_BinarySearchType type,
|
||||
Dqn_BinarySearchLessThanProc<T> less_than)
|
||||
{
|
||||
Dqn_BinarySearchResult result = {};
|
||||
if (!array || array_size <= 0)
|
||||
return result;
|
||||
|
||||
T const *end = array + array_size;
|
||||
T const *first = array;
|
||||
T const *last = end;
|
||||
while (first != last) {
|
||||
Dqn_usize count = last - first;
|
||||
T const *it = first + (count / 2);
|
||||
|
||||
bool advance_first = false;
|
||||
if (type == Dqn_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 Dqn_BinarySearchType_Match: {
|
||||
result.found = first != end && !less_than(find, *first);
|
||||
} break;
|
||||
|
||||
case Dqn_BinarySearchType_LowerBound: /*FALLTHRU*/
|
||||
case Dqn_BinarySearchType_UpperBound: {
|
||||
result.found = first != end;
|
||||
} break;
|
||||
}
|
||||
|
||||
result.index = first - array;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$BITS] Dqn_Bit ===========================================================================
|
||||
DQN_API void Dqn_Bit_UnsetInplace(uint32_t *flags, uint32_t bitfield);
|
||||
DQN_API void Dqn_Bit_SetInplace (uint32_t *flags, uint32_t bitfield);
|
||||
DQN_API bool Dqn_Bit_IsSet (uint32_t bits, uint32_t bits_to_set);
|
||||
DQN_API bool Dqn_Bit_IsNotSet (uint32_t bits, uint32_t bits_to_check);
|
||||
|
||||
// NOTE: [$SAFE] Dqn_Safe ==========================================================================
|
||||
// @proc Dqn_Safe_AddI64, Dqn_Safe_MulI64
|
||||
// @desc Add/multiply 2 I64's together, safe asserting that the operation does
|
||||
// not overflow.
|
||||
// @return The result of operation, INT64_MAX if it overflowed.
|
||||
|
||||
// @proc Dqn_Safe_AddU64, Dqn_Safe_MulU64
|
||||
// @desc Add/multiply 2 U64's together, safe asserting that the operation does
|
||||
// not overflow.
|
||||
// @return The result of operation, UINT64_MAX if it overflowed.
|
||||
|
||||
// @proc Dqn_Safe_SubU64, Dqn_Safe_SubU32
|
||||
// @desc Subtract 2xU64 or 2xU32's together, safe asserting that the operation
|
||||
// does not overflow.
|
||||
// @return The result of operation, 0 if it overflowed.
|
||||
|
||||
// @proc Dqn_Safe_SaturateCastUSizeToInt,
|
||||
// Dqn_Safe_SaturateCastUSizeToI8,
|
||||
// Dqn_Safe_SaturateCastUSizeToI16,
|
||||
// Dqn_Safe_SaturateCastUSizeToI32,
|
||||
// Dqn_Safe_SaturateCastUSizeToI64
|
||||
//
|
||||
// Dqn_Safe_SaturateCastU64ToUInt,
|
||||
// Dqn_Safe_SaturateCastU64ToU8,
|
||||
// Dqn_Safe_SaturateCastU64ToU16,
|
||||
// Dqn_Safe_SaturateCastU64ToU32,
|
||||
//
|
||||
// Dqn_Safe_SaturateCastUSizeToU8,
|
||||
// Dqn_Safe_SaturateCastUSizeToU16,
|
||||
// Dqn_Safe_SaturateCastUSizeToU32,
|
||||
// Dqn_Safe_SaturateCastUSizeToU64
|
||||
//
|
||||
// Dqn_Safe_SaturateCastISizeToInt,
|
||||
// Dqn_Safe_SaturateCastISizeToI8,
|
||||
// Dqn_Safe_SaturateCastISizeToI16,
|
||||
// Dqn_Safe_SaturateCastISizeToI32,
|
||||
// Dqn_Safe_SaturateCastISizeToI64,
|
||||
//
|
||||
// int Dqn_Safe_SaturateCastISizeToUInt,
|
||||
// Dqn_Safe_SaturateCastISizeToU8,
|
||||
// Dqn_Safe_SaturateCastISizeToU16,
|
||||
// Dqn_Safe_SaturateCastISizeToU32,
|
||||
// Dqn_Safe_SaturateCastISizeToU64,
|
||||
//
|
||||
// Dqn_Safe_SaturateCastI64ToISize,
|
||||
// Dqn_Safe_SaturateCastI64ToI8,
|
||||
// Dqn_Safe_SaturateCastI64ToI16,
|
||||
// Dqn_Safe_SaturateCastI64ToI32,
|
||||
//
|
||||
// Dqn_Safe_SaturateCastIntToI8,
|
||||
// Dqn_Safe_SaturateCastIntToU8,
|
||||
// Dqn_Safe_SaturateCastIntToU16,
|
||||
// Dqn_Safe_SaturateCastIntToU32,
|
||||
// Dqn_Safe_SaturateCastIntToU64,
|
||||
//
|
||||
// @desc Truncate the lhs operand to the right clamping the result to the max
|
||||
// value of the desired data type. Safe asserts if clamping occurs.
|
||||
//
|
||||
// The following sentinel values are returned when saturated,
|
||||
// USize -> Int: INT_MAX
|
||||
// USize -> I8: INT8_MAX
|
||||
// USize -> I16: INT16_MAX
|
||||
// USize -> I32: INT32_MAX
|
||||
// USize -> I64: INT64_MAX
|
||||
//
|
||||
// U64 -> UInt: UINT_MAX
|
||||
// U64 -> U8: UINT8_MAX
|
||||
// U64 -> U16: UINT16_MAX
|
||||
// U64 -> U32: UINT32_MAX
|
||||
//
|
||||
// USize -> U8: UINT8_MAX
|
||||
// USize -> U16: UINT16_MAX
|
||||
// USize -> U32: UINT32_MAX
|
||||
// USize -> U64: UINT64_MAX
|
||||
//
|
||||
// ISize -> Int: INT_MIN or INT_MAX
|
||||
// ISize -> I8: INT8_MIN or INT8_MAX
|
||||
// ISize -> I16: INT16_MIN or INT16_MAX
|
||||
// ISize -> I32: INT32_MIN or INT32_MAX
|
||||
// ISize -> I64: INT64_MIN or INT64_MAX
|
||||
//
|
||||
// ISize -> UInt: 0 or UINT_MAX
|
||||
// ISize -> U8: 0 or UINT8_MAX
|
||||
// ISize -> U16: 0 or UINT16_MAX
|
||||
// ISize -> U32: 0 or UINT32_MAX
|
||||
// ISize -> U64: 0 or UINT64_MAX
|
||||
//
|
||||
// I64 -> ISize: DQN_ISIZE_MIN or DQN_ISIZE_MAX
|
||||
// I64 -> I8: INT8_MIN or INT8_MAX
|
||||
// I64 -> I16: INT16_MIN or INT16_MAX
|
||||
// I64 -> I32: INT32_MIN or INT32_MAX
|
||||
//
|
||||
// Int -> I8: INT8_MIN or INT8_MAX
|
||||
// Int -> I16: INT16_MIN or INT16_MAX
|
||||
// Int -> U8: 0 or UINT8_MAX
|
||||
// Int -> U16: 0 or UINT16_MAX
|
||||
// Int -> U32: 0 or UINT32_MAX
|
||||
// Int -> U64: 0 or UINT64_MAX
|
||||
|
||||
DQN_API int64_t Dqn_Safe_AddI64 (int64_t a, int64_t b);
|
||||
DQN_API int64_t Dqn_Safe_MulI64 (int64_t a, int64_t b);
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_AddU64 (uint64_t a, uint64_t b);
|
||||
DQN_API uint64_t Dqn_Safe_MulU64 (uint64_t a, uint64_t b);
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SubU64 (uint64_t a, uint64_t b);
|
||||
DQN_API uint32_t Dqn_Safe_SubU32 (uint32_t a, uint32_t b);
|
||||
|
||||
DQN_API int Dqn_Safe_SaturateCastUSizeToInt (Dqn_usize val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8 (Dqn_usize val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16 (Dqn_usize val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32 (Dqn_usize val);
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64 (Dqn_usize val);
|
||||
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt (uint64_t val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8 (uint64_t val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16 (uint64_t val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32 (uint64_t val);
|
||||
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8 (Dqn_usize val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16 (Dqn_usize val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32 (Dqn_usize val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64 (Dqn_usize val);
|
||||
|
||||
DQN_API int Dqn_Safe_SaturateCastISizeToInt (Dqn_isize val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8 (Dqn_isize val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16 (Dqn_isize val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32 (Dqn_isize val);
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64 (Dqn_isize val);
|
||||
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt(Dqn_isize val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8 (Dqn_isize val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16 (Dqn_isize val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32 (Dqn_isize val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64 (Dqn_isize val);
|
||||
|
||||
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize (int64_t val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8 (int64_t val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16 (int64_t val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32 (int64_t val);
|
||||
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8 (int val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16 (int val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8 (int val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16 (int val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32 (int val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64 (int val);
|
||||
|
||||
// NOTE: [$MISC] Misc ==============================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_SNPrintFDotTruncate
|
||||
// @desc Write the format string to the buffer truncating with a trailing ".."
|
||||
// if there is insufficient space in the buffer followed by null-terminating
|
||||
// the buffer (uses stb_sprintf underneath).
|
||||
// @return The size of the string written to the buffer *not* including the
|
||||
// null-terminator.
|
||||
//
|
||||
// @proc Dqn_U64ToStr8
|
||||
// @desc Convert a 64 bit unsigned value to its string representation.
|
||||
// @param[in] val Value to convert into a string
|
||||
// @param[in] separator The separator to insert every 3 digits. Set this to
|
||||
// 0 if no separator is desired.
|
||||
|
||||
// NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_U64Str8
|
||||
{
|
||||
char data[27+1]; // NOTE(dqn): 27 is the maximum size of uint64_t including a separtor
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_U64Str8 Dqn_U64ToStr8 (uint64_t val, char separator);
|
||||
enum Dqn_U64ByteSizeType
|
||||
{
|
||||
Dqn_U64ByteSizeType_B,
|
||||
Dqn_U64ByteSizeType_KiB,
|
||||
Dqn_U64ByteSizeType_MiB,
|
||||
Dqn_U64ByteSizeType_GiB,
|
||||
Dqn_U64ByteSizeType_TiB,
|
||||
Dqn_U64ByteSizeType_Count,
|
||||
Dqn_U64ByteSizeType_Auto,
|
||||
};
|
||||
|
||||
struct Dqn_U64ByteSize
|
||||
{
|
||||
Dqn_U64ByteSizeType type;
|
||||
Dqn_Str8 suffix; // "KiB", "MiB", "GiB" .. e.t.c
|
||||
Dqn_f64 bytes;
|
||||
};
|
||||
|
||||
enum Dqn_U64AgeUnit
|
||||
{
|
||||
Dqn_U64AgeUnit_Sec = 1 << 0,
|
||||
Dqn_U64AgeUnit_Min = 1 << 1,
|
||||
Dqn_U64AgeUnit_Hr = 1 << 2,
|
||||
Dqn_U64AgeUnit_Day = 1 << 3,
|
||||
Dqn_U64AgeUnit_Week = 1 << 4,
|
||||
Dqn_U64AgeUnit_Year = 1 << 5,
|
||||
Dqn_U64AgeUnit_HMS = Dqn_U64AgeUnit_Sec | Dqn_U64AgeUnit_Min | Dqn_U64AgeUnit_Hr,
|
||||
Dqn_U64AgeUnit_All = Dqn_U64AgeUnit_HMS | Dqn_U64AgeUnit_Day | Dqn_U64AgeUnit_Week | Dqn_U64AgeUnit_Year,
|
||||
};
|
||||
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
// NOTE: [$PROF] Dqn_Profiler ======================================================================
|
||||
// A profiler based off Casey Muratori's Computer Enhance course, Performance
|
||||
// Aware Programming. This profiler measures function elapsed time using the
|
||||
// CPU's time stamp counter (e.g. rdtsc) providing a rough cycle count
|
||||
// that can be converted into a duration.
|
||||
//
|
||||
// This profiler uses a double buffer scheme for storing profiling markers.
|
||||
// After an application's typical update/frame cycle you can swap the profiler's
|
||||
// buffer whereby the front buffer contains the previous frames profiling
|
||||
// metrics and the back buffer will be populated with the new frame's profiling
|
||||
// metrics.
|
||||
#if 0
|
||||
uint64_t tsc_per_seconds = Dqn_EstimateTSCPerSecond(/*duration_ms_to_gauge_tsc_frequency*/ 100);
|
||||
for (;;) { // Main update loop
|
||||
enum Zone { Zone_MainLoop, Zone_Count };
|
||||
Dqn_ProfilerZone profiler_zone_main_update = Dqn_Profiler_BeginZone(Zone_MainLoop);
|
||||
Dqn_ProfilerAnchor *anchors = Dqn_ProfilerAnchorBuffer(Dqn_ProfilerAnchorBuffer_Front);
|
||||
for (size_t index = 0; index < Zone_Count; index++) {
|
||||
Dqn_ProfilerAnchor *anchor = anchors + index;
|
||||
printf("%.*s[%u] %zu cycles (%.1fms)\n",
|
||||
DQN_STRING_FMT(anchor->anchor_index),
|
||||
anchor->hit_count,
|
||||
anchor->tsc_inclusive,
|
||||
anchor->tsc_inclusive * tsc_per_seconds * 1000.f);
|
||||
}
|
||||
Dqn_Profiler_EndZone(&profiler_zone_main_update);
|
||||
Dqn_Profiler_SwapAnchorBuffer(Zone_Count); // Should occur after all profiling zones are ended!
|
||||
}
|
||||
#endif
|
||||
|
||||
// @proc Dqn_Profiler_AnchorBuffer
|
||||
// Retrieve the requested buffer from the profiler for writing/reading
|
||||
// profiling metrics.
|
||||
//
|
||||
// @param buffer Enum that lets you specify which buffer to grab from the
|
||||
// profiler. The front buffer contains the previous frame's profiling metrics
|
||||
// and the back buffer is where the profiler is current writing to.
|
||||
//
|
||||
// For end user intents and purposes, you likely only need to read the front
|
||||
// buffer which contain the metrics that you can visualise regarding the most
|
||||
// profiling metrics recorded.
|
||||
|
||||
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_PROFILER_ANCHOR_BUFFER_SIZE)
|
||||
#define DQN_PROFILER_ANCHOR_BUFFER_SIZE 128
|
||||
#endif
|
||||
@ -601,81 +196,68 @@ struct Dqn_Profiler
|
||||
uint8_t active_anchor_buffer;
|
||||
uint16_t parent_zone;
|
||||
};
|
||||
|
||||
// NOTE: Macros ====================================================================================
|
||||
#define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneWithIndex(DQN_STRING8(name), __COUNTER__ + 1)
|
||||
|
||||
// NOTE: API =======================================================================================
|
||||
Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex(Dqn_Str8 name, uint16_t anchor_index);
|
||||
void Dqn_Profiler_EndZone (Dqn_ProfilerZone zone);
|
||||
Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer (Dqn_ProfilerAnchorBuffer buffer);
|
||||
void Dqn_Profiler_SwapAnchorBuffer ();
|
||||
void Dqn_Profiler_Dump (uint64_t tsc_per_second);
|
||||
#endif // !defined(DQN_NO_PROFILER)
|
||||
|
||||
// NOTE: [$DLIB] Dqn_Library =======================================================================
|
||||
// NOTE: [$JOBQ] Dqn_JobQueue ///////////////////////////////////////////////////////////////////////
|
||||
typedef void (Dqn_JobQueueFunc)(void *user_context);
|
||||
struct Dqn_Job
|
||||
{
|
||||
Dqn_Arena *arena;
|
||||
Dqn_JobQueueFunc *func;
|
||||
void *user_context;
|
||||
};
|
||||
|
||||
struct Dqn_JobQueueSPMC
|
||||
{
|
||||
Dqn_OSMutex mutex;
|
||||
Dqn_OSSemaphore thread_wait_for_job_semaphore;
|
||||
Dqn_OSSemaphore wait_for_completion_semaphore;
|
||||
uint32_t threads_waiting_for_completion;
|
||||
|
||||
Dqn_Job jobs[32];
|
||||
Dqn_b32 quit;
|
||||
uint32_t quit_exit_code;
|
||||
uint32_t volatile read_index;
|
||||
uint32_t volatile complete_index;
|
||||
uint32_t volatile write_index;
|
||||
};
|
||||
|
||||
// NOTE: [$DLIB] Dqn_Library ///////////////////////////////////////////////////////////////////////
|
||||
// Book-keeping data for the library and allow customisation of certain features
|
||||
// provided.
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Library_SetLogCallback
|
||||
// @desc Update the default logging function, all logging functions will run through
|
||||
// this callback
|
||||
// @param[in] proc The new logging function, set to nullptr to revert back to
|
||||
// the default logger.
|
||||
// @param[in] user_data A user defined parameter to pass to the callback
|
||||
|
||||
// @proc Dqn_Library_SetLogFile
|
||||
// @param[in] file Pass in nullptr to turn off writing logs to disk, otherwise
|
||||
// point it to the FILE that you wish to write to.
|
||||
|
||||
// @proc Dqn_Library_DumpThreadContextArenaStat
|
||||
// @desc Dump the per-thread arena statistics to the specified file
|
||||
|
||||
struct Dqn_Library
|
||||
{
|
||||
bool lib_init; // True if the library has been initialised via `Dqn_Library_Init`
|
||||
Dqn_TicketMutex lib_mutex;
|
||||
Dqn_Str8 exe_dir; // The directory of the current executable
|
||||
|
||||
Dqn_Arena arena;
|
||||
Dqn_ChunkPool pool; // Uses 'arena' for malloc-like allocations
|
||||
Dqn_ArenaCatalog arena_catalog;
|
||||
|
||||
bool slow_verification_checks; // Enable expensive library verification checks
|
||||
// NOTE: Logging ///////////////////////////////////////////////////////////////////////////////
|
||||
Dqn_LogProc * log_callback; // Set this pointer to override the logging routine
|
||||
void * log_user_data;
|
||||
bool log_to_file; // Output logs to file as well as standard out
|
||||
Dqn_FsFile log_file; // TODO(dqn): Hmmm, how should we do this... ?
|
||||
Dqn_OSFile log_file; // TODO(dqn): Hmmm, how should we do this... ?
|
||||
Dqn_TicketMutex log_file_mutex; // Is locked when instantiating the log_file for the first time
|
||||
bool log_no_colour; // Disable colours in the logging output
|
||||
|
||||
// NOTE: Leak Tracing ==========================================================================
|
||||
|
||||
bool allocs_are_allowed_to_leak;
|
||||
bool alloc_tracking_disabled;
|
||||
#if defined(DQN_LEAK_TRACING)
|
||||
#if defined(DQN_NO_DSMAP)
|
||||
#error "DSMap is required for allocation tracing"
|
||||
#endif
|
||||
|
||||
// TODO(doyle): @robust Setting some of these flags is not multi-thread safe
|
||||
uint16_t stack_trace_offset_to_our_call_stack;
|
||||
// NOTE: Leak Tracing //////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DQN_LEAK_TRACKING)
|
||||
Dqn_DSMap<Dqn_DebugAlloc> alloc_table;
|
||||
Dqn_TicketMutex alloc_table_mutex;
|
||||
Dqn_DSMap<Dqn_AllocRecord> alloc_table;
|
||||
Dqn_Arena alloc_table_arena;
|
||||
#endif
|
||||
|
||||
// NOTE: OS ====================================================================================
|
||||
|
||||
// NOTE: Win32 /////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DQN_OS_WIN32)
|
||||
LARGE_INTEGER win32_qpc_frequency;
|
||||
Dqn_TicketMutex win32_bcrypt_rng_mutex;
|
||||
void * win32_bcrypt_rng_handle;
|
||||
#endif
|
||||
|
||||
bool win32_sym_initialised;
|
||||
// NOTE: OS ////////////////////////////////////////////////////////////////////////////////////
|
||||
uint32_t os_page_size;
|
||||
uint32_t os_alloc_granularity;
|
||||
|
||||
// NOTE: Profiler ==============================================================================
|
||||
|
||||
// NOTE: Profiler //////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
Dqn_Profiler *profiler;
|
||||
Dqn_Profiler profiler_default_instance;
|
||||
@ -688,7 +270,174 @@ enum Dqn_LibraryOnInit
|
||||
Dqn_LibraryOnInit_LogFeatures,
|
||||
};
|
||||
|
||||
// NOTE: API =======================================================================================
|
||||
// NOTE: [$PCGX] Dqn_PCG32 /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_PCG32 Dqn_PCG32_Init (uint64_t seed);
|
||||
DQN_API uint32_t Dqn_PCG32_Next (Dqn_PCG32 *rng);
|
||||
DQN_API uint64_t Dqn_PCG32_Next64 (Dqn_PCG32 *rng);
|
||||
DQN_API uint32_t Dqn_PCG32_Range (Dqn_PCG32 *rng, uint32_t low, uint32_t high);
|
||||
DQN_API Dqn_f32 Dqn_PCG32_NextF32 (Dqn_PCG32 *rng);
|
||||
DQN_API Dqn_f64 Dqn_PCG32_NextF64 (Dqn_PCG32 *rng);
|
||||
DQN_API void Dqn_PCG32_Advance (Dqn_PCG32 *rng, uint64_t delta);
|
||||
|
||||
#if !defined(DQN_NO_JSON_BUILDER)
|
||||
// NOTE: [$JSON] Dqn_JSONBuilder ///////////////////////////////////////////////////////////////////
|
||||
#define Dqn_JSONBuilder_Object(builder) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBegin(builder), \
|
||||
Dqn_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_ObjectNamed(builder, name) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBeginNamed(builder, name), \
|
||||
Dqn_JSONBuilder_ObjectEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_Array(builder) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBegin(builder), \
|
||||
Dqn_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
#define Dqn_JSONBuilder_ArrayNamed(builder, name) \
|
||||
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBeginNamed(builder, name), \
|
||||
Dqn_JSONBuilder_ArrayEnd(builder))
|
||||
|
||||
|
||||
DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init (Dqn_Arena *arena, int spaces_per_indent);
|
||||
DQN_API Dqn_Str8 Dqn_JSONBuilder_Build (Dqn_JSONBuilder const *builder, Dqn_Arena *arena);
|
||||
DQN_API void Dqn_JSONBuilder_KeyValue (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
|
||||
DQN_API void Dqn_JSONBuilder_KeyValueF (Dqn_JSONBuilder *builder, Dqn_Str8 key, char const *value_fmt, ...);
|
||||
DQN_API void Dqn_JSONBuilder_ObjectBeginNamed (Dqn_JSONBuilder *builder, Dqn_Str8 name);
|
||||
DQN_API void Dqn_JSONBuilder_ObjectEnd (Dqn_JSONBuilder *builder);
|
||||
DQN_API void Dqn_JSONBuilder_ArrayBeginNamed (Dqn_JSONBuilder *builder, Dqn_Str8 name);
|
||||
DQN_API void Dqn_JSONBuilder_ArrayEnd (Dqn_JSONBuilder *builder);
|
||||
DQN_API void Dqn_JSONBuilder_Str8Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
|
||||
DQN_API void Dqn_JSONBuilder_LiteralNamed (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
|
||||
DQN_API void Dqn_JSONBuilder_U64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, uint64_t value);
|
||||
DQN_API void Dqn_JSONBuilder_I64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, int64_t value);
|
||||
DQN_API void Dqn_JSONBuilder_F64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, double value, int decimal_places);
|
||||
DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBuilder *builder, Dqn_Str8 key, bool value);
|
||||
|
||||
#define Dqn_JSONBuilder_ObjectBegin(builder) Dqn_JSONBuilder_ObjectBeginNamed(builder, DQN_STR8(""))
|
||||
#define Dqn_JSONBuilder_ArrayBegin(builder) Dqn_JSONBuilder_ArrayBeginNamed(builder, DQN_STR8(""))
|
||||
#define Dqn_JSONBuilder_Str8(builder, value) Dqn_JSONBuilder_Str8Named(builder, DQN_STR8(""), value)
|
||||
#define Dqn_JSONBuilder_Literal(builder, value) Dqn_JSONBuilder_LiteralNamed(builder, DQN_STR8(""), value)
|
||||
#define Dqn_JSONBuilder_U64(builder, value) Dqn_JSONBuilder_U64Named(builder, DQN_STR8(""), value)
|
||||
#define Dqn_JSONBuilder_I64(builder, value) Dqn_JSONBuilder_I64Named(builder, DQN_STR8(""), value)
|
||||
#define Dqn_JSONBuilder_F64(builder, value) Dqn_JSONBuilder_F64Named(builder, DQN_STR8(""), value)
|
||||
#define Dqn_JSONBuilder_Bool(builder, value) Dqn_JSONBuilder_BoolNamed(builder, DQN_STR8(""), value)
|
||||
#endif // !defined(DQN_NO_JSON_BUILDER)
|
||||
#if !defined(DQN_NO_BIN)
|
||||
// NOTE: [$BHEX] Dqn_Bin ///////////////////////////////////////////////////////////////////////////
|
||||
// TODO(doyle): I'm not happy with this API. Its ugly and feels complicated
|
||||
// because I designed it as a C-API first (e.g. ptr + length) vs just accepting
|
||||
// that Dqn_Str8/Dqn_Slice is a superior API to design for first.
|
||||
DQN_API char const * Dqn_Bin_HexBufferTrim0x (char const *hex, Dqn_usize size, Dqn_usize *real_size);
|
||||
DQN_API Dqn_Str8 Dqn_Bin_HexTrim0x (Dqn_Str8 string);
|
||||
|
||||
DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8 (uint64_t number, uint32_t flags);
|
||||
DQN_API Dqn_Str8 Dqn_Bin_U64ToHex (Dqn_Arena *arena, uint64_t number, uint32_t flags);
|
||||
|
||||
DQN_API uint64_t Dqn_Bin_HexToU64 (Dqn_Str8 hex);
|
||||
DQN_API uint64_t Dqn_Bin_HexPtrToU64 (char const *hex, Dqn_usize size);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Bin_BytesToHexArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
DQN_API char * Dqn_Bin_BytesToHexBufferArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
|
||||
DQN_API bool Dqn_Bin_BytesToHexBuffer (void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size);
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
|
||||
DQN_API char * Dqn_Bin_HexBufferToBytesArena (Dqn_Arena *arena, char const *hex, Dqn_usize hex_size, Dqn_usize *real_size);
|
||||
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_usize Dqn_Bin_HexToBytes (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
|
||||
DQN_API Dqn_Str8 Dqn_Bin_HexToBytesArena (Dqn_Arena *arena, Dqn_Str8 hex);
|
||||
#endif // !defined(DQN_NO_BIN)
|
||||
|
||||
// NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
|
||||
template <typename T> bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
|
||||
template <typename T> Dqn_BinarySearchResult Dqn_BinarySearch (T const *array,
|
||||
Dqn_usize array_size,
|
||||
T const &find,
|
||||
Dqn_BinarySearchType type = Dqn_BinarySearchType_Match,
|
||||
Dqn_BinarySearchLessThanProc<T> less_than = Dqn_BinarySearch_DefaultLessThan);
|
||||
|
||||
// NOTE: [$BITS] Dqn_Bit ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_Bit_UnsetInplace (Dqn_usize *flags, Dqn_usize bitfield);
|
||||
DQN_API void Dqn_Bit_SetInplace (Dqn_usize *flags, Dqn_usize bitfield);
|
||||
DQN_API bool Dqn_Bit_IsSet (Dqn_usize bits, Dqn_usize bits_to_set);
|
||||
DQN_API bool Dqn_Bit_IsNotSet (Dqn_usize bits, Dqn_usize bits_to_check);
|
||||
|
||||
// NOTE: [$SAFE] Dqn_Safe //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API int64_t Dqn_Safe_AddI64 (int64_t a, int64_t b);
|
||||
DQN_API int64_t Dqn_Safe_MulI64 (int64_t a, int64_t b);
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_AddU64 (uint64_t a, uint64_t b);
|
||||
DQN_API uint64_t Dqn_Safe_MulU64 (uint64_t a, uint64_t b);
|
||||
|
||||
DQN_API uint64_t Dqn_Safe_SubU64 (uint64_t a, uint64_t b);
|
||||
DQN_API uint32_t Dqn_Safe_SubU32 (uint32_t a, uint32_t b);
|
||||
|
||||
DQN_API int Dqn_Safe_SaturateCastUSizeToInt (Dqn_usize val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8 (Dqn_usize val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16 (Dqn_usize val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32 (Dqn_usize val);
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64 (Dqn_usize val);
|
||||
|
||||
DQN_API int Dqn_Safe_SaturateCastU64ToInt (uint64_t val);
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt (uint64_t val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8 (uint64_t val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16 (uint64_t val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32 (uint64_t val);
|
||||
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8 (Dqn_usize val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16 (Dqn_usize val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32 (Dqn_usize val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64 (Dqn_usize val);
|
||||
|
||||
DQN_API int Dqn_Safe_SaturateCastISizeToInt (Dqn_isize val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8 (Dqn_isize val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16 (Dqn_isize val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32 (Dqn_isize val);
|
||||
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64 (Dqn_isize val);
|
||||
|
||||
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt (Dqn_isize val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8 (Dqn_isize val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16 (Dqn_isize val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32 (Dqn_isize val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64 (Dqn_isize val);
|
||||
|
||||
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize (int64_t val);
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8 (int64_t val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16 (int64_t val);
|
||||
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32 (int64_t val);
|
||||
|
||||
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8 (int val);
|
||||
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16 (int val);
|
||||
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8 (int val);
|
||||
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16 (int val);
|
||||
DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32 (int val);
|
||||
DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64 (int val);
|
||||
|
||||
// NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API int Dqn_FmtBuffer3DotTruncate (char *buffer, int size, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_U64Str8 Dqn_U64ToStr8 (uint64_t val, char separator);
|
||||
DQN_API Dqn_U64ByteSize Dqn_U64ToByteSize (uint64_t bytes, Dqn_U64ByteSizeType type);
|
||||
DQN_API Dqn_Str8 Dqn_U64ToByteSizeStr8 (Dqn_Arena *arena, uint64_t bytes, Dqn_U64ByteSizeType desired_type);
|
||||
DQN_API Dqn_Str8 Dqn_U64ByteSizeTypeString (Dqn_U64ByteSizeType type);
|
||||
DQN_API Dqn_Str8 Dqn_U64ToAge (Dqn_Arena *arena, uint64_t age_s, Dqn_usize type);
|
||||
|
||||
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
|
||||
#define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneWithIndex(DQN_STR8(name), __COUNTER__ + 1)
|
||||
DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex (Dqn_Str8 name, uint16_t anchor_index);
|
||||
DQN_API void Dqn_Profiler_EndZone (Dqn_ProfilerZone zone);
|
||||
DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer (Dqn_ProfilerAnchorBuffer buffer);
|
||||
DQN_API void Dqn_Profiler_SwapAnchorBuffer ();
|
||||
DQN_API void Dqn_Profiler_Dump (uint64_t tsc_per_second);
|
||||
|
||||
// NOTE: [$JOBQ] Dqn_JobQueue ///////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_JobQueueSPMC Dqn_OS_JobQueueSPMCInit ();
|
||||
DQN_API bool Dqn_OS_JobQueueSPMCAddArray (Dqn_JobQueueSPMC *job_queue, Dqn_Job *jobs, uint32_t count);
|
||||
DQN_API bool Dqn_OS_JobQueueSPMCAdd (Dqn_JobQueueSPMC *job_queue, Dqn_Job job);
|
||||
DQN_API void Dqn_OS_JobQueueSPMCWaitForCompletion (Dqn_JobQueueSPMC *job_queue);
|
||||
DQN_API int32_t Dqn_OS_JobQueueSPMCThread (Dqn_OSThread *thread);
|
||||
|
||||
// NOTE: Dqn_Library ///////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Library * Dqn_Library_Init (Dqn_LibraryOnInit on_init);
|
||||
DQN_API void Dqn_Library_SetPointer (Dqn_Library *library);
|
||||
#if !defined(DQN_NO_PROFILER)
|
||||
@ -696,4 +445,58 @@ DQN_API void Dqn_Library_SetProfiler (Dqn_Profiler *profil
|
||||
#endif
|
||||
DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data);
|
||||
DQN_API void Dqn_Library_DumpThreadContextArenaStat (Dqn_Str8 file_path);
|
||||
DQN_API Dqn_Arena * Dqn_Library_AllocArenaF (Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, char const *fmt, ...);
|
||||
|
||||
// NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
|
||||
template <typename T>
|
||||
bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
|
||||
{
|
||||
bool result = lhs < rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Dqn_BinarySearchResult Dqn_BinarySearch(T const *array,
|
||||
Dqn_usize array_size,
|
||||
T const &find,
|
||||
Dqn_BinarySearchType type,
|
||||
Dqn_BinarySearchLessThanProc<T> less_than)
|
||||
{
|
||||
Dqn_BinarySearchResult result = {};
|
||||
if (!array || array_size <= 0)
|
||||
return result;
|
||||
|
||||
T const *end = array + array_size;
|
||||
T const *first = array;
|
||||
T const *last = end;
|
||||
while (first != last) {
|
||||
Dqn_usize count = last - first;
|
||||
T const *it = first + (count / 2);
|
||||
|
||||
bool advance_first = false;
|
||||
if (type == Dqn_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 Dqn_BinarySearchType_Match: {
|
||||
result.found = first != end && !less_than(find, *first);
|
||||
} break;
|
||||
|
||||
case Dqn_BinarySearchType_LowerBound: /*FALLTHRU*/
|
||||
case Dqn_BinarySearchType_UpperBound: {
|
||||
result.found = first != end;
|
||||
} break;
|
||||
}
|
||||
|
||||
result.index = first - array;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
151
dqn_math.cpp
151
dqn_math.cpp
@ -1,5 +1,20 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$$$$$$$\ $$\ $$\
|
||||
// $$$\ $$$ |$$ __$$\\__$$ __|$$ | $$ |
|
||||
// $$$$\ $$$$ |$$ / $$ | $$ | $$ | $$ |
|
||||
// $$\$$\$$ $$ |$$$$$$$$ | $$ | $$$$$$$$ |
|
||||
// $$ \$$$ $$ |$$ __$$ | $$ | $$ __$$ |
|
||||
// $$ |\$ /$$ |$$ | $$ | $$ | $$ | $$ |
|
||||
// $$ | \_/ $$ |$$ | $$ | $$ | $$ | $$ |
|
||||
// \__| \__|\__| \__| \__| \__| \__|
|
||||
//
|
||||
// dqn_math.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if !defined(DQN_NO_V2)
|
||||
// NOTE: [$VEC2] Vector2 ===========================================================================
|
||||
// NOTE: [$VEC2] Vector2 ///////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: Dqn_V2I
|
||||
DQN_API bool operator!=(Dqn_V2I lhs, Dqn_V2I rhs)
|
||||
{
|
||||
@ -139,6 +154,24 @@ DQN_API Dqn_V2I &operator+=(Dqn_V2I &lhs, Dqn_V2I rhs)
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2I Dqn_V2I_Min(Dqn_V2I a, Dqn_V2I b)
|
||||
{
|
||||
Dqn_V2I result = Dqn_V2I_InitNx2(DQN_MIN(a.x, b.x), DQN_MIN(a.y, b.y));
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2I Dqn_V2I_Max(Dqn_V2I a, Dqn_V2I b)
|
||||
{
|
||||
Dqn_V2I result = Dqn_V2I_InitNx2(DQN_MAX(a.x, b.x), DQN_MAX(a.y, b.y));
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2I Dqn_V2I_Abs(Dqn_V2I a)
|
||||
{
|
||||
Dqn_V2I result = Dqn_V2I_InitNx2(DQN_ABS(a.x), DQN_ABS(a.y));
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_V2U16
|
||||
DQN_API bool operator!=(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
|
||||
{
|
||||
@ -309,42 +342,75 @@ DQN_API bool operator>(Dqn_V2 lhs, Dqn_V2 rhs)
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_V2 operator- //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_V2 operator-(Dqn_V2 lhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(-lhs.x, -lhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 operator-(Dqn_V2 lhs, Dqn_V2 rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs.x, lhs.y - rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 operator-(Dqn_V2 lhs, Dqn_V2I rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs.x, lhs.y - rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 operator-(Dqn_V2 lhs, Dqn_f32 rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs, lhs.y - rhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 operator-(Dqn_V2 lhs)
|
||||
DQN_API Dqn_V2 operator-(Dqn_V2 lhs, int32_t rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(-lhs.x, -lhs.y);
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs, lhs.y - rhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_V2 operator+ //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_V2 operator+(Dqn_V2 lhs, Dqn_V2 rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs.x, lhs.y + rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 operator+(Dqn_V2 lhs, Dqn_V2I rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs.x, lhs.y + rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 operator+(Dqn_V2 lhs, Dqn_f32 rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs, lhs.y + rhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 operator+(Dqn_V2 lhs, int32_t rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs, lhs.y + rhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_V2 operator* //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_V2 operator*(Dqn_V2 lhs, Dqn_V2 rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x * rhs.x, lhs.y * rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 operator*(Dqn_V2 lhs, Dqn_V2I rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x * rhs.x, lhs.y * rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 operator*(Dqn_V2 lhs, Dqn_f32 rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x * rhs, lhs.y * rhs);
|
||||
@ -357,12 +423,19 @@ DQN_API Dqn_V2 operator*(Dqn_V2 lhs, int32_t rhs)
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_V2 operator/ //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_V2 operator/(Dqn_V2 lhs, Dqn_V2 rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x / rhs.x, lhs.y / rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 operator/(Dqn_V2 lhs, Dqn_V2I rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x / rhs.x, lhs.y / rhs.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 operator/(Dqn_V2 lhs, Dqn_f32 rhs)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x / rhs, lhs.y / rhs);
|
||||
@ -375,12 +448,19 @@ DQN_API Dqn_V2 operator/(Dqn_V2 lhs, int32_t rhs)
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_V2 operator*/ /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, Dqn_V2 rhs)
|
||||
{
|
||||
lhs = lhs * rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, Dqn_V2I rhs)
|
||||
{
|
||||
lhs = lhs * rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, Dqn_f32 rhs)
|
||||
{
|
||||
lhs = lhs * rhs;
|
||||
@ -393,12 +473,19 @@ DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, int32_t rhs)
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_V2 operator// /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, Dqn_V2 rhs)
|
||||
{
|
||||
lhs = lhs / rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, Dqn_V2I rhs)
|
||||
{
|
||||
lhs = lhs / rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, Dqn_f32 rhs)
|
||||
{
|
||||
lhs = lhs / rhs;
|
||||
@ -411,30 +498,56 @@ DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, int32_t rhs)
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_V2 operator-/ /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, Dqn_V2 rhs)
|
||||
{
|
||||
lhs = lhs - rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, Dqn_V2I rhs)
|
||||
{
|
||||
lhs = lhs - rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, Dqn_f32 rhs)
|
||||
{
|
||||
lhs = lhs - rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, int32_t rhs)
|
||||
{
|
||||
lhs = lhs - rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// NOTE: Dqn_V2 operator+/ /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, Dqn_V2 rhs)
|
||||
{
|
||||
lhs = lhs + rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, Dqn_V2I rhs)
|
||||
{
|
||||
lhs = lhs + rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, Dqn_f32 rhs)
|
||||
{
|
||||
lhs = lhs + rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, int32_t rhs)
|
||||
{
|
||||
lhs = lhs + rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 Dqn_V2_Min(Dqn_V2 a, Dqn_V2 b)
|
||||
{
|
||||
Dqn_V2 result = Dqn_V2_InitNx2(DQN_MIN(a.x, b.x), DQN_MIN(a.y, b.y));
|
||||
@ -455,7 +568,7 @@ DQN_API Dqn_V2 Dqn_V2_Abs(Dqn_V2 a)
|
||||
|
||||
DQN_API Dqn_f32 Dqn_V2_Dot(Dqn_V2 a, Dqn_V2 b)
|
||||
{
|
||||
// NOTE: Scalar projection of B onto A =========================================================
|
||||
// NOTE: Scalar projection of B onto A /////////////////////////////////////////////////////////
|
||||
//
|
||||
// Scalar projection calculates the signed distance between `b` and `a`
|
||||
// where `a` is a unit vector then, the dot product calculates the projection
|
||||
@ -488,7 +601,7 @@ DQN_API Dqn_f32 Dqn_V2_Dot(Dqn_V2 a, Dqn_V2 b)
|
||||
// 2 objects. One of the vectors must be normalised (e.g. turned into a unit
|
||||
// vector).
|
||||
//
|
||||
// NOTE: Vector projection =====================================================================
|
||||
// NOTE: Vector projection /////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Vector projection calculates the exact X,Y coordinates of where `b` meets
|
||||
// `a` when it was projected. This is calculated by multipying the
|
||||
@ -578,7 +691,7 @@ DQN_API Dqn_f32 Dqn_V2_Area(Dqn_V2 a)
|
||||
#endif // !defined(DQN_NO_V2)
|
||||
|
||||
#if !defined(DQN_NO_V3)
|
||||
// NOTE: [$VEC3] Vector3 ===========================================================================
|
||||
// NOTE: [$VEC3] Vector3 ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool operator!=(Dqn_V3 lhs, Dqn_V3 rhs)
|
||||
{
|
||||
bool result = !(lhs == rhs);
|
||||
@ -739,7 +852,7 @@ DQN_API Dqn_V3 Dqn_V3_Normalise(Dqn_V3 a)
|
||||
#endif // !defined(DQN_NO_V3)
|
||||
|
||||
#if !defined(DQN_NO_V4)
|
||||
// NOTE: [$VEC4] Vector4 ===========================================================================
|
||||
// NOTE: [$VEC4] Vector4 ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool operator!=(Dqn_V4 lhs, Dqn_V4 rhs)
|
||||
{
|
||||
bool result = !(lhs == rhs);
|
||||
@ -856,7 +969,7 @@ DQN_API Dqn_f32 Dqn_V4Dot(Dqn_V4 a, Dqn_V4 b)
|
||||
#endif // !defined(DQN_NO_V4)
|
||||
|
||||
#if !defined(DQN_NO_M4)
|
||||
// NOTE: [$MAT4] Dqn_M4 ============================================================================
|
||||
// NOTE: [$MAT4] Dqn_M4 ////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_M4 Dqn_M4_Identity()
|
||||
{
|
||||
Dqn_M4 result =
|
||||
@ -924,7 +1037,7 @@ DQN_API Dqn_M4 Dqn_M4_Translate(Dqn_V3 xyz)
|
||||
|
||||
DQN_API Dqn_M4 Dqn_M4_Transpose(Dqn_M4 mat)
|
||||
{
|
||||
Dqn_M4 result;
|
||||
Dqn_M4 result = {};
|
||||
for (int col = 0; col < 4; col++)
|
||||
for (int row = 0; row < 4; row++)
|
||||
result.columns[col][row] = mat.columns[row][col];
|
||||
@ -1106,7 +1219,7 @@ DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat)
|
||||
#endif
|
||||
#endif // !defined(DQN_M4)
|
||||
|
||||
// NOTE: [$M2x3] Dqn_M2x3 ==========================================================================
|
||||
// NOTE: [$M2x3] Dqn_M2x3 //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool operator==(Dqn_M2x3 const &lhs, Dqn_M2x3 const &rhs)
|
||||
{
|
||||
bool result = DQN_MEMCMP(lhs.e, rhs.e, sizeof(lhs.e[0]) * DQN_ARRAY_UCOUNT(lhs.e)) == 0;
|
||||
@ -1178,7 +1291,7 @@ DQN_API Dqn_M2x3 Dqn_M2x3_Mul(Dqn_M2x3 m1, Dqn_M2x3 m2)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 Dqn_M2x3_MulV2(Dqn_M2x3 m1, Dqn_V2 v2)
|
||||
DQN_API Dqn_V2 Dqn_M2x3_Mul2F32(Dqn_M2x3 m1, Dqn_f32 x, Dqn_f32 y)
|
||||
{
|
||||
// NOTE: Ordinarily you can't multiply M2x3 with V2 because column count (3)
|
||||
// != row count (2). We pretend we have a V3 with `z` set to `1`.
|
||||
@ -1188,14 +1301,20 @@ DQN_API Dqn_V2 Dqn_M2x3_MulV2(Dqn_M2x3 m1, Dqn_V2 v2)
|
||||
// | 1 |
|
||||
|
||||
Dqn_V2 result = {{
|
||||
m1.e[0]*v2.x + m1.e[1]*v2.y + m1.e[2], // a*x + b*y + c*1
|
||||
m1.e[3]*v2.x + m1.e[4]*v2.y + m1.e[5], // d*x + e*y + f*1
|
||||
m1.e[0]*x + m1.e[1]*y + m1.e[2], // a*x + b*y + c*1
|
||||
m1.e[3]*x + m1.e[4]*y + m1.e[5], // d*x + e*y + f*1
|
||||
}};
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_V2 Dqn_M2x3_MulV2(Dqn_M2x3 m1, Dqn_V2 v2)
|
||||
{
|
||||
Dqn_V2 result = Dqn_M2x3_Mul2F32(m1, v2.x, v2.y);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_RECT)
|
||||
// NOTE: [$RECT] Dqn_Rect ==========================================================================
|
||||
// NOTE: [$RECT] Dqn_Rect //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool operator==(const Dqn_Rect& lhs, const Dqn_Rect& rhs)
|
||||
{
|
||||
bool result = (lhs.pos == rhs.pos) && (lhs.size == rhs.size);
|
||||
@ -1399,7 +1518,7 @@ DQN_API Dqn_V2 Dqn_Rect_BottomRight(Dqn_Rect rect)
|
||||
}
|
||||
#endif // !defined(DQN_NO_RECT)
|
||||
|
||||
// NOTE: [$MATH] Raycast ===========================================================================
|
||||
// NOTE: [$MATH] Raycast ///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DQN_API Dqn_RaycastLineIntersectV2Result Dqn_Raycast_LineIntersectV2(Dqn_V2 origin_a, Dqn_V2 dir_a, Dqn_V2 origin_b, Dqn_V2 dir_b)
|
||||
{
|
||||
@ -1432,7 +1551,7 @@ DQN_API Dqn_RaycastLineIntersectV2Result Dqn_Raycast_LineIntersectV2(Dqn_V2 orig
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$MATH] Other =============================================================================
|
||||
// NOTE: [$MATH] Other /////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_V2 Dqn_Lerp_V2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b)
|
||||
{
|
||||
Dqn_V2 result = {};
|
||||
|
296
dqn_math.h
296
dqn_math.h
@ -1,8 +1,32 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$$$$$$$\ $$\ $$\
|
||||
// $$$\ $$$ |$$ __$$\\__$$ __|$$ | $$ |
|
||||
// $$$$\ $$$$ |$$ / $$ | $$ | $$ | $$ |
|
||||
// $$\$$\$$ $$ |$$$$$$$$ | $$ | $$$$$$$$ |
|
||||
// $$ \$$$ $$ |$$ __$$ | $$ | $$ __$$ |
|
||||
// $$ |\$ /$$ |$$ | $$ | $$ | $$ | $$ |
|
||||
// $$ | \_/ $$ |$$ | $$ | $$ | $$ | $$ |
|
||||
// \__| \__|\__| \__| \__| \__| \__|
|
||||
//
|
||||
// dqn_math.h -- Basic math functions
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$VEC2] Dqn_V2, V2i -- DQN_V2
|
||||
// [$VEC3] Dqn_V3, V3i -- DQN_V3
|
||||
// [$VEC4] Dqn_V4, V4i -- DQN_V4
|
||||
// [$MAT4] Dqn_M4 -- DQN_M4
|
||||
// [$M2x3] Dqn_M2x3 --
|
||||
// [$RECT] Dqn_Rect -- DQN_RECT
|
||||
// [$MATH] Other --
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
|
||||
|
||||
#if !defined(DQN_NO_V2)
|
||||
// NOTE: [$VEC2] Vector2 ===========================================================================
|
||||
// NOTE: [$VEC2] Vector2 ///////////////////////////////////////////////////////////////////////////
|
||||
union Dqn_V2I
|
||||
{
|
||||
struct { int32_t x, y; };
|
||||
@ -10,6 +34,111 @@ union Dqn_V2I
|
||||
int32_t data[2];
|
||||
};
|
||||
|
||||
union Dqn_V2U16
|
||||
{
|
||||
struct { uint16_t x, y; };
|
||||
struct { uint16_t w, h; };
|
||||
uint16_t data[2];
|
||||
};
|
||||
|
||||
union Dqn_V2
|
||||
{
|
||||
struct { Dqn_f32 x, y; };
|
||||
struct { Dqn_f32 w, h; };
|
||||
Dqn_f32 data[2];
|
||||
};
|
||||
#endif // !defined(DQN_NO_V2)
|
||||
|
||||
#if !defined(DQN_NO_V3)
|
||||
// NOTE: [$VEC3] Vector3 ///////////////////////////////////////////////////////////////////////////
|
||||
union Dqn_V3
|
||||
{
|
||||
struct { Dqn_f32 x, y, z; };
|
||||
struct { Dqn_f32 r, g, b; };
|
||||
Dqn_f32 data[3];
|
||||
};
|
||||
|
||||
#endif // !defined(DQN_NO_V3)
|
||||
|
||||
#if !defined(DQN_NO_V4)
|
||||
// NOTE: [$VEC4] Vector4 ///////////////////////////////////////////////////////////////////////////
|
||||
union Dqn_V4
|
||||
{
|
||||
struct { Dqn_f32 x, y, z, w; };
|
||||
struct { Dqn_f32 r, g, b, a; };
|
||||
#if !defined(DQN_NO_V3)
|
||||
Dqn_V3 rgb;
|
||||
Dqn_V3 xyz;
|
||||
#endif
|
||||
Dqn_f32 data[4];
|
||||
};
|
||||
#endif // !defined(DQN_NO_V4)
|
||||
DQN_MSVC_WARNING_POP
|
||||
|
||||
#if !defined(DQN_NO_M4)
|
||||
// NOTE: [$MAT4] Dqn_M4 ////////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_M4
|
||||
{
|
||||
Dqn_f32 columns[4][4]; // Column major matrix
|
||||
};
|
||||
#endif // !defined(DQN_M4)
|
||||
|
||||
// NOTE: [$M2x3] Dqn_M2x3 //////////////////////////////////////////////////////////////////////////
|
||||
union Dqn_M2x3
|
||||
{
|
||||
Dqn_f32 e[6];
|
||||
Dqn_f32 row[2][3];
|
||||
};
|
||||
|
||||
// NOTE: [$RECT] Dqn_Rect //////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_RECT)
|
||||
#if defined(DQN_NO_V2)
|
||||
#error "Rectangles requires V2, DQN_NO_V2 must not be defined"
|
||||
#endif
|
||||
struct Dqn_Rect
|
||||
{
|
||||
Dqn_V2 pos, size;
|
||||
};
|
||||
|
||||
struct Dqn_RectMinMax
|
||||
{
|
||||
Dqn_V2 min, max;
|
||||
};
|
||||
|
||||
enum Dqn_RectCutClip
|
||||
{
|
||||
Dqn_RectCutClip_No,
|
||||
Dqn_RectCutClip_Yes,
|
||||
};
|
||||
|
||||
enum Dqn_RectCutSide
|
||||
{
|
||||
Dqn_RectCutSide_Left,
|
||||
Dqn_RectCutSide_Right,
|
||||
Dqn_RectCutSide_Top,
|
||||
Dqn_RectCutSide_Bottom,
|
||||
};
|
||||
|
||||
struct Dqn_RectCut
|
||||
{
|
||||
Dqn_Rect* rect;
|
||||
Dqn_RectCutSide side;
|
||||
};
|
||||
#endif // !defined(DQN_NO_RECT)
|
||||
|
||||
// NOTE: [$MATH] Other /////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: API
|
||||
struct Dqn_RaycastLineIntersectV2Result
|
||||
{
|
||||
bool hit; // True if there was an intersection, false if the lines are parallel
|
||||
Dqn_f32 t_a; // Distance along `dir_a` that the intersection occurred, e.g. `origin_a + (dir_a * t_a)`
|
||||
Dqn_f32 t_b; // Distance along `dir_b` that the intersection occurred, e.g. `origin_b + (dir_b * t_b)`
|
||||
};
|
||||
|
||||
#if !defined(DQN_NO_V2)
|
||||
// NOTE: [$VEC2] Vector2 ///////////////////////////////////////////////////////////////////////////
|
||||
#define Dqn_V2I_Zero DQN_LITERAL(Dqn_V2I){{(int32_t)(0), (int32_t)(0)}}
|
||||
#define Dqn_V2I_One DQN_LITERAL(Dqn_V2I){{(int32_t)(1), (int32_t)(1)}}
|
||||
#define Dqn_V2I_InitNx1(x) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(x)}}
|
||||
#define Dqn_V2I_InitNx2(x, y) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(y)}}
|
||||
#define Dqn_V2I_InitV2(xy) DQN_LITERAL(Dqn_V2I){{(int32_t)(xy).x, (int32_t)(xy).y}}
|
||||
@ -38,13 +167,12 @@ DQN_API Dqn_V2I &operator/=(Dqn_V2I& lhs, int32_t rhs);
|
||||
DQN_API Dqn_V2I & operator-= (Dqn_V2I& lhs, Dqn_V2I rhs);
|
||||
DQN_API Dqn_V2I & operator+= (Dqn_V2I& lhs, Dqn_V2I rhs);
|
||||
|
||||
union Dqn_V2U16
|
||||
{
|
||||
struct { uint16_t x, y; };
|
||||
struct { uint16_t w, h; };
|
||||
uint16_t data[2];
|
||||
};
|
||||
DQN_API Dqn_V2I Dqn_V2I_Min (Dqn_V2I a, Dqn_V2I b);
|
||||
DQN_API Dqn_V2I Dqn_V2I_Max (Dqn_V2I a, Dqn_V2I b);
|
||||
DQN_API Dqn_V2I Dqn_V2I_Abs (Dqn_V2I a);
|
||||
|
||||
#define Dqn_V2U16_Zero DQN_LITERAL(Dqn_V2U16){{(uint16_t)(0), (uint16_t)(0)}}
|
||||
#define Dqn_V2U16_One DQN_LITERAL(Dqn_V2U16){{(uint16_t)(1), (uint16_t)(1)}}
|
||||
#define Dqn_V2U16_InitNx1(x) DQN_LITERAL(Dqn_V2U16){{(uint16_t)(x), (uint16_t)(x)}}
|
||||
#define Dqn_V2U16_InitNx2(x, y) DQN_LITERAL(Dqn_V2U16){{(uint16_t)(x), (uint16_t)(y)}}
|
||||
|
||||
@ -71,13 +199,6 @@ DQN_API Dqn_V2U16 &operator/=(Dqn_V2U16& lhs, int32_t rhs);
|
||||
DQN_API Dqn_V2U16 & operator-= (Dqn_V2U16& lhs, Dqn_V2U16 rhs);
|
||||
DQN_API Dqn_V2U16 & operator+= (Dqn_V2U16& lhs, Dqn_V2U16 rhs);
|
||||
|
||||
union Dqn_V2
|
||||
{
|
||||
struct { Dqn_f32 x, y; };
|
||||
struct { Dqn_f32 w, h; };
|
||||
Dqn_f32 data[2];
|
||||
};
|
||||
|
||||
#define Dqn_V2_Zero DQN_LITERAL(Dqn_V2){{(Dqn_f32)(0), (Dqn_f32)(0)}}
|
||||
#define Dqn_V2_One DQN_LITERAL(Dqn_V2){{(Dqn_f32)(1), (Dqn_f32)(1)}}
|
||||
#define Dqn_V2_InitNx1(x) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(x), (Dqn_f32)(x)}}
|
||||
@ -90,29 +211,48 @@ DQN_API bool operator>=(Dqn_V2 lhs, Dqn_V2 rhs);
|
||||
DQN_API bool operator<= (Dqn_V2 lhs, Dqn_V2 rhs);
|
||||
DQN_API bool operator< (Dqn_V2 lhs, Dqn_V2 rhs);
|
||||
DQN_API bool operator> (Dqn_V2 lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 operator- (Dqn_V2 lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 operator- (Dqn_V2 lhs, Dqn_f32 rhs);
|
||||
|
||||
DQN_API Dqn_V2 operator- (Dqn_V2 lhs);
|
||||
DQN_API Dqn_V2 operator- (Dqn_V2 lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 operator- (Dqn_V2 lhs, Dqn_V2I rhs);
|
||||
DQN_API Dqn_V2 operator- (Dqn_V2 lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2 operator- (Dqn_V2 lhs, int32_t rhs);
|
||||
|
||||
DQN_API Dqn_V2 operator+ (Dqn_V2 lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 operator+ (Dqn_V2 lhs, Dqn_V2I rhs);
|
||||
DQN_API Dqn_V2 operator+ (Dqn_V2 lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2 operator+ (Dqn_V2 lhs, int32_t rhs);
|
||||
|
||||
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, Dqn_V2I rhs);
|
||||
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, int32_t rhs);
|
||||
|
||||
DQN_API Dqn_V2 operator/ (Dqn_V2 lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 operator/ (Dqn_V2 lhs, Dqn_V2I rhs);
|
||||
DQN_API Dqn_V2 operator/ (Dqn_V2 lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2 operator/ (Dqn_V2 lhs, int32_t rhs);
|
||||
|
||||
DQN_API Dqn_V2 & operator*= (Dqn_V2& lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 & operator*= (Dqn_V2& lhs, Dqn_V2I rhs);
|
||||
DQN_API Dqn_V2 & operator*= (Dqn_V2& lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2 & operator*= (Dqn_V2& lhs, int32_t rhs);
|
||||
|
||||
DQN_API Dqn_V2 & operator/= (Dqn_V2& lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 & operator/= (Dqn_V2& lhs, Dqn_V2I rhs);
|
||||
DQN_API Dqn_V2 & operator/= (Dqn_V2& lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2 & operator/= (Dqn_V2& lhs, int32_t rhs);
|
||||
DQN_API Dqn_V2 &operator-=(Dqn_V2& lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 &operator-=(Dqn_V2& lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2 &operator+=(Dqn_V2& lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 &operator+=(Dqn_V2& lhs, Dqn_f32 rhs);
|
||||
|
||||
DQN_API Dqn_V2I Dqn_V2_ToV2I (Dqn_V2 a);
|
||||
DQN_API Dqn_V2 & operator-= (Dqn_V2& lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 & operator-= (Dqn_V2& lhs, Dqn_V2I rhs);
|
||||
DQN_API Dqn_V2 & operator-= (Dqn_V2& lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2 & operator-= (Dqn_V2& lhs, int32_t rhs);
|
||||
|
||||
DQN_API Dqn_V2 & operator+= (Dqn_V2& lhs, Dqn_V2 rhs);
|
||||
DQN_API Dqn_V2 & operator+= (Dqn_V2& lhs, Dqn_V2I rhs);
|
||||
DQN_API Dqn_V2 & operator+= (Dqn_V2& lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V2 & operator+= (Dqn_V2& lhs, int32_t rhs);
|
||||
|
||||
DQN_API Dqn_V2 Dqn_V2_Min (Dqn_V2 a, Dqn_V2 b);
|
||||
DQN_API Dqn_V2 Dqn_V2_Max (Dqn_V2 a, Dqn_V2 b);
|
||||
DQN_API Dqn_V2 Dqn_V2_Abs (Dqn_V2 a);
|
||||
@ -126,16 +266,8 @@ DQN_API Dqn_V2 Dqn_V2_Perpendicular(Dqn_V2 a);
|
||||
DQN_API Dqn_V2 Dqn_V2_Reflect (Dqn_V2 in, Dqn_V2 surface);
|
||||
DQN_API Dqn_f32 Dqn_V2_Area (Dqn_V2 a);
|
||||
#endif // !defined(DQN_NO_V2)
|
||||
|
||||
#if !defined(DQN_NO_V3)
|
||||
// NOTE: [$VEC3] Vector3 ===========================================================================
|
||||
union Dqn_V3
|
||||
{
|
||||
struct { Dqn_f32 x, y, z; };
|
||||
struct { Dqn_f32 r, g, b; };
|
||||
Dqn_f32 data[3];
|
||||
};
|
||||
|
||||
// NOTE: [$VEC3] Vector3 ///////////////////////////////////////////////////////////////////////////
|
||||
#define Dqn_V3_InitNx1(x) DQN_LITERAL(Dqn_V3){{(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}}
|
||||
#define Dqn_V3_InitNx3(x, y, z) DQN_LITERAL(Dqn_V3){{(Dqn_f32)(x), (Dqn_f32)(y), (Dqn_f32)(z)}}
|
||||
#define Dqn_V3_InitV2x1_Nx1(xy, z) DQN_LITERAL(Dqn_V3){{(Dqn_f32)(xy.x), (Dqn_f32)(xy.y), (Dqn_f32)(z)}}
|
||||
@ -163,29 +295,15 @@ DQN_API Dqn_V3 &operator/=(Dqn_V3 &lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_V3 & operator/= (Dqn_V3 &lhs, int32_t rhs);
|
||||
DQN_API Dqn_V3 & operator-= (Dqn_V3 &lhs, Dqn_V3 rhs);
|
||||
DQN_API Dqn_V3 & operator+= (Dqn_V3 &lhs, Dqn_V3 rhs);
|
||||
|
||||
DQN_API Dqn_f32 Dqn_V3_LengthSq (Dqn_V3 a);
|
||||
DQN_API Dqn_f32 Dqn_V3_Length (Dqn_V3 a);
|
||||
DQN_API Dqn_V3 Dqn_V3_Normalise (Dqn_V3 a);
|
||||
#endif // !defined(DQN_NO_V3)
|
||||
|
||||
#if !defined(DQN_NO_V4)
|
||||
// NOTE: [$VEC4] Vector4 ===========================================================================
|
||||
union Dqn_V4
|
||||
{
|
||||
struct { Dqn_f32 x, y, z, w; };
|
||||
struct { Dqn_f32 r, g, b, a; };
|
||||
#if !defined(DQN_NO_V3)
|
||||
Dqn_V3 rgb;
|
||||
Dqn_V3 xyz;
|
||||
#endif
|
||||
Dqn_f32 data[4];
|
||||
};
|
||||
|
||||
// NOTE: [$VEC4] Vector4 ///////////////////////////////////////////////////////////////////////////
|
||||
#define Dqn_V4_InitNx1(x) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}}
|
||||
#define Dqn_V4_InitNx4(x, y, z, w) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(y), (Dqn_f32)(z), (Dqn_f32)(w)}}
|
||||
#define Dqn_V4_Init_V3x1_Nx1(xyz, w) DQN_LITERAL(Dqn_V4){{xyz.x, xyz.y, xyz.z, w}}
|
||||
|
||||
DQN_API bool operator!= (Dqn_V4 lhs, Dqn_V4 rhs);
|
||||
DQN_API bool operator== (Dqn_V4 lhs, Dqn_V4 rhs);
|
||||
DQN_API bool operator>= (Dqn_V4 lhs, Dqn_V4 rhs);
|
||||
@ -205,17 +323,9 @@ DQN_API Dqn_V4 &operator*=(Dqn_V4 &lhs, int32_t rhs);
|
||||
DQN_API Dqn_V4 & operator-= (Dqn_V4 &lhs, Dqn_V4 rhs);
|
||||
DQN_API Dqn_V4 & operator+= (Dqn_V4 &lhs, Dqn_V4 rhs);
|
||||
#endif // !defined(DQN_NO_V4)
|
||||
|
||||
#if !defined(DQN_NO_M4)
|
||||
// NOTE: [$MAT4] Dqn_M4 ============================================================================
|
||||
// NOTE: Column major matrix
|
||||
struct Dqn_M4
|
||||
{
|
||||
Dqn_f32 columns[4][4];
|
||||
};
|
||||
|
||||
// NOTE: [$MAT4] Dqn_M4 ////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_f32 Dqn_V4Dot (Dqn_V4 a, Dqn_V4 b);
|
||||
|
||||
DQN_API Dqn_M4 Dqn_M4_Identity ();
|
||||
DQN_API Dqn_M4 Dqn_M4_ScaleF (Dqn_f32 x, Dqn_f32 y, Dqn_f32 z);
|
||||
DQN_API Dqn_M4 Dqn_M4_Scale (Dqn_V3 xyz);
|
||||
@ -233,18 +343,11 @@ DQN_API Dqn_M4 Dqn_M4_AddF(Dqn_M4 lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_M4 Dqn_M4_SubF (Dqn_M4 lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_M4 Dqn_M4_MulF (Dqn_M4 lhs, Dqn_f32 rhs);
|
||||
DQN_API Dqn_M4 Dqn_M4_DivF (Dqn_M4 lhs, Dqn_f32 rhs);
|
||||
|
||||
#if !defined(DQN_NO_FSTR8)
|
||||
DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString (Dqn_M4 mat);
|
||||
#endif
|
||||
#endif // !defined(DQN_M4)
|
||||
|
||||
union Dqn_M2x3
|
||||
{
|
||||
Dqn_f32 e[6];
|
||||
Dqn_f32 row[2][3];
|
||||
};
|
||||
|
||||
#endif // !defined(DQN_NO_M4)
|
||||
// NOTE: [$M2x3] Dqn_M2x3 //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool operator== (Dqn_M2x3 const &lhs, Dqn_M2x3 const &rhs);
|
||||
DQN_API bool operator!= (Dqn_M2x3 const &lhs, Dqn_M2x3 const &rhs);
|
||||
DQN_API Dqn_M2x3 Dqn_M2x3_Identity ();
|
||||
@ -252,26 +355,13 @@ DQN_API Dqn_M2x3 Dqn_M2x3_Translate(Dqn_V2 offset);
|
||||
DQN_API Dqn_M2x3 Dqn_M2x3_Scale (Dqn_V2 scale);
|
||||
DQN_API Dqn_M2x3 Dqn_M2x3_Rotate (Dqn_f32 radians);
|
||||
DQN_API Dqn_M2x3 Dqn_M2x3_Mul (Dqn_M2x3 m1, Dqn_M2x3 m2);
|
||||
DQN_API Dqn_V2 Dqn_M2x3_Mul2F32 (Dqn_M2x3 m1, Dqn_f32 x, Dqn_f32 y);
|
||||
DQN_API Dqn_V2 Dqn_M2x3_MulV2 (Dqn_M2x3 m1, Dqn_V2 v2);
|
||||
|
||||
// NOTE: [$RECT] Dqn_Rect ==========================================================================
|
||||
#if !defined(DQN_NO_RECT)
|
||||
#if defined(DQN_NO_V2)
|
||||
#error "Rectangles requires V2, DQN_NO_V2 must not be defined"
|
||||
#endif
|
||||
|
||||
struct Dqn_Rect
|
||||
{
|
||||
Dqn_V2 pos, size;
|
||||
};
|
||||
|
||||
struct Dqn_RectMinMax
|
||||
{
|
||||
Dqn_V2 min, max;
|
||||
};
|
||||
|
||||
// NOTE: [$RECT] Dqn_Rect //////////////////////////////////////////////////////////////////////////
|
||||
#define Dqn_Rect_InitV2x2(pos, size) DQN_LITERAL(Dqn_Rect){(pos), (size)}
|
||||
#define Dqn_Rect_InitNx4(pos_x, pos_y, size_w, size_h) DQN_LITERAL(Dqn_Rect){DQN_LITERAL(Dqn_V2){{pos_x, pos_y}}, DQN_LITERAL(Dqn_V2){{size_w, size_h}}}
|
||||
#define Dqn_Rect_InitNx4(x, y, w, h) DQN_LITERAL(Dqn_Rect){DQN_LITERAL(Dqn_V2){{x, y}}, DQN_LITERAL(Dqn_V2){{w, h}}}
|
||||
|
||||
DQN_API bool operator== (const Dqn_Rect& lhs, const Dqn_Rect& rhs);
|
||||
DQN_API Dqn_V2 Dqn_Rect_Center (Dqn_Rect rect);
|
||||
@ -290,12 +380,6 @@ DQN_API Dqn_V2 Dqn_Rect_TopRight (Dqn_Rect rect);
|
||||
DQN_API Dqn_V2 Dqn_Rect_BottomLeft (Dqn_Rect rect);
|
||||
DQN_API Dqn_V2 Dqn_Rect_BottomRight (Dqn_Rect rect);
|
||||
|
||||
enum Dqn_RectCutClip
|
||||
{
|
||||
Dqn_RectCutClip_No,
|
||||
Dqn_RectCutClip_Yes,
|
||||
};
|
||||
|
||||
DQN_API Dqn_Rect Dqn_Rect_CutLeftClip (Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
|
||||
DQN_API Dqn_Rect Dqn_Rect_CutRightClip (Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
|
||||
DQN_API Dqn_Rect Dqn_Rect_CutTopClip (Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
|
||||
@ -311,50 +395,14 @@ DQN_API Dqn_Rect Dqn_Rect_CutBottomClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_Rect
|
||||
#define Dqn_Rect_CutTopNoClip(rect, amount) Dqn_Rect_CutTopClip(rect, amount, Dqn_RectCutClip_No)
|
||||
#define Dqn_Rect_CutBottomNoClip(rect, amount) Dqn_Rect_CutBottomClip(rect, amount, Dqn_RectCutClip_No)
|
||||
|
||||
enum Dqn_RectCutSide
|
||||
{
|
||||
Dqn_RectCutSide_Left,
|
||||
Dqn_RectCutSide_Right,
|
||||
Dqn_RectCutSide_Top,
|
||||
Dqn_RectCutSide_Bottom,
|
||||
};
|
||||
|
||||
struct Dqn_RectCut
|
||||
{
|
||||
Dqn_Rect* rect;
|
||||
Dqn_RectCutSide side;
|
||||
};
|
||||
|
||||
DQN_API Dqn_Rect Dqn_RectCut_Cut (Dqn_RectCut rect_cut, Dqn_V2 size, Dqn_RectCutClip clip);
|
||||
#define Dqn_RectCut_Init(rect, side) DQN_LITERAL(Dqn_RectCut){rect, side}
|
||||
#define Dqn_RectCut_Left(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Left}
|
||||
#define Dqn_RectCut_Right(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Right}
|
||||
#define Dqn_RectCut_Top(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Top}
|
||||
#define Dqn_RectCut_Bottom(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Bottom}
|
||||
|
||||
DQN_API Dqn_Rect Dqn_RectCut_Cut(Dqn_RectCut rect_cut, Dqn_V2 size, Dqn_RectCutClip clip);
|
||||
#endif // !defined(DQN_NO_RECT)
|
||||
|
||||
// NOTE: [$MATH] Raycast ===========================================================================
|
||||
//
|
||||
// NOTE: API
|
||||
// @proc Dqn_Raycast_LineIntersectV2
|
||||
// @desc Calculate the intersection point of 2 rays returning a `t` value
|
||||
// which is how much along the direction of the 'ray' did the intersection
|
||||
// occur.
|
||||
//
|
||||
// The arguments passed in do not need to be normalised for the function to
|
||||
// work.
|
||||
|
||||
struct Dqn_RaycastLineIntersectV2Result
|
||||
{
|
||||
bool hit; // True if there was an intersection, false if the lines are parallel
|
||||
Dqn_f32 t_a; // Distance along `dir_a` that the intersection occurred, e.g. `origin_a + (dir_a * t_a)`
|
||||
Dqn_f32 t_b; // Distance along `dir_b` that the intersection occurred, e.g. `origin_b + (dir_b * t_b)`
|
||||
};
|
||||
|
||||
// NOTE: [$MATH] Other /////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_RaycastLineIntersectV2Result Dqn_Raycast_LineIntersectV2(Dqn_V2 origin_a, Dqn_V2 dir_a, Dqn_V2 origin_b, Dqn_V2 dir_b);
|
||||
|
||||
// NOTE: [$MATH] Other =============================================================================
|
||||
DQN_API Dqn_V2 Dqn_Lerp_V2 (Dqn_V2 a, Dqn_f32 t, Dqn_V2 b);
|
||||
DQN_API Dqn_f32 Dqn_Lerp_F32 (Dqn_f32 a, Dqn_f32 t, Dqn_f32 b);
|
||||
DQN_MSVC_WARNING_POP
|
||||
|
608
dqn_memory.cpp
608
dqn_memory.cpp
@ -1,608 +0,0 @@
|
||||
#if !defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
// NOTE: [$VMEM] Dqn_VMem ==========================================================================
|
||||
DQN_FILE_SCOPE uint32_t Dqn_VMem_ConvertPageToOSFlags_(uint32_t protect)
|
||||
{
|
||||
DQN_ASSERT((protect & ~(Dqn_VMemPage_ReadWrite | Dqn_VMemPage_Guard)) == 0);
|
||||
DQN_ASSERT(protect != 0);
|
||||
uint32_t result = 0;
|
||||
|
||||
#if defined(DQN_OS_WIN32)
|
||||
if (protect & Dqn_VMemPage_NoAccess) {
|
||||
result = PAGE_NOACCESS;
|
||||
} else {
|
||||
if (protect & Dqn_VMemPage_ReadWrite) {
|
||||
result = PAGE_READWRITE;
|
||||
} else if (protect & Dqn_VMemPage_Read) {
|
||||
result = PAGE_READONLY;
|
||||
} else if (protect & Dqn_VMemPage_Write) {
|
||||
Dqn_Log_WarningF("Windows does not support write-only pages, granting read+write access");
|
||||
result = PAGE_READWRITE;
|
||||
}
|
||||
}
|
||||
|
||||
if (protect & Dqn_VMemPage_Guard)
|
||||
result |= PAGE_GUARD;
|
||||
|
||||
DQN_ASSERTF(result != PAGE_GUARD, "Page guard is a modifier, you must also specify a page permission like read or/and write");
|
||||
#else
|
||||
if (protect & (Dqn_VMemPage_NoAccess | Dqn_VMemPage_Guard)) {
|
||||
result = PROT_NONE;
|
||||
} else {
|
||||
if (protect & Dqn_VMemPage_Read)
|
||||
result = PROT_READ;
|
||||
if (protect & Dqn_VMemPage_Write)
|
||||
result = PROT_WRITE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_VMem_Reserve(Dqn_usize size, Dqn_VMemCommit commit, uint32_t page_flags)
|
||||
{
|
||||
unsigned long os_page_flags = Dqn_VMem_ConvertPageToOSFlags_(page_flags);
|
||||
|
||||
#if defined(DQN_OS_WIN32)
|
||||
unsigned long flags = MEM_RESERVE | (commit == Dqn_VMemCommit_Yes ? MEM_COMMIT : 0);
|
||||
void *result = VirtualAlloc(nullptr, size, flags, os_page_flags);
|
||||
|
||||
#elif defined(DQN_OS_UNIX)
|
||||
if (commit == Dqn_VMemCommit_Yes)
|
||||
os_page_flags |= (PROT_READ | PROT_WRITE);
|
||||
|
||||
void *result = mmap(nullptr, size, os_page_flags, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
if (result == MAP_FAILED)
|
||||
result = nullptr;
|
||||
|
||||
#else
|
||||
#error "Missing implementation for Dqn_VMem_Reserve"
|
||||
#endif
|
||||
|
||||
Dqn_Debug_TrackAlloc(result, size, (page_flags & Dqn_VMemPage_AllocRecordLeakPermitted));
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_VMem_Commit(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
{
|
||||
bool result = false;
|
||||
if (!ptr || size == 0)
|
||||
return false;
|
||||
|
||||
unsigned long os_page_flags = Dqn_VMem_ConvertPageToOSFlags_(page_flags);
|
||||
#if defined(DQN_OS_WIN32)
|
||||
result = VirtualAlloc(ptr, size, MEM_COMMIT, os_page_flags) != nullptr;
|
||||
#elif defined(DQN_OS_UNIX)
|
||||
result = mprotect(ptr, size, os_page_flags) == 0;
|
||||
#else
|
||||
#error "Missing implementation for Dqn_VMem_Commit"
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_VMem_Decommit(void *ptr, Dqn_usize size)
|
||||
{
|
||||
#if defined(DQN_OS_WIN32)
|
||||
|
||||
// NOTE: This is a decommit call, which is explicitly saying to free the
|
||||
// pages but not the VADs, you would use VMem_Release to release everything.
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(6250) // Calling 'VirtualFree' without the MEM_RELEASE flag might free memory but not address descriptors (VADs). This causes address space leaks.
|
||||
VirtualFree(ptr, size, MEM_DECOMMIT);
|
||||
DQN_MSVC_WARNING_POP
|
||||
|
||||
#elif defined(DQN_OS_UNIX)
|
||||
mprotect(ptr, size, PROT_NONE);
|
||||
madvise(ptr, size, MADV_FREE);
|
||||
#else
|
||||
#error "Missing implementation for Dqn_VMem_Decommit"
|
||||
#endif
|
||||
}
|
||||
|
||||
DQN_API void Dqn_VMem_Release(void *ptr, Dqn_usize size)
|
||||
{
|
||||
#if defined(DQN_OS_WIN32)
|
||||
(void)size;
|
||||
VirtualFree(ptr, 0, MEM_RELEASE);
|
||||
#elif defined(DQN_OS_UNIX)
|
||||
munmap(ptr, size);
|
||||
#else
|
||||
#error "Missing implementation for Dqn_VMem_Release"
|
||||
#endif
|
||||
Dqn_Debug_TrackDealloc(ptr);
|
||||
}
|
||||
|
||||
DQN_API int Dqn_VMem_Protect(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
{
|
||||
if (!ptr || size == 0)
|
||||
return 0;
|
||||
|
||||
static Dqn_Str8 const ALIGNMENT_ERROR_MSG =
|
||||
DQN_STR8("Page protection requires pointers to be page aligned because we "
|
||||
"can only guard memory at a multiple of the page boundary.");
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(DQN_CAST(uintptr_t)ptr, g_dqn_library->os_page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(size, g_dqn_library->os_page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||
|
||||
unsigned long os_page_flags = Dqn_VMem_ConvertPageToOSFlags_(page_flags);
|
||||
#if defined(DQN_OS_WIN32)
|
||||
unsigned long prev_flags = 0;
|
||||
int result = VirtualProtect(ptr, size, os_page_flags, &prev_flags);
|
||||
(void)prev_flags;
|
||||
if (result == 0) {
|
||||
#if defined(DQN_NO_WIN)
|
||||
DQN_ASSERTF(result, "VirtualProtect failed");
|
||||
#else
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
|
||||
DQN_ASSERTF(result, "VirtualProtect failed (%u): %.*s", error.code, DQN_STR_FMT(error.msg));
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
int result = mprotect(result->memory, result->size, os_page_flags);
|
||||
DQN_ASSERTF(result == 0, "mprotect failed (%d)", errno);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
|
||||
// NOTE: [$MEMF] Dqn_MemAPI ==================================================================
|
||||
#if !defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
DQN_API Dqn_MemAPI Dqn_MemAPI_InitOSVirtual()
|
||||
{
|
||||
Dqn_MemAPI result = {};
|
||||
result.reserve = Dqn_VMem_Reserve;
|
||||
result.commit = Dqn_VMem_Commit;
|
||||
result.release = Dqn_VMem_Release;
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
|
||||
void *Dqn_MemAPI_CRTReserve(Dqn_usize size, Dqn_VMemCommit commit, uint32_t page_flags)
|
||||
{
|
||||
(void)page_flags;
|
||||
(void)commit;
|
||||
void *result = calloc(1, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Dqn_MemAPI_CRTCommit(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
{
|
||||
(void)ptr; (void)size; (void)page_flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Dqn_MemAPI_CRTRelease(void *ptr, Dqn_usize size)
|
||||
{
|
||||
(void)size;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
DQN_API Dqn_MemAPI Dqn_MemAPI_InitCRT()
|
||||
{
|
||||
Dqn_MemAPI result = {};
|
||||
result.reserve = Dqn_MemAPI_CRTReserve;
|
||||
result.commit = Dqn_MemAPI_CRTCommit;
|
||||
result.release = Dqn_MemAPI_CRTRelease;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$MEMB] Dqn_MemBlock ======================================================================
|
||||
DQN_API Dqn_MemBlockSizeRequiredResult Dqn_MemBlock_SizeRequired(Dqn_MemBlock const *block, Dqn_usize size, uint8_t alignment, uint32_t flags)
|
||||
{
|
||||
DQN_ASSERT(alignment > 0 && Dqn_IsPowerOfTwo(alignment));
|
||||
|
||||
Dqn_MemBlockSizeRequiredResult result = {};
|
||||
result.alloc_size = size;
|
||||
Dqn_MemBlockFlag block_flags = DQN_CAST(Dqn_MemBlockFlag)((block ? block->flags : 0) | flags);
|
||||
uint8_t ptr_alignment = alignment;
|
||||
|
||||
if (DQN_ASAN_POISON) {
|
||||
// NOTE: Guard a page after with poisoned memory. The first allocation
|
||||
// is always guarded with poison-ed memory to prevent read/writes behind
|
||||
// the block of memory.
|
||||
if ((block_flags & Dqn_MemBlockFlag_AllocsAreContiguous) == 0) {
|
||||
result.alloc_size = Dqn_AlignUpPowerOfTwo(size + DQN_ASAN_POISON_GUARD_SIZE, DQN_ASAN_POISON_ALIGNMENT);
|
||||
}
|
||||
ptr_alignment = DQN_MAX(alignment, DQN_ASAN_POISON_ALIGNMENT);
|
||||
}
|
||||
|
||||
if (block) {
|
||||
uintptr_t address = DQN_CAST(uintptr_t)block->data + block->used;
|
||||
uintptr_t next_address = Dqn_AlignUpPowerOfTwo(address, ptr_alignment);
|
||||
result.data_offset = next_address - DQN_CAST(uintptr_t)block->data;
|
||||
Dqn_usize new_used = result.data_offset + result.alloc_size;
|
||||
result.block_size = new_used - block->used;
|
||||
} else {
|
||||
result.block_size = result.alloc_size + (ptr_alignment - 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_MemBlock_MetadataSize()
|
||||
{
|
||||
Dqn_usize init_poison_page = DQN_ASAN_POISON ? DQN_ASAN_POISON_GUARD_SIZE : 0;
|
||||
Dqn_usize alignment = DQN_ASAN_POISON ? DQN_ASAN_POISON_ALIGNMENT : alignof(Dqn_MemBlock);
|
||||
Dqn_usize result = Dqn_AlignUpPowerOfTwo(sizeof(Dqn_MemBlock), alignment) + init_poison_page;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_MemBlock *Dqn_MemBlock_InitMemAPI(Dqn_usize reserve, Dqn_usize commit, uint32_t flags, Dqn_MemAPI mem_api)
|
||||
{
|
||||
DQN_ASSERTF(g_dqn_library->os_page_size, "Library needs to be initialised by calling Dqn_Library_Init()");
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwo(g_dqn_library->os_page_size), "Invalid page size");
|
||||
DQN_ASSERTF((flags & ~Dqn_MemBlockFlag_All) == 0, "Invalid flag combination, must adhere to Dqn_MemBlockFlags");
|
||||
|
||||
if (reserve == 0)
|
||||
return nullptr;
|
||||
|
||||
Dqn_usize metadata_size = Dqn_MemBlock_MetadataSize();
|
||||
Dqn_usize reserve_aligned = Dqn_AlignUpPowerOfTwo(metadata_size + reserve, g_dqn_library->os_page_size);
|
||||
Dqn_usize commit_aligned = Dqn_AlignUpPowerOfTwo(metadata_size + commit, g_dqn_library->os_page_size);
|
||||
commit_aligned = DQN_MIN(commit_aligned, reserve_aligned);
|
||||
|
||||
// NOTE: Avoid 1 syscall by committing on reserve if amounts are equal
|
||||
Dqn_VMemCommit commit_on_reserve = commit_aligned == reserve_aligned ? Dqn_VMemCommit_Yes : Dqn_VMemCommit_No;
|
||||
auto *result = DQN_CAST(Dqn_MemBlock *)mem_api.reserve(reserve_aligned, commit_on_reserve, Dqn_VMemPage_ReadWrite);
|
||||
if (result) {
|
||||
// NOTE: Commit pages if we did not commit on the initial range.
|
||||
if (!commit_on_reserve)
|
||||
mem_api.commit(result, commit_aligned, Dqn_VMemPage_ReadWrite);
|
||||
|
||||
result->mem_api = mem_api;
|
||||
result->data = DQN_CAST(uint8_t *)result + metadata_size;
|
||||
result->size = reserve_aligned - metadata_size;
|
||||
result->commit = commit_aligned - metadata_size;
|
||||
result->flags = DQN_CAST(uint8_t)flags;
|
||||
|
||||
// NOTE: Poison (guard page + commit). We do *not* poison the entire
|
||||
// block, only the commit pages. Since we may reserve large amounts of
|
||||
// space vs commit we'd waste time marking those pages as poisoned as
|
||||
// reads or writes outside of committed pages will page fault.
|
||||
if (DQN_ASAN_POISON) {
|
||||
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result->data, DQN_ASAN_POISON_ALIGNMENT));
|
||||
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result->size, DQN_ASAN_POISON_ALIGNMENT));
|
||||
void *poison_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo(DQN_CAST(char *)result + sizeof(Dqn_MemBlock), DQN_ASAN_POISON_ALIGNMENT);
|
||||
Dqn_usize bytes_to_poison = DQN_ASAN_POISON_GUARD_SIZE + result->commit;
|
||||
Dqn_ASAN_PoisonMemoryRegion(poison_ptr, bytes_to_poison);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_MemBlock *Dqn_MemBlock_Init(Dqn_usize reserve, Dqn_usize commit, uint32_t flags, Dqn_MemAPI mem_api)
|
||||
{
|
||||
Dqn_MemBlock *result = Dqn_MemBlock_InitMemAPI(reserve, commit, flags, mem_api);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_MemBlock_Alloc(Dqn_MemBlock *block, Dqn_usize size, uint8_t alignment, Dqn_ZeroMem zero_mem)
|
||||
{
|
||||
DQN_ASSERT(zero_mem == Dqn_ZeroMem_Yes || zero_mem == Dqn_ZeroMem_No);
|
||||
|
||||
void *result = nullptr;
|
||||
if (!block)
|
||||
return result;
|
||||
|
||||
Dqn_MemBlockSizeRequiredResult size_required = Dqn_MemBlock_SizeRequired(block, size, alignment, Dqn_MemBlockFlag_Nil);
|
||||
Dqn_usize new_used = size_required.data_offset + size_required.alloc_size;
|
||||
if (new_used > block->size)
|
||||
return result;
|
||||
|
||||
result = DQN_CAST(char *)block->data + size_required.data_offset;
|
||||
block->used = new_used;
|
||||
block->used_hwm = DQN_MAX(block->used_hwm, new_used);
|
||||
DQN_ASSERT(Dqn_IsPowerOfTwoAligned(result, alignment));
|
||||
|
||||
if (DQN_ASAN_POISON)
|
||||
Dqn_ASAN_UnpoisonMemoryRegion(result, size);
|
||||
|
||||
if (zero_mem == Dqn_ZeroMem_Yes) {
|
||||
Dqn_usize reused_bytes = DQN_MIN(block->commit - size_required.data_offset, size);
|
||||
DQN_MEMSET(result, DQN_MEMSET_BYTE, reused_bytes);
|
||||
}
|
||||
|
||||
if (block->commit < block->used) {
|
||||
Dqn_usize commit_size = Dqn_AlignUpPowerOfTwo(block->used - block->commit, g_dqn_library->os_page_size);
|
||||
void *commit_ptr = (void *)Dqn_AlignUpPowerOfTwo((char *)block->data + block->commit, g_dqn_library->os_page_size);
|
||||
block->commit += commit_size;
|
||||
block->mem_api.commit(commit_ptr, commit_size, Dqn_VMemPage_ReadWrite);
|
||||
DQN_ASSERT(block->commit <= block->size);
|
||||
|
||||
if (DQN_ASAN_POISON) { // NOTE: Poison newly committed pages that aren't being used.
|
||||
void *poison_ptr = DQN_CAST(char *)block->data + block->used;
|
||||
Dqn_usize bytes_to_poison = block->commit - block->used;
|
||||
Dqn_ASAN_PoisonMemoryRegion(poison_ptr, bytes_to_poison);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_MemBlock_Free(Dqn_MemBlock *block)
|
||||
{
|
||||
if (!block)
|
||||
return;
|
||||
Dqn_usize release_size = block->size + Dqn_MemBlock_MetadataSize();
|
||||
if (DQN_ASAN_POISON)
|
||||
Dqn_ASAN_UnpoisonMemoryRegion(block, release_size);
|
||||
block->mem_api.release(block, release_size);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_MemBlock_Pop(Dqn_MemBlock *block, Dqn_usize size)
|
||||
{
|
||||
if (!block)
|
||||
return;
|
||||
Dqn_usize size_adjusted = DQN_MIN(size, block->used);
|
||||
Dqn_usize to = block->used - size_adjusted;
|
||||
Dqn_MemBlock_PopTo(block, to);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_MemBlock_PopTo(Dqn_MemBlock *block, Dqn_usize to)
|
||||
{
|
||||
if (!block || to >= block->used)
|
||||
return;
|
||||
|
||||
if (DQN_ASAN_POISON) {
|
||||
void *poison_ptr = DQN_CAST(char *)block->data + to;
|
||||
void *end_ptr = DQN_CAST(void *)Dqn_AlignUpPowerOfTwo((DQN_CAST(uintptr_t)block->data + block->used), DQN_ASAN_POISON_ALIGNMENT);
|
||||
uintptr_t bytes_to_poison = DQN_CAST(uintptr_t)end_ptr - DQN_CAST(uintptr_t)poison_ptr;
|
||||
Dqn_ASAN_PoisonMemoryRegion(poison_ptr, bytes_to_poison);
|
||||
}
|
||||
block->used = to;
|
||||
}
|
||||
|
||||
// NOTE: [$AREN] Dqn_Arena =========================================================================
|
||||
DQN_FILE_SCOPE void *Dqn_Arena_AllocatorAlloc(size_t size, uint8_t align, Dqn_ZeroMem zero_mem, void *user_context)
|
||||
{
|
||||
void *result = NULL;
|
||||
if (!user_context)
|
||||
return result;
|
||||
|
||||
Dqn_Arena *arena = DQN_CAST(Dqn_Arena *)user_context;
|
||||
result = Dqn_Arena_Alloc(arena, size, align, zero_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_FILE_SCOPE void Dqn_Arena_AllocatorDealloc(void *, size_t, void *)
|
||||
{
|
||||
// NOTE: No-op, arenas batch allocate and batch deallocate. Call free on the
|
||||
// underlying arena, since we can't free individual pointers.
|
||||
}
|
||||
|
||||
DQN_API Dqn_Allocator Dqn_Arena_Allocator(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Allocator result = {};
|
||||
if (arena) {
|
||||
result.user_context = arena;
|
||||
result.alloc = Dqn_Arena_AllocatorAlloc;
|
||||
result.dealloc = Dqn_Arena_AllocatorDealloc;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_MemBlock *Dqn_Arena_Grow(Dqn_Arena *arena, Dqn_usize reserve, Dqn_usize commit, uint8_t flags)
|
||||
{
|
||||
if (!arena)
|
||||
return nullptr;
|
||||
|
||||
uint8_t mem_block_flags = flags;
|
||||
if (arena->allocs_are_allowed_to_leak)
|
||||
mem_block_flags |= Dqn_MemBlockFlag_AllocRecordLeakPermitted;
|
||||
|
||||
if (!arena->mem_api.reserve) {
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
arena->mem_api = Dqn_MemAPI_InitCRT();
|
||||
#else
|
||||
arena->mem_api = Dqn_MemAPI_InitOSVirtual();
|
||||
#endif
|
||||
}
|
||||
|
||||
Dqn_MemBlock *result = Dqn_MemBlock_Init(reserve, commit, mem_block_flags, arena->mem_api);
|
||||
if (result) {
|
||||
if (!arena->head)
|
||||
arena->head = result;
|
||||
|
||||
if (arena->tail)
|
||||
arena->tail->next = result;
|
||||
|
||||
if (!arena->curr)
|
||||
arena->curr = result;
|
||||
|
||||
result->prev = arena->tail;
|
||||
arena->tail = result;
|
||||
arena->blocks += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_Alloc(Dqn_Arena *arena, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem)
|
||||
{
|
||||
DQN_ASSERT(Dqn_IsPowerOfTwo(align));
|
||||
|
||||
void *result = nullptr;
|
||||
if (!arena || size == 0 || align == 0)
|
||||
return result;
|
||||
|
||||
for (;;) {
|
||||
while (arena->curr && (arena->curr->flags & Dqn_MemBlockFlag_ArenaPrivate))
|
||||
arena->curr = arena->curr->next;
|
||||
|
||||
if (!arena->curr) {
|
||||
Dqn_MemBlockSizeRequiredResult size_required = Dqn_MemBlock_SizeRequired(nullptr, size, align, Dqn_MemBlockFlag_Nil);
|
||||
Dqn_usize block_size = size_required.block_size;
|
||||
if (!Dqn_Arena_Grow(arena, block_size, block_size, Dqn_MemBlockFlag_Nil))
|
||||
return result;
|
||||
DQN_ASSERT(arena->curr);
|
||||
}
|
||||
|
||||
result = Dqn_MemBlock_Alloc(arena->curr, size, align, zero_mem);
|
||||
if (result)
|
||||
break;
|
||||
|
||||
arena->curr = arena->curr->next;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_Copy(Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t align)
|
||||
{
|
||||
void *result = Dqn_Arena_Alloc(arena, size, align, Dqn_ZeroMem_No);
|
||||
DQN_MEMCPY(result, src, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_Arena_CopyZ(Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t align)
|
||||
{
|
||||
void *result = Dqn_Arena_Alloc(arena, size + 1, align, Dqn_ZeroMem_No);
|
||||
DQN_MEMCPY(result, src, size);
|
||||
(DQN_CAST(char *)result)[size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_Free(Dqn_Arena *arena)
|
||||
{
|
||||
if (!arena)
|
||||
return;
|
||||
|
||||
for (Dqn_MemBlock *block = arena->head; block; ) {
|
||||
Dqn_MemBlock *next = block->next;
|
||||
Dqn_MemBlock_Free(block);
|
||||
block = next;
|
||||
}
|
||||
|
||||
arena->curr = arena->head = arena->tail = nullptr;
|
||||
arena->blocks = 0;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ArenaTempMemory Dqn_Arena_BeginTempMemory(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_ArenaTempMemory result = {};
|
||||
if (arena) {
|
||||
result.arena = arena;
|
||||
result.head = arena->head;
|
||||
result.curr = arena->curr;
|
||||
result.tail = arena->tail;
|
||||
result.curr_used = (arena->curr) ? arena->curr->used : 0;
|
||||
result.blocks = arena->blocks;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Arena_EndTempMemory(Dqn_ArenaTempMemory temp_memory, bool cancel)
|
||||
{
|
||||
if (cancel || !temp_memory.arena)
|
||||
return;
|
||||
|
||||
// NOTE: The arena has been freed or invalidly manipulated (e.g. freed)
|
||||
// since the temp memory started as the head cannot change once it is
|
||||
// captured in the temp memory.
|
||||
Dqn_Arena *arena = temp_memory.arena;
|
||||
if (arena->head != temp_memory.head)
|
||||
return;
|
||||
|
||||
// NOTE: Revert the current block to the temp_memory's current block
|
||||
arena->blocks = temp_memory.blocks;
|
||||
arena->head = temp_memory.head;
|
||||
arena->curr = temp_memory.curr;
|
||||
Dqn_MemBlock_PopTo(arena->curr, temp_memory.curr_used);
|
||||
|
||||
// NOTE: Free the tail blocks until we reach the temp_memory's tail block
|
||||
while (arena->tail != temp_memory.tail) {
|
||||
Dqn_MemBlock *tail = arena->tail;
|
||||
arena->tail = tail->prev;
|
||||
Dqn_MemBlock_Free(tail);
|
||||
}
|
||||
|
||||
// NOTE: Chop the restored tail link
|
||||
if (arena->tail)
|
||||
arena->tail->next = nullptr;
|
||||
|
||||
// NOTE: Reset the usage of all the blocks between the tail and current block's
|
||||
for (Dqn_MemBlock *block = arena->tail; block && (block != arena->curr); block = block->prev)
|
||||
Dqn_MemBlock_PopTo(block, 0);
|
||||
}
|
||||
|
||||
Dqn_ArenaTempMemoryScope::Dqn_ArenaTempMemoryScope(Dqn_Arena *arena)
|
||||
{
|
||||
temp_memory = Dqn_Arena_BeginTempMemory(arena);
|
||||
}
|
||||
|
||||
Dqn_ArenaTempMemoryScope::~Dqn_ArenaTempMemoryScope()
|
||||
{
|
||||
Dqn_Arena_EndTempMemory(temp_memory, cancel);
|
||||
}
|
||||
|
||||
DQN_API Dqn_ArenaInfo Dqn_Arena_Info(Dqn_Arena const *arena)
|
||||
{
|
||||
Dqn_ArenaInfo result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
for (Dqn_MemBlock const *block = arena->head; block; block = block->next) {
|
||||
result.capacity += block->size;
|
||||
result.used += block->used;
|
||||
result.used_hwm += block->used_hwm;
|
||||
result.commit += block->commit;
|
||||
result.wasted += block->next ? (block->size - block->used) : 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$ACAT] Dqn_ArenaCatalog ==================================================================
|
||||
DQN_API void Dqn_ArenaCatalog_Init(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena)
|
||||
{
|
||||
catalog->arena = arena;
|
||||
catalog->sentinel.next = &catalog->sentinel;
|
||||
catalog->sentinel.prev = &catalog->sentinel;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_Add(Dqn_ArenaCatalog *catalog, Dqn_Arena *arena)
|
||||
{
|
||||
// NOTE: We could use an atomic for appending to the sentinel but it is such
|
||||
// a rare operation to append to the catalog that we don't bother.
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
|
||||
// NOTE: Create item in the catalog
|
||||
Dqn_ArenaCatalogItem *result = Dqn_Arena_New(catalog->arena, Dqn_ArenaCatalogItem, Dqn_ZeroMem_Yes);
|
||||
result->arena = arena;
|
||||
|
||||
// NOTE: Add to the catalog (linked list)
|
||||
Dqn_ArenaCatalogItem *sentinel = &catalog->sentinel;
|
||||
result->next = sentinel;
|
||||
result->prev = sentinel->prev;
|
||||
result->next->prev = result;
|
||||
result->prev->next = result;
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
|
||||
Dqn_Atomic_AddU32(&catalog->arena_count, 1);
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_Alloc(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit)
|
||||
{
|
||||
Dqn_TicketMutex_Begin(&catalog->ticket_mutex);
|
||||
Dqn_Arena *result = Dqn_Arena_New(catalog->arena, Dqn_Arena, Dqn_ZeroMem_Yes);
|
||||
Dqn_TicketMutex_End(&catalog->ticket_mutex);
|
||||
|
||||
Dqn_Arena_Grow(result, byte_size, commit, 0 /*flags*/);
|
||||
Dqn_ArenaCatalog_Add(catalog, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_Alloc(catalog, byte_size, commit);
|
||||
result->label = Dqn_Str8_InitFV(Dqn_Arena_Allocator(result), fmt, args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Arena *result = Dqn_ArenaCatalog_AllocFV(catalog, byte_size, commit, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
342
dqn_memory.h
342
dqn_memory.h
@ -1,342 +0,0 @@
|
||||
// NOTE: [$VMEM] Dqn_VMem ==========================================================================
|
||||
enum Dqn_VMemCommit
|
||||
{
|
||||
Dqn_VMemCommit_No,
|
||||
Dqn_VMemCommit_Yes,
|
||||
};
|
||||
|
||||
enum Dqn_VMemPage
|
||||
{
|
||||
// Exception on read/write with a page. This flag overrides the read/write
|
||||
// access.
|
||||
Dqn_VMemPage_NoAccess = 1 << 0,
|
||||
|
||||
// Only read permitted on the page.
|
||||
Dqn_VMemPage_Read = 1 << 1,
|
||||
|
||||
// Only write permitted on the page. On Windows this is not supported and
|
||||
// will be promoted to read+write permissions.
|
||||
Dqn_VMemPage_Write = 1 << 2,
|
||||
|
||||
Dqn_VMemPage_ReadWrite = Dqn_VMemPage_Read | Dqn_VMemPage_Write,
|
||||
|
||||
// Modifier used in conjunction with previous flags. Raises exception on
|
||||
// first access to the page, then, the underlying protection flags are
|
||||
// active. This is supported on Windows, on other OS's using this flag will
|
||||
// set the OS equivalent of Dqn_VMemPage_NoAccess.
|
||||
// This flag must only be used in Dqn_VMem_Protect
|
||||
Dqn_VMemPage_Guard = 1 << 3,
|
||||
|
||||
// If leak tracing is enabled, this flag will allow the allocation recorded
|
||||
// from the reserve call to be leaked, e.g. not printed when leaks are
|
||||
// dumped to the console.
|
||||
Dqn_VMemPage_AllocRecordLeakPermitted = 1 << 2,
|
||||
};
|
||||
|
||||
#if !defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
DQN_API void *Dqn_VMem_Reserve (Dqn_usize size, Dqn_VMemCommit commit, uint32_t page_flags);
|
||||
DQN_API bool Dqn_VMem_Commit (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||
DQN_API void Dqn_VMem_Decommit(void *ptr, Dqn_usize size);
|
||||
DQN_API void Dqn_VMem_Release (void *ptr, Dqn_usize size);
|
||||
DQN_API int Dqn_VMem_Protect (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||
#endif
|
||||
|
||||
// NOTE: [$MEMF] Dqn_MemAPI ==================================================================
|
||||
// Interface for specifying the routines for memory allocation for Dqn_MemBlock
|
||||
|
||||
typedef void *(Dqn_MemReserveFunc)(Dqn_usize size, Dqn_VMemCommit commit, uint32_t page_flags);
|
||||
typedef bool (Dqn_MemCommitFunc) (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||
typedef void (Dqn_MemReleaseFunc)(void *ptr, Dqn_usize size);
|
||||
|
||||
struct Dqn_MemAPI
|
||||
{
|
||||
Dqn_MemReserveFunc *reserve;
|
||||
Dqn_MemCommitFunc *commit;
|
||||
Dqn_MemReleaseFunc *release;
|
||||
};
|
||||
|
||||
#if !defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
DQN_API Dqn_MemAPI Dqn_MemAPI_InitOSVirtual();
|
||||
#endif
|
||||
DQN_API Dqn_MemAPI Dqn_MemAPI_InitCRT();
|
||||
|
||||
// NOTE: [$MEMB] Dqn_MemBlock ======================================================================
|
||||
// Encapsulates allocation of objects from a raw block of memory by bumping a
|
||||
// a pointer in the block. Some examples include our memory arenas are
|
||||
// implemented as light wrappers over chained memory blocks and our arrays
|
||||
// backed by virtual memory take memory blocks.
|
||||
//
|
||||
// One pattern we take advantage of under this design is that our virtual arrays
|
||||
// can ask an arena for a memory block and sub-allocate its contiguous items
|
||||
// from it. Since the arena created the memory block, the array's lifetime is
|
||||
// bound to the arena which could also be managing a bunch of other allocations
|
||||
// with the same lifetime.
|
||||
//
|
||||
// This provides an advantage over creating a specific arena for that array that
|
||||
// is configured not to grow or chain (in order to adhere to the contiguous
|
||||
// layout requirement) thus limiting the arena to that 1 specific usecase.
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_MemBlockSizeRequiredResult
|
||||
// @desc Calculate various size metrics about how many bytes it'd take to
|
||||
// allocate a pointer from the given block. The size of the allocation is
|
||||
// treated as one object and the padding and page-guards are applied
|
||||
// accordingly to the one object
|
||||
//
|
||||
// If you are trying to determine how many bytes are required for `N` distinct
|
||||
// objects then you must multiple the result of this function by `N` to
|
||||
// account for the per-item page-guard paddding.
|
||||
//
|
||||
// @param `block` Pass in the block you wish to allocate from to calculate
|
||||
// size metrics for. You may pass in `null` to calculate how many bytes are
|
||||
// needed to `Dqn_MemBlock_Init` a fresh block capable of allocating the size
|
||||
// requested.
|
||||
//
|
||||
// @param `flags` The `Dqn_MemBlockFlag`s to apply in the calculation. Various
|
||||
// features may influence the sizes required for allocating the requested
|
||||
// amount of bytes. If `block` is passed in, the flags will be OR'ed together
|
||||
// to determine the flags to account for.
|
||||
|
||||
enum Dqn_MemBlockFlag
|
||||
{
|
||||
Dqn_MemBlockFlag_Nil = 0,
|
||||
Dqn_MemBlockFlag_ArenaPrivate = 1 << 0,
|
||||
|
||||
// Enforce that adjacent allocations from this block are contiguous in
|
||||
// memory (as long as the alignment used between allocations are
|
||||
/// consistent).
|
||||
//
|
||||
// This flag is currently only used when ASAN memory poison-ing is enabled
|
||||
// via `DQN_ASAN_POISON`. In this mode all allocations are sandwiched with a
|
||||
// page's worth of poison-ed memory breaking the contiguous requirement of
|
||||
// arrays. Passing this flag will stop the block from padding pointers with
|
||||
// poison.
|
||||
Dqn_MemBlockFlag_AllocsAreContiguous = 1 << 1,
|
||||
|
||||
// If leak tracing is enabled, this flag will allow the allocation recorded
|
||||
// from the reserve call to be leaked, e.g. not printed when leaks are
|
||||
// dumped to the console.
|
||||
Dqn_MemBlockFlag_AllocRecordLeakPermitted = 1 << 2,
|
||||
Dqn_MemBlockFlag_All = Dqn_MemBlockFlag_ArenaPrivate |
|
||||
Dqn_MemBlockFlag_AllocsAreContiguous |
|
||||
Dqn_MemBlockFlag_AllocRecordLeakPermitted,
|
||||
};
|
||||
|
||||
struct Dqn_MemBlock
|
||||
{
|
||||
Dqn_MemAPI mem_api;
|
||||
void *data;
|
||||
Dqn_usize used;
|
||||
Dqn_usize used_hwm;
|
||||
Dqn_usize size;
|
||||
Dqn_usize commit;
|
||||
Dqn_MemBlock *next;
|
||||
Dqn_MemBlock *prev;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
struct Dqn_MemBlockSizeRequiredResult
|
||||
{
|
||||
// Offset from the block's data pointer that the allocation will start at
|
||||
// If `block` was null then this is always set to 0.
|
||||
Dqn_usize data_offset;
|
||||
|
||||
// How many bytes will be allocated for the amount requested by the user.
|
||||
// This is usually the same as the number requested except when ASAN
|
||||
// poison-ing is enabled. In that case, the pointer will be padded at the
|
||||
// end with a page's worth of poison-ed memory.
|
||||
Dqn_usize alloc_size;
|
||||
|
||||
// How many bytes of space is needed in a block for allocating the requested
|
||||
// pointer. This may differ from the allocation size depending on additional
|
||||
// alignment requirements *and* whether or not ASAN poison-ing is required.
|
||||
Dqn_usize block_size;
|
||||
};
|
||||
|
||||
DQN_API Dqn_usize Dqn_MemBlock_MetadataSize (uint8_t flags);
|
||||
DQN_API Dqn_MemBlockSizeRequiredResult Dqn_MemBlock_SizeRequired (Dqn_MemBlock const *block, Dqn_usize size, uint8_t alignment, uint32_t flags);
|
||||
DQN_API Dqn_MemBlock * Dqn_MemBlock_InitMemAPI(Dqn_usize reserve, Dqn_usize commit, uint32_t flags, Dqn_MemAPI mem_functions);
|
||||
DQN_API Dqn_MemBlock * Dqn_MemBlock_Init (Dqn_usize reserve, Dqn_usize commit, uint32_t flags);
|
||||
DQN_API void * Dqn_MemBlock_Alloc (Dqn_MemBlock *block, Dqn_usize size, uint8_t alignment, Dqn_ZeroMem zero_mem);
|
||||
DQN_API void Dqn_MemBlock_Free (Dqn_MemBlock *block);
|
||||
DQN_API void Dqn_MemBlock_Pop (Dqn_MemBlock *block, Dqn_usize size);
|
||||
DQN_API void Dqn_MemBlock_PopTo (Dqn_MemBlock *block, Dqn_usize to);
|
||||
|
||||
#define Dqn_MemBlock_New(block, Type, zero_mem) (Type *)Dqn_MemBlock_Alloc(block, sizeof(Type), alignof(Type), zero_mem)
|
||||
#define Dqn_MemBlock_NewArray(block, Type, count, zero_mem) (Type *)Dqn_MemBlock_Alloc(block, sizeof(Type) * count, alignof(Type), zero_mem)
|
||||
|
||||
// NOTE: [$AREN] Dqn_Arena =========================================================================
|
||||
// A bump-allocator that can grow dynamically by chaining blocks of memory
|
||||
// together. The arena's memory is backed by virtual memory allowing the
|
||||
// allocator to reserve and commit physical pages as memory is given from
|
||||
// the block of memory.
|
||||
//
|
||||
// Arena's allow grouping of multiple allocations into one lifetime that is
|
||||
// bound to the arena. Allocation involves a simple 'bump' of the pointer in the
|
||||
// memory block. Freeing involves resetting the pointer to the start of the
|
||||
// block and/or releasing the single pointer to the entire block of memory.
|
||||
//
|
||||
// This allocator reserves memory blocks at a 64k granularity as per the minimum
|
||||
// granularity reserve size of VirtualAlloc on Windows. Memory is commit at
|
||||
// a 4k granularity for similar reasons. On 64 bit platforms you have access
|
||||
// to 48 bits of address space for applications, this is 256TB of address space
|
||||
// you can reserve. The typical usage for this style of arena is to reserve
|
||||
// as much space as you possibly need, ever, for the lifetime of the arena (e.g.
|
||||
// 64GB) since the arena only commits as much as needed.
|
||||
//
|
||||
// NOTE: API
|
||||
// @proc Dqn_Arena_Grow
|
||||
// @desc Grow the arena's capacity by allocating a block of memory with the
|
||||
// requested size. The requested size is rounded up to the nearest 64k
|
||||
// boundary as that is the minimum reserve granularity (atleast on Windows)
|
||||
// for virtual memory.
|
||||
// @param size[in] The size in bytes to expand the capacity of the arena
|
||||
// @param commit[in] The amount of bytes to request to be physically backed by
|
||||
// pages from the OS.
|
||||
// @param flags[in] Bit flags from 'Dqn_ArenaBlockFlags', set to 0 if none
|
||||
// @return The block of memory that
|
||||
|
||||
// @proc Dqn_Arena_Alloc, Dqn_Arena_New, Dqn_Arena_NewArray,
|
||||
// Dqn_Arena_NewArrayWithBlock,
|
||||
// @desc Alloc byte/objects
|
||||
// `Alloc` allocates bytes
|
||||
// `New` allocates an object
|
||||
// `NewArray` allocates an array of objects
|
||||
// `NewArrayWithBlock` allocates an array of objects from the given memory 'block'
|
||||
// @return A pointer to the allocated bytes/object. Null pointer on failure
|
||||
|
||||
// @proc Dqn_Arena_Copy, Dqn_Arena_CopyZ
|
||||
// @desc Alloc a copy of an object's bytes. The 'Z' variant adds
|
||||
// a null-terminating byte at the end of the stream.
|
||||
// @return A pointer to the allocated object. Null pointer on failure.
|
||||
|
||||
// @proc Dqn_Arena_Reset
|
||||
// @desc Set the arena's current block to the first block in the linked list
|
||||
// of blocks and mark all blocks free.
|
||||
// @param[in] zero_mem When yes, the memory is cleared using DQN_MEMSET with the
|
||||
// value of DQN_MEMSET_BYTE
|
||||
|
||||
// @proc Dqn_Arena_Free
|
||||
// @desc Free the arena returning all memory back to the OS
|
||||
// @param[in] zero_mem: When true, the memory is cleared using DQN_MEMSET with
|
||||
// the value of DQN_MEMSET_BYTE
|
||||
|
||||
// @proc Dqn_Arena_BeginTempMemory
|
||||
// @desc Begin an allocation scope where all allocations between begin and end
|
||||
// calls will be reverted. Useful for short-lived or highly defined lifetime
|
||||
// allocations. An allocation scope is invalidated if the arena is freed
|
||||
// between the begin and end call.
|
||||
|
||||
// @proc Dqn_Arena_EndTempMemory
|
||||
// @desc End an allocation scope previously begun by calling begin scope.
|
||||
struct Dqn_ArenaInfo
|
||||
{
|
||||
Dqn_usize capacity; // Total allocating capacity of the arena in bytes
|
||||
Dqn_usize used; // Total amount of bytes used in the arena
|
||||
Dqn_usize commit; // Total amount of bytes committed in the arena
|
||||
Dqn_usize wasted; // Orphaned space in blocks due to allocations requiring more space than available in the active block
|
||||
Dqn_usize used_hwm; // High-water mark for 'used'
|
||||
};
|
||||
|
||||
struct Dqn_ArenaBlock
|
||||
{
|
||||
struct Dqn_Arena *arena; // Arena that owns this block
|
||||
void *memory; // Backing memory of the block
|
||||
Dqn_usize size; // Size of the block
|
||||
Dqn_usize used; // Number of bytes used up in the block. Always less than the commit amount.
|
||||
Dqn_usize used_hwm;// High-water mark for 'used' bytes in this block
|
||||
Dqn_usize commit; // Number of bytes in the block physically backed by pages
|
||||
Dqn_ArenaBlock *prev; // Previous linked block
|
||||
Dqn_ArenaBlock *next; // Next linked block
|
||||
uint8_t flags; // Bit field for 'Dqn_ArenaBlockFlags'
|
||||
};
|
||||
|
||||
struct Dqn_ArenaStatString
|
||||
{
|
||||
char data[256];
|
||||
uint16_t size;
|
||||
};
|
||||
|
||||
struct Dqn_Arena
|
||||
{
|
||||
Dqn_MemAPI mem_api;
|
||||
bool allocs_are_allowed_to_leak;
|
||||
Dqn_Str8 label; // Optional label to describe the arena
|
||||
Dqn_MemBlock *head; // Active block the arena is allocating from
|
||||
Dqn_MemBlock *curr; // Active block the arena is allocating from
|
||||
Dqn_MemBlock *tail; // Last block in the linked list of blocks
|
||||
uint64_t blocks;
|
||||
};
|
||||
|
||||
struct Dqn_ArenaTempMemory
|
||||
{
|
||||
Dqn_Arena *arena; // Arena the scope is for
|
||||
Dqn_MemBlock *head; // Head block of the arena at the beginning of the scope
|
||||
Dqn_MemBlock *curr; // Current block of the arena at the beginning of the scope
|
||||
Dqn_MemBlock *tail; // Tail block of the arena at the beginning of the scope
|
||||
Dqn_usize blocks;
|
||||
Dqn_usize curr_used; // Current used amount of the current block
|
||||
};
|
||||
|
||||
// Automatically begin and end a temporary memory scope on object construction
|
||||
// and destruction respectively.
|
||||
#define Dqn_Arena_TempMemoryScope(arena) Dqn_ArenaTempMemoryScope DQN_UNIQUE_NAME(temp_memory_) = Dqn_ArenaTempMemoryScope(arena)
|
||||
struct Dqn_ArenaTempMemoryScope
|
||||
{
|
||||
Dqn_ArenaTempMemoryScope(Dqn_Arena *arena);
|
||||
~Dqn_ArenaTempMemoryScope();
|
||||
Dqn_ArenaTempMemory temp_memory;
|
||||
bool cancel = false;
|
||||
};
|
||||
|
||||
enum Dqn_ArenaCommit
|
||||
{
|
||||
// Commit the pages to ensure the block has the requested commit amount.
|
||||
// No-op if the block has sufficient commit space already.
|
||||
Dqn_ArenaCommit_EnsureSpace,
|
||||
Dqn_ArenaCommit_GetNewPages, // Grow the block by the requested commit amount
|
||||
};
|
||||
|
||||
// NOTE: Allocation ================================================================================
|
||||
#define Dqn_Arena_New(arena, Type, zero_mem) (Type *)Dqn_Arena_Alloc(arena, sizeof(Type), alignof(Type), zero_mem)
|
||||
#define Dqn_Arena_NewCopy(arena, Type, src) (Type *)Dqn_Arena_Copy(arena, (src), sizeof(*src), alignof(Type))
|
||||
#define Dqn_Arena_NewCopyZ(arena, Type, src) (Type *)Dqn_Arena_Copy(arena, (src), sizeof(*src), alignof(Type))
|
||||
#define Dqn_Arena_NewArray(arena, Type, count, zero_mem) (Type *)Dqn_Arena_Alloc(arena, sizeof(Type) * (count), alignof(Type), zero_mem)
|
||||
#define Dqn_Arena_NewArrayCopy(arena, Type, src, count) (Type *)Dqn_Arena_Copy(arena, (src), sizeof(*src) * (count), alignof(Type))
|
||||
#define Dqn_Arena_NewArrayCopyZ(arena, Type, src, count) (Type *)Dqn_Arena_CopyZ(arena, (src), sizeof(*src) * (count), alignof(Type))
|
||||
|
||||
DQN_API Dqn_Allocator Dqn_Arena_Allocator (Dqn_Arena *arena);
|
||||
DQN_API Dqn_MemBlock * Dqn_Arena_Grow (Dqn_Arena *arena, Dqn_usize size, Dqn_usize commit, uint8_t flags);
|
||||
DQN_API void * Dqn_Arena_Alloc (Dqn_Arena *arena, Dqn_usize size, uint8_t align, Dqn_ZeroMem zero_mem);
|
||||
DQN_API void * Dqn_Arena_Copy (Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t alignment);
|
||||
DQN_API void * Dqn_Arena_CopyZ (Dqn_Arena *arena, void *src, Dqn_usize size, uint8_t alignment);
|
||||
DQN_API void Dqn_Arena_Free (Dqn_Arena *arena, Dqn_ZeroMem zero_mem);
|
||||
|
||||
// NOTE: Temp Memory ===============================================================================
|
||||
DQN_API Dqn_ArenaTempMemory Dqn_Arena_BeginTempMemory(Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Arena_EndTempMemory (Dqn_ArenaTempMemory temp_memory, bool cancel);
|
||||
|
||||
// NOTE: Arena Info ===============================================================================
|
||||
DQN_API Dqn_ArenaInfo Dqn_Arena_Info (Dqn_Arena const *arena);
|
||||
|
||||
// NOTE: [$ACAT] Dqn_ArenaCatalog ==================================================================
|
||||
struct Dqn_ArenaCatalogItem
|
||||
{
|
||||
Dqn_Arena *arena;
|
||||
Dqn_ArenaCatalogItem *next;
|
||||
Dqn_ArenaCatalogItem *prev;
|
||||
};
|
||||
|
||||
struct Dqn_ArenaCatalog
|
||||
{
|
||||
Dqn_TicketMutex ticket_mutex; // Mutex for adding to the linked list of arenas
|
||||
Dqn_Arena *arena;
|
||||
Dqn_ArenaCatalogItem sentinel;
|
||||
uint16_t arena_count;
|
||||
};
|
||||
|
||||
DQN_API void Dqn_ArenaCatalog_Init (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena);
|
||||
DQN_API void Dqn_ArenaCatalog_Add (Dqn_ArenaCatalog *catalog, Dqn_Arena *arena);
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_Alloc (Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit);
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocFV(Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Arena *Dqn_ArenaCatalog_AllocF (Dqn_ArenaCatalog *catalog, Dqn_usize byte_size, Dqn_usize commit, DQN_FMT_ATTRIB char const *fmt, ...);
|
500
dqn_os.cpp
500
dqn_os.cpp
@ -1,136 +1,442 @@
|
||||
DQN_API void Dqn_OS_Exit(uint32_t exit_code)
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\
|
||||
// $$ / $$ |$$ / \__|
|
||||
// $$ | $$ |\$$$$$$\
|
||||
// $$ | $$ | \____$$\
|
||||
// $$ | $$ |$$\ $$ |
|
||||
// $$$$$$ |\$$$$$$ |
|
||||
// \______/ \______/
|
||||
//
|
||||
// dqn_os.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$DATE] Date //////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8(Dqn_OSDateTime time, char date_separator, char hms_separator)
|
||||
{
|
||||
#if defined(DQN_OS_WIN32)
|
||||
ExitProcess(exit_code);
|
||||
#else
|
||||
exit(exit_code);
|
||||
#endif
|
||||
Dqn_OSDateTimeStr8 result = {};
|
||||
result.hms_size = DQN_CAST(uint8_t) DQN_SNPRINTF(result.hms,
|
||||
DQN_ARRAY_ICOUNT(result.hms),
|
||||
"%02hhu%c%02hhu%c%02hhu",
|
||||
time.hour,
|
||||
hms_separator,
|
||||
time.minutes,
|
||||
hms_separator,
|
||||
time.seconds);
|
||||
|
||||
result.date_size = DQN_CAST(uint8_t) DQN_SNPRINTF(result.date,
|
||||
DQN_ARRAY_ICOUNT(result.date),
|
||||
"%hu%c%02hhu%c%02hhu",
|
||||
time.year,
|
||||
date_separator,
|
||||
time.month,
|
||||
date_separator,
|
||||
time.day);
|
||||
|
||||
DQN_ASSERT(result.hms_size < DQN_ARRAY_UCOUNT(result.hms));
|
||||
DQN_ASSERT(result.date_size < DQN_ARRAY_UCOUNT(result.date));
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$EXEC] Dqn_OSExec ========================================================================
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle)
|
||||
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8Now(char date_separator, char hms_separator)
|
||||
{
|
||||
Dqn_OSExecResult result = {};
|
||||
if (!handle.process || handle.os_error_code) {
|
||||
result.os_error_code = handle.os_error_code;
|
||||
Dqn_OSDateTime time = Dqn_OS_DateLocalTimeNow();
|
||||
Dqn_OSDateTimeStr8 result = Dqn_OS_DateLocalTimeStr8(time, date_separator, hms_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(DQN_OS_WIN32)
|
||||
DWORD exec_result = WaitForSingleObject(handle.process, INFINITE);
|
||||
if (exec_result == WAIT_FAILED) {
|
||||
result.os_error_code = GetLastError();
|
||||
DQN_API Dqn_Str8 Dqn_OS_EXEDir(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_Str8 exe_path = Dqn_OS_EXEPath(scratch.arena);
|
||||
Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")};
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverseArray(exe_path, separators, DQN_ARRAY_UCOUNT(separators));
|
||||
result = Dqn_Str8_Copy(arena, split.lhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
DWORD exit_status;
|
||||
if (!GetExitCodeProcess(handle.process, &exit_status)) {
|
||||
result.os_error_code = GetLastError();
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterS(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = Dqn_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
Dqn_f64 result = ticks / DQN_CAST(Dqn_f64)frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.exit_code = exit_status;
|
||||
CloseHandle(handle.process);
|
||||
#elif defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
DQN_ASSERTF(false, "Unsupported operation");
|
||||
#else
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterMs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = Dqn_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
Dqn_f64 result = (ticks * 1'000) / DQN_CAST(Dqn_f64)frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterUs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = Dqn_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
Dqn_f64 result = (ticks * 1'000'000) / DQN_CAST(Dqn_f64)frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterNs(uint64_t begin, uint64_t end)
|
||||
{
|
||||
uint64_t frequency = Dqn_OS_PerfCounterFrequency();
|
||||
uint64_t ticks = end - begin;
|
||||
Dqn_f64 result = (ticks * 1'000'000'000) / DQN_CAST(Dqn_f64)frequency;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DQN_API Dqn_OSTimer Dqn_OS_TimerBegin()
|
||||
{
|
||||
Dqn_OSTimer result = {};
|
||||
result.start = Dqn_OS_PerfCounterNow();
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_TimerEnd(Dqn_OSTimer *timer)
|
||||
{
|
||||
timer->end = Dqn_OS_PerfCounterNow();
|
||||
}
|
||||
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerS(Dqn_OSTimer timer)
|
||||
{
|
||||
Dqn_f64 result = Dqn_OS_PerfCounterS(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerMs(Dqn_OSTimer timer)
|
||||
{
|
||||
Dqn_f64 result = Dqn_OS_PerfCounterMs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerUs(Dqn_OSTimer timer)
|
||||
{
|
||||
Dqn_f64 result = Dqn_OS_PerfCounterUs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerNs(Dqn_OSTimer timer)
|
||||
{
|
||||
Dqn_f64 result = Dqn_OS_PerfCounterNs(timer.start, timer.end);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency)
|
||||
{
|
||||
uint64_t os_frequency = Dqn_OS_PerfCounterFrequency();
|
||||
uint64_t os_target_elapsed = duration_ms_to_gauge_tsc_frequency * os_frequency / 1000ULL;
|
||||
uint64_t tsc_begin = Dqn_CPU_TSC();
|
||||
uint64_t result = 0;
|
||||
if (tsc_begin) {
|
||||
uint64_t os_elapsed = 0;
|
||||
for (uint64_t os_begin = Dqn_OS_PerfCounterNow(); os_elapsed < os_target_elapsed; )
|
||||
os_elapsed = Dqn_OS_PerfCounterNow() - os_begin;
|
||||
uint64_t tsc_end = Dqn_CPU_TSC();
|
||||
uint64_t tsc_elapsed = tsc_end - tsc_begin;
|
||||
result = tsc_elapsed / os_elapsed * os_frequency;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_OS_FILE_API)
|
||||
// NOTE: [$FILE] Dqn_OSPathInfo/File ///////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_OS_WriteFile(Dqn_OSFile *file, Dqn_Str8 buffer)
|
||||
{
|
||||
bool result = Dqn_OS_WriteFileBuffer(file, buffer.data, buffer.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_WriteFileFV(Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
bool result = false;
|
||||
if (!file || !fmt)
|
||||
return result;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
result = Dqn_OS_WriteFileBuffer(file, buffer.data, buffer.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_WriteFileF(Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = Dqn_OS_WriteFileFV(file, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_OS_WriteAll(Dqn_Str8 path, Dqn_Str8 buffer)
|
||||
{
|
||||
Dqn_OSFile file = Dqn_OS_OpenFile(path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_Write);
|
||||
bool result = Dqn_OS_WriteFile(&file, buffer);
|
||||
Dqn_OS_CloseFile(&file);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_WriteAllFV(Dqn_Str8 file_path, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
bool result = Dqn_OS_WriteAll(file_path, buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_WriteAllF(Dqn_Str8 file_path, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = Dqn_OS_WriteAllFV(file_path, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_WriteAllSafe(Dqn_Str8 path, Dqn_Str8 buffer)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 tmp_path = Dqn_Str8_InitF(scratch.arena, "%.*s.tmp", DQN_STR_FMT(path));
|
||||
if (!Dqn_OS_WriteAll(tmp_path, buffer)) {
|
||||
Dqn_Log_ErrorF("Failed to write to temporary file [path=%.*s]", DQN_STR_FMT(tmp_path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Dqn_OS_FileCopy(tmp_path, path, true /*overwrite*/)) {
|
||||
Dqn_Log_ErrorF("Failed to overwrite file at '%.*s' with temporary file '%.*s' to complete the safe-write",
|
||||
DQN_STR_FMT(tmp_path),
|
||||
DQN_STR_FMT(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Dqn_OS_PathDelete(tmp_path)) {
|
||||
Dqn_Log_ErrorF("Failed to delete the temporary file at '%.*s' to clean-up the safe-write", DQN_STR_FMT(tmp_path));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
bool result = Dqn_OS_WriteAllSafe(path, buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_WriteAllSafeF(Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
bool result = Dqn_OS_WriteAllSafeFV(path, fmt, args);
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_NO_OS_FILE_API)
|
||||
|
||||
// NOTE: [$PATH] Dqn_OSPath ////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_OS_PathAddRef(Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path)
|
||||
{
|
||||
if (!arena || !fs_path || !Dqn_Str8_HasData(path))
|
||||
return false;
|
||||
|
||||
if (path.size <= 0)
|
||||
return true;
|
||||
|
||||
Dqn_Str8 const delimiter_array[] = {
|
||||
DQN_STR8("\\"),
|
||||
DQN_STR8("/")
|
||||
};
|
||||
|
||||
if (fs_path->links_size == 0) {
|
||||
fs_path->has_prefix_path_separator = (path.data[0] == '/');
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int status = 0;
|
||||
if (waitpid(DQN_CAST(pid_t)handle.process, &status, 0) < 0) {
|
||||
result.os_error_code = errno;
|
||||
Dqn_Str8BinarySplitResult delimiter = Dqn_Str8_BinarySplitArray(path, delimiter_array, DQN_ARRAY_UCOUNT(delimiter_array));
|
||||
for (; delimiter.lhs.data; delimiter = Dqn_Str8_BinarySplitArray(delimiter.rhs, delimiter_array, DQN_ARRAY_UCOUNT(delimiter_array))) {
|
||||
if (delimiter.lhs.size <= 0)
|
||||
continue;
|
||||
|
||||
Dqn_OSPathLink *link = Dqn_Arena_New(arena, Dqn_OSPathLink, Dqn_ZeroMem_Yes);
|
||||
if (!link)
|
||||
return false;
|
||||
|
||||
link->string = delimiter.lhs;
|
||||
link->prev = fs_path->tail;
|
||||
if (fs_path->tail) {
|
||||
fs_path->tail->next = link;
|
||||
} else {
|
||||
fs_path->head = link;
|
||||
}
|
||||
fs_path->tail = link;
|
||||
fs_path->links_size += 1;
|
||||
fs_path->string_size += delimiter.lhs.size;
|
||||
}
|
||||
|
||||
if (!delimiter.lhs.data)
|
||||
break;
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
result.exit_code = WEXITSTATUS(status);
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WIFSIGNALLED(status)) {
|
||||
result.os_error_code = WTERMSIG(status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Str8 cmd, Dqn_Str8 working_dir)
|
||||
DQN_API bool Dqn_OS_PathAdd(Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path)
|
||||
{
|
||||
Dqn_OSExecAsyncHandle result = {};
|
||||
if (cmd.size == 0)
|
||||
return result;
|
||||
|
||||
#if defined(DQN_OS_WIN32)
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str16 cmd16 = Dqn_Win_Str8ToStr16(scratch.arena, cmd);
|
||||
Dqn_Str16 working_dir16 = Dqn_Win_Str8ToStr16(scratch.arena, working_dir);
|
||||
|
||||
PROCESS_INFORMATION proc_info = {};
|
||||
STARTUPINFOW startup_info = {};
|
||||
startup_info.cb = sizeof(STARTUPINFOW);
|
||||
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
||||
BOOL create_result = CreateProcessW(nullptr, cmd16.data, nullptr, nullptr, true, 0, nullptr, working_dir16.data, &startup_info, &proc_info);
|
||||
if (!create_result) {
|
||||
result.os_error_code = GetLastError();
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(arena, path);
|
||||
bool result = Dqn_Str8_HasData(copy) ? true : Dqn_OS_PathAddRef(arena, fs_path, copy);
|
||||
return result;
|
||||
}
|
||||
|
||||
CloseHandle(proc_info.hThread);
|
||||
result.process = proc_info.hProcess;
|
||||
#else
|
||||
DQN_ASSERTF(false, "Unsupported operation");
|
||||
// TODO: This API will need to switch to an array of strings for unix
|
||||
#if 0
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid < 0) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (child_pid == 0) {
|
||||
if (working_dir.size) {
|
||||
if (chdir(working_dir.data) == -1) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (execvp(cmd.items[0], (char * const*) cmd_null.items) < 0) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
DQN_INVALID_CODE_PATH;
|
||||
}
|
||||
|
||||
result.process = DQN_CAST(void *)child_pid;
|
||||
#endif
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_Exec(Dqn_Str8 cmd, Dqn_Str8 working_dir)
|
||||
DQN_API bool Dqn_OS_PathAddF(Dqn_Arena *arena, Dqn_OSPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
Dqn_OSExecAsyncHandle async_handle = Dqn_OS_ExecAsync(cmd, working_dir);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Str8 path = Dqn_Str8_InitFV(arena, fmt, args);
|
||||
va_end(args);
|
||||
bool result = Dqn_OS_PathAddRef(arena, fs_path, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_PathPop(Dqn_OSPath *fs_path)
|
||||
{
|
||||
if (!fs_path)
|
||||
return false;
|
||||
|
||||
if (fs_path->tail) {
|
||||
DQN_ASSERT(fs_path->head);
|
||||
fs_path->links_size -= 1;
|
||||
fs_path->string_size -= fs_path->tail->string.size;
|
||||
fs_path->tail = fs_path->tail->prev;
|
||||
if (fs_path->tail) {
|
||||
fs_path->tail->next = nullptr;
|
||||
} else {
|
||||
fs_path->head = nullptr;
|
||||
}
|
||||
} else {
|
||||
DQN_ASSERT(!fs_path->head);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertTo(Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separator)
|
||||
{
|
||||
Dqn_OSPath fs_path = {};
|
||||
Dqn_OS_PathAddRef(arena, &fs_path, path);
|
||||
Dqn_Str8 result = Dqn_OS_PathBuildWithSeparator(arena, &fs_path, path_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertToF(Dqn_Arena *arena, Dqn_Str8 path_separator, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Str8 path = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
va_end(args);
|
||||
Dqn_Str8 result = Dqn_OS_PathConvertTo(arena, path, path_separator);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvert(Dqn_Arena *arena, Dqn_Str8 path)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_OS_PathConvertTo(arena, path, Dqn_OSPathSeperatorString);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertF(Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_Str8 path = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||
va_end(args);
|
||||
Dqn_Str8 result = Dqn_OS_PathConvert(arena, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathBuildWithSeparator(Dqn_Arena *arena, Dqn_OSPath const *fs_path, Dqn_Str8 path_separator)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!fs_path || fs_path->links_size <= 0)
|
||||
return result;
|
||||
|
||||
// NOTE: Each link except the last one needs the path separator appended to it, '/' or '\\'
|
||||
Dqn_usize string_size = (fs_path->has_prefix_path_separator ? path_separator.size : 0) + fs_path->string_size + ((fs_path->links_size - 1) * path_separator.size);
|
||||
result = Dqn_Str8_Alloc(arena, string_size, Dqn_ZeroMem_No);
|
||||
if (result.data) {
|
||||
char *dest = result.data;
|
||||
if (fs_path->has_prefix_path_separator) {
|
||||
DQN_MEMCPY(dest, path_separator.data, path_separator.size);
|
||||
dest += path_separator.size;
|
||||
}
|
||||
|
||||
for (Dqn_OSPathLink *link = fs_path->head; link; link = link->next) {
|
||||
Dqn_Str8 string = link->string;
|
||||
DQN_MEMCPY(dest, string.data, string.size);
|
||||
dest += string.size;
|
||||
|
||||
if (link != fs_path->tail) {
|
||||
DQN_MEMCPY(dest, path_separator.data, path_separator.size);
|
||||
dest += path_separator.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.data[string_size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_Exec(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir)
|
||||
{
|
||||
Dqn_OSExecAsyncHandle async_handle = Dqn_OS_ExecAsync(cmd_line, working_dir);
|
||||
Dqn_OSExecResult result = Dqn_OS_ExecWait(async_handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_ExecOrAbort(Dqn_Str8 cmd, Dqn_Str8 working_dir)
|
||||
DQN_API void Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir)
|
||||
{
|
||||
Dqn_OSExecResult result = Dqn_OS_Exec(cmd, working_dir);
|
||||
Dqn_OSExecResult result = Dqn_OS_Exec(cmd_line, working_dir);
|
||||
if (result.os_error_code || result.exit_code) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 cmd_combined = Dqn_Slice_Str8Render(scratch.arena, cmd_line, DQN_STR8(" ") /*separator*/);
|
||||
if (result.os_error_code) {
|
||||
Dqn_Log_ErrorF("OS failed to execute the requested command returning the error code %u. The command was\n\n%.*s", result.os_error_code, DQN_STR_FMT(cmd));
|
||||
Dqn_Log_ErrorF("OS failed to execute the requested command returning the error code %u. The command was\n\n%.*s", result.os_error_code, DQN_STR_FMT(cmd_combined));
|
||||
Dqn_OS_Exit(result.os_error_code);
|
||||
}
|
||||
|
||||
if (result.exit_code) {
|
||||
Dqn_Log_ErrorF("OS executed command and returned a non-zero status: %u. The command was\n\n%.*s", result.exit_code, DQN_STR_FMT(cmd));
|
||||
Dqn_Log_ErrorF("OS executed command and returned a non-zero status: %u. The command was\n\n%.*s", result.exit_code, DQN_STR_FMT(cmd_combined));
|
||||
Dqn_OS_Exit(result.exit_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: [$HTTP] Dqn_OSHttp ////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_OS_HttpRequestWait(Dqn_OSHttpResponse *response)
|
||||
{
|
||||
if (response && Dqn_OS_SemaphoreHasData(&response->on_complete_semaphore))
|
||||
Dqn_OS_SemaphoreWait(&response->on_complete_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
}
|
||||
|
||||
DQN_API Dqn_OSHttpResponse Dqn_OS_HttpRequest(Dqn_Arena *arena, Dqn_Str8 host, Dqn_Str8 path, Dqn_OSHttpRequestSecure secure, Dqn_Str8 method, Dqn_Str8 body, Dqn_Str8 headers)
|
||||
{
|
||||
// TODO(doyle): Revise the memory allocation and its lifetime
|
||||
Dqn_OSHttpResponse result = {};
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
result.scratch_arena = scratch.arena;
|
||||
|
||||
Dqn_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers);
|
||||
Dqn_OS_HttpRequestWait(&result);
|
||||
return result;
|
||||
}
|
||||
|
372
dqn_os.h
372
dqn_os.h
@ -1,7 +1,179 @@
|
||||
// NOTE: [$EXEC] Dqn_OSExec ========================================================================
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\
|
||||
// $$ / $$ |$$ / \__|
|
||||
// $$ | $$ |\$$$$$$\
|
||||
// $$ | $$ | \____$$\
|
||||
// $$ | $$ |$$\ $$ |
|
||||
// $$$$$$ |\$$$$$$ |
|
||||
// \______/ \______/
|
||||
//
|
||||
// dqn_os.h -- Common APIs/services provided by the operating system/platform layer
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$OMEM] Dqn_OSMem -- -- Memory allocation (typically virtual memory if supported)
|
||||
// [$DATE] Dqn_OSDate -- -- Date time APIs
|
||||
// [$FILE] Dqn_OSPathInfo/File -- -- File path info/reading/writing
|
||||
// [$PATH] Dqn_OSPath -- -- Construct native OS paths helpers
|
||||
// [$EXEC] Dqn_OSExec -- -- Execute programs programatically
|
||||
// [$SEMA] Dqn_OSSemaphore -- DQN_SEMAPHORE --
|
||||
// [$MUTX] Dqn_OSMutex -- --
|
||||
// [$THRD] Dqn_OSThread -- DQN_THREAD --
|
||||
// [$HTTP] Dqn_OSHttp -- --
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$OMEM] Dqn_OSMem //////////////////////////////////////////////////////////////////////////
|
||||
enum Dqn_OSMemCommit
|
||||
{
|
||||
Dqn_OSMemCommit_No,
|
||||
Dqn_OSMemCommit_Yes,
|
||||
};
|
||||
|
||||
enum Dqn_OSMemPage
|
||||
{
|
||||
// Exception on read/write with a page. This flag overrides the read/write
|
||||
// access.
|
||||
Dqn_OSMemPage_NoAccess = 1 << 0,
|
||||
|
||||
Dqn_OSMemPage_Read = 1 << 1, // Only read permitted on the page.
|
||||
|
||||
// Only write permitted on the page. On Windows this is not supported and
|
||||
// will be promoted to read+write permissions.
|
||||
Dqn_OSMemPage_Write = 1 << 2,
|
||||
|
||||
Dqn_OSMemPage_ReadWrite = Dqn_OSMemPage_Read | Dqn_OSMemPage_Write,
|
||||
|
||||
// Modifier used in conjunction with previous flags. Raises exception on
|
||||
// first access to the page, then, the underlying protection flags are
|
||||
// active. This is supported on Windows, on other OS's using this flag will
|
||||
// set the OS equivalent of Dqn_OSMemPage_NoAccess.
|
||||
// This flag must only be used in Dqn_OSMem_Protect
|
||||
Dqn_OSMemPage_Guard = 1 << 3,
|
||||
|
||||
// If leak tracing is enabled, this flag will allow the allocation recorded
|
||||
// from the reserve call to be leaked, e.g. not printed when leaks are
|
||||
// dumped to the console.
|
||||
Dqn_OSMemPage_AllocRecordLeakPermitted = 1 << 4,
|
||||
|
||||
// If leak tracing is enabled this flag will prevent any allocation record
|
||||
// from being created in the allocation table at all. If this flag is
|
||||
// enabled, 'OSMemPage_AllocRecordLeakPermitted' has no effect since the
|
||||
// record will never be created.
|
||||
Dqn_OSMemPage_NoAllocRecordEntry = 1 << 5,
|
||||
|
||||
// [INTERNAL] Do not use. All flags together do not constitute a correct
|
||||
// configuration of pages.
|
||||
Dqn_OSMemPage_All = Dqn_OSMemPage_NoAccess |
|
||||
Dqn_OSMemPage_ReadWrite |
|
||||
Dqn_OSMemPage_Guard |
|
||||
Dqn_OSMemPage_AllocRecordLeakPermitted |
|
||||
Dqn_OSMemPage_NoAllocRecordEntry,
|
||||
};
|
||||
|
||||
// NOTE: [$DATE] Dqn_OSDate ////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_OSDateTimeStr8
|
||||
{
|
||||
char date[DQN_ARRAY_UCOUNT("YYYY-MM-SS")];
|
||||
uint8_t date_size;
|
||||
char hms[DQN_ARRAY_UCOUNT("HH:MM:SS")];
|
||||
uint8_t hms_size;
|
||||
};
|
||||
|
||||
struct Dqn_OSDateTime
|
||||
{
|
||||
uint8_t day;
|
||||
uint8_t month;
|
||||
uint16_t year;
|
||||
uint8_t hour;
|
||||
uint8_t minutes;
|
||||
uint8_t seconds;
|
||||
};
|
||||
|
||||
struct Dqn_OSTimer /// Record time between two time-points using the OS's performance counter.
|
||||
{
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
};
|
||||
|
||||
#if !defined(DQN_NO_OS_FILE_API)
|
||||
// NOTE: [$FSYS] Dqn_OSFile ////////////////////////////////////////////////////////////////////////
|
||||
enum Dqn_OSPathInfoType
|
||||
{
|
||||
Dqn_OSPathInfoType_Unknown,
|
||||
Dqn_OSPathInfoType_Directory,
|
||||
Dqn_OSPathInfoType_File,
|
||||
};
|
||||
|
||||
struct Dqn_OSPathInfo
|
||||
{
|
||||
bool exists;
|
||||
Dqn_OSPathInfoType type;
|
||||
uint64_t create_time_in_s;
|
||||
uint64_t last_write_time_in_s;
|
||||
uint64_t last_access_time_in_s;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_OSFile
|
||||
{
|
||||
void *handle;
|
||||
char error[512];
|
||||
uint16_t error_size;
|
||||
};
|
||||
|
||||
enum Dqn_OSFileOpen
|
||||
{
|
||||
Dqn_OSFileOpen_CreateAlways, // Create file if it does not exist, otherwise, zero out the file and open
|
||||
Dqn_OSFileOpen_OpenIfExist, // Open file at path only if it exists
|
||||
Dqn_OSFileOpen_OpenAlways, // Open file at path, create file if it does not exist
|
||||
};
|
||||
|
||||
enum Dqn_OSFileAccess
|
||||
{
|
||||
Dqn_OSFileAccess_Read = 1 << 0,
|
||||
Dqn_OSFileAccess_Write = 1 << 1,
|
||||
Dqn_OSFileAccess_Execute = 1 << 2,
|
||||
Dqn_OSFileAccess_AppendOnly = 1 << 3, // This flag cannot be combined with any other access mode
|
||||
Dqn_OSFileAccess_ReadWrite = Dqn_OSFileAccess_Read | Dqn_OSFileAccess_Write,
|
||||
Dqn_OSFileAccess_All = Dqn_OSFileAccess_ReadWrite | Dqn_OSFileAccess_Execute,
|
||||
};
|
||||
#endif // DQN_NO_OS_FILE_API
|
||||
|
||||
// NOTE: Dqn_OSPath ////////////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(Dqn_OSPathSeperator)
|
||||
#if defined(DQN_OS_WIN32)
|
||||
#define Dqn_OSPathSeperator "\\"
|
||||
#else
|
||||
#define Dqn_OSPathSeperator "/"
|
||||
#endif
|
||||
#define Dqn_OSPathSeperatorString DQN_STR8(Dqn_OSPathSeperator)
|
||||
#endif
|
||||
|
||||
struct Dqn_OSPathLink
|
||||
{
|
||||
Dqn_Str8 string;
|
||||
Dqn_OSPathLink *next;
|
||||
Dqn_OSPathLink *prev;
|
||||
};
|
||||
|
||||
struct Dqn_OSPath
|
||||
{
|
||||
bool has_prefix_path_separator;
|
||||
Dqn_OSPathLink *head;
|
||||
Dqn_OSPathLink *tail;
|
||||
Dqn_usize string_size;
|
||||
uint16_t links_size;
|
||||
};
|
||||
|
||||
// NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_OSExecAsyncHandle
|
||||
{
|
||||
uint32_t os_error_code;
|
||||
uint32_t exit_code;
|
||||
void *process;
|
||||
};
|
||||
|
||||
@ -11,8 +183,200 @@ struct Dqn_OSExecResult
|
||||
uint32_t exit_code;
|
||||
};
|
||||
|
||||
#if !defined(DQN_NO_SEMAPHORE)
|
||||
// NOTE: [$SEMA] Dqn_OSSemaphore ///////////////////////////////////////////////////////////////////
|
||||
uint32_t const DQN_OS_SEMAPHORE_INFINITE_TIMEOUT = UINT32_MAX;
|
||||
|
||||
struct Dqn_OSSemaphore
|
||||
{
|
||||
#if defined(DQN_OS_WIN32) && !defined(DQN_OS_WIN32_USE_PTHREADS)
|
||||
void *win32_handle;
|
||||
#else
|
||||
sem_t posix_handle;
|
||||
bool posix_init;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum Dqn_OSSemaphoreWaitResult
|
||||
{
|
||||
Dqn_OSSemaphoreWaitResult_Failed,
|
||||
Dqn_OSSemaphoreWaitResult_Success,
|
||||
Dqn_OSSemaphoreWaitResult_Timeout,
|
||||
};
|
||||
#endif // !defined(DQN_NO_SEMAPHORE)
|
||||
|
||||
// NOTE: [$MUTX] Dqn_OSMutex ///////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_OSMutex
|
||||
{
|
||||
#if defined(DQN_OS_WIN32) && !defined(DQN_OS_WIN32_USE_PTHREADS)
|
||||
char win32_handle[48];
|
||||
#else
|
||||
pthread_mutex_t posix_handle;
|
||||
pthread_mutexattr_t posix_attribs;
|
||||
#endif
|
||||
};
|
||||
|
||||
// NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE)
|
||||
typedef int32_t (Dqn_OSThreadFunc)(struct Dqn_OSThread*);
|
||||
|
||||
struct Dqn_OSThread
|
||||
{
|
||||
void *handle;
|
||||
uint64_t thread_id;
|
||||
void *user_context;
|
||||
Dqn_OSThreadFunc *func;
|
||||
Dqn_OSSemaphore init_semaphore;
|
||||
};
|
||||
#endif // !defined(DQN_NO_THREAD)
|
||||
|
||||
// NOTE: [$HTTP] Dqn_OSHttp ////////////////////////////////////////////////////////////////////////
|
||||
enum Dqn_OSHttpRequestSecure
|
||||
{
|
||||
Dqn_OSHttpRequestSecure_No,
|
||||
Dqn_OSHttpRequestSecure_Yes,
|
||||
};
|
||||
|
||||
struct Dqn_OSHttpResponse
|
||||
{
|
||||
// NOTE: Response data
|
||||
uint32_t error_code;
|
||||
Dqn_Str8 error_msg;
|
||||
uint16_t http_status;
|
||||
Dqn_Str8 body;
|
||||
Dqn_b32 done;
|
||||
|
||||
// NOTE: Book-keeping
|
||||
Dqn_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.
|
||||
Dqn_Arena tmp_arena;
|
||||
Dqn_Arena *scratch_arena;
|
||||
Dqn_Str8Builder builder;
|
||||
Dqn_OSSemaphore on_complete_semaphore;
|
||||
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
emscripten_fetch_t *em_handle;
|
||||
#elif defined(DQN_OS_WIN32)
|
||||
HINTERNET win32_request_session;
|
||||
HINTERNET win32_request_connection;
|
||||
HINTERNET win32_request_handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
// NOTE: [$OMEM] Memory //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void * Dqn_OS_MemReserve (Dqn_usize size, Dqn_OSMemCommit commit, uint32_t page_flags);
|
||||
DQN_API bool Dqn_OS_MemCommit (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||
DQN_API void Dqn_OS_MemDecommit(void *ptr, Dqn_usize size);
|
||||
DQN_API void Dqn_OS_MemRelease (void *ptr, Dqn_usize size);
|
||||
DQN_API int Dqn_OS_MemProtect (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||
|
||||
// NOTE: [$DATE] Date //////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSDateTime Dqn_OS_DateLocalTimeNow ();
|
||||
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8Now(char date_separator = '-', char hms_separator = ':');
|
||||
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8 (Dqn_OSDateTime time, char date_separator = '-', char hms_separator = ':');
|
||||
DQN_API uint64_t Dqn_OS_DateUnixTime ();
|
||||
|
||||
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size);
|
||||
DQN_API Dqn_Str8 Dqn_OS_EXEPath (Dqn_Arena *arena);
|
||||
DQN_API Dqn_Str8 Dqn_OS_EXEDir (Dqn_Arena *arena);
|
||||
DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds);
|
||||
|
||||
// NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API uint64_t Dqn_OS_PerfCounterNow ();
|
||||
DQN_API uint64_t Dqn_OS_PerfCounterFrequency();
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterS (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterMs (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterUs (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterNs (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_OSTimer Dqn_OS_TimerBegin ();
|
||||
DQN_API void Dqn_OS_TimerEnd (Dqn_OSTimer *timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerS (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerMs (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerUs (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerNs (Dqn_OSTimer timer);
|
||||
DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency);
|
||||
|
||||
#if !defined(DQN_NO_OS_FILE_API)
|
||||
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo (Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_PathDelete(Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_FileCopy (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite);
|
||||
DQN_API bool Dqn_OS_FileMove (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite);
|
||||
DQN_API bool Dqn_OS_DirExists (Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_DirMake (Dqn_Str8 path);
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSFile Dqn_OS_OpenFile (Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint32_t access);
|
||||
DQN_API bool Dqn_OS_WriteFileBuffer(Dqn_OSFile *file, void const *data, Dqn_usize size);
|
||||
DQN_API bool Dqn_OS_WriteFile (Dqn_OSFile *file, Dqn_Str8 buffer);
|
||||
DQN_API bool Dqn_OS_WriteFileFV (Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_OS_WriteFileF (Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API void Dqn_OS_CloseFile (Dqn_OSFile *file);
|
||||
|
||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str8 Dqn_OS_ReadAll (Dqn_Str8 path, Dqn_Arena *arena);
|
||||
DQN_API bool Dqn_OS_WriteAll (Dqn_Str8 path, Dqn_Str8 buffer);
|
||||
DQN_API bool Dqn_OS_WriteAllFV (Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_OS_WriteAllF (Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_OS_WriteAllSafe (Dqn_Str8 path, Dqn_Str8 buffer);
|
||||
DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_OS_WriteAllSafeF (Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
#endif // !defined(DQN_NO_OS_FILE_API)
|
||||
|
||||
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_OS_PathAddRef (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_PathAdd (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path);
|
||||
DQN_API bool Dqn_OS_PathAddF (Dqn_Arena *arena, Dqn_OSPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_OS_PathPop (Dqn_OSPath *fs_path);
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathBuildWithSeparator(Dqn_Arena *arena, Dqn_OSPath const *fs_path, Dqn_Str8 path_separator);
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertTo (Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separtor);
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertToF (Dqn_Arena *arena, Dqn_Str8 path_separator, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvert (Dqn_Arena *arena, Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_OS_PathConvertF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define Dqn_OS_PathBuildFwdSlash(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, DQN_STR8("/"))
|
||||
#define Dqn_OS_PathBuildBackSlash(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, DQN_STR8("\\"))
|
||||
#if defined(DQN_OS_WIN32)
|
||||
#define Dqn_OS_PathBuild(allocator, fs_path) Dqn_OS_PathBuildBackSlash(allocator, fs_path)
|
||||
#else
|
||||
#define Dqn_OS_PathBuild(allocator, fs_path) Dqn_OS_PathBuildFwdSlash(allocator, fs_path)
|
||||
#endif
|
||||
|
||||
// NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_OS_Exit (uint32_t exit_code);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait (Dqn_OSExecAsyncHandle handle);
|
||||
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Str8 cmd, Dqn_Str8 working_dir);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Str8 cmd, Dqn_Str8 working_dir);
|
||||
DQN_API void Dqn_OS_ExecOrAbort(Dqn_Str8 cmd, Dqn_Str8 working_dir);
|
||||
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir);
|
||||
DQN_API void Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir);
|
||||
|
||||
// NOTE: [$SEMA] Dqn_OSSemaphore ///////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_SEMAPHORE)
|
||||
DQN_API Dqn_OSSemaphore Dqn_OS_SemaphoreInit (uint32_t initial_count);
|
||||
DQN_API bool Dqn_OS_SemaphoreIsValid (Dqn_OSSemaphore *semaphore);
|
||||
DQN_API void Dqn_OS_SemaphoreDeinit (Dqn_OSSemaphore *semaphore);
|
||||
DQN_API void Dqn_OS_SemaphoreIncrement(Dqn_OSSemaphore *semaphore, uint32_t amount);
|
||||
DQN_API Dqn_OSSemaphoreWaitResult Dqn_OS_SemaphoreWait (Dqn_OSSemaphore *semaphore, uint32_t timeout_ms);
|
||||
#endif // !defined(DQN_NO_SEMAPHORE)
|
||||
|
||||
// NOTE: [$MUTX] Dqn_OSMutex ///////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSMutex Dqn_OS_MutexInit (uint32_t initial_count, uint32_t max_count);
|
||||
DQN_API void Dqn_OS_MutexDeinit(Dqn_OSMutex *mutex);
|
||||
DQN_API void Dqn_OS_MutexLock (Dqn_OSMutex mutex);
|
||||
DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex mutex);
|
||||
|
||||
// NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE)
|
||||
DQN_API bool Dqn_OS_ThreadInit (Dqn_OSThread *thread, Dqn_OSThreadFunc *func, void *user_context);
|
||||
DQN_API void Dqn_OS_ThreadDeinit(Dqn_OSThread thread);
|
||||
DQN_API uint32_t Dqn_OS_ThreadID ();
|
||||
#endif // !defined(DQN_NO_THREAD)
|
||||
|
||||
// NOTE: [$HTTP] Dqn_OSHttp ////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response, Dqn_Arena *arena, Dqn_Str8 host, Dqn_Str8 path, Dqn_OSHttpRequestSecure secure, Dqn_Str8 method, Dqn_Str8 body, Dqn_Str8 headers);
|
||||
DQN_API void Dqn_OS_HttpRequestWait (Dqn_OSHttpResponse *response);
|
||||
DQN_API void Dqn_OS_HttpRequestFree (Dqn_OSHttpResponse *response);
|
||||
DQN_API Dqn_OSHttpResponse Dqn_OS_HttpRequest (Dqn_Arena *arena, Dqn_Str8 host, Dqn_Str8 path, Dqn_OSHttpRequestSecure secure, Dqn_Str8 method, Dqn_Str8 body, Dqn_Str8 headers);
|
||||
|
926
dqn_os_posix.cpp
Normal file
926
dqn_os_posix.cpp
Normal file
@ -0,0 +1,926 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\
|
||||
// $$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ \_$$ _|$$ | $$ |
|
||||
// $$ / $$ |$$ / \__| $$ | $$ |$$ / $$ |$$ / \__| $$ | \$$\ $$ |
|
||||
// $$ | $$ |\$$$$$$\ $$$$$$$ |$$ | $$ |\$$$$$$\ $$ | \$$$$ /
|
||||
// $$ | $$ | \____$$\ $$ ____/ $$ | $$ | \____$$\ $$ | $$ $$<
|
||||
// $$ | $$ |$$\ $$ | $$ | $$ | $$ |$$\ $$ | $$ | $$ /\$$\
|
||||
// $$$$$$ |\$$$$$$ | $$ | $$$$$$ |\$$$$$$ |$$$$$$\ $$ / $$ |
|
||||
// \______/ \______/ \__| \______/ \______/ \______|\__| \__|
|
||||
//
|
||||
// dqn_os_posix.cpp -- Posix implementation of the OS layer
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$VMEM] Dqn_OSMem //////////////////////////////////////////////////////////////////////////
|
||||
static uint32_t Dqn_OS_MemConvertPageToOSFlags_(uint32_t protect)
|
||||
{
|
||||
DQN_ASSERT((protect & ~Dqn_OSMemPage_All) == 0);
|
||||
DQN_ASSERT(protect != 0);
|
||||
uint32_t result = 0;
|
||||
|
||||
if (protect & (Dqn_OSMemPage_NoAccess | Dqn_OSMemPage_Guard)) {
|
||||
result = PROT_NONE;
|
||||
} else {
|
||||
if (protect & Dqn_OSMemPage_Read)
|
||||
result = PROT_READ;
|
||||
if (protect & Dqn_OSMemPage_Write)
|
||||
result = PROT_WRITE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void *Dqn_OS_MemReserve(Dqn_usize size, Dqn_OSMemCommit commit, uint32_t page_flags)
|
||||
{
|
||||
unsigned long os_page_flags = Dqn_OS_MemConvertPageToOSFlags_(page_flags);
|
||||
|
||||
if (commit == Dqn_OSMemCommit_Yes)
|
||||
os_page_flags |= (PROT_READ | PROT_WRITE);
|
||||
|
||||
void *result = mmap(nullptr, size, os_page_flags, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
if (result == MAP_FAILED)
|
||||
result = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_MemCommit(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
{
|
||||
bool result = false;
|
||||
if (!ptr || size == 0)
|
||||
return false;
|
||||
|
||||
unsigned long os_page_flags = Dqn_OS_MemConvertPageToOSFlags_(page_flags);
|
||||
result = mprotect(ptr, size, os_page_flags) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_MemDecommit(void *ptr, Dqn_usize size)
|
||||
{
|
||||
mprotect(ptr, size, PROT_NONE);
|
||||
madvise(ptr, size, MADV_FREE);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_MemRelease(void *ptr, Dqn_usize size)
|
||||
{
|
||||
munmap(ptr, size);
|
||||
}
|
||||
|
||||
DQN_API int Dqn_OS_MemProtect(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
{
|
||||
if (!ptr || size == 0)
|
||||
return 0;
|
||||
|
||||
static Dqn_Str8 const ALIGNMENT_ERROR_MSG =
|
||||
DQN_STR8("Page protection requires pointers to be page aligned because we "
|
||||
"can only guard memory at a multiple of the page boundary.");
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(DQN_CAST(uintptr_t)ptr, g_dqn_library->os_page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(size, g_dqn_library->os_page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||
|
||||
unsigned long os_page_flags = Dqn_OS_MemConvertPageToOSFlags_(page_flags);
|
||||
int result = mprotect(ptr, size, os_page_flags);
|
||||
DQN_ASSERTF(result == 0, "mprotect failed (%d)", errno);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$DATE] Date //////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSDateTime Dqn_OS_DateLocalTimeNow()
|
||||
{
|
||||
Dqn_OSDateTime result = {};
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
|
||||
// NOTE: localtime_r is used because it is thread safe
|
||||
// See: https://linux.die.net/man/3/localtime
|
||||
// According to POSIX.1-2004, localtime() is required to behave as though
|
||||
// tzset(3) was called, while localtime_r() does not have this requirement.
|
||||
// For portable code tzset(3) should be called before localtime_r().
|
||||
for (static bool once = true; once; once = false)
|
||||
tzset();
|
||||
|
||||
struct tm time = {};
|
||||
localtime_r(&ts.tv_sec, &time);
|
||||
|
||||
result.hour = time.tm_hour;
|
||||
result.minutes = time.tm_min;
|
||||
result.seconds = time.tm_sec;
|
||||
|
||||
result.day = DQN_CAST(uint8_t)time.tm_mday;
|
||||
result.month = DQN_CAST(uint8_t)time.tm_mon + 1;
|
||||
result.year = 1900 + DQN_CAST(int16_t)time.tm_year;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_OS_DateUnixTime()
|
||||
{
|
||||
uint64_t result = time(nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_SecureRNGBytes(void *buffer, uint32_t size)
|
||||
{
|
||||
if (!buffer || size < 0)
|
||||
return false;
|
||||
|
||||
if (size == 0)
|
||||
return true;
|
||||
|
||||
DQN_ASSERTF(size <= 32,
|
||||
"We can increase this by chunking the buffer and filling 32 bytes at a time. *Nix guarantees 32 "
|
||||
"bytes can always be fulfilled by this system at a time");
|
||||
// TODO(doyle): https://github.com/jedisct1/libsodium/blob/master/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c
|
||||
// TODO(doyle): https://man7.org/linux/man-pages/man2/getrandom.2.html
|
||||
uint32_t read_bytes = 0;
|
||||
do {
|
||||
read_bytes = getrandom(buffer, size, 0); // NOTE: EINTR can not be triggered if size <= 32 bytes
|
||||
} while (read_bytes != size || errno == EAGAIN);
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_OS_EXEPath(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
int required_size_wo_null_terminator = 0;
|
||||
for (int try_size = 128;; try_size *= 2) {
|
||||
auto scoped_arena = Dqn_ArenaTempMemScope(arena);
|
||||
char *try_buf = Dqn_Arena_NewArray(arena, char, try_size, Dqn_ZeroMem_No);
|
||||
int bytes_written = readlink("/proc/self/exe", try_buf, try_size);
|
||||
if (bytes_written == -1) {
|
||||
// Failed, we're unable to determine the executable directory
|
||||
break;
|
||||
} else if (bytes_written == try_size) {
|
||||
// Try again, if returned size was equal- we may of prematurely
|
||||
// truncated according to the man pages
|
||||
continue;
|
||||
} else {
|
||||
// readlink will give us the path to the executable. Once we
|
||||
// determine the correct buffer size required to get the full file
|
||||
// path, we do some post-processing on said string and extract just
|
||||
// the directory.
|
||||
|
||||
// TODO(dqn): It'd be nice if there's some way of keeping this
|
||||
// try_buf around, memcopy the byte and trash the try_buf from the
|
||||
// arena. Instead we just get the size and redo the call one last
|
||||
// time after this "calculate" step.
|
||||
DQN_ASSERTF(bytes_written < try_size, "bytes_written can never be greater than the try size, function writes at most try_size");
|
||||
required_size_wo_null_terminator = bytes_written;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (required_size_wo_null_terminator) {
|
||||
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(arena);
|
||||
char *exe_path = Dqn_Arena_NewArray(arena, char, required_size_wo_null_terminator + 1, Dqn_ZeroMem_No);
|
||||
exe_path[required_size_wo_null_terminator] = 0;
|
||||
|
||||
int bytes_written = readlink("/proc/self/exe", exe_path, required_size_wo_null_terminator);
|
||||
if (bytes_written == -1) {
|
||||
// Note that if read-link fails again can be because there's
|
||||
// a potential race condition here, our exe or directory could have
|
||||
// been deleted since the last call, so we need to be careful.
|
||||
Dqn_Arena_TempMemEnd(temp_mem);
|
||||
} else {
|
||||
result = Dqn_Str8_Init(exe_path, required_size_wo_null_terminator);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_OS_PerfCounterFrequency()
|
||||
{
|
||||
// NOTE: On Linux we use clock_gettime(CLOCK_MONOTONIC_RAW) which
|
||||
// increments at nanosecond granularity.
|
||||
uint64_t result = 1'000'000'000;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API uint64_t Dqn_OS_PerfCounterNow()
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
uint64_t result = DQN_CAST(uint64_t) ts.tv_sec * 1'000'000'000 + DQN_CAST(uint64_t) ts.tv_nsec;
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_OS_FILE_API)
|
||||
DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo(Dqn_Str8 path)
|
||||
{
|
||||
Dqn_OSPathInfo result = {};
|
||||
if (!Dqn_Str8_HasData(path))
|
||||
return result;
|
||||
|
||||
struct stat file_stat;
|
||||
if (lstat(path.data, &file_stat) != -1) {
|
||||
result.exists = true;
|
||||
result.size = file_stat.st_size;
|
||||
result.last_access_time_in_s = file_stat.st_atime;
|
||||
result.last_write_time_in_s = file_stat.st_mtime;
|
||||
// TODO(dqn): Seems linux does not support creation time via stat. We
|
||||
// shoddily deal with this.
|
||||
result.create_time_in_s = DQN_MIN(result.last_access_time_in_s, result.last_write_time_in_s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path)
|
||||
{
|
||||
bool result = false;
|
||||
if (!Dqn_Str8_HasData(path))
|
||||
return result;
|
||||
|
||||
struct stat stat_result;
|
||||
if (lstat(path.data, &stat_result) != -1)
|
||||
result = S_ISREG(stat_result.st_mode) || S_ISLNK(stat_result.st_mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_FileCopy(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite)
|
||||
{
|
||||
bool result = false;
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
DQN_ASSERTF(false, "Unsupported on Emscripten because of their VFS model");
|
||||
#else
|
||||
int src_fd = open(src.data, O_RDONLY);
|
||||
int dest_fd = open(dest.data, O_WRONLY | O_CREAT | (overwrite ? O_TRUNC : 0));
|
||||
|
||||
if (src_fd != -1 && dest_fd != -1) {
|
||||
struct stat stat_existing;
|
||||
fstat(src_fd, &stat_existing);
|
||||
ssize_t bytes_written = sendfile64(dest_fd, src_fd, 0, stat_existing.st_size);
|
||||
result = (bytes_written == stat_existing.st_size);
|
||||
}
|
||||
|
||||
if (src_fd != -1)
|
||||
close(src_fd);
|
||||
|
||||
if (dest_fd != -1)
|
||||
close(dest_fd);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_FileMove(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite)
|
||||
{
|
||||
// See: https://github.com/gingerBill/gb/blob/master/gb.h
|
||||
bool result = false;
|
||||
bool file_moved = true;
|
||||
if (link(src.data, dest.data) == -1) {
|
||||
// NOTE: Link can fail if we're trying to link across different volumes
|
||||
// so we fall back to a binary directory.
|
||||
file_moved |= Dqn_OS_FileCopy(src, dest, overwrite);
|
||||
}
|
||||
|
||||
if (file_moved)
|
||||
result = (unlink(src.data) != -1); // Remove original file
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_DirExists(Dqn_Str8 path)
|
||||
{
|
||||
bool result = false;
|
||||
if (!Dqn_Str8_HasData(path))
|
||||
return result;
|
||||
|
||||
struct stat stat_result;
|
||||
if (lstat(path.data, &stat_result) != -1)
|
||||
result = S_ISDIR(stat_result.st_mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_DirMake(Dqn_Str8 path)
|
||||
{
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
bool result = true;
|
||||
|
||||
// TODO(doyle): Implement this without using the path indexes, it's not
|
||||
// necessary. See Windows implementation.
|
||||
Dqn_usize path_indexes_size = 0;
|
||||
uint16_t path_indexes[64] = {};
|
||||
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(scratch.arena, path);
|
||||
for (Dqn_usize index = copy.size - 1; index < copy.size; index--) {
|
||||
bool first_char = index == (copy.size - 1);
|
||||
char ch = copy.data[index];
|
||||
if (ch == '/' || first_char) {
|
||||
char temp = copy.data[index];
|
||||
|
||||
if (!first_char)
|
||||
copy.data[index] = 0; // Temporarily null terminate it
|
||||
|
||||
bool is_file = Dqn_OS_FileExists(copy);
|
||||
|
||||
if (!first_char)
|
||||
copy.data[index] = temp; // Undo null termination
|
||||
|
||||
if (is_file) {
|
||||
// NOTE: There's something that exists in at this path, but
|
||||
// it's not a directory. This request to make a directory is
|
||||
// invalid.
|
||||
return false;
|
||||
} else {
|
||||
if (Dqn_OS_DirExists(copy)) {
|
||||
// NOTE: We found a directory, we can stop here and start
|
||||
// building up all the directories that didn't exist up to
|
||||
// this point.
|
||||
break;
|
||||
} else {
|
||||
// NOTE: There's nothing that exists at this path, we can
|
||||
// create a directory here
|
||||
path_indexes[path_indexes_size++] = DQN_CAST(uint16_t)index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Dqn_usize index = path_indexes_size - 1; result && index < path_indexes_size; index--) {
|
||||
uint16_t path_index = path_indexes[index];
|
||||
char temp = copy.data[path_index];
|
||||
|
||||
if (index != 0) copy.data[path_index] = 0;
|
||||
result |= mkdir(copy.data, 0774) == 0;
|
||||
if (index != 0) copy.data[path_index] = temp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_PathDelete(Dqn_Str8 path)
|
||||
{
|
||||
bool result = false;
|
||||
if (Dqn_Str8_HasData(path))
|
||||
result = remove(path.data) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSFile Dqn_OS_OpenFile(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint32_t access)
|
||||
{
|
||||
Dqn_OSFile result = {};
|
||||
if (!Dqn_Str8_HasData(path) || path.size <= 0)
|
||||
return result;
|
||||
|
||||
if ((access & ~Dqn_OSFileAccess_All) || ((access & Dqn_OSFileAccess_All) == 0)) {
|
||||
DQN_INVALID_CODE_PATH;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (access & Dqn_OSFileAccess_Execute) {
|
||||
result.error_size = DQN_CAST(uint16_t) Dqn_SNPrintFDotTruncate(
|
||||
result.error,
|
||||
DQN_ARRAY_UCOUNT(result.error),
|
||||
"Open file failed: execute access not supported for \"%.*s\"",
|
||||
DQN_STR_FMT(path));
|
||||
DQN_INVALID_CODE_PATH; // TODO: Not supported via fopen
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: fopen interface is not as expressive as the Win32
|
||||
// We will fopen the file beforehand to setup the state/check for validity
|
||||
// before closing and reopening it if valid with the correct request access
|
||||
// permissions.
|
||||
{
|
||||
FILE *handle = nullptr;
|
||||
switch (open_mode) {
|
||||
case Dqn_OSFileOpen_CreateAlways: handle = fopen(path.data, "w"); break;
|
||||
case Dqn_OSFileOpen_OpenIfExist: handle = fopen(path.data, "r"); break;
|
||||
case Dqn_OSFileOpen_OpenAlways: handle = fopen(path.data, "a"); break;
|
||||
default: DQN_INVALID_CODE_PATH; break;
|
||||
}
|
||||
if (!handle) {
|
||||
result.error_size = DQN_CAST(uint16_t)Dqn_SNPrintFDotTruncate(
|
||||
result.error,
|
||||
DQN_ARRAY_UCOUNT(result.error),
|
||||
"Open file failed: Could not open file in requested mode %d for \"%.*s\"",
|
||||
open_mode,
|
||||
DQN_STR_FMT(path));
|
||||
return result;
|
||||
}
|
||||
fclose(handle);
|
||||
}
|
||||
|
||||
char const *fopen_mode = nullptr;
|
||||
if (access & Dqn_OSFileAccess_AppendOnly) {
|
||||
fopen_mode = "a+";
|
||||
} else if (access & Dqn_OSFileAccess_Write) {
|
||||
fopen_mode = "w+";
|
||||
} else if (access & Dqn_OSFileAccess_Read) {
|
||||
fopen_mode = "r+";
|
||||
}
|
||||
|
||||
FILE *handle = fopen(path.data, fopen_mode);
|
||||
if (!handle) {
|
||||
result.error_size = DQN_CAST(uint16_t) Dqn_SNPrintFDotTruncate(
|
||||
result.error,
|
||||
DQN_ARRAY_UCOUNT(result.error),
|
||||
"Open file failed: Could not open file in fopen mode \"%s\" for \"%.*s\"",
|
||||
fopen_mode,
|
||||
DQN_STR_FMT(path));
|
||||
return result;
|
||||
}
|
||||
result.handle = handle;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_WriteFileBuffer(Dqn_OSFile *file, void const *buffer, Dqn_usize size)
|
||||
{
|
||||
if (!file || !file->handle || !buffer || size <= 0 || file->error_size)
|
||||
return false;
|
||||
bool result = fwrite(buffer, DQN_CAST(Dqn_usize)size, 1 /*count*/, DQN_CAST(FILE *)file->handle) == 1 /*count*/;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_CloseFile(Dqn_OSFile *file)
|
||||
{
|
||||
if (!file || !file->handle || file->error_size)
|
||||
return;
|
||||
fclose(DQN_CAST(FILE *)file->handle);
|
||||
*file = {};
|
||||
}
|
||||
|
||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Str8 path, Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!arena)
|
||||
return result;
|
||||
|
||||
Dqn_ArenaTempMemScope temp_mem = Dqn_ArenaTempMemScope(arena);
|
||||
FILE *file_handle = fopen(path.data, "rb");
|
||||
if (!file_handle) {
|
||||
Dqn_Log_ErrorF("Failed to open file '%.*s' using fopen", DQN_STR_FMT(path));
|
||||
return result;
|
||||
}
|
||||
DQN_DEFER { fclose(file_handle); };
|
||||
|
||||
fseek(file_handle, 0, SEEK_END);
|
||||
Dqn_usize file_size = ftell(file_handle);
|
||||
|
||||
if (DQN_CAST(long)(file_size) == -1L) {
|
||||
Dqn_Log_ErrorF("Failed to determine '%.*s' file size using ftell", DQN_STR_FMT(path));
|
||||
return result;
|
||||
}
|
||||
|
||||
rewind(file_handle);
|
||||
Dqn_Str8 buffer = Dqn_Str8_Alloc(arena, file_size, Dqn_ZeroMem_No);
|
||||
if (!buffer.data) {
|
||||
Dqn_Log_ErrorF("Failed to allocate %zu bytes to read file '%.*s'", file_size + 1, DQN_STR_FMT(path));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (fread(buffer.data, file_size, 1, file_handle) != 1) {
|
||||
Dqn_Log_ErrorF("Failed to read %zu bytes into buffer from '%.*s'", file_size, DQN_STR_FMT(path));
|
||||
return result;
|
||||
}
|
||||
|
||||
buffer.data[file_size] = 0;
|
||||
result = buffer;
|
||||
temp_mem.mem = {};
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_NO_OS_FILE_API)
|
||||
|
||||
// NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_OS_Exit(uint32_t exit_code)
|
||||
{
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle)
|
||||
{
|
||||
Dqn_OSExecResult result = {};
|
||||
if (!handle.process || handle.os_error_code) {
|
||||
result.os_error_code = handle.os_error_code;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (handle.exit_code) {
|
||||
result.exit_code = handle.exit_code;
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
DQN_ASSERTF(false, "Unsupported operation");
|
||||
#else
|
||||
static_assert(sizeof(pid_t) <= sizeof(handle.process), "We store the PID opaquely in a register sized pointer");
|
||||
pid_t process = {};
|
||||
DQN_MEMCPY(&process, &handle.process, sizeof(process));
|
||||
for (;;) {
|
||||
int status = 0;
|
||||
if (waitpid(process, &status, 0) < 0) {
|
||||
result.os_error_code = errno;
|
||||
break;
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
result.exit_code = WEXITSTATUS(status);
|
||||
break;
|
||||
}
|
||||
|
||||
if (WIFSIGNALED(status)) {
|
||||
result.os_error_code = WTERMSIG(status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir)
|
||||
{
|
||||
Dqn_OSExecAsyncHandle result = {};
|
||||
if (cmd_line.size == 0)
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
// TODO: This API will need to switch to an array of strings for unix
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid < 0) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (child_pid == 0) {
|
||||
|
||||
// NOTE: Convert the command into something suitable for execvp
|
||||
char **argv = Dqn_Arena_NewArray(scratch.arena, char*, cmd_line.size + 1 /*null*/, Dqn_ZeroMem_Yes);
|
||||
if (!argv) {
|
||||
result.exit_code = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
for (Dqn_usize arg_index = 0; arg_index < cmd_line.size; arg_index++) {
|
||||
Dqn_Str8 arg = cmd_line.data[arg_index];
|
||||
argv[arg_index] = Dqn_Str8_Copy(scratch.arena, arg).data; // NOTE: Copy string to guarantee it is null-terminated
|
||||
}
|
||||
|
||||
// NOTE: Change the working directory if there is one
|
||||
char *prev_working_dir = nullptr;
|
||||
DQN_DEFER {
|
||||
if (!prev_working_dir)
|
||||
return;
|
||||
if (result.os_error_code == 0)
|
||||
chdir(prev_working_dir);
|
||||
free(prev_working_dir);
|
||||
};
|
||||
|
||||
if (working_dir.size) {
|
||||
prev_working_dir = get_current_dir_name();
|
||||
if (chdir(working_dir.data) == -1) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Execute the command. We reuse argv because the first arg, the
|
||||
// binary to execute is guaranteed to be null-terminated.
|
||||
if (execvp(argv[0], argv) < 0) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
DQN_MEMCPY(&result.process, &child_pid, sizeof(child_pid));
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(DQN_NO_SEMAPHORE)
|
||||
// NOTE: [$SEMA] Dqn_OSSemaphore ///////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSSemaphore Dqn_OS_SemaphoreInit(uint32_t initial_count)
|
||||
{
|
||||
Dqn_OSSemaphore result = {};
|
||||
int pshared = 0; // Share the semaphore across all threads in the process
|
||||
if (sem_init(&result.posix_handle, pshared, initial_count) == 0)
|
||||
result.posix_init = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_SemaphoreIsValid(Dqn_OSSemaphore *semaphore)
|
||||
{
|
||||
bool result = false;
|
||||
if (semaphore) {
|
||||
result = semaphore->posix_init;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_SemaphoreDeinit(Dqn_OSSemaphore *semaphore)
|
||||
{
|
||||
if (!Dqn_OS_SemaphoreIsValid(semaphore))
|
||||
return;
|
||||
// TODO(doyle): Error handling?
|
||||
if (semaphore->posix_init)
|
||||
sem_destroy(&semaphore->posix_handle);
|
||||
*semaphore = {};
|
||||
}
|
||||
|
||||
// NOTE: These functions don't need semaphore to be passed by pointer, **BUT**
|
||||
// the POSIX implementation disallows copies of sem_t. In particular:
|
||||
//
|
||||
// Source: The Open Group Base Specifications Issue 7, 2018 edition
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_09
|
||||
//
|
||||
// 2.9.9 Synchronization Object Copies and Alternative Mappings
|
||||
//
|
||||
// For barriers, condition variables, mutexes, and read-write locks, [TSH]
|
||||
// [Option Start] if the process-shared attribute is set to
|
||||
// PTHREAD_PROCESS_PRIVATE, [Option End] only the synchronization object at the
|
||||
// address used to initialize it can be used for performing synchronization. The
|
||||
// effect of referring to another mapping of the same object when locking,
|
||||
// unlocking, or destroying the object is undefined. [...] The effect of
|
||||
// referring to a copy of the object when locking, unlocking, or destroying it
|
||||
// is undefined.
|
||||
|
||||
DQN_API void Dqn_OS_SemaphoreIncrement(Dqn_OSSemaphore *semaphore, uint32_t amount)
|
||||
{
|
||||
if (!Dqn_OS_SemaphoreIsValid(semaphore))
|
||||
return;
|
||||
|
||||
#if defined(DQN_OS_WIN32)
|
||||
sem_post_multiple(&semaphore->posix_handle, amount); // mingw extension
|
||||
#else
|
||||
DQN_FOR_UINDEX(index, amount)
|
||||
sem_post(&semaphore->posix_handle);
|
||||
#endif // !defined(DQN_OS_WIN32)
|
||||
}
|
||||
|
||||
DQN_API Dqn_OSSemaphoreWaitResult Dqn_OS_SemaphoreWait(Dqn_OSSemaphore *semaphore, uint32_t timeout_ms)
|
||||
{
|
||||
Dqn_OSSemaphoreWaitResult result = {};
|
||||
if (!Dqn_OS_SemaphoreIsValid(semaphore))
|
||||
return result;
|
||||
|
||||
if (timeout_ms == DQN_OS_SEMAPHORE_INFINITE_TIMEOUT) {
|
||||
int wait_result = 0;
|
||||
do {
|
||||
wait_result = sem_wait(&semaphore->posix_handle);
|
||||
} while (wait_result == -1 && errno == EINTR);
|
||||
|
||||
if (wait_result == 0)
|
||||
result = Dqn_OSSemaphoreWaitResult_Success;
|
||||
} else {
|
||||
struct timespec abs_timeout = {};
|
||||
abs_timeout.tv_sec = timeout_ms / 1000;
|
||||
abs_timeout.tv_nsec = (timeout_ms % 1000) * 1'000'000;
|
||||
if (sem_timedwait(&semaphore->posix_handle, &abs_timeout) == 0) {
|
||||
result = Dqn_OSSemaphoreWaitResult_Success;
|
||||
} else {
|
||||
if (errno == ETIMEDOUT)
|
||||
result = Dqn_OSSemaphoreWaitResult_Timeout;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_NO_SEMAPHORE)
|
||||
|
||||
#if !defined(DQN_NO_THREAD)
|
||||
// NOTE: [$MUTX] Dqn_OSMutex ///////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSMutex Dqn_OS_MutexInit()
|
||||
{
|
||||
Dqn_OSMutex result = {};
|
||||
if (pthread_mutexattr_init(&result.posix_attribs) != 0)
|
||||
return result;
|
||||
if (pthread_mutex_init(&result.posix_handle, &result.posix_attribs) != 0)
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_MutexDeinit(Dqn_OSMutex *mutex)
|
||||
{
|
||||
if (!mutex)
|
||||
return;
|
||||
pthread_mutexattr_destroy(&mutex->posix_attribs);
|
||||
pthread_mutex_destroy(&mutex->posix_handle);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_MutexLock(Dqn_OSMutex *mutex)
|
||||
{
|
||||
if (!mutex)
|
||||
return;
|
||||
pthread_mutex_lock(&mutex->posix_handle);
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex *mutex)
|
||||
{
|
||||
if (!mutex)
|
||||
return;
|
||||
pthread_mutex_unlock(&mutex->posix_handle);
|
||||
}
|
||||
|
||||
// NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
|
||||
static void *Dqn_OS_ThreadFunc_(void *user_context)
|
||||
{
|
||||
Dqn_OSThread *thread = DQN_CAST(Dqn_OSThread *)user_context;
|
||||
Dqn_OS_SemaphoreWait(&thread->init_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
thread->func(thread);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_ThreadInit(Dqn_OSThread *thread, Dqn_OSThreadFunc *func, void *user_context)
|
||||
{
|
||||
bool result = false;
|
||||
if (!thread)
|
||||
return result;
|
||||
|
||||
thread->func = func;
|
||||
thread->user_context = user_context;
|
||||
thread->init_semaphore = Dqn_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
|
||||
// TODO(doyle): Check if semaphore is valid
|
||||
// NOTE: pthread_t is essentially the thread ID. In Windows, the handle and
|
||||
// the ID are different things. For pthreads then we just duplicate the
|
||||
// thread ID to both variables
|
||||
pthread_t p_thread = {};
|
||||
static_assert(sizeof(p_thread) <= sizeof(thread->handle),
|
||||
"We store the thread handle opaquely in our abstraction, "
|
||||
"there must be enough bytes to store pthread's structure");
|
||||
static_assert(sizeof(p_thread) <= sizeof(thread->thread_id),
|
||||
"We store the thread handle opaquely in our abstraction, "
|
||||
"there must be enough bytes to store pthread's structure");
|
||||
|
||||
pthread_attr_t attribs = {};
|
||||
pthread_attr_init(&attribs);
|
||||
result = pthread_create(&p_thread, &attribs, Dqn_OS_ThreadFunc_, thread) == 0;
|
||||
pthread_attr_destroy(&attribs);
|
||||
|
||||
if (result) {
|
||||
DQN_MEMCPY(&thread->handle, &p_thread, sizeof(p_thread));
|
||||
DQN_MEMCPY(&thread->thread_id, &p_thread, sizeof(p_thread));
|
||||
}
|
||||
|
||||
if (result) {
|
||||
Dqn_OS_SemaphoreIncrement(&thread->init_semaphore, 1);
|
||||
} else {
|
||||
Dqn_OS_SemaphoreDeinit(&thread->init_semaphore);
|
||||
*thread = {};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_ThreadDeinit(Dqn_OSThread *thread)
|
||||
{
|
||||
if (!thread || !thread->handle)
|
||||
return;
|
||||
|
||||
pthread_t thread_id = {};
|
||||
DQN_MEMCPY(&thread_id, &thread->thread_id, sizeof(thread_id));
|
||||
|
||||
void *return_val = nullptr;
|
||||
pthread_join(thread_id, &return_val);
|
||||
thread->handle = {};
|
||||
thread->thread_id = {};
|
||||
}
|
||||
|
||||
DQN_API uint32_t Dqn_OS_ThreadID()
|
||||
{
|
||||
pid_t result = gettid();
|
||||
DQN_ASSERT(gettid() >= 0);
|
||||
return DQN_CAST(uint32_t)result;
|
||||
}
|
||||
#endif // !defined(DQN_NO_THREAD)
|
||||
|
||||
// NOTE: [$HTTP] Dqn_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)
|
||||
// Dqn_Log_InfoF("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) {
|
||||
Dqn_Log_InfoF("Received: %.*s", event->numBytes, event->data);
|
||||
} else {
|
||||
Dqn_Log_InfoF("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(DQN_PLATFORM_EMSCRIPTEN)
|
||||
static void Dqn_OS_HttpRequestEMFetchOnSuccessCallback(emscripten_fetch_t *fetch)
|
||||
{
|
||||
Dqn_OSHttpResponse *response = DQN_CAST(Dqn_OSHttpResponse *)fetch->userData;
|
||||
if (!DQN_CHECK(response))
|
||||
return;
|
||||
|
||||
response->http_status = DQN_CAST(uint32_t)fetch->status;
|
||||
response->body = Dqn_Str8_Alloc(response->arena, fetch->numBytes, Dqn_ZeroMem_No);
|
||||
if (response->body.data)
|
||||
DQN_MEMCPY(response->body.data, fetch->data, fetch->numBytes);
|
||||
|
||||
Dqn_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
|
||||
Dqn_Atomic_AddU32(&response->done, 1);
|
||||
}
|
||||
|
||||
static void Dqn_OS_HttpRequestEMFetchOnErrorCallback(emscripten_fetch_t *fetch)
|
||||
{
|
||||
Dqn_OSHttpResponse *response = DQN_CAST(Dqn_OSHttpResponse *)fetch->userData;
|
||||
if (!DQN_CHECK(response))
|
||||
return;
|
||||
|
||||
response->http_status = DQN_CAST(uint32_t)fetch->status;
|
||||
response->body = Dqn_Str8_Alloc(response->arena, fetch->numBytes, Dqn_ZeroMem_No);
|
||||
if (response->body.size)
|
||||
DQN_MEMCPY(response->body.data, fetch->data, fetch->numBytes);
|
||||
|
||||
Dqn_OS_SemaphoreIncrement(&response->on_complete_semaphore, 1);
|
||||
Dqn_Atomic_AddU32(&response->done, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response,
|
||||
Dqn_Arena *arena,
|
||||
Dqn_Str8 host,
|
||||
Dqn_Str8 path,
|
||||
Dqn_OSHttpRequestSecure secure,
|
||||
Dqn_Str8 method,
|
||||
Dqn_Str8 body,
|
||||
Dqn_Str8 headers)
|
||||
{
|
||||
if (!response || !arena)
|
||||
return;
|
||||
|
||||
response->arena = arena;
|
||||
response->builder.arena = response->scratch_arena ? response->scratch_arena : &response->tmp_arena;
|
||||
|
||||
Dqn_Arena *scratch_arena = response->scratch_arena;
|
||||
Dqn_Scratch scratch_ = Dqn_Scratch_Get(arena);
|
||||
if (!scratch_arena)
|
||||
scratch_arena = scratch_.arena;
|
||||
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
emscripten_fetch_attr_t fetch_attribs = {};
|
||||
emscripten_fetch_attr_init(&fetch_attribs);
|
||||
|
||||
if (method.size >= sizeof(fetch_attribs.requestMethod)) {
|
||||
response->error_msg = Dqn_Str8_InitF(arena,
|
||||
"Request method in EM has a size limit of 31 characters, method was '%.*s' which is %zu characters long",
|
||||
DQN_STR_FMT(method),
|
||||
method.size);
|
||||
DQN_CHECKF(method.size < sizeof(fetch_attribs.requestMethod), "%.*s", DQN_STR_FMT(response->error_msg));
|
||||
response->error_code = DQN_CAST(uint32_t)-1;
|
||||
Dqn_Atomic_AddU32(&response->done, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
DQN_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 = Dqn_OS_HttpRequestEMFetchOnSuccessCallback;
|
||||
fetch_attribs.onerror = Dqn_OS_HttpRequestEMFetchOnErrorCallback;
|
||||
fetch_attribs.userData = response;
|
||||
|
||||
Dqn_Str8 url = Dqn_Str8_InitF(scratch_arena, "%.*s%.*s", DQN_STR_FMT(host), DQN_STR_FMT(path));
|
||||
Dqn_Log_InfoF("Initiating HTTP '%s' request to '%.*s' with payload '%.*s'", fetch_attribs.requestMethod, DQN_STR_FMT(url), DQN_STR_FMT(body));
|
||||
response->on_complete_semaphore = Dqn_OS_SemaphoreInit(0);
|
||||
response->em_handle = emscripten_fetch(&fetch_attribs, url.data);
|
||||
#else // #elif defined(DQN_OS_WIN32)
|
||||
DQN_INVALID_CODE_PATHF("Unimplemented function");
|
||||
#endif
|
||||
}
|
||||
|
||||
DQN_API void Dqn_OS_HttpRequestFree(Dqn_OSHttpResponse *response)
|
||||
{
|
||||
// NOTE: Cleanup
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
if (response->em_handle) {
|
||||
emscripten_fetch_close(response->em_handle);
|
||||
response->em_handle = nullptr;
|
||||
}
|
||||
#endif // #elif defined(DQN_OS_WIN32)
|
||||
|
||||
Dqn_Arena_Deinit(&response->tmp_arena);
|
||||
if (Dqn_OS_SemaphoreIsValid(&response->on_complete_semaphore))
|
||||
Dqn_OS_SemaphoreDeinit(&response->on_complete_semaphore);
|
||||
*response = {};
|
||||
}
|
1295
dqn_os_win32.cpp
Normal file
1295
dqn_os_win32.cpp
Normal file
File diff suppressed because it is too large
Load Diff
52
dqn_os_win32.h
Normal file
52
dqn_os_win32.h
Normal file
@ -0,0 +1,52 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$ | $\ $$ |\_$$ _|$$$\ $$ |$$ ___$$\ $$ __$$\
|
||||
// $$ / $$ |$$ / \__| $$ |$$$\ $$ | $$ | $$$$\ $$ |\_/ $$ |\__/ $$ |
|
||||
// $$ | $$ |\$$$$$$\ $$ $$ $$\$$ | $$ | $$ $$\$$ | $$$$$ / $$$$$$ |
|
||||
// $$ | $$ | \____$$\ $$$$ _$$$$ | $$ | $$ \$$$$ | \___$$\ $$ ____/
|
||||
// $$ | $$ |$$\ $$ | $$$ / \$$$ | $$ | $$ |\$$$ |$$\ $$ |$$ |
|
||||
// $$$$$$ |\$$$$$$ | $$ / \$$ |$$$$$$\ $$ | \$$ |\$$$$$$ |$$$$$$$$\
|
||||
// \______/ \______/ \__/ \__|\______|\__| \__| \______/ \________|
|
||||
//
|
||||
// dqn_os_win32.h -- Windows only functions, and, implementation of the OS layer
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct Dqn_WinError
|
||||
{
|
||||
unsigned long code;
|
||||
Dqn_Str8 msg;
|
||||
};
|
||||
|
||||
// NOTE: Windows Str8 <-> Str16 ///////////////////////////////////////////
|
||||
struct Dqn_Win_FolderIteratorW
|
||||
{
|
||||
void *handle;
|
||||
Dqn_Str16 file_name;
|
||||
wchar_t file_name_buf[512];
|
||||
};
|
||||
|
||||
struct Dqn_Win_FolderIterator
|
||||
{
|
||||
void *handle;
|
||||
Dqn_Str8 file_name;
|
||||
char file_name_buf[512];
|
||||
};
|
||||
|
||||
// NOTE: [$WIND] Dqn_Win ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_WinError Dqn_Win_ErrorCodeToMsg (Dqn_Arena *arena, uint32_t error_code);
|
||||
DQN_API Dqn_WinError Dqn_Win_LastError (Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Win_MakeProcessDPIAware();
|
||||
// NOTE: Windows Str8 <-> Str16 ////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str16 Dqn_Win_Str8ToStr16 (Dqn_Arena *arena, Dqn_Str8 src);
|
||||
DQN_API int Dqn_Win_Str8ToStr16Buffer (Dqn_Str16 src, char *dest, int dest_size);
|
||||
DQN_API Dqn_Str8 Dqn_Win_Str16ToStr8 (Dqn_Arena *arena, Dqn_Str16 src);
|
||||
DQN_API int Dqn_Win_Str16ToStr8Buffer (Dqn_Str16 src, char *dest, int dest_size);
|
||||
// NOTE: Path navigation ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str16 Dqn_Win_EXEPathW (Dqn_Arena *arena);
|
||||
DQN_API Dqn_Str16 Dqn_Win_EXEDirW (Dqn_Arena *arena);
|
||||
DQN_API Dqn_Str8 Dqn_Win_WorkingDir (Dqn_Arena *arena, Dqn_Str8 suffix);
|
||||
DQN_API Dqn_Str16 Dqn_Win_WorkingDirW (Dqn_Arena *arena, Dqn_Str16 suffix);
|
||||
DQN_API bool Dqn_Win_FolderIterate (Dqn_Str8 path, Dqn_Win_FolderIterator *it);
|
||||
DQN_API bool Dqn_Win_FolderWIterate (Dqn_Str16 path, Dqn_Win_FolderIteratorW *it);
|
1993
dqn_platform.cpp
1993
dqn_platform.cpp
File diff suppressed because it is too large
Load Diff
497
dqn_platform.h
497
dqn_platform.h
@ -1,497 +0,0 @@
|
||||
#if !defined(DQN_NO_FS)
|
||||
#if defined(DQN_OS_WIN32) && defined(DQN_NO_WIN)
|
||||
#error "Filesystem APIs requires Windows API, DQN_NO_WIN must not be defined"
|
||||
#endif
|
||||
// NOTE: [$FSYS] Dqn_Fs ============================================================================
|
||||
// NOTE: FS Manipulation ===========================================================================
|
||||
// TODO(dqn): We should have a Dqn_Str8 interface and a CStr8 interface
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_FsDelete
|
||||
// @desc Delete the item specified at the path. This function *CAN* not delete directories unless
|
||||
// the directory is empty.
|
||||
// @return True if deletion was successful, false otherwise
|
||||
|
||||
enum Dqn_FsInfoType
|
||||
{
|
||||
Dqn_FsInfoType_Unknown,
|
||||
Dqn_FsInfoType_Directory,
|
||||
Dqn_FsInfoType_File,
|
||||
};
|
||||
|
||||
struct Dqn_FsInfo
|
||||
{
|
||||
bool exists;
|
||||
Dqn_FsInfoType type;
|
||||
uint64_t create_time_in_s;
|
||||
uint64_t last_write_time_in_s;
|
||||
uint64_t last_access_time_in_s;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
DQN_API bool Dqn_Fs_Exists (Dqn_Str8 path);
|
||||
DQN_API bool Dqn_Fs_DirExists(Dqn_Str8 path);
|
||||
DQN_API Dqn_FsInfo Dqn_Fs_GetInfo (Dqn_Str8 path);
|
||||
DQN_API bool Dqn_Fs_Copy (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite);
|
||||
DQN_API bool Dqn_Fs_MakeDir (Dqn_Str8 path);
|
||||
DQN_API bool Dqn_Fs_Move (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite);
|
||||
DQN_API bool Dqn_Fs_Delete (Dqn_Str8 path);
|
||||
|
||||
// NOTE: R/W Entire File ===========================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Fs_WriteStr8, Dqn_Fs_WriteCStr8
|
||||
// @desc Write the string to a file at the path overwriting if necessary.
|
||||
|
||||
// @proc Dqn_Fs_ReadStr8, Dqn_Fs_ReadCStr8
|
||||
// @desc Read the file at the path to a string.
|
||||
|
||||
DQN_API bool Dqn_Fs_WriteCStr8(char const *file_path, Dqn_usize file_path_size, char const *buffer, Dqn_usize buffer_size);
|
||||
DQN_API bool Dqn_Fs_Write (Dqn_Str8 file_path, Dqn_Str8 buffer);
|
||||
DQN_API char *Dqn_Fs_ReadCStr8 (char const *path, Dqn_usize path_size, Dqn_usize *file_size, Dqn_Allocator allocator);
|
||||
DQN_API Dqn_Str8 Dqn_Fs_Read (Dqn_Str8 path, Dqn_Allocator allocator);
|
||||
|
||||
// NOTE: R/W Stream API ============================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Fs_OpenFile
|
||||
// @desc Open a handle to the file
|
||||
|
||||
// @proc Dqn_Fs_WriteFile
|
||||
// @desc Append to the file specified by the handle with the given buffer.
|
||||
|
||||
// @proc Dqn_Fs_CloseFile
|
||||
// @desc Close the file at specified by the handle
|
||||
|
||||
struct Dqn_FsFile
|
||||
{
|
||||
void *handle;
|
||||
char error[512];
|
||||
uint16_t error_size;
|
||||
};
|
||||
|
||||
enum Dqn_FsFileOpen
|
||||
{
|
||||
Dqn_FsFileOpen_CreateAlways, // Create file if it does not exist, otherwise, zero out the file and open
|
||||
Dqn_FsFileOpen_OpenIfExist, // Open file at path only if it exists
|
||||
Dqn_FsFileOpen_OpenAlways, // Open file at path, create file if it does not exist
|
||||
};
|
||||
|
||||
enum Dqn_FsFileAccess
|
||||
{
|
||||
Dqn_FsFileAccess_Read = 1 << 0,
|
||||
Dqn_FsFileAccess_Write = 1 << 1,
|
||||
Dqn_FsFileAccess_Execute = 1 << 2,
|
||||
Dqn_FsFileAccess_AppendOnly = 1 << 3, // This flag cannot be combined with any other access mode
|
||||
Dqn_FsFileAccess_ReadWrite = Dqn_FsFileAccess_Read | Dqn_FsFileAccess_Write,
|
||||
Dqn_FsFileAccess_All = Dqn_FsFileAccess_ReadWrite | Dqn_FsFileAccess_Execute,
|
||||
};
|
||||
|
||||
DQN_API Dqn_FsFile Dqn_Fs_OpenFile (Dqn_Str8 path, Dqn_FsFileOpen open_mode, uint32_t access);
|
||||
DQN_API bool Dqn_Fs_WriteFileBuffer(Dqn_FsFile *file, void const *data, Dqn_usize size);
|
||||
DQN_API bool Dqn_Fs_WriteFile (Dqn_FsFile *file, Dqn_Str8 buffer);
|
||||
DQN_API bool Dqn_Fs_WriteFileFV (Dqn_FsFile *file, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_Fs_WriteFileF (Dqn_FsFile *file, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API void Dqn_Fs_CloseFile (Dqn_FsFile *file);
|
||||
#endif // !defined(DQN_NO_FS)
|
||||
|
||||
// NOTE: File system paths =========================================================================
|
||||
// Helper data structure for building paths suitable for OS consumption.
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_FsPath_AddRef, Dqn_FsPath_Add
|
||||
// @desc Append a path to the file path. The passed in path can be specify
|
||||
// both a single level or multiple directories with different path separators.
|
||||
// The path will be decomposed into individual sections in the function.
|
||||
//
|
||||
// For example passing
|
||||
// - "path/to/your/desired/folder" is valid
|
||||
// - "path" is valid
|
||||
// - "path/to\your/desired\folder" is valid
|
||||
|
||||
// @proc Dqn_FsPath_Pop
|
||||
// @desc Remove the last appended path level from the current path stored in
|
||||
// the FsPath.
|
||||
//
|
||||
// For example "path/to/your/desired/folder" popped produces
|
||||
// "path/to/your/desired"
|
||||
|
||||
// @proc Dqn_FsPath_Convert
|
||||
// @desc Convert the path specified in the string to the OS native separated
|
||||
// path.
|
||||
|
||||
#if !defined(Dqn_FsPathOSSeperator)
|
||||
#if defined(DQN_OS_WIN32)
|
||||
#define Dqn_FsPathOSSeperator "\\"
|
||||
#else
|
||||
#define Dqn_FsPathOSSeperator "/"
|
||||
#endif
|
||||
#define Dqn_FsPathOSSeperatorString DQN_STR8(Dqn_FsPathOSSeperator)
|
||||
#endif
|
||||
|
||||
struct Dqn_FsPathLink
|
||||
{
|
||||
Dqn_Str8 string;
|
||||
Dqn_FsPathLink *next;
|
||||
Dqn_FsPathLink *prev;
|
||||
};
|
||||
|
||||
|
||||
struct Dqn_FsPath
|
||||
{
|
||||
Dqn_FsPathLink *head;
|
||||
Dqn_FsPathLink *tail;
|
||||
Dqn_usize string_size;
|
||||
uint16_t links_size;
|
||||
};
|
||||
|
||||
DQN_API bool Dqn_FsPath_AddRef (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_Str8 path);
|
||||
DQN_API bool Dqn_FsPath_Add (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_Str8 path);
|
||||
DQN_API bool Dqn_FsPath_AddF (Dqn_Arena *arena, Dqn_FsPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_FsPath_Pop (Dqn_FsPath *fs_path);
|
||||
DQN_API Dqn_Str8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath const *fs_path, Dqn_Str8 path_separator);
|
||||
DQN_API Dqn_Str8 Dqn_FsPath_Convert (Dqn_Arena *arena, Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_FsPath_ConvertF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
#define Dqn_FsPath_BuildFwdSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STR8("/"))
|
||||
#define Dqn_FsPath_BuildBackSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STR8("\\"))
|
||||
|
||||
#if !defined(Dqn_FsPath_Build)
|
||||
#if defined(DQN_OS_WIN32)
|
||||
#define Dqn_FsPath_Build(arena, fs_path) Dqn_FsPath_BuildBackSlash(arena, fs_path)
|
||||
#else
|
||||
#define Dqn_FsPath_Build(arena, fs_path) Dqn_FsPath_BuildFwdSlash(arena, fs_path)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// NOTE: [$DATE] Dqn_Date ==========================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Date_EpochTime
|
||||
// @desc Produce the time elapsed since the Unix epoch
|
||||
// (e.g. 1970-01-01T00:00:00Z) in seconds
|
||||
|
||||
struct Dqn_DateHMSTimeStr8
|
||||
{
|
||||
char date[DQN_ARRAY_UCOUNT("YYYY-MM-SS")];
|
||||
uint8_t date_size;
|
||||
|
||||
char hms[DQN_ARRAY_UCOUNT("HH:MM:SS")];
|
||||
uint8_t hms_size;
|
||||
};
|
||||
|
||||
struct Dqn_DateHMSTime
|
||||
{
|
||||
uint8_t day;
|
||||
uint8_t month;
|
||||
int16_t year;
|
||||
|
||||
uint8_t hour;
|
||||
uint8_t minutes;
|
||||
uint8_t seconds;
|
||||
};
|
||||
|
||||
DQN_API Dqn_DateHMSTime Dqn_Date_LocalTimeHMSNow ();
|
||||
DQN_API Dqn_DateHMSTimeStr8 Dqn_Date_LocalTimeHMSStr8Now(char date_separator = '-', char hms_separator = ':');
|
||||
DQN_API Dqn_DateHMSTimeStr8 Dqn_Date_LocalTimeHMSStr8 (Dqn_DateHMSTime time, char date_separator = '-', char hms_separator = ':');
|
||||
DQN_API uint64_t Dqn_Date_EpochTime ();
|
||||
|
||||
#if defined(DQN_OS_WIN32)
|
||||
#if !defined(DQN_NO_WIN)
|
||||
// NOTE: [$WIND] Dqn_Win ===========================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Win_LastErrorToBuffer, Dqn_Win_LastError
|
||||
// @desc Retrieve the latest error code and message Windows produced for the
|
||||
// most recent Win32 API call.
|
||||
|
||||
// @proc Dqn_Win_MakeProcessDPIAware
|
||||
// @desc Call once at application start-up to ensure that the application is
|
||||
// DPI aware on Windows and ensure that application UI is scaled up
|
||||
// appropriately for the monitor.
|
||||
|
||||
struct Dqn_WinError
|
||||
{
|
||||
unsigned long code;
|
||||
Dqn_Str8 msg;
|
||||
};
|
||||
DQN_API Dqn_WinError Dqn_Win_LastError(Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Win_MakeProcessDPIAware();
|
||||
|
||||
// NOTE: Windows Str8 <-> Str16 ===========================================
|
||||
// Convert a UTF8 <-> UTF16 string.
|
||||
//
|
||||
// The exact size buffer required for this function can be determined by
|
||||
// calling this function with the 'dest' set to null and 'dest_size' set to 0,
|
||||
// the return size is the size required for conversion not-including space for
|
||||
// the null-terminator. This function *always* null-terminates the input
|
||||
// buffer.
|
||||
//
|
||||
// Returns the number of u8's (for UTF16->8) OR u16's (for UTF8->16)
|
||||
// written/required for conversion. 0 if there was a conversion error and can be
|
||||
// queried using 'Dqn_Win_LastError'
|
||||
|
||||
DQN_API Dqn_Str16 Dqn_Win_Str8ToStr16 (Dqn_Arena *arena, Dqn_Str8 src);
|
||||
DQN_API int Dqn_Win_Str8ToStr16Buffer(Dqn_Str16 src, char *dest, int dest_size);
|
||||
DQN_API Dqn_Str8 Dqn_Win_Str16ToStr8 (Dqn_Arena *arena, Dqn_Str16 src);
|
||||
DQN_API int Dqn_Win_Str16ToStr8Buffer(Dqn_Str16 src, char *dest, int dest_size);
|
||||
|
||||
// NOTE: Path navigation ===========================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Win_EXEDirW, Dqn_Win_EXEDirWArena
|
||||
// @desc Evaluate the current executable's directory that is running when this
|
||||
// function is called.
|
||||
// @param[out] buffer The buffer to write the executable directory into. Set
|
||||
// this to null to calculate the required buffer size for the directory.
|
||||
// @param[in] size The size of the buffer given. Set this to 0 to calculate the
|
||||
// required buffer size for the directory.
|
||||
// @return The length of the executable directory string. If this return value
|
||||
// exceeds the capacity of the 'buffer', the 'buffer' is untouched.
|
||||
|
||||
// @proc Dqn_Win_WorkingDir, Dqn_Win_WorkingDirW
|
||||
// @param[in] suffix (Optional) A suffix to append to the current working directory
|
||||
|
||||
// @proc Dqn_Win_FolderIterate, Dqn_Win_FolderWIterate
|
||||
// @desc Iterate the files in the specified folder at the path
|
||||
#if 0
|
||||
for (Dqn_WinFolderIterator it = {}; Dqn_Win_FolderIterate("C:/your/path/", &it); ) {
|
||||
printf("%.*s\n", DQN_STRING_FMT(it.file_name));
|
||||
}
|
||||
#endif
|
||||
|
||||
struct Dqn_Win_FolderIteratorW
|
||||
{
|
||||
void *handle;
|
||||
Dqn_Str16 file_name;
|
||||
wchar_t file_name_buf[512];
|
||||
};
|
||||
|
||||
struct Dqn_Win_FolderIterator
|
||||
{
|
||||
void *handle;
|
||||
Dqn_Str8 file_name;
|
||||
char file_name_buf[512];
|
||||
};
|
||||
|
||||
DQN_API Dqn_Str16 Dqn_Win_EXEPathW (Dqn_Arena *arena);
|
||||
DQN_API Dqn_Str16 Dqn_Win_EXEDirW (Dqn_Arena *arena);
|
||||
DQN_API Dqn_Str8 Dqn_Win_WorkingDir (Dqn_Allocator allocator, Dqn_Str8 suffix);
|
||||
DQN_API Dqn_Str16 Dqn_Win_WorkingDirW (Dqn_Allocator allocator, Dqn_Str16 suffix);
|
||||
DQN_API bool Dqn_Win_FolderIterate (Dqn_Str8 path, Dqn_Win_FolderIterator *it);
|
||||
DQN_API bool Dqn_Win_FolderWIterate(Dqn_Str16 path, Dqn_Win_FolderIteratorW *it);
|
||||
#endif // !defined(DQN_NO_WIN)
|
||||
|
||||
#if !defined(DQN_NO_WINNET)
|
||||
// NOTE: [$WINN] Dqn_WinNet ========================================================================
|
||||
// TODO(dqn): Useful options to expose in the handle
|
||||
// https://docs.microsoft.com/en-us/windows/win32/wininet/option-flags
|
||||
// INTERNET_OPTION_CONNECT_RETRIES -- default is 5 retries
|
||||
// INTERNET_OPTION_CONNECT_TIMEOUT -- milliseconds
|
||||
// INTERNET_OPTION_RECEIVE_TIMEOUT
|
||||
// INTERNET_OPTION_SEND_TIMEOUT
|
||||
//
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Win_NetHandleInitHTTPMethod, Dqn_Win_NetHandleInitHTTPMethodCStr8
|
||||
// @desc Setup a handle to the URL with the given HTTP verb.
|
||||
//
|
||||
// This function is the same as calling Dqn_Win_NetHandleInit() followed by
|
||||
// Dqn_Win_NetHandleSetHTTPMethod().
|
||||
//
|
||||
// @param http_method The HTTP request type, e.g. "GET" or "POST" e.t.c
|
||||
|
||||
// @proc Dqn_Win_NetHandleSetHTTPMethod
|
||||
// @desc Set the HTTP request method for the given handle. This function can
|
||||
// be used on a pre-existing valid handle that has at the minimum been
|
||||
// initialised.
|
||||
|
||||
enum Dqn_WinNetHandleState
|
||||
{
|
||||
Dqn_WinNetHandleState_Invalid,
|
||||
Dqn_WinNetHandleState_Initialised,
|
||||
Dqn_WinNetHandleState_HttpMethodReady,
|
||||
Dqn_WinNetHandleState_RequestFailed,
|
||||
Dqn_WinNetHandleState_RequestGood,
|
||||
};
|
||||
|
||||
// The number of bytes each pump of the connection downloads at most. If this is
|
||||
// zero we default to DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE.
|
||||
#if !defined(DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE)
|
||||
#define DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE 4096
|
||||
#endif
|
||||
|
||||
struct Dqn_WinNetHandle
|
||||
{
|
||||
// NOTE: We copy out the host name because it needs to be null-terminated.
|
||||
// Luckily, we can assume a DNS domain won't exceed 256 characters so this
|
||||
// will generally always work.
|
||||
char host_name[256];
|
||||
int host_name_size;
|
||||
|
||||
// NOTE: Everything after the domain/host name part of the string i.e. the
|
||||
// '/test' part of the full url 'mywebsite.com/test'.
|
||||
// TODO(dqn): I don't want to make our network API allocate here so we don't
|
||||
// copy the string since we require that the string is null-terminated so
|
||||
// then taking a pointer to the input string should work .. maybe this is
|
||||
// ok?
|
||||
char *url;
|
||||
int url_size;
|
||||
|
||||
// NOTE: docs.microsoft.com/en-us/windows/win32/wininet/setting-and-retrieving-internet-options#scope-of-hinternet-handle
|
||||
// These handles have three levels:
|
||||
//
|
||||
// The root HINTERNET handle (created by a call to InternetOpen) would contain all the Internet options that affect this instance of WinINet.
|
||||
// HINTERNET handles that connect to a server (created by a call to InternetConnect)
|
||||
// HINTERNET handles associated with a resource or enumeration of resources on a particular server.
|
||||
//
|
||||
// More detailed information about the HINTERNET dependency is listed here
|
||||
// NOTE: https://docs.microsoft.com/en-us/windows/win32/wininet/appendix-a-hinternet-handles
|
||||
void *internet_open_handle;
|
||||
void *internet_connect_handle;
|
||||
void *http_handle;
|
||||
Dqn_WinNetHandleState state;
|
||||
};
|
||||
|
||||
enum Dqn_WinNetHandleRequestHeaderFlag
|
||||
{
|
||||
Dqn_WinNetHandleRequestHeaderFlag_Add,
|
||||
Dqn_WinNetHandleRequestHeaderFlag_AddIfNew,
|
||||
Dqn_WinNetHandleRequestHeaderFlag_Replace,
|
||||
Dqn_WinNetHandleRequestHeaderFlag_Count,
|
||||
};
|
||||
|
||||
struct Dqn_WinNetHandleResponse
|
||||
{
|
||||
Dqn_Str8 raw_headers;
|
||||
Dqn_Str8 *headers;
|
||||
Dqn_usize headers_size;
|
||||
|
||||
// NOTE: Headers pulled from the 'raw_headers' for convenience
|
||||
uint64_t content_length;
|
||||
Dqn_Str8 content_type;
|
||||
};
|
||||
|
||||
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitCStr8 (char const *url, int url_size);
|
||||
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInit (Dqn_Str8 url);
|
||||
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethodCStr8 (char const *url, int url_size, char const *http_method);
|
||||
DQN_API Dqn_WinNetHandle Dqn_Win_NetHandleInitHTTPMethod (Dqn_Str8 url, Dqn_Str8 http_method);
|
||||
DQN_API void Dqn_Win_NetHandleClose (Dqn_WinNetHandle *handle);
|
||||
DQN_API bool Dqn_Win_NetHandleIsValid (Dqn_WinNetHandle const *handle);
|
||||
DQN_API void Dqn_Win_NetHandleSetUserAgentCStr8 (Dqn_WinNetHandle *handle, char const *user_agent, int user_agent_size);
|
||||
DQN_API bool Dqn_Win_NetHandleSetHTTPMethod (Dqn_WinNetHandle *handle, char const *method);
|
||||
DQN_API bool Dqn_Win_NetHandleSetRequestHeaderCStr8(Dqn_WinNetHandle *handle, char const *header, int header_size, uint32_t mode);
|
||||
DQN_API bool Dqn_Win_NetHandleSetRequestHeaderStr8 (Dqn_WinNetHandle *handle, Dqn_Str8 header, uint32_t mode);
|
||||
DQN_API Dqn_WinNetHandleResponse Dqn_Win_NetHandleSendRequest (Dqn_WinNetHandle *handle, Dqn_Allocator allocator, char const *post_data, unsigned long post_data_size);
|
||||
DQN_API bool Dqn_Win_NetHandlePump (Dqn_WinNetHandle *handle, char *dest, int dest_size, size_t *download_size);
|
||||
DQN_API char * Dqn_Win_NetHandlePumpCStr8 (Dqn_WinNetHandle *handle, Dqn_Arena *arena, size_t *download_size);
|
||||
DQN_API Dqn_Str8 Dqn_Win_NetHandlePumpStr8 (Dqn_WinNetHandle *handle, Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Win_NetHandlePumpToCRTFile (Dqn_WinNetHandle *handle, FILE *file);
|
||||
DQN_API char * Dqn_Win_NetHandlePumpToAllocCStr8 (Dqn_WinNetHandle *handle, size_t *download_size);
|
||||
DQN_API Dqn_Str8 Dqn_Win_NetHandlePumpToAllocStr8 (Dqn_WinNetHandle *handle);
|
||||
#endif // !defined(DQN_NO_WINNET)
|
||||
#endif // defined(DQN_OS_WIN32)
|
||||
|
||||
// NOTE: [$OSYS] Dqn_OS ============================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_OS_SecureRNGBytes
|
||||
// @desc Generate cryptographically secure bytes
|
||||
|
||||
// @proc Dqn_OS_EXEDir
|
||||
// @desc Retrieve the executable directory without the trailing '/' or
|
||||
// ('\' for windows). If this fails an empty string is returned.
|
||||
|
||||
// @proc Dqn_OS_PerfCounterFrequency
|
||||
// @desc Get the number of ticks in the performance counter per second for the
|
||||
// operating system you're running on. This value can be used to calculate
|
||||
// duration from OS performance counter ticks.
|
||||
|
||||
// @proc Dqn_OS_EstimateTSCPerSecond
|
||||
// @desc Estimate how many timestamp count's (TSC) there are per second. TSC
|
||||
// is evaluated by calling __rdtsc() or the equivalent on the platform. This
|
||||
// value can be used to convert TSC durations into seconds.
|
||||
//
|
||||
// @param duration_ms_to_gauge_tsc_frequency How many milliseconds to spend
|
||||
// measuring the TSC rate of the current machine. 100ms is sufficient to
|
||||
// produce a fairly accurate result with minimal blocking in applications.
|
||||
//
|
||||
// This may return 0 if querying the CPU timestamp counter is not supported
|
||||
// on the platform (e.g. __rdtsc() or __builtin_readcyclecounter() returns 0).
|
||||
|
||||
/// Record time between two time-points using the OS's performance counter.
|
||||
struct Dqn_OSTimer
|
||||
{
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
};
|
||||
|
||||
DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size);
|
||||
#if (defined(DQN_OS_WIN32) && !defined(DQN_NO_WIN)) || !defined(DQN_OS_WIN32)
|
||||
DQN_API Dqn_Str8 Dqn_OS_EXEPath (Dqn_Arena *arena);
|
||||
DQN_API Dqn_Str8 Dqn_OS_EXEDir (Dqn_Arena* arena);
|
||||
#endif
|
||||
DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds);
|
||||
DQN_API uint64_t Dqn_OS_PerfCounterNow ();
|
||||
DQN_API uint64_t Dqn_OS_PerfCounterFrequency();
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterS (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterMs (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterMicroS (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_f64 Dqn_OS_PerfCounterNs (uint64_t begin, uint64_t end);
|
||||
DQN_API Dqn_OSTimer Dqn_OS_TimerBegin ();
|
||||
DQN_API void Dqn_OS_TimerEnd (Dqn_OSTimer *timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerS (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerMs (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerMicroS (Dqn_OSTimer timer);
|
||||
DQN_API Dqn_f64 Dqn_OS_TimerNs (Dqn_OSTimer timer);
|
||||
DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency);
|
||||
|
||||
// NOTE: [$TCTX] Dqn_ThreadContext =================================================================
|
||||
// Each thread is assigned in their thread-local storage (TLS) scratch and
|
||||
// permanent arena allocators. These can be used for allocations with a lifetime
|
||||
// scoped to the lexical scope or for storing data permanently using the arena
|
||||
// paradigm.
|
||||
//
|
||||
// TLS in this implementation is implemented using the `thread_local` C/C++
|
||||
// keyword.
|
||||
//
|
||||
// NOTE: API
|
||||
//
|
||||
// @proc Dqn_Thread_GetContext
|
||||
// @desc Get the current thread's context- this contains all the metadata for managing
|
||||
// the thread scratch data. In general you probably want Dqn_Thread_GetScratch()
|
||||
// which ensures you get a usable scratch arena for temporary allocations
|
||||
// without having to worry about selecting the right arena from the state.
|
||||
//
|
||||
// @proc Dqn_Thread_GetScratch
|
||||
// @desc Retrieve the per-thread temporary arena allocator that is reset on scope
|
||||
// exit.
|
||||
//
|
||||
// The scratch arena must be deconflicted with any existing arenas in the
|
||||
// function to avoid trampling over each other's memory. Consider the situation
|
||||
// where the scratch arena is passed into the function. Inside the function, if
|
||||
// the same arena is reused then, if both arenas allocate, when the inner arena
|
||||
// is reset, this will undo the passed in arena's allocations in the function.
|
||||
//
|
||||
// @param[in] conflict_arena A pointer to the arena currently being used in the
|
||||
// function
|
||||
|
||||
struct Dqn_ThreadContext
|
||||
{
|
||||
Dqn_b32 init;
|
||||
|
||||
// Scratch memory arena's for the calling thread
|
||||
Dqn_Arena *scratch_arenas[2];
|
||||
|
||||
// Allocators that use the corresponding arena from the thread context.
|
||||
// Provided for convenience when interfacing with allocator interfaces.
|
||||
Dqn_Allocator scratch_allocators[2];
|
||||
};
|
||||
|
||||
struct Dqn_ThreadScratch
|
||||
{
|
||||
Dqn_ThreadScratch(Dqn_ThreadContext *context, uint8_t context_index);
|
||||
~Dqn_ThreadScratch();
|
||||
|
||||
Dqn_Allocator allocator;
|
||||
Dqn_Arena *arena;
|
||||
Dqn_b32 destructed;
|
||||
Dqn_ArenaTempMemory temp_memory;
|
||||
};
|
||||
|
||||
// NOTE: Context ===================================================================================
|
||||
DQN_API uint32_t Dqn_Thread_GetID();
|
||||
DQN_API Dqn_ThreadContext *Dqn_Thread_GetContext();
|
||||
DQN_API Dqn_ThreadScratch Dqn_Thread_GetScratch(void const *conflict_arena);
|
@ -1,9 +1,24 @@
|
||||
// NOTE: [$CSTR] Dqn_CStr8 ======================================================================
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\\__$$ __|$$ __$$\ \_$$ _|$$$\ $$ |$$ __$$\
|
||||
// $$ / \__| $$ | $$ | $$ | $$ | $$$$\ $$ |$$ / \__|
|
||||
// \$$$$$$\ $$ | $$$$$$$ | $$ | $$ $$\$$ |$$ |$$$$\
|
||||
// \____$$\ $$ | $$ __$$< $$ | $$ \$$$$ |$$ |\_$$ |
|
||||
// $$\ $$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ |
|
||||
// \$$$$$$ | $$ | $$ | $$ |$$$$$$\ $$ | \$$ |\$$$$$$ |
|
||||
// \______/ \__| \__| \__|\______|\__| \__| \______/
|
||||
//
|
||||
// dqn_string.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE: [$CSTR] Dqn_CStr8 /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_usize Dqn_CStr8_FSize(DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_usize result = STB_SPRINTF_DECORATE(vsnprintf)(nullptr, 0, fmt, args);
|
||||
Dqn_usize result = DQN_VSNPRINTF(nullptr, 0, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
@ -12,7 +27,7 @@ DQN_API Dqn_usize Dqn_CStr8_FVSize(DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
Dqn_usize result = STB_SPRINTF_DECORATE(vsnprintf)(nullptr, 0, fmt, args_copy);
|
||||
Dqn_usize result = DQN_VSNPRINTF(nullptr, 0, fmt, args_copy);
|
||||
va_end(args_copy);
|
||||
return result;
|
||||
}
|
||||
@ -38,7 +53,22 @@ DQN_API Dqn_usize Dqn_CStr16_Size(wchar_t const *src)
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$STR8] Dqn_Str8 =======================================================================
|
||||
// NOTE: [$STR6] Dqn_Str16 /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool operator==(Dqn_Str16 const &lhs, Dqn_Str16 const &rhs)
|
||||
{
|
||||
bool result = false;
|
||||
if (lhs.size == rhs.size)
|
||||
result = DQN_MEMCMP(lhs.data, rhs.data, lhs.size * sizeof(*lhs.data)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool operator!=(Dqn_Str16 const &lhs, Dqn_Str16 const &rhs)
|
||||
{
|
||||
bool result = !(lhs == rhs);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$STR8] Dqn_Str8 //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitCStr8(char const *src)
|
||||
{
|
||||
Dqn_usize size = Dqn_CStr8_Size(src);
|
||||
@ -48,7 +78,7 @@ DQN_API Dqn_Str8 Dqn_Str8_InitCStr8(char const *src)
|
||||
|
||||
DQN_API bool Dqn_Str8_IsAll(Dqn_Str8 string, Dqn_Str8IsAll is_all)
|
||||
{
|
||||
bool result = Dqn_Str8_IsValid(string);
|
||||
bool result = Dqn_Str8_HasData(string);
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
@ -73,7 +103,7 @@ DQN_API bool Dqn_Str8_IsAll(Dqn_Str8 string, Dqn_Str8IsAll is_all)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Slice(Dqn_Str8 string, Dqn_usize offset, Dqn_usize size)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_Str8_Init(string.data, 0);
|
||||
if (!Dqn_Str8_IsValid(result))
|
||||
if (!Dqn_Str8_HasData(string))
|
||||
return result;
|
||||
|
||||
Dqn_usize capped_offset = DQN_MIN(offset, string.size);
|
||||
@ -92,7 +122,7 @@ DQN_API Dqn_Str8 Dqn_Str8_Advance(Dqn_Str8 string, Dqn_usize amount)
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
|
||||
{
|
||||
Dqn_Str8BinarySplitResult result = {};
|
||||
if (!Dqn_Str8_IsValid(string) || !find || find_size == 0)
|
||||
if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
|
||||
return result;
|
||||
|
||||
result.lhs = string;
|
||||
@ -121,7 +151,7 @@ DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit(Dqn_Str8 string, Dqn_Str8
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
|
||||
{
|
||||
Dqn_Str8BinarySplitResult result = {};
|
||||
if (!Dqn_Str8_IsValid(string) || !find || find_size == 0)
|
||||
if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
|
||||
return result;
|
||||
|
||||
result.lhs = string;
|
||||
@ -147,17 +177,17 @@ DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverse(Dqn_Str8 string, D
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_usize Dqn_Str8_Split(Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count)
|
||||
DQN_API Dqn_usize Dqn_Str8_Split(Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode)
|
||||
{
|
||||
Dqn_usize result = 0; // The number of splits in the actual string.
|
||||
if (!Dqn_Str8_IsValid(string) || !Dqn_Str8_IsValid(delimiter) || delimiter.size <= 0)
|
||||
if (!Dqn_Str8_HasData(string) || !Dqn_Str8_HasData(delimiter) || delimiter.size <= 0)
|
||||
return result;
|
||||
|
||||
Dqn_Str8BinarySplitResult split = {};
|
||||
Dqn_Str8 first = string;
|
||||
do {
|
||||
split = Dqn_Str8_BinarySplit(first, delimiter);
|
||||
if (split.lhs.size) {
|
||||
if (split.lhs.size || mode == Dqn_Str8SplitIncludeEmptyStrings_Yes) {
|
||||
if (splits && result < splits_count)
|
||||
splits[result] = split.lhs;
|
||||
result++;
|
||||
@ -168,15 +198,13 @@ DQN_API Dqn_usize Dqn_Str8_Split(Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8SplitAllocResult Dqn_Str8_SplitAlloc(Dqn_Allocator allocator,
|
||||
Dqn_Str8 string,
|
||||
Dqn_Str8 delimiter)
|
||||
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc(Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode)
|
||||
{
|
||||
Dqn_Str8SplitAllocResult result = {};
|
||||
Dqn_usize splits_required = Dqn_Str8_Split(string, delimiter, /*splits*/ nullptr, /*count*/ 0);
|
||||
result.data = Dqn_Allocator_NewArray(allocator, Dqn_Str8, splits_required, Dqn_ZeroMem_No);
|
||||
Dqn_Slice<Dqn_Str8> result = {};
|
||||
Dqn_usize splits_required = Dqn_Str8_Split(string, delimiter, /*splits*/ nullptr, /*count*/ 0, mode);
|
||||
result.data = Dqn_Arena_NewArray(arena, Dqn_Str8, splits_required, Dqn_ZeroMem_No);
|
||||
if (result.data) {
|
||||
result.size = Dqn_Str8_Split(string, delimiter, result.data, splits_required);
|
||||
result.size = Dqn_Str8_Split(string, delimiter, result.data, splits_required, mode);
|
||||
DQN_ASSERT(splits_required == result.size);
|
||||
}
|
||||
return result;
|
||||
@ -185,7 +213,7 @@ DQN_API Dqn_Str8SplitAllocResult Dqn_Str8_SplitAlloc(Dqn_Allocator allocator,
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
|
||||
{
|
||||
Dqn_Str8FindResult result = {};
|
||||
if (!Dqn_Str8_IsValid(string) || !find || find_size == 0)
|
||||
if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
|
||||
return result;
|
||||
|
||||
for (Dqn_usize index = 0; !result.found && index < string.size; index++) {
|
||||
@ -229,18 +257,26 @@ DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirst(Dqn_Str8 string, uint32_t flags)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Segment(Dqn_Allocator allocator, Dqn_Str8 src, Dqn_usize segment_size, char segment_char)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Segment(Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char)
|
||||
{
|
||||
Dqn_usize result_size = src.size;
|
||||
if (result_size > segment_size)
|
||||
result_size += (src.size / segment_size) - 1; // NOTE: No segment on the first chunk.
|
||||
if (!segment_size || !Dqn_Str8_HasData(src)) {
|
||||
Dqn_Str8 result = Dqn_Str8_Copy(arena, src);
|
||||
return result;
|
||||
}
|
||||
|
||||
Dqn_Str8 result = Dqn_Str8_Allocate(allocator, result_size, Dqn_ZeroMem_Yes);
|
||||
Dqn_usize segments = src.size / segment_size;
|
||||
if (src.size % segment_size == 0)
|
||||
segments--;
|
||||
|
||||
Dqn_usize segment_counter = 0;
|
||||
Dqn_Str8 result = Dqn_Str8_Alloc(arena, src.size + segments, Dqn_ZeroMem_Yes);
|
||||
Dqn_usize write_index = 0;
|
||||
DQN_FOR_UINDEX(src_index, src.size) {
|
||||
result.data[write_index++] = src.data[src_index];
|
||||
if ((src_index + 1) % segment_size == 0 && (src_index + 1) < src.size)
|
||||
if ((src_index + 1) % segment_size == 0 && segment_counter < segments) {
|
||||
result.data[write_index++] = segment_char;
|
||||
segment_counter++;
|
||||
}
|
||||
DQN_ASSERTF(write_index <= result.size, "result.size=%zu, write_index=%zu", result.size, write_index);
|
||||
}
|
||||
|
||||
@ -248,6 +284,38 @@ DQN_API Dqn_Str8 Dqn_Str8_Segment(Dqn_Allocator allocator, Dqn_Str8 src, Dqn_usi
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_ReverseSegment(Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char)
|
||||
{
|
||||
if (!segment_size || !Dqn_Str8_HasData(src)) {
|
||||
Dqn_Str8 result = Dqn_Str8_Copy(arena, src);
|
||||
return result;
|
||||
}
|
||||
|
||||
Dqn_usize segments = src.size / segment_size;
|
||||
if (src.size % segment_size == 0)
|
||||
segments--;
|
||||
|
||||
Dqn_usize write_counter = 0;
|
||||
Dqn_usize segment_counter = 0;
|
||||
Dqn_Str8 result = Dqn_Str8_Alloc(arena, src.size + segments, Dqn_ZeroMem_Yes);
|
||||
Dqn_usize write_index = result.size - 1;
|
||||
|
||||
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(6293) // NOTE: Ill-defined loop
|
||||
for (size_t src_index = src.size - 1; src_index < src.size; src_index--) {
|
||||
DQN_MSVC_WARNING_POP
|
||||
result.data[write_index--] = src.data[src_index];
|
||||
if (++write_counter % segment_size == 0 && segment_counter < segments) {
|
||||
result.data[write_index--] = segment_char;
|
||||
segment_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
DQN_ASSERT(write_index == SIZE_MAX);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DQN_API bool Dqn_Str8_Eq(Dqn_Str8 lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case)
|
||||
{
|
||||
@ -342,7 +410,7 @@ DQN_API Dqn_Str8 Dqn_Str8_TrimAround(Dqn_Str8 string, Dqn_Str8 trim_string)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround(Dqn_Str8 string)
|
||||
{
|
||||
Dqn_Str8 result = string;
|
||||
if (!Dqn_Str8_IsValid(string))
|
||||
if (!Dqn_Str8_HasData(string))
|
||||
return result;
|
||||
|
||||
char const *start = string.data;
|
||||
@ -360,7 +428,7 @@ DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround(Dqn_Str8 string)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark(Dqn_Str8 string)
|
||||
{
|
||||
Dqn_Str8 result = string;
|
||||
if (!Dqn_Str8_IsValid(result))
|
||||
if (!Dqn_Str8_HasData(result))
|
||||
return result;
|
||||
|
||||
// TODO(dqn): This is little endian
|
||||
@ -382,7 +450,7 @@ DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath(Dqn_Str8 path)
|
||||
{
|
||||
Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")};
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverseArray(path, separators, DQN_ARRAY_UCOUNT(separators));
|
||||
Dqn_Str8 result = split.rhs;
|
||||
Dqn_Str8 result = Dqn_Str8_HasData(split.rhs) ? split.rhs : split.lhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -400,17 +468,26 @@ DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension(Dqn_Str8 path)
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileExtension(Dqn_Str8 path)
|
||||
{
|
||||
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverse(path, DQN_STR8("."));
|
||||
Dqn_Str8 result = split.rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8ToU64Result Dqn_Str8_ToU64(Dqn_Str8 string, char separator)
|
||||
{
|
||||
// NOTE: Argument check
|
||||
Dqn_Str8ToU64Result result = {};
|
||||
if (!Dqn_Str8_IsValid(string))
|
||||
if (!Dqn_Str8_HasData(string)) {
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Sanitize input/output
|
||||
Dqn_Str8 trim_string = Dqn_Str8_TrimWhitespaceAround(string);
|
||||
if (trim_string.size == 0) {
|
||||
result.success = false;
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -446,13 +523,15 @@ DQN_API Dqn_Str8ToI64Result Dqn_Str8_ToI64(Dqn_Str8 string, char separator)
|
||||
{
|
||||
// NOTE: Argument check
|
||||
Dqn_Str8ToI64Result result = {};
|
||||
if (!Dqn_Str8_IsValid(string))
|
||||
if (!Dqn_Str8_HasData(string)) {
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Sanitize input/output
|
||||
Dqn_Str8 trim_string = Dqn_Str8_TrimWhitespaceAround(string);
|
||||
if (trim_string.size == 0) {
|
||||
result.success = false;
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -492,18 +571,18 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string,
|
||||
Dqn_Str8 find,
|
||||
Dqn_Str8 replace,
|
||||
Dqn_usize start_index,
|
||||
Dqn_Allocator allocator,
|
||||
Dqn_Arena *arena,
|
||||
Dqn_Str8EqCase eq_case)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!Dqn_Str8_IsValid(string) || !Dqn_Str8_IsValid(find) || find.size > string.size || find.size == 0 || string.size == 0) {
|
||||
result = Dqn_Str8_Copy(allocator, string);
|
||||
if (!Dqn_Str8_HasData(string) || !Dqn_Str8_HasData(find) || find.size > string.size || find.size == 0 || string.size == 0) {
|
||||
result = Dqn_Str8_Copy(arena, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
Dqn_Str8Builder string_builder = {};
|
||||
string_builder.allocator = scratch.allocator;
|
||||
string_builder.arena = scratch.arena;
|
||||
Dqn_usize max = string.size - find.size;
|
||||
Dqn_usize head = start_index;
|
||||
|
||||
@ -530,25 +609,25 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string,
|
||||
|
||||
if (string_builder.string_size == 0) {
|
||||
// NOTE: No replacement possible, so we just do a full-copy
|
||||
result = Dqn_Str8_Copy(allocator, string);
|
||||
result = Dqn_Str8_Copy(arena, string);
|
||||
} else {
|
||||
Dqn_Str8 remainder = Dqn_Str8_Init(string.data + head, string.size - head);
|
||||
Dqn_Str8Builder_AppendRef(&string_builder, remainder);
|
||||
result = Dqn_Str8Builder_Build(&string_builder, allocator);
|
||||
result = Dqn_Str8Builder_Build(&string_builder, arena);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive(Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Allocator allocator)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive(Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_Str8_Replace(string, find, replace, start_index, allocator, Dqn_Str8EqCase_Insensitive);
|
||||
Dqn_Str8 result = Dqn_Str8_Replace(string, find, replace, start_index, arena, Dqn_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API void Dqn_Str8_Remove(Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size)
|
||||
{
|
||||
if (!string || !Dqn_Str8_IsValid(*string))
|
||||
if (!string || !Dqn_Str8_HasData(*string))
|
||||
return;
|
||||
|
||||
char *end = string->data + string->size;
|
||||
@ -573,16 +652,16 @@ DQN_API bool operator!=(Dqn_Str8 const &lhs, Dqn_Str8 const &rhs)
|
||||
}
|
||||
#endif
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitF(Dqn_Allocator allocator, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitF(Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
Dqn_Str8 result = Dqn_Str8_InitFV(allocator, fmt, va);
|
||||
Dqn_Str8 result = Dqn_Str8_InitFV(arena, fmt, va);
|
||||
va_end(va);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitFV(Dqn_Allocator allocator, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitFV(Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!fmt)
|
||||
@ -590,49 +669,53 @@ DQN_API Dqn_Str8 Dqn_Str8_InitFV(Dqn_Allocator allocator, DQN_FMT_ATTRIB char co
|
||||
|
||||
Dqn_usize size = Dqn_CStr8_FVSize(fmt, args);
|
||||
if (size) {
|
||||
result = Dqn_Str8_Allocate(allocator, size, Dqn_ZeroMem_No);
|
||||
if (Dqn_Str8_IsValid(result))
|
||||
STB_SPRINTF_DECORATE(vsnprintf)(result.data, Dqn_Safe_SaturateCastISizeToInt(size + 1 /*null-terminator*/), fmt, args);
|
||||
result = Dqn_Str8_Alloc(arena, size, Dqn_ZeroMem_No);
|
||||
if (Dqn_Str8_HasData(result))
|
||||
DQN_VSNPRINTF(result.data, Dqn_Safe_SaturateCastISizeToInt(size + 1 /*null-terminator*/), fmt, args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Allocate(Dqn_Allocator allocator, Dqn_usize size, Dqn_ZeroMem zero_mem)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Alloc(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
result.data = (char *)Dqn_Allocator_Alloc(allocator, size + 1, alignof(char), zero_mem);
|
||||
result.data = Dqn_Arena_NewArray(arena, char, size + 1, zero_mem);
|
||||
if (result.data)
|
||||
result.size = size;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_CopyCString(Dqn_Allocator allocator, char const *string, Dqn_usize size)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_CopyCString(Dqn_Arena *arena, char const *string, Dqn_usize size)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!string)
|
||||
return result;
|
||||
|
||||
result = Dqn_Str8_Allocate(allocator, size, Dqn_ZeroMem_No);
|
||||
if (Dqn_Str8_IsValid(result)) {
|
||||
result = Dqn_Str8_Alloc(arena, size, Dqn_ZeroMem_No);
|
||||
if (Dqn_Str8_HasData(result)) {
|
||||
DQN_MEMCPY(result.data, string, size);
|
||||
result.data[size] = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Copy(Dqn_Allocator allocator, Dqn_Str8 string)
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Copy(Dqn_Arena *arena, Dqn_Str8 string)
|
||||
{
|
||||
Dqn_Str8 result = Dqn_Str8_CopyCString(allocator, string.data, string.size);
|
||||
Dqn_Str8 result = Dqn_Str8_CopyCString(arena, string.data, string.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$STRB] Dqn_Str8Builder ================================================================
|
||||
DQN_API bool Dqn_Str8Builder_AppendRef(Dqn_Str8Builder *builder, Dqn_Str8 string)
|
||||
// NOTE: [$STRB] Dqn_Str8Builder ////////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_Str8Builder_AppendRefArray(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array)
|
||||
{
|
||||
if (!builder)
|
||||
return false;
|
||||
|
||||
for (Dqn_Str8 string : array) {
|
||||
if (!builder || !string.data || string.size <= 0)
|
||||
return false;
|
||||
|
||||
Dqn_Str8Link *link = Dqn_Allocator_New(builder->allocator, Dqn_Str8Link, Dqn_ZeroMem_No);
|
||||
Dqn_Str8Link *link = Dqn_Arena_New(builder->arena, Dqn_Str8Link, Dqn_ZeroMem_No);
|
||||
if (!link)
|
||||
return false;
|
||||
|
||||
@ -647,25 +730,31 @@ DQN_API bool Dqn_Str8Builder_AppendRef(Dqn_Str8Builder *builder, Dqn_Str8 string
|
||||
builder->tail = link;
|
||||
builder->count++;
|
||||
builder->string_size += string.size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AppendCopy(Dqn_Str8Builder *builder, Dqn_Str8 string)
|
||||
DQN_API bool Dqn_Str8Builder_AppendCopyArray(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array)
|
||||
{
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(builder->allocator, string);
|
||||
bool result = Dqn_Str8Builder_AppendRef(builder, copy);
|
||||
return result;
|
||||
for (Dqn_Str8 string : array) {
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(builder->arena, string);
|
||||
if (!Dqn_Str8Builder_AppendRef(builder, copy))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AppendFV(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
Dqn_Str8 string = Dqn_Str8_InitFV(builder->allocator, fmt, args);
|
||||
Dqn_Str8 string = Dqn_Str8_InitFV(builder->arena, fmt, args);
|
||||
if (string.size == 0)
|
||||
return true;
|
||||
|
||||
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(builder->arena);
|
||||
bool result = Dqn_Str8Builder_AppendRef(builder, string);
|
||||
if (!result)
|
||||
Dqn_Allocator_Dealloc(builder->allocator, string.data, string.size + 1);
|
||||
Dqn_Arena_TempMemEnd(temp_mem);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -678,13 +767,27 @@ DQN_API bool Dqn_Str8Builder_AppendF(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB ch
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_Build(Dqn_Str8Builder const *builder, Dqn_Allocator allocator)
|
||||
DQN_API bool Dqn_Str8Builder_AppendRef(Dqn_Str8Builder *builder, Dqn_Str8 string)
|
||||
{
|
||||
Dqn_Slice<Dqn_Str8> array = Dqn_Slice_Init(&string, 1);
|
||||
bool result = Dqn_Str8Builder_AppendRefArray(builder, array);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AppendCopy(Dqn_Str8Builder *builder, Dqn_Str8 string)
|
||||
{
|
||||
Dqn_Slice<Dqn_Str8> array = Dqn_Slice_Init(&string, 1);
|
||||
bool result = Dqn_Str8Builder_AppendCopyArray(builder, array);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_Build(Dqn_Str8Builder const *builder, Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Str8 result = DQN_ZERO_INIT;
|
||||
if (!builder || builder->string_size <= 0 || builder->count <= 0)
|
||||
return result;
|
||||
|
||||
result.data = Dqn_Allocator_NewArray(allocator, char, builder->string_size + 1, Dqn_ZeroMem_No);
|
||||
result.data = Dqn_Arena_NewArray(arena, char, builder->string_size + 1, Dqn_ZeroMem_No);
|
||||
if (!result.data)
|
||||
return result;
|
||||
|
||||
@ -698,7 +801,46 @@ DQN_API Dqn_Str8 Dqn_Str8Builder_Build(Dqn_Str8Builder const *builder, Dqn_Alloc
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$CHAR] Dqn_Char ==========================================================================
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_BuildCRT(Dqn_Str8Builder const *builder)
|
||||
{
|
||||
Dqn_Str8 result = DQN_ZERO_INIT;
|
||||
if (!builder || builder->string_size <= 0 || builder->count <= 0)
|
||||
return result;
|
||||
|
||||
result.data = DQN_CAST(char *)malloc(builder->string_size + 1);
|
||||
if (!result.data)
|
||||
return result;
|
||||
|
||||
for (Dqn_Str8Link *link = builder->head; link; link = link->next) {
|
||||
DQN_MEMCPY(result.data + result.size, link->string.data, link->string.size);
|
||||
result.size += link->string.size;
|
||||
}
|
||||
|
||||
result.data[result.size] = 0;
|
||||
DQN_ASSERT(result.size == builder->string_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8Builder_BuildSlice(Dqn_Str8Builder const *builder, Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_Slice<Dqn_Str8> result = DQN_ZERO_INIT;
|
||||
if (!builder || builder->string_size <= 0 || builder->count <= 0)
|
||||
return result;
|
||||
|
||||
result = Dqn_Slice_Alloc<Dqn_Str8>(arena, builder->count, Dqn_ZeroMem_No);
|
||||
if (!result.data)
|
||||
return result;
|
||||
|
||||
Dqn_usize slice_index = 0;
|
||||
for (Dqn_Str8Link *link = builder->head; link; link = link->next)
|
||||
result.data[slice_index++] = Dqn_Str8_Copy(arena, link->string);
|
||||
|
||||
DQN_ASSERT(slice_index == builder->count);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: [$CHAR] Dqn_Char //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_Char_IsAlphabet(char ch)
|
||||
{
|
||||
bool result = (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
|
||||
@ -766,7 +908,7 @@ DQN_API char Dqn_Char_ToLower(char ch)
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: [$UTFX] Dqn_UTF ===========================================================================
|
||||
// NOTE: [$UTFX] Dqn_UTF ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API int Dqn_UTF8_EncodeCodepoint(uint8_t utf8[4], uint32_t codepoint)
|
||||
{
|
||||
// NOTE: Table from https://www.reedbeta.com/blog/programmers-intro-to-unicode/
|
||||
@ -779,29 +921,25 @@ DQN_API int Dqn_UTF8_EncodeCodepoint(uint8_t utf8[4], uint32_t codepoint)
|
||||
// 1111'0xxx 10yy'yyyy 10zz'zzzz 10ww'wwww | x'xxyy'yyyy'zzzz'zzww'wwww | U+10000 - U+10FFFF |
|
||||
// ----------------------------------------+----------------------------+--------------------+
|
||||
|
||||
if (codepoint <= 0b0111'1111)
|
||||
{
|
||||
if (codepoint <= 0b0111'1111) {
|
||||
utf8[0] = DQN_CAST(uint8_t) codepoint;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (codepoint <= 0b0111'1111'1111)
|
||||
{
|
||||
if (codepoint <= 0b0111'1111'1111) {
|
||||
utf8[0] = (0b1100'0000 | ((codepoint >> 6) & 0b01'1111)); // x
|
||||
utf8[1] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // y
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (codepoint <= 0b1111'1111'1111'1111)
|
||||
{
|
||||
if (codepoint <= 0b1111'1111'1111'1111) {
|
||||
utf8[0] = (0b1110'0000 | ((codepoint >> 12) & 0b00'1111)); // x
|
||||
utf8[1] = (0b1000'0000 | ((codepoint >> 6) & 0b11'1111)); // y
|
||||
utf8[2] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // z
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (codepoint <= 0b1'1111'1111'1111'1111'1111)
|
||||
{
|
||||
if (codepoint <= 0b1'1111'1111'1111'1111'1111) {
|
||||
utf8[0] = (0b1111'0000 | ((codepoint >> 18) & 0b00'0111)); // x
|
||||
utf8[1] = (0b1000'0000 | ((codepoint >> 12) & 0b11'1111)); // y
|
||||
utf8[2] = (0b1000'0000 | ((codepoint >> 6) & 0b11'1111)); // z
|
||||
@ -822,14 +960,12 @@ DQN_API int Dqn_UTF16_EncodeCodepoint(uint16_t utf16[2], uint32_t codepoint)
|
||||
// 1101'10xx'xxxx'xxxx 1101'11yy'yyyy'yyyy | xxxx'xxxx'xxyy'yyyy'yyyy + 0x10000 | U+10000–U+10FFFF |
|
||||
// ----------------------------------------+------------------------------------+------------------+
|
||||
|
||||
if (codepoint <= 0b1111'1111'1111'1111)
|
||||
{
|
||||
if (codepoint <= 0b1111'1111'1111'1111) {
|
||||
utf16[0] = DQN_CAST(uint16_t) codepoint;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (codepoint <= 0b1111'1111'1111'1111'1111)
|
||||
{
|
||||
if (codepoint <= 0b1111'1111'1111'1111'1111) {
|
||||
uint32_t surrogate_codepoint = codepoint + 0x10000;
|
||||
utf16[0] = 0b1101'1000'0000'0000 | ((surrogate_codepoint >> 10) & 0b11'1111'1111); // x
|
||||
utf16[1] = 0b1101'1100'0000'0000 | ((surrogate_codepoint >> 0) & 0b11'1111'1111); // y
|
388
dqn_string.h
Normal file
388
dqn_string.h
Normal file
@ -0,0 +1,388 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\\__$$ __|$$ __$$\ \_$$ _|$$$\ $$ |$$ __$$\
|
||||
// $$ / \__| $$ | $$ | $$ | $$ | $$$$\ $$ |$$ / \__|
|
||||
// \$$$$$$\ $$ | $$$$$$$ | $$ | $$ $$\$$ |$$ |$$$$\
|
||||
// \____$$\ $$ | $$ __$$< $$ | $$ \$$$$ |$$ |\_$$ |
|
||||
// $$\ $$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ |
|
||||
// \$$$$$$ | $$ | $$ | $$ |$$$$$$\ $$ | \$$ |\$$$$$$ |
|
||||
// \______/ \__| \__| \__|\______|\__| \__| \______/
|
||||
//
|
||||
// dqn_string.h -- UTF8/16 string manipulation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// [$CSTR] Dqn_CStr8 -- C-string helpers
|
||||
// [$STR8] Dqn_Str8 -- Pointer and length strings
|
||||
// [$STRB] Dqn_Str8Builder -- Construct strings dynamically
|
||||
// [$FSTR] Dqn_FStr8 -- Fixed-size strings
|
||||
// [$CHAR] Dqn_Char -- Character ascii/digit.. helpers
|
||||
// [$UTFX] Dqn_UTF -- Unicode helpers
|
||||
//
|
||||
// NOTE: [$STR8] Dqn_Str8 //////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_Str8Link
|
||||
{
|
||||
Dqn_Str8 string; // The string
|
||||
Dqn_Str8Link *next; // The next string in the linked list
|
||||
};
|
||||
|
||||
struct Dqn_Str16 // A pointer and length style string that holds slices to UTF16 bytes.
|
||||
{
|
||||
wchar_t *data; // The UTF16 bytes of the string
|
||||
Dqn_usize size; // The number of characters in the string
|
||||
|
||||
#if defined(__cplusplus)
|
||||
wchar_t const *begin() const { return data; } // Const begin iterator for range-for loops
|
||||
wchar_t const *end () const { return data + size; } // Const end iterator for range-for loops
|
||||
wchar_t *begin() { return data; } // Begin iterator for range-for loops
|
||||
wchar_t *end () { return data + size; } // End iterator for range-for loops
|
||||
#endif
|
||||
};
|
||||
|
||||
struct Dqn_Str8BinarySplitResult
|
||||
{
|
||||
Dqn_Str8 lhs;
|
||||
Dqn_Str8 rhs;
|
||||
};
|
||||
|
||||
struct Dqn_Str8FindResult
|
||||
{
|
||||
bool found; // True if string was found. If false, the subsequent fields below are not set.
|
||||
Dqn_usize index; // The index in the buffer where the found string starts
|
||||
Dqn_Str8 match; // The matching string in the buffer that was searched
|
||||
Dqn_Str8 match_to_end_of_buffer; // The substring containing the found string to the end of the buffer
|
||||
Dqn_Str8 start_to_before_match; // The substring from the start of the buffer up until the found string, not including it
|
||||
};
|
||||
|
||||
enum Dqn_Str8IsAll
|
||||
{
|
||||
Dqn_Str8IsAll_Digits,
|
||||
Dqn_Str8IsAll_Hex,
|
||||
};
|
||||
|
||||
enum Dqn_Str8EqCase
|
||||
{
|
||||
Dqn_Str8EqCase_Sensitive,
|
||||
Dqn_Str8EqCase_Insensitive,
|
||||
};
|
||||
|
||||
enum Dqn_Str8FindFlag
|
||||
{
|
||||
Dqn_Str8FindFlag_Digit = 1 << 0, // 0-9
|
||||
Dqn_Str8FindFlag_Whitespace = 1 << 1, // '\r', '\t', '\n', ' '
|
||||
Dqn_Str8FindFlag_Alphabet = 1 << 2, // A-Z, a-z
|
||||
Dqn_Str8FindFlag_Plus = 1 << 3, // +
|
||||
Dqn_Str8FindFlag_Minus = 1 << 4, // -
|
||||
Dqn_Str8FindFlag_AlphaNum = Dqn_Str8FindFlag_Alphabet | Dqn_Str8FindFlag_Digit,
|
||||
};
|
||||
|
||||
enum Dqn_Str8SplitIncludeEmptyStrings
|
||||
{
|
||||
Dqn_Str8SplitIncludeEmptyStrings_No,
|
||||
Dqn_Str8SplitIncludeEmptyStrings_Yes,
|
||||
};
|
||||
|
||||
struct Dqn_Str8ToU64Result
|
||||
{
|
||||
bool success;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
struct Dqn_Str8ToI64Result
|
||||
{
|
||||
bool success;
|
||||
int64_t value;
|
||||
};
|
||||
|
||||
// NOTE: [$FSTR] Dqn_FStr8 /////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_FSTR8)
|
||||
template <Dqn_usize N> struct Dqn_FStr8
|
||||
{
|
||||
char data[N+1];
|
||||
Dqn_usize size;
|
||||
|
||||
char *begin() { return data; }
|
||||
char *end () { return data + size; }
|
||||
char const *begin() const { return data; }
|
||||
char const *end () const { return data + size; }
|
||||
};
|
||||
#endif // !defined(DQN_NO_FSTR8)
|
||||
|
||||
struct Dqn_Str8Builder
|
||||
{
|
||||
Dqn_Arena *arena; // Allocator to use to back the string list
|
||||
Dqn_Str8Link *head; // First string in the linked list of strings
|
||||
Dqn_Str8Link *tail; // Last string in the linked list of strings
|
||||
Dqn_usize string_size; // The size in bytes necessary to construct the current string
|
||||
Dqn_usize count; // The number of links in the linked list of strings
|
||||
};
|
||||
|
||||
// NOTE: [$CSTR] Dqn_CStr8 /////////////////////////////////////////////////////////////////////////
|
||||
template <Dqn_usize N> constexpr Dqn_usize Dqn_CStr8_ArrayUCount (char const (&literal)[N]) { (void)literal; return N - 1; }
|
||||
template <Dqn_usize N> constexpr Dqn_usize Dqn_CStr8_ArrayICount (char const (&literal)[N]) { (void)literal; return N - 1; }
|
||||
DQN_API Dqn_usize Dqn_CStr8_FSize (DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_usize Dqn_CStr8_FVSize (DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_usize Dqn_CStr8_Size (char const *a);
|
||||
DQN_API Dqn_usize Dqn_CStr16_Size (wchar_t const *a);
|
||||
|
||||
// NOTE: [$STR6] Dqn_Str16 /////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_STR16(string) Dqn_Str16{(wchar_t *)(string), sizeof(string)/sizeof(string[0]) - 1}
|
||||
#define Dqn_Str16_HasData(string) ((string).data && (string).size)
|
||||
|
||||
#if defined(__cplusplus)
|
||||
DQN_API bool operator== (Dqn_Str16 const &lhs, Dqn_Str16 const &rhs);
|
||||
DQN_API bool operator!= (Dqn_Str16 const &lhs, Dqn_Str16 const &rhs);
|
||||
#endif
|
||||
|
||||
// NOTE: [$STR8] Dqn_Str8 //////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_STR8(string) Dqn_Str8{(char *)(string), (sizeof(string) - 1)}
|
||||
#define DQN_STR_FMT(string) (int)((string).size), (string).data
|
||||
#define Dqn_Str8_Init(data, size) Dqn_Str8{(char *)(data), (size_t)(size)}
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitCStr8 (char const *src);
|
||||
#define Dqn_Str8_HasData(string) ((string).data && (string).size)
|
||||
DQN_API bool Dqn_Str8_IsAll (Dqn_Str8 string, Dqn_Str8IsAll is_all);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitFV (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Alloc (Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_CopyCString (Dqn_Arena *arena, char const *string, Dqn_usize size);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Copy (Dqn_Arena *arena, Dqn_Str8 string);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Slice (Dqn_Str8 string, Dqn_usize offset, Dqn_usize size);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Advance (Dqn_Str8 string, Dqn_usize amount);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverse (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_usize Dqn_Str8_Split (Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode);
|
||||
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc (Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode);
|
||||
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstString (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirst (Dqn_Str8 string, uint32_t flags);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Segment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_ReverseSegment (Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char);
|
||||
|
||||
DQN_API bool Dqn_Str8_Eq (Dqn_Str8 lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_EqInsensitive (Dqn_Str8 lhs, Dqn_Str8 rhs);
|
||||
DQN_API bool Dqn_Str8_StartsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_StartsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix);
|
||||
DQN_API bool Dqn_Str8_EndsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_EndsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix);
|
||||
DQN_API bool Dqn_Str8_HasChar (Dqn_Str8 string, char ch);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimPrefix (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimSuffix (Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimAround (Dqn_Str8 string, Dqn_Str8 trim_string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround (Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark (Dqn_Str8 string);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileNameNoExtension (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileExtension (Dqn_Str8 path);
|
||||
|
||||
DQN_API Dqn_Str8ToU64Result Dqn_Str8_ToU64 (Dqn_Str8 string, char separator);
|
||||
DQN_API Dqn_Str8ToI64Result Dqn_Str8_ToI64 (Dqn_Str8 string, char separator);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Replace (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena);
|
||||
DQN_API void Dqn_Str8_Remove (Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
DQN_API bool operator== (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs);
|
||||
DQN_API bool operator!= (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs);
|
||||
#endif
|
||||
|
||||
// NOTE: [$FSTR] Dqn_Str8Builder ///////////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_Str8Builder_AppendRefArray (Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> string);
|
||||
DQN_API bool Dqn_Str8Builder_AppendCopyArray(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> string);
|
||||
DQN_API bool Dqn_Str8Builder_AppendFV (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_Str8Builder_AppendF (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_Str8Builder_AppendRef (Dqn_Str8Builder *builder, Dqn_Str8 string);
|
||||
DQN_API bool Dqn_Str8Builder_AppendCopy (Dqn_Str8Builder *builder, Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_Build (Dqn_Str8Builder const *builder, Dqn_Arena *arena);
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_BuildCRT (Dqn_Str8Builder const *builder);
|
||||
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8Builder_BuildSlice (Dqn_Str8Builder const *builder, Dqn_Arena *arena);
|
||||
|
||||
// NOTE: [$FSTR] Dqn_FStr8 //////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_FSTR8)
|
||||
template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF (DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
template <Dqn_usize N> Dqn_usize Dqn_FStr8_Max (Dqn_FStr8<N> const *string);
|
||||
template <Dqn_usize N> void Dqn_FStr8_Clear (Dqn_FStr8<N> *string);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendFV (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list va);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendF (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendCStr8 (Dqn_FStr8<N> *string, char const *value, Dqn_usize size);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Append (Dqn_FStr8<N> *string, Dqn_Str8 value);
|
||||
template <Dqn_usize N> Dqn_Str8 Dqn_FStr8_ToStr8 (Dqn_FStr8<N> const *string);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Eq (Dqn_FStr8<N> const *lhs, Dqn_FStr8<N> const *rhs, Dqn_Str8EqCase eq_case);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqStr8 (Dqn_FStr8<N> const *lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqInsensitive (Dqn_FStr8<N> const *lhs, Dqn_FStr8<N> const *rhs);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqStr8Insensitive (Dqn_FStr8<N> const *lhs, Dqn_Str8 rhs);
|
||||
template <Dqn_usize A, Dqn_usize B> bool Dqn_FStr8_EqFStr8 (Dqn_FStr8<A> const *lhs, Dqn_FStr8<B> const *rhs, Dqn_Str8EqCase eq_case);
|
||||
template <Dqn_usize A, Dqn_usize B> bool Dqn_FStr8_EqFStr8Insensitive (Dqn_FStr8<A> const *lhs, Dqn_FStr8<B> const *rhs);
|
||||
template <Dqn_usize N> bool operator== (Dqn_FStr8<N> const &lhs, Dqn_FStr8<N> const &rhs);
|
||||
template <Dqn_usize N> bool operator!= (Dqn_FStr8<N> const &lhs, Dqn_FStr8<N> const &rhs);
|
||||
#endif // !defined(DQN_NO_FSTR8)
|
||||
|
||||
// NOTE: [$CHAR] Dqn_Char //////////////////////////////////////////////////////////////////////////
|
||||
DQN_API bool Dqn_Char_IsAlphabet (char ch);
|
||||
DQN_API bool Dqn_Char_IsDigit (char ch);
|
||||
DQN_API bool Dqn_Char_IsAlphaNum (char ch);
|
||||
DQN_API bool Dqn_Char_IsWhitespace (char ch);
|
||||
DQN_API bool Dqn_Char_IsHex (char ch);
|
||||
DQN_API uint8_t Dqn_Char_HexToU8 (char ch);
|
||||
DQN_API char Dqn_Char_ToHex (char ch);
|
||||
DQN_API char Dqn_Char_ToHexUnchecked (char ch);
|
||||
DQN_API char Dqn_Char_ToLower (char ch);
|
||||
|
||||
// NOTE: [$UTFX] Dqn_UTF ///////////////////////////////////////////////////////////////////////////
|
||||
DQN_API int Dqn_UTF8_EncodeCodepoint (uint8_t utf8[4], uint32_t codepoint);
|
||||
DQN_API int Dqn_UTF16_EncodeCodepoint (uint16_t utf16[2], uint32_t codepoint);
|
||||
|
||||
#if !defined(DQN_NO_FSTR8)
|
||||
// NOTE: [$FSTR] Dqn_FStr8 /////////////////////////////////////////////////////////////////////////
|
||||
template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF(DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
Dqn_FStr8<N> result = {};
|
||||
if (fmt) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_FStr8_AppendFV(&result, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> Dqn_usize Dqn_FStr8_Max(Dqn_FStr8<N> const *)
|
||||
{
|
||||
Dqn_usize result = N;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> void Dqn_FStr8_Clear(Dqn_FStr8<N> *string)
|
||||
{
|
||||
*string = {};
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendFV(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
bool result = false;
|
||||
if (!string || !fmt)
|
||||
return result;
|
||||
|
||||
Dqn_usize require = Dqn_CStr8_FVSize(fmt, args) + 1 /*null_terminate*/;
|
||||
Dqn_usize space = (N + 1) - string->size;
|
||||
result = require <= space;
|
||||
string->size += DQN_VSNPRINTF(string->data + string->size, DQN_CAST(int)space, fmt, args);
|
||||
|
||||
// NOTE: snprintf returns the required size of the format string
|
||||
// irrespective of if there's space or not.
|
||||
string->size = DQN_MIN(string->size, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendF(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
bool result = false;
|
||||
if (!string || !fmt)
|
||||
return result;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
result = Dqn_FStr8_AppendFV(string, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendCStr8(Dqn_FStr8<N> *string, char const *src, Dqn_usize size)
|
||||
{
|
||||
DQN_ASSERT(string->size <= N);
|
||||
bool result = false;
|
||||
if (!string || !src || size == 0 || string->size >= N)
|
||||
return result;
|
||||
|
||||
Dqn_usize space = N - string->size;
|
||||
result = size <= space;
|
||||
DQN_MEMCPY(string->data + string->size, src, DQN_MIN(space, size));
|
||||
string->size = DQN_MIN(string->size + size, N);
|
||||
string->data[string->size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Append(Dqn_FStr8<N> *string, Dqn_Str8 src)
|
||||
{
|
||||
bool result = Dqn_FStr8_AppendCStr8(string, src.data, src.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> Dqn_Str8 Dqn_FStr8_ToStr8(Dqn_FStr8<N> const *string)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!string || string->size <= 0)
|
||||
return result;
|
||||
|
||||
result.data = DQN_CAST(char *)string->data;
|
||||
result.size = string->size;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Eq(Dqn_FStr8<N> const *lhs, Dqn_FStr8<N> const *rhs, Dqn_Str8EqCase eq_case)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, eq_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqStr8(Dqn_FStr8<N> const *lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs, eq_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqInsensitive(Dqn_FStr8<N> const *lhs, Dqn_FStr8<N> const *rhs)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, Dqn_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqStr8Insensitive(Dqn_FStr8<N> const *lhs, Dqn_Str8 rhs)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs, Dqn_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize A, Dqn_usize B> bool Dqn_FStr8_EqFStr8(Dqn_FStr8<A> const *lhs, Dqn_FStr8<B> const *rhs, Dqn_Str8EqCase eq_case)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, eq_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize A, Dqn_usize B> bool Dqn_FStr8_EqFStr8Insensitive(Dqn_FStr8<A> const *lhs, Dqn_FStr8<B> const *rhs)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, Dqn_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool operator==(Dqn_FStr8<N> const &lhs, Dqn_FStr8<N> const &rhs)
|
||||
{
|
||||
bool result = Dqn_FStr8_Eq(&lhs, &rhs, Dqn_Str8EqCase_Sensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool operator!=(Dqn_FStr8<N> const &lhs, Dqn_FStr8<N> const &rhs)
|
||||
{
|
||||
bool result = !(lhs == rhs);
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_NO_FSTR8)
|
649
dqn_strings.h
649
dqn_strings.h
@ -1,649 +0,0 @@
|
||||
// NOTE: [$CSTR] Dqn_CStr8 ======================================================================
|
||||
// @proc Dqn_CStr8_ArrayCount
|
||||
// @desc Calculate the size of a cstring literal/array at compile time
|
||||
// @param literal The cstring literal/array to calculate the size for
|
||||
// @return The size of the cstring not including the null-terminating byte
|
||||
|
||||
// @proc Dqn_CStr8_FSize, Dqn_CStr8_FVSize
|
||||
// Calculate the required size to format the given format cstring.
|
||||
// @param[in] fmt The format string to calculate the size for
|
||||
// @return The size required to format the string, not including the null
|
||||
// terminator.
|
||||
|
||||
// @proc Dqn_CStr8_Size
|
||||
// @desc Calculate the string length of the null-terminated string.
|
||||
// @param[in] a The string whose length is to be determined
|
||||
// @return The length of the string
|
||||
|
||||
DQN_API template <Dqn_usize N> constexpr Dqn_usize Dqn_CStr8_ArrayUCount(char const (&literal)[N]) { (void)literal; return N - 1; }
|
||||
DQN_API template <Dqn_usize N> constexpr Dqn_usize Dqn_CStr8_ArrayICount(char const (&literal)[N]) { (void)literal; return N - 1; }
|
||||
DQN_API Dqn_usize Dqn_CStr8_FSize (DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_usize Dqn_CStr8_FVSize (DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_usize Dqn_CStr8_Size (char const *a);
|
||||
DQN_API Dqn_usize Dqn_CStr16_Size (wchar_t const *a);
|
||||
|
||||
// NOTE: [$STR8] Dqn_Str8 =======================================================================
|
||||
// NOTE: API
|
||||
// @proc Dqn_Str8_Init
|
||||
// @desc Initialise a string from a pointer and length
|
||||
// The string is invalid (i.e. Dqn_Str8_IsValid() returns false) if size is
|
||||
// negative or the string is null.
|
||||
|
||||
// @proc Dqn_Str8_InitCString
|
||||
// @desc Initialise a string from a cstring
|
||||
// The cstring must be null-terminated as its length is evaluated using
|
||||
// strlen. The string is invalid (i.e. Dqn_Str8_IsValid() returns false) if
|
||||
// size is negative or the string is null.
|
||||
|
||||
// @proc Dqn_Str8_InitF
|
||||
// @desc Create a string from a printf style format string
|
||||
// @param[in] allocator The allocator the string will be allocated from
|
||||
// @param[in] fmt The printf style format cstring
|
||||
|
||||
// @proc Dqn_Str8_InitFV
|
||||
// @desc Create a string from a printf style format string using a va_list
|
||||
// @param[in] arena The allocator the string will be allocated from
|
||||
// @param[in] fmt The printf style format cstring
|
||||
// @param[in] va The variable argument list
|
||||
//
|
||||
// @proc Dqn_Str8_IsValid
|
||||
// @desc Determine if the values of the given string are valid
|
||||
// A string is invalid if size is negative or the string is null.
|
||||
// @return True if the string is valid, false otherwise.
|
||||
|
||||
// @proc Dqn_Str8 Dqn_Str8_Slice
|
||||
// @desc Create a slice from a pre-existing string.
|
||||
// The requested slice is clamped to within the bounds of the original string.
|
||||
// @param[in] string The string to slice
|
||||
// @param[in] offset The starting byte to slice from
|
||||
// @param[in] size The size of the slice
|
||||
// @return The sliced string
|
||||
|
||||
// @proc Dqn_Str8_BinarySplit
|
||||
// @desc Split a string into the substring occuring prior and after the first
|
||||
// occurence of the `delimiter`. Neither strings include the `delimiter`.
|
||||
//
|
||||
// @param[in] string The string to split
|
||||
// @param[in] string_size The size of the string
|
||||
// @param[in] delimiter The character to split the string on
|
||||
// @param[out] lhs_size The size of the left hand side of the split string
|
||||
// @param[out] rhs The right hand side of the split string
|
||||
// @param[out] rhs_size The size of the right hand side of the split string
|
||||
//
|
||||
// @return The left hand side of the split string. The original pointer is
|
||||
// returned if the arguments were invalid.
|
||||
|
||||
// @proc Dqn_Str8_Split
|
||||
// @desc Split a string by the delimiting character.
|
||||
// This function can evaluate the number of splits required in the return value
|
||||
// by setting `splits` to null and `splits_count` to 0.
|
||||
// @param[in] string The source string to split
|
||||
// @param[in] delimiter The substring to split the string on
|
||||
// @param[out] splits (Optional) The destination array to write the splits to.
|
||||
// @param[in] splits_count The number of splits that can be written into the
|
||||
// `splits` array.
|
||||
// @return The number of splits in the `string`. If the return value is >=
|
||||
// 'splits_count' then there are more splits in the string than can be written
|
||||
// to the `splits` array. The number of splits written is capped to the
|
||||
// capacity given by the caller, i.e. `splits_count`. This function should be
|
||||
// called again with a sufficiently sized array if all splits are desired.
|
||||
|
||||
// @proc Dqn_Str8_Segment
|
||||
// @desc Segment a string by inserting the `segment_char` every `segment_size`
|
||||
// characters in the string. For example, '123456789' split with
|
||||
// `segment_char` ' ' and `segment_size` '3' would yield, '123 456 789'.
|
||||
|
||||
// @proc Dqn_Str8_Allocate
|
||||
// @desc Create an empty string with the requested size
|
||||
// @param[in] allocator The allocator the string will be allocated from
|
||||
// @param[in] size The size in bytes of the string to allocate
|
||||
// @param[in] zero_mem Enum to indicate if the string's memory should be cleared
|
||||
|
||||
// @proc Dqn_Str8_CopyCString
|
||||
// @desc Create a copy of the given cstring
|
||||
// @param[in] allocator The allocator the string will be allocated from
|
||||
// @param[in] string The cstring to copy
|
||||
// @param[in] size The size of the cstring to copy. This cannot be <= 0
|
||||
// @return A copy of the string, invalid string if any argument was invalid.
|
||||
|
||||
// @proc Dqn_Str8_Copy
|
||||
// @desc Create a copy of the given string
|
||||
// @param[in] allocator The allocator the string will be allocated from
|
||||
// @param[in] string The string to copy
|
||||
// @return A copy of the string, invalid string if any argument was invalid.
|
||||
|
||||
// @proc Dqn_Str8_Eq, Dqn_Str8_EqInsensitive
|
||||
// @desc Compare a string for equality with or without case sensitivity.
|
||||
// @param[in] lhs The first string to compare equality with
|
||||
// @param[in] rhs The second string to compare equality with
|
||||
// @param[in] lhs The first string's size
|
||||
// @param[in] rhs The second string's size
|
||||
// @param[in] eq_case Set the comparison to be case sensitive or insensitive
|
||||
// @return True if the arguments are valid, non-null and the strings
|
||||
// are equal, false otherwise.
|
||||
|
||||
// @proc Dqn_Str8_StartsWith, Dqn_Str8_StartsWithInsensitive,
|
||||
// Dqn_Str8_EndsWith, Dqn_Str8_EndswithInsensitive
|
||||
// @desc Check if a string starts/ends with the specified prefix
|
||||
// `EndsWithInsensitive` is case insensitive
|
||||
// @param[in] string The string to check for the prefix
|
||||
// @param[in] prefix The prefix to check against the string
|
||||
// @param[in] eq_case Set the comparison to be case sensitive or insensitive
|
||||
// @return True if the string is valid, non-null and has the specified prefix,
|
||||
// false otherwise.
|
||||
|
||||
// @proc Dqn_Str8_TrimPrefix, Dqn_Str8_TrimSuffix
|
||||
// @desc Remove the prefix/suffix respectively from the given `string.
|
||||
//
|
||||
// @param[in] string The string to trim
|
||||
// @param[in] prefix The prefix to trim from the string
|
||||
// @param[in] suffix The suffix to trim from the string
|
||||
// @param[in] eq_case Set the comparison to be case sensitive or insensitive
|
||||
// @param[out] trimmed_string The size of the trimmed string
|
||||
//
|
||||
// @return The trimmed string. The original input string is returned if
|
||||
// arguments are invalid or no trim was possible.
|
||||
|
||||
// @proc Dqn_Str8_TrimWhitespaceAround
|
||||
// @desc Trim whitespace from the prefix and suffix of the string
|
||||
//
|
||||
// @param[in] string The string to trim
|
||||
// @param[in] string_size The size of the string
|
||||
// @param[out] trimmed_string The size of the trimmed string
|
||||
//
|
||||
// @return The trimmed string. The original input string is returned if
|
||||
// arguments are invalid or no trim was possible.
|
||||
|
||||
// @proc Dqn_Str8_TrimByteOrderMark
|
||||
// @desc Trim UTF8, UTF16 BE/LE, UTF32 BE/LE byte order mark prefix in the string.
|
||||
//
|
||||
// @param[in] string The string to trim
|
||||
// @param[in] string_size The size of the string
|
||||
// @param[out] trimmed_string The size of the trimmed string
|
||||
//
|
||||
// @return The trimmed string. The original input string is returned if
|
||||
// arguments are invalid or no trim was possible.
|
||||
|
||||
// @proc Dqn_Str8_FileNameFromPath
|
||||
// @desc Get the file name from a file path. The file name is evaluated by
|
||||
// searching from the end of the string backwards to the first occurring path
|
||||
// separator '/' or '\'. If no path separator is found, the original string is
|
||||
// returned. This function preserves the file extension if there were any.
|
||||
//
|
||||
// @param[in] path A file path on the disk
|
||||
// @param[in] size The size of the file path string, if size is '-1' the null
|
||||
// terminated string length is evaluated.
|
||||
// @param[out] file_name_size The size of the returned file name
|
||||
//
|
||||
// @return The file name in the file path, if none is found, the original path
|
||||
// string is returned. Null pointer if arguments are null or invalid.
|
||||
|
||||
// @proc Dqn_Str8_ToI64, Dqn_Str8_ToU64
|
||||
// @desc Convert a number represented as a string to a signed 64 bit number.
|
||||
//
|
||||
// The `separator` is an optional digit separator for example, if `separator`
|
||||
// is set to ',' then "1,234" will successfully be parsed to '1234'.
|
||||
//
|
||||
// Real numbers are truncated. Both '-' and '+' prefixed strings are permitted,
|
||||
// i.e. "+1234" -> 1234 and "-1234" -> -1234. Strings must consist entirely of
|
||||
// digits, the seperator or the permitted prefixes as previously mentioned
|
||||
// otherwise this function will return false, i.e. "1234 dog" will cause the
|
||||
// function to return false, however, the output is greedily converted and
|
||||
// will be evaluated to "1234".
|
||||
//
|
||||
// `ToU64` only '+' prefix is permitted
|
||||
// `ToI64` either '+' or '-' prefix is permitted
|
||||
//
|
||||
// @param[in] separator The character used to separate the digits, if any. Set
|
||||
// this to 0, if no separators are permitted.
|
||||
|
||||
// @proc Dqn_Str8_Replace, Dqn_Str8_ReplaceInsensitive
|
||||
// @desc TODO(doyle): Write description
|
||||
|
||||
// @proc Dqn_Str8_Remove
|
||||
// @desc Remove the substring denoted by the begin index and the size from the string
|
||||
// string in-place using MEMMOVE to shift the string back.
|
||||
|
||||
// @proc Dqn_Str8_Find
|
||||
// @desc @param start_index Set an index within the string string to start the search
|
||||
// from, if not desired, set to 0
|
||||
// @return A string that points to the matching find, otherwise a 0 length string.
|
||||
|
||||
// @proc DQN_STR8
|
||||
// @desc Construct a UTF8 c-string literal into a Dqn_Str8 referencing a
|
||||
// string stored in the data-segment. This string is read-only.
|
||||
|
||||
// @proc DQN_STR16
|
||||
// @desc Construct a UTF16 c-string literal into a Dqn_Str16 referencing a string
|
||||
// stored in the data-segment. This string is read-only.
|
||||
|
||||
// @proc DQN_STR_FMT
|
||||
// @desc Unpack a string into arguments for printing a string into a printf style
|
||||
// format string.
|
||||
|
||||
struct Dqn_Str8Link
|
||||
{
|
||||
Dqn_Str8 string; // The string
|
||||
Dqn_Str8Link *next; // The next string in the linked list
|
||||
};
|
||||
|
||||
struct Dqn_Str16 /// A pointer and length style string that holds slices to UTF16 bytes.
|
||||
{
|
||||
wchar_t *data; // The UTF16 bytes of the string
|
||||
Dqn_usize size; // The number of characters in the string
|
||||
|
||||
#if defined(__cplusplus)
|
||||
wchar_t const *begin() const { return data; } ///< Const begin iterator for range-for loops
|
||||
wchar_t const *end () const { return data + size; } ///< Const end iterator for range-for loops
|
||||
wchar_t *begin() { return data; } ///< Begin iterator for range-for loops
|
||||
wchar_t *end () { return data + size; } ///< End iterator for range-for loops
|
||||
#endif
|
||||
};
|
||||
|
||||
struct Dqn_Str8BinarySplitResult
|
||||
{
|
||||
Dqn_Str8 lhs;
|
||||
Dqn_Str8 rhs;
|
||||
};
|
||||
|
||||
struct Dqn_Str8FindResult
|
||||
{
|
||||
bool found; // True if string was found. If false, the subsequent fields below are not set.
|
||||
Dqn_usize index; // The index in the buffer where the found string starts
|
||||
Dqn_Str8 match; // The matching string in the buffer that was searched
|
||||
Dqn_Str8 match_to_end_of_buffer; // The substring containing the found string to the end of the buffer
|
||||
Dqn_Str8 start_to_before_match; // The substring from the start of the buffer up until the found string, not including it
|
||||
};
|
||||
|
||||
// NOTE: Macros ====================================================================================
|
||||
#define DQN_STR8(string) Dqn_Str8{(char *)(string), sizeof(string) - 1}
|
||||
#define DQN_STR16(string) Dqn_Str16{(wchar_t *)(string), (sizeof(string)/sizeof(string[0])) - 1}
|
||||
#define DQN_STR_FMT(string) (int)((string).size), (string).data
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#define Dqn_Str8_Init(data, size) (Dqn_Str8{(char *)(data), (Dqn_usize)(size)})
|
||||
#else
|
||||
#define Dqn_Str8_Init(data, size) (Dqn_Str8){(data), (size)}
|
||||
#endif
|
||||
|
||||
// NOTE: API =======================================================================================
|
||||
enum Dqn_Str8IsAll
|
||||
{
|
||||
Dqn_Str8IsAll_Digits,
|
||||
Dqn_Str8IsAll_Hex,
|
||||
};
|
||||
|
||||
enum Dqn_Str8EqCase
|
||||
{
|
||||
Dqn_Str8EqCase_Sensitive,
|
||||
Dqn_Str8EqCase_Insensitive,
|
||||
};
|
||||
|
||||
enum Dqn_Str8FindFlag
|
||||
{
|
||||
Dqn_Str8FindFlag_Digit = 1 << 0, // 0-9
|
||||
Dqn_Str8FindFlag_Whitespace = 1 << 1, // '\r', '\t', '\n', ' '
|
||||
Dqn_Str8FindFlag_Alphabet = 1 << 2, // A-Z, a-z
|
||||
Dqn_Str8FindFlag_Plus = 1 << 3, // +
|
||||
Dqn_Str8FindFlag_Minus = 1 << 4, // -
|
||||
Dqn_Str8FindFlag_AlphaNum = Dqn_Str8FindFlag_Alphabet | Dqn_Str8FindFlag_Digit,
|
||||
};
|
||||
|
||||
struct Dqn_Str8SplitAllocResult
|
||||
{
|
||||
Dqn_Str8 *data;
|
||||
Dqn_usize size;
|
||||
};
|
||||
|
||||
struct Dqn_Str8ToU64Result
|
||||
{
|
||||
bool success;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
struct Dqn_Str8ToI64Result
|
||||
{
|
||||
bool success;
|
||||
int64_t value;
|
||||
};
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitCStr8 (char const *src);
|
||||
#define Dqn_Str8_IsValid(string) ((string).data)
|
||||
DQN_API bool Dqn_Str8_IsAll (Dqn_Str8 string, Dqn_Str8IsAll is_all);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitF (Dqn_Allocator allocator, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_InitFV (Dqn_Allocator allocator, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Allocate (Dqn_Allocator allocator, Dqn_usize size, Dqn_ZeroMem zero_mem);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_CopyCString (Dqn_Allocator allocator, char const *string, Dqn_usize size);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Copy (Dqn_Allocator allocator, Dqn_Str8 string);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Slice (Dqn_Str8 string, Dqn_usize offset, Dqn_usize size);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Advance (Dqn_Str8 string, Dqn_usize amount);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverse (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_usize Dqn_Str8_Split (Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count);
|
||||
DQN_API Dqn_Str8SplitAllocResult Dqn_Str8_SplitAlloc (Dqn_Allocator allocator, Dqn_Str8 string, Dqn_Str8 delimiter);
|
||||
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray (Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstString (Dqn_Str8 string, Dqn_Str8 find);
|
||||
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirst (Dqn_Str8 string, uint32_t flags);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Segment (Dqn_Allocator allocator, Dqn_Str8 src, Dqn_usize segment_size, char segment_char);
|
||||
|
||||
DQN_API bool Dqn_Str8_Eq (Dqn_Str8 lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_EqInsensitive (Dqn_Str8 lhs, Dqn_Str8 rhs);
|
||||
DQN_API bool Dqn_Str8_StartsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_StartsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix);
|
||||
DQN_API bool Dqn_Str8_EndsWith (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API bool Dqn_Str8_EndsWithInsensitive (Dqn_Str8 string, Dqn_Str8 prefix);
|
||||
DQN_API bool Dqn_Str8_HasChar (Dqn_Str8 string, char ch);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimPrefix (Dqn_Str8 string, Dqn_Str8 prefix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimSuffix (Dqn_Str8 string, Dqn_Str8 suffix, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimAround (Dqn_Str8 string, Dqn_Str8 trim_string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround (Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark (Dqn_Str8 string);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FileNameNoExtension (Dqn_Str8 path);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension (Dqn_Str8 path);
|
||||
|
||||
DQN_API Dqn_Str8ToU64Result Dqn_Str8_ToU64 (Dqn_Str8 string, char separator);
|
||||
DQN_API Dqn_Str8ToI64Result Dqn_Str8_ToI64 (Dqn_Str8 string, char separator);
|
||||
|
||||
DQN_API Dqn_Str8 Dqn_Str8_Replace (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Allocator allocator, Dqn_Str8EqCase eq_case = Dqn_Str8EqCase_Sensitive);
|
||||
DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive (Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Allocator allocator);
|
||||
DQN_API void Dqn_Str8_Remove (Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
DQN_API bool operator== (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs);
|
||||
DQN_API bool operator!= (Dqn_Str8 const &lhs, Dqn_Str8 const &rhs);
|
||||
#endif
|
||||
|
||||
#if !defined(DQN_NO_FSTR8)
|
||||
// NOTE: [$FSTR] Dqn_FStr8 =========================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_FStr8_InitF
|
||||
// @desc Create a fixed string from the format string. The result string is
|
||||
// null-terminated.
|
||||
// @param fmt[in] Format string specifier to create the fixed string from
|
||||
// @return The created string, truncated if there was insufficient space
|
||||
|
||||
// @proc Dqn_FStr8_Max
|
||||
// @desc @param string[in] The string to query the maximum capacity of
|
||||
// @return Maximum capacity of the fixed string
|
||||
|
||||
// @proc Dqn_FStr8_Clear
|
||||
// @desc Reset the characters in the string
|
||||
// @param string[in] The string to clear
|
||||
|
||||
// @proc Dqn_FStr8_AppendFV
|
||||
// @desc Append a format string to the fixed string. On failure the string is
|
||||
// appended to but truncated ensuring null-termination.
|
||||
// @param string[in] The string to append to
|
||||
// @param fmt[in] Format string to append to the fixed string
|
||||
// @return True if append was successful, false otherwise.
|
||||
|
||||
// @proc Dqn_FStr8_AppendF
|
||||
// @desc @copydocs Dqn_FStr8_AppendF
|
||||
|
||||
// @proc Dqn_FStr8_AppendCStr8
|
||||
// @desc Append a cstring to the fixed string. On failure the string is
|
||||
// appended to but truncated ensuring null-termination.
|
||||
// @param string[in] The string to append to
|
||||
// @param value[in] Cstring to append to the fixed string
|
||||
// @param size[in] Size of the cstring
|
||||
// @return True if append was successful, false otherwise.
|
||||
|
||||
// @proc Dqn_FStr8_Append
|
||||
// @desc Append a string to the fixed string. On failure the string is
|
||||
// appended to but truncated ensuring null-termination.
|
||||
// @param string[in] The string to append to
|
||||
// @param value[in] String to append to the fixed string
|
||||
// determined before appending.
|
||||
// @return True if append was successful, false otherwise.
|
||||
|
||||
// @proc Dqn_FStr8_ToStr8
|
||||
// @desc Convert a fixed string to a string. The string holds a reference to the
|
||||
// fixed string and is invalidated once fixed string is deleted.
|
||||
// @param string[in] The fixed string to create a string from
|
||||
// @return String referencing the contents of `string`
|
||||
|
||||
// @proc Dqn_FStr8_Eq
|
||||
// @desc @see Dqn_Str8_Eq
|
||||
|
||||
// @proc Dqn_FStr8_EqStr8
|
||||
// @desc @see Dqn_Str8_Eq
|
||||
|
||||
// @proc Dqn_FStr8_EqInsensitive
|
||||
// @desc Compare a string for equality, case insensitive
|
||||
// @see Dqn_Str8_Eq
|
||||
|
||||
// @proc Dqn_FStr8_EqStr8Insensitive
|
||||
// @desc Compare a string for equality, case insensitive
|
||||
// @see Dqn_Str8_Eq
|
||||
|
||||
template <Dqn_usize N> struct Dqn_FStr8
|
||||
{
|
||||
char data[N+1];
|
||||
Dqn_usize size;
|
||||
|
||||
bool operator==(Dqn_FStr8 const &other) const {
|
||||
if (size != other.size) return false;
|
||||
bool result = DQN_MEMCMP(data, other.data, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator!=(Dqn_FStr8 const &other) const { return !(*this == other); }
|
||||
|
||||
char *begin() { return data; }
|
||||
char *end () { return data + size; }
|
||||
char const *begin() const { return data; }
|
||||
char const *end () const { return data + size; }
|
||||
};
|
||||
|
||||
template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF (DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
template <Dqn_usize N> Dqn_usize Dqn_FStr8_Max (Dqn_FStr8<N> const *string);
|
||||
template <Dqn_usize N> void Dqn_FStr8_Clear (Dqn_FStr8<N> *string);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendFV (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list va);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendF (Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendCStr8 (Dqn_FStr8<N> *string, char const *value, Dqn_usize size);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Append (Dqn_FStr8<N> *string, Dqn_Str8 value);
|
||||
template <Dqn_usize N> Dqn_Str8 Dqn_FStr8_ToStr8 (Dqn_FStr8<N> const *string);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Eq (Dqn_FStr8<N> const *lhs, Dqn_FStr8<N> const *rhs, Dqn_Str8EqCase eq_case);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqStr8 (Dqn_FStr8<N> const *lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqInsensitive (Dqn_FStr8<N> const *lhs, Dqn_FStr8<N> const *rhs);
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqStr8Insensitive (Dqn_FStr8<N> const *lhs, Dqn_Str8 rhs);
|
||||
template <Dqn_usize A, Dqn_usize B> bool Dqn_FStr8_EqFStr8 (Dqn_FStr8<A> const *lhs, Dqn_FStr8<B> const *rhs, Dqn_Str8EqCase eq_case);
|
||||
template <Dqn_usize A, Dqn_usize B> bool Dqn_FStr8_EqFStr8Insensitive(Dqn_FStr8<A> const *lhs, Dqn_FStr8<B> const *rhs);
|
||||
#endif // !defined(DQN_NO_FSTR8)
|
||||
|
||||
// NOTE: [$STRB] Dqn_Str8Builder ================================================================
|
||||
// NOTE: API =======================================================================================
|
||||
// @proc Dqn_Str8Builder_AppendRef, Dqn_Str8_AppendCopy,
|
||||
// Dqn_Str8_AppendFV, Dqn_Str8_AppendF
|
||||
// @desc Append a string to the list of strings in the builder.
|
||||
//
|
||||
// The string is appended to the builder as follows
|
||||
// - AppendRef: By reference
|
||||
// - AppendCopy: By copy using the builder's allocator to copy the string
|
||||
// - AppendFV, AppendF: Using a format string, allocated using the builder's
|
||||
// allocator
|
||||
//
|
||||
// The string's data must persist whilst the string builder is being used.
|
||||
// @param builder The builder to append the string to
|
||||
// @param string The string to append to the builder
|
||||
// @return True if append was successful, false if parameters are invalid
|
||||
// or memory allocation failure.
|
||||
|
||||
// @proc Dqn_Str8Builder_Build
|
||||
// @desc Build the list of strings into the final composite string from the
|
||||
// string builder
|
||||
// @param builder The string builder to build the string from
|
||||
// @param allocator The allocator to use to build the string
|
||||
// @return The string if build was successful, empty string if parameters are
|
||||
// invalid or memory allocation failure.
|
||||
|
||||
struct Dqn_Str8Builder
|
||||
{
|
||||
Dqn_Allocator allocator; ///< Allocator to use to back the string list
|
||||
Dqn_Str8Link *head; ///< First string in the linked list of strings
|
||||
Dqn_Str8Link *tail; ///< Last string in the linked list of strings
|
||||
Dqn_usize string_size; ///< The size in bytes necessary to construct the current string
|
||||
Dqn_usize count; ///< The number of links in the linked list of strings
|
||||
};
|
||||
|
||||
DQN_API bool Dqn_Str8Builder_AppendF (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||
DQN_API bool Dqn_Str8Builder_AppendFV (Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||
DQN_API bool Dqn_Str8Builder_AppendRef (Dqn_Str8Builder *builder, Dqn_Str8 string);
|
||||
DQN_API bool Dqn_Str8Builder_AppendCopy(Dqn_Str8Builder *builder, Dqn_Str8 string);
|
||||
DQN_API Dqn_Str8 Dqn_Str8Builder_Build (Dqn_Str8Builder const *builder, Dqn_Allocator allocator);
|
||||
|
||||
// NOTE: [$CHAR] Dqn_Char ==========================================================================
|
||||
DQN_API bool Dqn_Char_IsAlphabet (char ch);
|
||||
DQN_API bool Dqn_Char_IsDigit (char ch);
|
||||
DQN_API bool Dqn_Char_IsAlphaNum (char ch);
|
||||
DQN_API bool Dqn_Char_IsWhitespace (char ch);
|
||||
DQN_API bool Dqn_Char_IsHex (char ch);
|
||||
DQN_API uint8_t Dqn_Char_HexToU8 (char ch);
|
||||
DQN_API char Dqn_Char_ToHex (char ch);
|
||||
DQN_API char Dqn_Char_ToHexUnchecked(char ch);
|
||||
DQN_API char Dqn_Char_ToLower (char ch);
|
||||
|
||||
// NOTE: [$UTFX] Dqn_UTF ===========================================================================
|
||||
DQN_API int Dqn_UTF8_EncodeCodepoint(uint8_t utf8[4], uint32_t codepoint);
|
||||
DQN_API int Dqn_UTF16_EncodeCodepoint(uint16_t utf16[2], uint32_t codepoint);
|
||||
|
||||
#if !defined(DQN_NO_FSTR8)
|
||||
// NOTE: [$FSTR] Dqn_FStr8 ======================================================================
|
||||
template <Dqn_usize N> Dqn_FStr8<N> Dqn_FStr8_InitF(DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
Dqn_FStr8<N> result = {};
|
||||
if (fmt) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Dqn_FStr8_AppendFV(&result, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> Dqn_usize Dqn_FStr8_Max(Dqn_FStr8<N> const *)
|
||||
{
|
||||
Dqn_usize result = N;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> void Dqn_FStr8_Clear(Dqn_FStr8<N> *string)
|
||||
{
|
||||
*string = {};
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendFV(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||
{
|
||||
bool result = false;
|
||||
if (!string || !fmt)
|
||||
return result;
|
||||
|
||||
Dqn_usize require = Dqn_CStr8_FVSize(fmt, args) + 1 /*null_terminate*/;
|
||||
Dqn_usize space = (N + 1) - string->size;
|
||||
result = require <= space;
|
||||
string->size += STB_SPRINTF_DECORATE(vsnprintf)(string->data + string->size, DQN_CAST(int)space, fmt, args);
|
||||
|
||||
// NOTE: snprintf returns the required size of the format string
|
||||
// irrespective of if there's space or not.
|
||||
string->size = DQN_MIN(string->size, N);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendF(Dqn_FStr8<N> *string, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||
{
|
||||
bool result = false;
|
||||
if (!string || !fmt)
|
||||
return result;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
result = Dqn_FStr8_AppendFV(string, fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_AppendCStr8(Dqn_FStr8<N> *string, char const *src, Dqn_usize size)
|
||||
{
|
||||
DQN_ASSERT(string->size <= N);
|
||||
bool result = false;
|
||||
if (!string || !src || size == 0 || string->size >= N)
|
||||
return result;
|
||||
|
||||
Dqn_usize space = N - string->size;
|
||||
result = size <= space;
|
||||
DQN_MEMCPY(string->data + string->size, src, DQN_MIN(space, size));
|
||||
string->size = DQN_MIN(string->size + size, N);
|
||||
string->data[string->size] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Append(Dqn_FStr8<N> *string, Dqn_Str8 src)
|
||||
{
|
||||
bool result = Dqn_FStr8_AppendCStr8(string, src.data, src.size);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> Dqn_Str8 Dqn_FStr8_ToStr8(Dqn_FStr8<N> const *string)
|
||||
{
|
||||
Dqn_Str8 result = {};
|
||||
if (!string || string->size <= 0)
|
||||
return result;
|
||||
|
||||
result.data = DQN_CAST(char *)string->data;
|
||||
result.size = string->size;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_Eq(Dqn_FStr8<N> const *lhs, Dqn_FStr8<N> const *rhs, Dqn_Str8EqCase eq_case)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, eq_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqStr8(Dqn_FStr8<N> const *lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs, eq_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqInsensitive(Dqn_FStr8<N> const *lhs, Dqn_FStr8<N> const *rhs)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, Dqn_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize N> bool Dqn_FStr8_EqStr8Insensitive(Dqn_FStr8<N> const *lhs, Dqn_Str8 rhs)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs, Dqn_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize A, Dqn_usize B> bool Dqn_FStr8_EqFStr8(Dqn_FStr8<A> const *lhs, Dqn_FStr8<B> const *rhs, Dqn_Str8EqCase eq_case)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, eq_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Dqn_usize A, Dqn_usize B> bool Dqn_FStr8_EqFStr8Insensitive(Dqn_FStr8<A> const *lhs, Dqn_FStr8<B> const *rhs)
|
||||
{
|
||||
Dqn_Str8 lhs_s8 = Dqn_FStr8_ToStr8(lhs);
|
||||
Dqn_Str8 rhs_s8 = Dqn_FStr8_ToStr8(rhs);
|
||||
bool result = Dqn_Str8_Eq(lhs_s8, rhs_s8, Dqn_Str8EqCase_Insensitive);
|
||||
return result;
|
||||
}
|
||||
#endif // !defined(DQN_NO_FSTR8)
|
107
dqn_thread_context.cpp
Normal file
107
dqn_thread_context.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
// \__$$ __|$$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ / $$ |$$ | $$ |
|
||||
// $$ | $$$$$$$$ |$$$$$$$ |$$$$$\ $$$$$$$$ |$$ | $$ |
|
||||
// $$ | $$ __$$ |$$ __$$< $$ __| $$ __$$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$$$$$$$\ $$ | $$ |$$$$$$$ |
|
||||
// \__| \__| \__|\__| \__|\________|\__| \__|\_______/
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ _____|$$ | $$ |\__$$ __|
|
||||
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ | \$$\ $$ | $$ |
|
||||
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$\ \$$$$ / $$ |
|
||||
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __| $$ $$< $$ |
|
||||
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ /\$$\ $$ |
|
||||
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$$$$$$$\ $$ / $$ | $$ |
|
||||
// \______/ \______/ \__| \__| \__| \________|\__| \__| \__|
|
||||
//
|
||||
// dqn_thread_context.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DQN_THREAD_LOCAL Dqn_ThreadContext g_dqn_thread_context;
|
||||
|
||||
// NOTE: [$TCTX] Dqn_ThreadContext /////////////////////////////////////////////////////////////////
|
||||
Dqn_Scratch::Dqn_Scratch(Dqn_ThreadContext *context, uint8_t context_index)
|
||||
{
|
||||
arena = context->scratch_arenas[context_index];
|
||||
temp_mem = Dqn_Arena_TempMemBegin(arena);
|
||||
destructed = false;
|
||||
}
|
||||
|
||||
Dqn_Scratch::~Dqn_Scratch()
|
||||
{
|
||||
DQN_ASSERT(destructed == false);
|
||||
Dqn_Arena_TempMemEnd(temp_mem);
|
||||
destructed = true;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_Thread_ContextIsInit()
|
||||
{
|
||||
bool result = g_dqn_thread_context.init;
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get()
|
||||
{
|
||||
Dqn_ThreadContext *result = &g_dqn_thread_context;
|
||||
if (result->init)
|
||||
return result;
|
||||
|
||||
result->init = true;
|
||||
Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog;
|
||||
DQN_HARD_ASSERTF(g_dqn_library && g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init()");
|
||||
|
||||
// NOTE: Setup scratch arenas
|
||||
DQN_FOR_UINDEX (index, DQN_ARRAY_UCOUNT(result->scratch_arenas)) {
|
||||
|
||||
// NOTE: We allocate arenas so that they all come from the memory
|
||||
// allocated from the address space of this library. This allows the
|
||||
// library to be used across DLL boundaries for example as long as
|
||||
// across the boundaries the same g_dqn_library instance is shared.
|
||||
//
|
||||
// On unload of the DLL, the address space is deallocated. If we stored
|
||||
// these as TLS stack variables, these arenas would persist and point to
|
||||
// invalid memory addresses.
|
||||
Dqn_FStr8<128> label = Dqn_FStr8_InitF<128>("T%05u Scratch %zu", Dqn_OS_ThreadID(), index);
|
||||
|
||||
// NOTE: Hence here we search for the arena. If it already exists then
|
||||
// we are in that DLL boundary situation where the TLS data has been
|
||||
// reinitialised and zero-ed out. We will try and find the matching
|
||||
// arena in the catalog and re-use it.
|
||||
//
|
||||
// NOTE: This operation is so infrequent and the number of arenas one
|
||||
// has in their program should be low that a string look-up should be
|
||||
// cheap and fine.
|
||||
Dqn_ArenaCatalogItem *catalog_item = Dqn_ArenaCatalog_Find(catalog, Dqn_FStr8_ToStr8(&label));
|
||||
if (catalog_item == &catalog->sentinel) {
|
||||
Dqn_Arena *scratch = Dqn_ArenaCatalog_AllocLabelCopy(catalog, 0, 0, Dqn_ArenaFlag_AllocCanLeak, Dqn_FStr8_ToStr8(&label));
|
||||
result->scratch_arenas[index] = scratch;
|
||||
} else {
|
||||
// NOTE: Reuse the arena
|
||||
result->scratch_arenas[index] = catalog_item->arena;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Is there a way to handle conflict arenas without the user needing to
|
||||
// manually pass it in?
|
||||
DQN_API Dqn_Scratch Dqn_Scratch_Get(void const *conflict_arena)
|
||||
{
|
||||
Dqn_ThreadContext *context = Dqn_ThreadContext_Get();
|
||||
uint8_t context_index = (uint8_t)-1;
|
||||
for (uint8_t index = 0; index < DQN_ARRAY_UCOUNT(context->scratch_arenas); index++) {
|
||||
Dqn_Arena *arena = context->scratch_arenas[index];
|
||||
if (!conflict_arena || arena != conflict_arena) {
|
||||
context_index = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DQN_ASSERT(context_index != (uint8_t)-1);
|
||||
return Dqn_Scratch(context, context_index);
|
||||
}
|
43
dqn_thread_context.h
Normal file
43
dqn_thread_context.h
Normal file
@ -0,0 +1,43 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
// \__$$ __|$$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ __$$\
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ / $$ |$$ | $$ |
|
||||
// $$ | $$$$$$$$ |$$$$$$$ |$$$$$\ $$$$$$$$ |$$ | $$ |
|
||||
// $$ | $$ __$$ |$$ __$$< $$ __| $$ __$$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$ | $$ | $$ |$$ | $$ |
|
||||
// $$ | $$ | $$ |$$ | $$ |$$$$$$$$\ $$ | $$ |$$$$$$$ |
|
||||
// \__| \__| \__|\__| \__|\________|\__| \__|\_______/
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$$$\
|
||||
// $$ __$$\ $$ __$$\ $$$\ $$ |\__$$ __|$$ _____|$$ | $$ |\__$$ __|
|
||||
// $$ / \__|$$ / $$ |$$$$\ $$ | $$ | $$ | \$$\ $$ | $$ |
|
||||
// $$ | $$ | $$ |$$ $$\$$ | $$ | $$$$$\ \$$$$ / $$ |
|
||||
// $$ | $$ | $$ |$$ \$$$$ | $$ | $$ __| $$ $$< $$ |
|
||||
// $$ | $$\ $$ | $$ |$$ |\$$$ | $$ | $$ | $$ /\$$\ $$ |
|
||||
// \$$$$$$ | $$$$$$ |$$ | \$$ | $$ | $$$$$$$$\ $$ / $$ | $$ |
|
||||
// \______/ \______/ \__| \__| \__| \________|\__| \__| \__|
|
||||
//
|
||||
// dqn_thread_context.h -- Per thread data (e.g. scratch arenas)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct Dqn_ThreadContext
|
||||
{
|
||||
Dqn_b32 init;
|
||||
Dqn_Arena *scratch_arenas[2];
|
||||
};
|
||||
|
||||
struct Dqn_Scratch
|
||||
{
|
||||
Dqn_Scratch(Dqn_ThreadContext *context, uint8_t context_index);
|
||||
~Dqn_Scratch();
|
||||
|
||||
Dqn_Arena *arena;
|
||||
Dqn_b32 destructed;
|
||||
Dqn_ArenaTempMem temp_mem;
|
||||
};
|
||||
|
||||
DQN_API bool Dqn_ThreadContext_IsInit();
|
||||
DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get();
|
||||
DQN_API Dqn_Scratch Dqn_Scratch_Get(void const *conflict_arena);
|
50
dqn_type_info.h
Normal file
50
dqn_type_info.h
Normal file
@ -0,0 +1,50 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\
|
||||
// \__$$ __|\$$\ $$ |$$ __$$\ $$ _____| \_$$ _|$$$\ $$ |$$ _____|$$ __$$\
|
||||
// $$ | \$$\ $$ / $$ | $$ |$$ | $$ | $$$$\ $$ |$$ | $$ / $$ |
|
||||
// $$ | \$$$$ / $$$$$$$ |$$$$$\ $$ | $$ $$\$$ |$$$$$\ $$ | $$ |
|
||||
// $$ | \$$ / $$ ____/ $$ __| $$ | $$ \$$$$ |$$ __| $$ | $$ |
|
||||
// $$ | $$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ | $$ |
|
||||
// $$ | $$ | $$ | $$$$$$$$\ $$$$$$\ $$ | \$$ |$$ | $$$$$$ |
|
||||
// \__| \__| \__| \________| \______|\__| \__|\__| \______/
|
||||
//
|
||||
// dqn_type_info.h -- C++ type introspection
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct Dqn_TypeEnumField
|
||||
{
|
||||
uint16_t index;
|
||||
Dqn_Str8 name;
|
||||
Dqn_isize value;
|
||||
};
|
||||
|
||||
struct Dqn_TypeStructField
|
||||
{
|
||||
uint16_t index;
|
||||
Dqn_Str8 name;
|
||||
struct Dqn_TypeInfo const *type;
|
||||
bool is_pointer;
|
||||
Dqn_TypeStructField const *array_count;
|
||||
uint16_t array_static_count;
|
||||
};
|
||||
|
||||
enum Dqn_TypeInfoKind
|
||||
{
|
||||
Dqn_TypeInfoKind_Basic,
|
||||
Dqn_TypeInfoKind_Enum,
|
||||
Dqn_TypeInfoKind_Struct,
|
||||
};
|
||||
|
||||
struct Dqn_TypeInfo
|
||||
{
|
||||
Dqn_Str8 name;
|
||||
Dqn_TypeInfoKind kind;
|
||||
Dqn_TypeStructField const *struct_field;
|
||||
uint16_t struct_field_count;
|
||||
Dqn_TypeEnumField const *enum_field;
|
||||
uint16_t enum_field_count;
|
||||
Dqn_isize enum_min;
|
||||
Dqn_isize enum_max;
|
||||
};
|
@ -1,23 +1,3 @@
|
||||
// NOTE: Preprocessor Config =======================================================================
|
||||
// #define DQN_TEST_WITH_MAIN Define this to enable the main function and allow standalone compiling
|
||||
// and running of the file.
|
||||
// #define DQN_TEST_WITH_KECCAK Define this to enable the main function and allow standalone compiling
|
||||
// and running of the file.
|
||||
|
||||
#if defined(DQN_TEST_WITH_MAIN)
|
||||
#define DQN_ASAN_POISON 1
|
||||
#define DQN_ASAN_VET_POISON 1
|
||||
#define DQN_NO_CHECK_BREAK
|
||||
#define DQN_IMPLEMENTATION
|
||||
#include "dqn.h"
|
||||
#endif
|
||||
|
||||
#if defined(DQN_TEST_WITH_KECCAK)
|
||||
#define DQN_KECCAK_IMPLEMENTATION
|
||||
#include "dqn_keccak.h"
|
||||
#include "dqn_tests_helpers.cpp"
|
||||
#endif
|
||||
|
||||
#define DQN_UTEST_IMPLEMENTATION
|
||||
#include "dqn_utest.h"
|
||||
|
||||
@ -25,28 +5,22 @@ static Dqn_UTest Dqn_Test_Arena()
|
||||
{
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_Arena") {
|
||||
DQN_UTEST_TEST("Grow arena, reserve 4k, commit 1k (e.g. different sizes)") {
|
||||
Dqn_Arena arena = {};
|
||||
Dqn_Arena_Grow(&arena, DQN_KILOBYTES(4), DQN_KILOBYTES(1), /*flags*/ 0);
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Reused memory is zeroed out") {
|
||||
|
||||
uint8_t alignment = 1;
|
||||
Dqn_usize alloc_size = DQN_KILOBYTES(128);
|
||||
Dqn_MemBlockSizeRequiredResult size_required = Dqn_MemBlock_SizeRequired(nullptr, alloc_size, alignment, Dqn_MemBlockFlag_Nil);
|
||||
Dqn_Arena arena = {};
|
||||
Dqn_Arena_Grow(&arena, size_required.block_size, /*commit*/ size_required.block_size, /*flags*/ 0);
|
||||
DQN_DEFER {
|
||||
Dqn_Arena_Deinit(&arena);
|
||||
};
|
||||
|
||||
// NOTE: Allocate 128 kilobytes, fill it with garbage, then reset the arena
|
||||
uintptr_t first_ptr_address = 0;
|
||||
{
|
||||
Dqn_ArenaTempMemory temp_mem = Dqn_Arena_BeginTempMemory(&arena);
|
||||
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(&arena);
|
||||
void *ptr = Dqn_Arena_Alloc(&arena, alloc_size, alignment, Dqn_ZeroMem_Yes);
|
||||
first_ptr_address = DQN_CAST(uintptr_t)ptr;
|
||||
DQN_MEMSET(ptr, 'z', alloc_size);
|
||||
Dqn_Arena_EndTempMemory(temp_mem, false /*cancel*/);
|
||||
Dqn_Arena_TempMemEnd(temp_mem);
|
||||
}
|
||||
|
||||
// NOTE: Reallocate 128 kilobytes
|
||||
@ -58,99 +32,60 @@ static Dqn_UTest Dqn_Test_Arena()
|
||||
// NOTE: Check that the bytes are set to 0
|
||||
for (Dqn_usize i = 0; i < alloc_size; i++)
|
||||
DQN_UTEST_ASSERT(&test, ptr[i] == 0);
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Test arena grows naturally, 1mb + 4mb") {
|
||||
Dqn_Arena arena = {};
|
||||
|
||||
// NOTE: Allocate 1mb, then 4mb, this should force the arena to grow
|
||||
char *ptr_1mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
char *ptr_4mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(4), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
Dqn_Arena arena = Dqn_Arena_InitSize(DQN_MEGABYTES(2), DQN_MEGABYTES(2), Dqn_ArenaFlag_Nil);
|
||||
DQN_DEFER {
|
||||
Dqn_Arena_Deinit(&arena);
|
||||
};
|
||||
|
||||
char *ptr_1mb = Dqn_Arena_NewArray(&arena, char, DQN_MEGABYTES(1), Dqn_ZeroMem_Yes);
|
||||
char *ptr_4mb = Dqn_Arena_NewArray(&arena, char, DQN_MEGABYTES(4), Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr_1mb);
|
||||
DQN_UTEST_ASSERT(&test, ptr_4mb);
|
||||
|
||||
Dqn_MemBlock const *block_1mb = arena.head;
|
||||
char const *block_1mb_begin = DQN_CAST(char *)block_1mb->data;
|
||||
char const *block_1mb_end = DQN_CAST(char *)block_1mb->data + block_1mb->size;
|
||||
Dqn_ArenaBlock const *block_4mb_begin = arena.curr;
|
||||
char const *block_4mb_end = DQN_CAST(char *)block_4mb_begin + block_4mb_begin->reserve;
|
||||
|
||||
Dqn_MemBlock const *block_4mb = arena.curr;
|
||||
char const *block_4mb_begin = DQN_CAST(char *)block_4mb->data;
|
||||
char const *block_4mb_end = DQN_CAST(char *)block_4mb->data + block_4mb->size;
|
||||
Dqn_ArenaBlock const *block_1mb_begin = block_4mb_begin->prev;
|
||||
DQN_UTEST_ASSERTF(&test, block_1mb_begin, "New block should have been allocated");
|
||||
char const *block_1mb_end = DQN_CAST(char *)block_1mb_begin + block_1mb_begin->reserve;
|
||||
|
||||
DQN_UTEST_ASSERTF(&test, block_1mb != block_4mb, "New block should have been allocated and linked");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_1mb >= block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_4mb >= block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERT (&test, arena.curr == arena.tail);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr != arena.head);
|
||||
|
||||
Dqn_Arena_Free(&arena);
|
||||
DQN_UTEST_ASSERTF(&test, block_1mb_begin != block_4mb_begin, "New block should have been allocated and linked");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_1mb >= DQN_CAST(char *)block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_4mb >= DQN_CAST(char *)block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Test arena grows naturally, 1mb, temp memory 4mb") {
|
||||
Dqn_Arena arena = {};
|
||||
Dqn_Arena arena = Dqn_Arena_InitSize(DQN_MEGABYTES(2), DQN_MEGABYTES(2), Dqn_ArenaFlag_Nil);
|
||||
DQN_DEFER {
|
||||
Dqn_Arena_Deinit(&arena);
|
||||
};
|
||||
|
||||
// NOTE: Allocate 1mb, then 4mb, this should force the arena to grow
|
||||
char *ptr_1mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr_1mb);
|
||||
|
||||
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
|
||||
Dqn_ArenaTempMem temp_memory = Dqn_Arena_TempMemBegin(&arena);
|
||||
{
|
||||
char *ptr_4mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(4), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
char *ptr_4mb = Dqn_Arena_NewArray(&arena, char, DQN_MEGABYTES(4), Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr_4mb);
|
||||
|
||||
Dqn_MemBlock const *block_1mb = arena.head;
|
||||
char const *block_1mb_begin = DQN_CAST(char *)block_1mb->data;
|
||||
char const *block_1mb_end = DQN_CAST(char *)block_1mb->data + block_1mb->size;
|
||||
Dqn_ArenaBlock const *block_4mb_begin = arena.curr;
|
||||
char const *block_4mb_end = DQN_CAST(char *) block_4mb_begin + block_4mb_begin->reserve;
|
||||
|
||||
Dqn_MemBlock const *block_4mb = arena.curr;
|
||||
char const *block_4mb_begin = DQN_CAST(char *)block_4mb->data;
|
||||
char const *block_4mb_end = DQN_CAST(char *)block_4mb->data + block_4mb->size;
|
||||
Dqn_ArenaBlock const *block_1mb_begin = block_4mb_begin->prev;
|
||||
char const *block_1mb_end = DQN_CAST(char *) block_1mb_begin + block_1mb_begin->reserve;
|
||||
|
||||
DQN_UTEST_ASSERTF(&test, block_1mb != block_4mb, "New block should have been allocated and linked");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_1mb >= block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_4mb >= block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERT (&test, arena.curr == arena.tail);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr != arena.head);
|
||||
DQN_UTEST_ASSERTF(&test, block_1mb_begin != block_4mb_begin, "New block should have been allocated and linked");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_1mb >= DQN_CAST(char *)block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_UTEST_ASSERTF(&test, ptr_4mb >= DQN_CAST(char *)block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
|
||||
}
|
||||
Dqn_Arena_EndTempMemory(temp_memory, false /*cancel*/);
|
||||
|
||||
DQN_UTEST_ASSERT (&test, arena.curr == arena.head);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr == arena.tail);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr->next == nullptr);
|
||||
DQN_UTEST_ASSERTF(&test, arena.curr->size >= DQN_MEGABYTES(1),
|
||||
"size=%zuMiB (%zuB), expect=%zuB", (arena.curr->size / 1024 / 1024), arena.curr->size, DQN_MEGABYTES(1));
|
||||
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Init arena, temp region then free inside regions") {
|
||||
Dqn_Arena arena = {};
|
||||
Dqn_Arena_Grow(&arena, DQN_KILOBYTES(1), 0, Dqn_ZeroMem_No);
|
||||
|
||||
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
|
||||
{
|
||||
char *ptr = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, ptr);
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
Dqn_Arena_EndTempMemory(temp_memory, false /*cancel*/);
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Init arena, allocate, temp region then free inside region") {
|
||||
Dqn_Arena arena = {};
|
||||
char *outside = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, outside);
|
||||
|
||||
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
|
||||
{
|
||||
char *inside = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(2), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_UTEST_ASSERT(&test, inside);
|
||||
Dqn_Arena_Free(&arena);
|
||||
}
|
||||
Dqn_Arena_EndTempMemory(temp_memory, false /*cancel*/);
|
||||
Dqn_Arena_Free(&arena);
|
||||
Dqn_Arena_TempMemEnd(temp_memory);
|
||||
DQN_UTEST_ASSERT (&test, arena.curr->prev == nullptr);
|
||||
DQN_UTEST_ASSERTF(&test, arena.curr->reserve >= DQN_MEGABYTES(1), "size=%zuMiB (%zuB), expect=%zuB", (arena.curr->reserve / 1024 / 1024), arena.curr->reserve, DQN_MEGABYTES(1));
|
||||
}
|
||||
}
|
||||
return test;
|
||||
@ -158,7 +93,7 @@ static Dqn_UTest Dqn_Test_Arena()
|
||||
|
||||
static Dqn_UTest Dqn_Test_Bin()
|
||||
{
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_Bin") {
|
||||
DQN_UTEST_TEST("Convert 0x123") {
|
||||
@ -553,11 +488,12 @@ static Dqn_UTest Dqn_Test_DSMap()
|
||||
{
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_DSMap") {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
{
|
||||
Dqn_Arena arena = {};
|
||||
uint32_t const MAP_SIZE = 64;
|
||||
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(MAP_SIZE);
|
||||
DQN_DEFER { Dqn_DSMap_Deinit(&map); };
|
||||
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE);
|
||||
DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); };
|
||||
|
||||
DQN_UTEST_TEST("Find non-existent value") {
|
||||
uint64_t *value = Dqn_DSMap_FindKeyStr8(&map, DQN_STR8("Foo")).value;
|
||||
@ -597,10 +533,11 @@ static Dqn_UTest Dqn_Test_DSMap()
|
||||
case DSMapTestType_MakeSlot: prefix = DQN_STR8("Make slot"); break;
|
||||
}
|
||||
|
||||
Dqn_Arena_TempMemoryScope(scratch.arena);
|
||||
Dqn_ArenaTempMemScope temp_mem_scope = Dqn_ArenaTempMemScope(scratch.arena);
|
||||
Dqn_Arena arena = {};
|
||||
uint32_t const MAP_SIZE = 64;
|
||||
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(MAP_SIZE);
|
||||
DQN_DEFER { Dqn_DSMap_Deinit(&map); };
|
||||
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE);
|
||||
DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); };
|
||||
|
||||
DQN_UTEST_TEST("%.*s: Test growing", DQN_STR_FMT(prefix)) {
|
||||
uint64_t map_start_size = map.size;
|
||||
@ -761,51 +698,51 @@ static Dqn_UTest Dqn_Test_Fs()
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_Fs") {
|
||||
DQN_UTEST_TEST("Make directory recursive \"abcd/efgh\"") {
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Fs_MakeDir(DQN_STR8("abcd/efgh")), "Failed to make directory");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(DQN_STR8("abcd")), "Directory was not made");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(DQN_STR8("abcd/efgh")), "Subdirectory was not made");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Fs_Exists(DQN_STR8("abcd")) == false, "This function should only return true for files");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Fs_Exists(DQN_STR8("abcd/efgh")) == false, "This function should only return true for files");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Fs_Delete(DQN_STR8("abcd/efgh")), "Failed to delete directory");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Fs_Delete(DQN_STR8("abcd")), "Failed to cleanup directory");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_OS_DirMake(DQN_STR8("abcd/efgh")), "Failed to make directory");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_OS_DirExists(DQN_STR8("abcd")), "Directory was not made");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_OS_DirExists(DQN_STR8("abcd/efgh")), "Subdirectory was not made");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_OS_FileExists(DQN_STR8("abcd")) == false, "This function should only return true for files");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_OS_FileExists(DQN_STR8("abcd/efgh")) == false, "This function should only return true for files");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_OS_PathDelete(DQN_STR8("abcd/efgh")), "Failed to delete directory");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_OS_PathDelete(DQN_STR8("abcd")), "Failed to cleanup directory");
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Write file, read it, copy it, move it and delete it") {
|
||||
DQN_UTEST_TEST("File write, read, copy, move and delete") {
|
||||
// NOTE: Write step
|
||||
Dqn_Str8 const SRC_FILE = DQN_STR8("dqn_test_file");
|
||||
Dqn_b32 write_result = Dqn_Fs_WriteCStr8(SRC_FILE.data, SRC_FILE.size, "test", 4);
|
||||
Dqn_b32 write_result = Dqn_OS_WriteAll(SRC_FILE, DQN_STR8("test"));
|
||||
DQN_UTEST_ASSERT(&test, write_result);
|
||||
DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(SRC_FILE));
|
||||
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(SRC_FILE));
|
||||
|
||||
// NOTE: Read step
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str8 read_file = Dqn_Fs_Read(SRC_FILE, scratch.allocator);
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_IsValid(read_file), "Failed to load file");
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 read_file = Dqn_OS_ReadAll(SRC_FILE, scratch.arena);
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_HasData(read_file), "Failed to load file");
|
||||
DQN_UTEST_ASSERTF(&test, read_file.size == 4, "File read wrong amount of bytes");
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(read_file, DQN_STR8("test")), "read(%zu): %.*s", read_file.size, DQN_STR_FMT(read_file));
|
||||
|
||||
// NOTE: Copy step
|
||||
Dqn_Str8 const COPY_FILE = DQN_STR8("dqn_test_file_copy");
|
||||
Dqn_b32 copy_result = Dqn_Fs_Copy(SRC_FILE, COPY_FILE, true /*overwrite*/);
|
||||
Dqn_b32 copy_result = Dqn_OS_FileCopy(SRC_FILE, COPY_FILE, true /*overwrite*/);
|
||||
DQN_UTEST_ASSERT(&test, copy_result);
|
||||
DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(COPY_FILE));
|
||||
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(COPY_FILE));
|
||||
|
||||
// NOTE: Move step
|
||||
Dqn_Str8 const MOVE_FILE = DQN_STR8("dqn_test_file_move");
|
||||
Dqn_b32 move_result = Dqn_Fs_Move(COPY_FILE, MOVE_FILE, true /*overwrite*/);
|
||||
Dqn_b32 move_result = Dqn_OS_FileMove(COPY_FILE, MOVE_FILE, true /*overwrite*/);
|
||||
DQN_UTEST_ASSERT(&test, move_result);
|
||||
DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(MOVE_FILE));
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Fs_Exists(COPY_FILE) == false, "Moving a file should remove the original");
|
||||
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(MOVE_FILE));
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_OS_FileExists(COPY_FILE) == false, "Moving a file should remove the original");
|
||||
|
||||
// NOTE: Delete step
|
||||
Dqn_b32 delete_src_file = Dqn_Fs_Delete(SRC_FILE);
|
||||
Dqn_b32 delete_moved_file = Dqn_Fs_Delete(MOVE_FILE);
|
||||
Dqn_b32 delete_src_file = Dqn_OS_PathDelete(SRC_FILE);
|
||||
Dqn_b32 delete_moved_file = Dqn_OS_PathDelete(MOVE_FILE);
|
||||
DQN_UTEST_ASSERT(&test, delete_src_file);
|
||||
DQN_UTEST_ASSERT(&test, delete_moved_file);
|
||||
|
||||
// NOTE: Deleting non-existent file fails
|
||||
Dqn_b32 delete_non_existent_src_file = Dqn_Fs_Delete(SRC_FILE);
|
||||
Dqn_b32 delete_non_existent_moved_file = Dqn_Fs_Delete(MOVE_FILE);
|
||||
Dqn_b32 delete_non_existent_src_file = Dqn_OS_PathDelete(SRC_FILE);
|
||||
Dqn_b32 delete_non_existent_moved_file = Dqn_OS_PathDelete(MOVE_FILE);
|
||||
DQN_UTEST_ASSERT(&test, delete_non_existent_moved_file == false);
|
||||
DQN_UTEST_ASSERT(&test, delete_non_existent_src_file == false);
|
||||
}
|
||||
@ -966,7 +903,7 @@ Dqn_Str8 const DQN_UTEST_HASH_STRING_[] =
|
||||
|
||||
void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input)
|
||||
{
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 input_hex = Dqn_Hex_BytesToStr8Arena(scratch.arena, input.data, input.size);
|
||||
|
||||
switch(hash_type)
|
||||
@ -1203,10 +1140,10 @@ static Dqn_UTest Dqn_Test_OS()
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Query executable directory") {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 result = Dqn_OS_EXEDir(scratch.arena);
|
||||
DQN_UTEST_ASSERT(&test, Dqn_Str8_IsValid(result));
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(result), "result(%zu): %.*s", result.size, DQN_STR_FMT(result));
|
||||
DQN_UTEST_ASSERT(&test, Dqn_Str8_HasData(result));
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_OS_DirExists(result), "result(%zu): %.*s", result.size, DQN_STR_FMT(result));
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Dqn_OS_PerfCounterNow") {
|
||||
@ -1225,11 +1162,11 @@ static Dqn_UTest Dqn_Test_OS()
|
||||
uint64_t b = Dqn_OS_PerfCounterNow();
|
||||
Dqn_f64 s = Dqn_OS_PerfCounterS(a, b);
|
||||
Dqn_f64 ms = Dqn_OS_PerfCounterMs(a, b);
|
||||
Dqn_f64 micro_s = Dqn_OS_PerfCounterMicroS(a, b);
|
||||
Dqn_f64 us = Dqn_OS_PerfCounterUs(a, b);
|
||||
Dqn_f64 ns = Dqn_OS_PerfCounterNs(a, b);
|
||||
DQN_UTEST_ASSERTF(&test, s <= ms, "s: %f, ms: %f", s, ms);
|
||||
DQN_UTEST_ASSERTF(&test, ms <= micro_s, "ms: %f, micro_s: %f", ms, micro_s);
|
||||
DQN_UTEST_ASSERTF(&test, micro_s <= ns, "micro_s: %f, ns: %f", micro_s, ns);
|
||||
DQN_UTEST_ASSERTF(&test, ms <= us, "ms: %f, us: %f", ms, us);
|
||||
DQN_UTEST_ASSERTF(&test, us <= ns, "us: %f, ns: %f", us, ns);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1376,8 +1313,8 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Initialise with format string") {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_InitF(scratch.allocator, "%s", "AB");
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_InitF(scratch.arena, "%s", "AB");
|
||||
DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64u", string.size);
|
||||
DQN_UTEST_ASSERTF(&test, string.data[0] == 'A', "string[0]: %c", string.data[0]);
|
||||
DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]);
|
||||
@ -1385,9 +1322,9 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Copy string") {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 string = DQN_STR8("AB");
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(scratch.allocator, string);
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(scratch.arena, string);
|
||||
DQN_UTEST_ASSERTF(&test, copy.size == 2, "size: %I64u", copy.size);
|
||||
DQN_UTEST_ASSERTF(&test, copy.data[0] == 'A', "copy[0]: %c", copy.data[0]);
|
||||
DQN_UTEST_ASSERTF(&test, copy.data[1] == 'B', "copy[1]: %c", copy.data[1]);
|
||||
@ -1400,8 +1337,8 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Allocate string from arena") {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_Allocate(scratch.allocator, 2, Dqn_ZeroMem_No);
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 string = Dqn_Str8_Alloc(scratch.arena, 2, Dqn_ZeroMem_No);
|
||||
DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64u", string.size);
|
||||
}
|
||||
|
||||
@ -1431,8 +1368,7 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(result, input), "%.*s", DQN_STR_FMT(result));
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Str8_IsAllDigits
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// NOTE: Dqn_Str8_IsAllDigits //////////////////////////////////////////////////////////////
|
||||
DQN_UTEST_TEST("Is all digits fails on non-digit string") {
|
||||
Dqn_b32 result = Dqn_Str8_IsAll(DQN_STR8("@123string"), Dqn_Str8IsAll_Digits);
|
||||
DQN_UTEST_ASSERT(&test, result == false);
|
||||
@ -1448,10 +1384,10 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
DQN_UTEST_ASSERT(&test, result == false);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Is all digits succeeds on string w/ 0 size") {
|
||||
DQN_UTEST_TEST("Is all digits fails on string w/ 0 size") {
|
||||
char const buf[] = "@123string";
|
||||
Dqn_b32 result = Dqn_Str8_IsAll(Dqn_Str8_Init(buf, 0), Dqn_Str8IsAll_Digits);
|
||||
DQN_UTEST_ASSERT(&test, result);
|
||||
DQN_UTEST_ASSERT(&test, !result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Is all digits success") {
|
||||
@ -1503,17 +1439,16 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Dqn_Str8_ToI64
|
||||
// =========================================================================================
|
||||
// NOTE: Dqn_Str8_ToI64 ////////////////////////////////////////////////////////////////////
|
||||
DQN_UTEST_TEST("To I64: Convert null string") {
|
||||
Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(Dqn_Str8_Init(nullptr, 5), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.value == 0);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To I64: Convert empty string") {
|
||||
Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(DQN_STR8(""), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.value == 0);
|
||||
}
|
||||
|
||||
@ -1563,13 +1498,13 @@ static Dqn_UTest Dqn_Test_Str8()
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
DQN_UTEST_TEST("To U64: Convert nullptr") {
|
||||
Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(Dqn_Str8_Init(nullptr, 5), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("To U64: Convert empty string") {
|
||||
Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(DQN_STR8(""), 0);
|
||||
DQN_UTEST_ASSERT(&test, !result.success);
|
||||
DQN_UTEST_ASSERT(&test, result.success);
|
||||
DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value);
|
||||
}
|
||||
|
||||
@ -1700,8 +1635,10 @@ static Dqn_UTest Dqn_Test_VArray()
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_VArray") {
|
||||
{
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_VArray<uint32_t> array = Dqn_VArray_InitByteSize<uint32_t>(scratch.arena, DQN_KILOBYTES(64));
|
||||
Dqn_VArray<uint32_t> array = Dqn_VArray_InitByteSize<uint32_t>(DQN_KILOBYTES(64), 0);
|
||||
DQN_DEFER {
|
||||
Dqn_VArray_Deinit(&array);
|
||||
};
|
||||
|
||||
DQN_UTEST_TEST("Test adding an array of items to the array") {
|
||||
uint32_t array_literal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
@ -1805,8 +1742,10 @@ static Dqn_UTest Dqn_Test_VArray()
|
||||
};
|
||||
DQN_MSVC_WARNING_POP
|
||||
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_VArray<UnalignedObject> array = Dqn_VArray_InitByteSize<UnalignedObject>(scratch.arena, DQN_KILOBYTES(64));
|
||||
Dqn_VArray<UnalignedObject> array = Dqn_VArray_InitByteSize<UnalignedObject>(DQN_KILOBYTES(64), 0);
|
||||
DQN_DEFER {
|
||||
Dqn_VArray_Deinit(&array);
|
||||
};
|
||||
|
||||
// NOTE: Verify that the items returned from the data array are
|
||||
// contiguous in memory.
|
||||
@ -1840,54 +1779,29 @@ static Dqn_UTest Dqn_Test_VArray()
|
||||
static Dqn_UTest Dqn_Test_Win()
|
||||
{
|
||||
Dqn_UTest test = {};
|
||||
DQN_UTEST_GROUP(test, "Dqn_Win") {
|
||||
DQN_UTEST_TEST("Str8 to Str16 size required") {
|
||||
int result = Dqn_Win_Str8ToStr16Buffer(DQN_STR8("a"), nullptr, 0);
|
||||
DQN_UTEST_ASSERTF(&test, result == 1, "Size returned: %d. This size should not include the null-terminator", result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Str16 to Str8 size required") {
|
||||
int result = Dqn_Win_Str16ToStr8Buffer(DQN_STR16(L"a"), nullptr, 0);
|
||||
DQN_UTEST_ASSERTF(&test, result == 1, "Size returned: %d. This size should not include the null-terminator", result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Str8 to Str16 size required") {
|
||||
int result = Dqn_Win_Str8ToStr16Buffer(DQN_STR8("String"), nullptr, 0);
|
||||
DQN_UTEST_ASSERTF(&test, result == 6, "Size returned: %d. This size should not include the null-terminator", result);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Str16 to Str8 size required") {
|
||||
int result = Dqn_Win_Str16ToStr8Buffer(DQN_STR16(L"String"), nullptr, 0);
|
||||
DQN_UTEST_ASSERTF(&test, result == 6, "Size returned: %d. This size should not include the null-terminator", result);
|
||||
}
|
||||
DQN_UTEST_GROUP(test, "OS Win32") {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 input8 = DQN_STR8("String");
|
||||
Dqn_Str16 input16 = Dqn_Str16{(wchar_t *)(L"String"), sizeof(L"String") / sizeof(L"String"[0]) - 1};
|
||||
|
||||
DQN_UTEST_TEST("Str8 to Str16") {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str8 const INPUT = DQN_STR8("String");
|
||||
int size_required = Dqn_Win_Str8ToStr16Buffer(INPUT, nullptr, 0);
|
||||
wchar_t *string = Dqn_Arena_NewArray(scratch.arena, wchar_t, size_required + 1, Dqn_ZeroMem_No);
|
||||
|
||||
// Fill the string with error sentinels, which ensures the string is zero terminated
|
||||
DQN_MEMSET(string, 'Z', size_required + 1);
|
||||
|
||||
int size_returned = Dqn_Win_Str8ToStr16Buffer(INPUT, string, size_required + 1);
|
||||
wchar_t const EXPECTED[] = {L'S', L't', L'r', L'i', L'n', L'g', 0};
|
||||
|
||||
DQN_UTEST_ASSERTF(&test, size_required == size_returned, "string_size: %d, result: %d", size_required, size_returned);
|
||||
DQN_UTEST_ASSERTF(&test, size_returned == DQN_ARRAY_UCOUNT(EXPECTED) - 1, "string_size: %d, expected: %zu", size_returned, DQN_ARRAY_UCOUNT(EXPECTED) - 1);
|
||||
DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string, sizeof(EXPECTED)) == 0);
|
||||
Dqn_Str16 result = Dqn_Win_Str8ToStr16(scratch.arena, input8);
|
||||
DQN_UTEST_ASSERT(&test, result == input16);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Str16 to Str8: No null-terminate") {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str16 INPUT = DQN_STR16(L"String");
|
||||
int size_required = Dqn_Win_Str16ToStr8Buffer(INPUT, nullptr, 0);
|
||||
DQN_UTEST_TEST("Str16 to Str8") {
|
||||
Dqn_Str8 result = Dqn_Win_Str16ToStr8(scratch.arena, input16);
|
||||
DQN_UTEST_ASSERT(&test, result == input8);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Str16 to Str8: Null terminates string") {
|
||||
int size_required = Dqn_Win_Str16ToStr8Buffer(input16, nullptr, 0);
|
||||
char *string = Dqn_Arena_NewArray(scratch.arena, char, size_required + 1, Dqn_ZeroMem_No);
|
||||
|
||||
// Fill the string with error sentinels, which ensures the string is zero terminated
|
||||
// Fill the string with error sentinels
|
||||
DQN_MEMSET(string, 'Z', size_required + 1);
|
||||
|
||||
int size_returned = Dqn_Win_Str16ToStr8Buffer(INPUT, string, size_required + 1);
|
||||
int size_returned = Dqn_Win_Str16ToStr8Buffer(input16, string, size_required + 1);
|
||||
char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0};
|
||||
|
||||
DQN_UTEST_ASSERTF(&test, size_required == size_returned, "string_size: %d, result: %d", size_required, size_returned);
|
||||
@ -1895,25 +1809,9 @@ static Dqn_UTest Dqn_Test_Win()
|
||||
DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string, sizeof(EXPECTED)) == 0);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Str8 to Str16 arena") {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str8 const INPUT = DQN_STR8("String");
|
||||
Dqn_Str16 string16 = Dqn_Win_Str8ToStr16(scratch.arena, INPUT);
|
||||
|
||||
int size_returned = Dqn_Win_Str8ToStr16Buffer(INPUT, nullptr, 0);
|
||||
wchar_t const EXPECTED[] = {L'S', L't', L'r', L'i', L'n', L'g', 0};
|
||||
|
||||
DQN_UTEST_ASSERTF(&test, DQN_CAST(int)string16.size == size_returned, "string_size: %d, result: %d", DQN_CAST(int)string16.size, size_returned);
|
||||
DQN_UTEST_ASSERTF(&test, DQN_CAST(int)string16.size == DQN_ARRAY_UCOUNT(EXPECTED) - 1, "string_size: %d, expected: %zu", DQN_CAST(int)string16.size, DQN_ARRAY_UCOUNT(EXPECTED) - 1);
|
||||
DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string16.data, sizeof(EXPECTED)) == 0);
|
||||
}
|
||||
|
||||
DQN_UTEST_TEST("Str16 to Str8: No null-terminate arena") {
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str16 INPUT = DQN_STR16(L"String");
|
||||
Dqn_Str8 string8 = Dqn_Win_Str16ToStr8(scratch.arena, INPUT);
|
||||
|
||||
int size_returned = Dqn_Win_Str16ToStr8Buffer(INPUT, nullptr, 0);
|
||||
DQN_UTEST_TEST("Str16 to Str8: Arena null terminates string") {
|
||||
Dqn_Str8 string8 = Dqn_Win_Str16ToStr8(scratch.arena, input16);
|
||||
int size_returned = Dqn_Win_Str16ToStr8Buffer(input16, nullptr, 0);
|
||||
char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0};
|
||||
|
||||
DQN_UTEST_ASSERTF(&test, DQN_CAST(int)string8.size == size_returned, "string_size: %d, result: %d", DQN_CAST(int)string8.size, size_returned);
|
||||
@ -1924,20 +1822,8 @@ static Dqn_UTest Dqn_Test_Win()
|
||||
return test;
|
||||
}
|
||||
|
||||
static void Dqn_Test_CustomLogProc(Dqn_Str8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args)
|
||||
void Dqn_Test_RunSuite()
|
||||
{
|
||||
(void)user_data;
|
||||
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
|
||||
Dqn_Str8 log = Dqn_Log_MakeStr8(scratch.allocator, true /*colour*/, type, log_type, call_site, fmt, args);
|
||||
DQN_UTEST_LOG("%.*s", DQN_STR_FMT(log));
|
||||
}
|
||||
|
||||
static void Dqn_Test_RunSuite()
|
||||
{
|
||||
Dqn_Library *dqn_library = Dqn_Library_Init(Dqn_LibraryOnInit_LogFeatures);
|
||||
auto *prev_log_callback = dqn_library->log_callback;
|
||||
dqn_library->log_callback = Dqn_Test_CustomLogProc;
|
||||
|
||||
Dqn_UTest tests[] =
|
||||
{
|
||||
Dqn_Test_Arena(),
|
||||
@ -1968,7 +1854,6 @@ static void Dqn_Test_RunSuite()
|
||||
}
|
||||
|
||||
fprintf(stdout, "Summary: %d/%d tests succeeded\n", total_good_tests, total_tests);
|
||||
dqn_library->log_callback = prev_log_callback;
|
||||
}
|
||||
|
||||
#if defined(DQN_TEST_WITH_MAIN)
|
@ -1,11 +1,25 @@
|
||||
#if !defined(DQN_UTEST_H)
|
||||
#define DQN_UTEST_H
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$$\
|
||||
// $$ | $$ |\__$$ __|$$ _____|$$ __$$\\__$$ __|
|
||||
// $$ | $$ | $$ | $$ | $$ / \__| $$ |
|
||||
// $$ | $$ | $$ | $$$$$\ \$$$$$$\ $$ |
|
||||
// $$ | $$ | $$ | $$ __| \____$$\ $$ |
|
||||
// $$ | $$ | $$ | $$ | $$\ $$ | $$ |
|
||||
// \$$$$$$ | $$ | $$$$$$$$\ \$$$$$$ | $$ |
|
||||
// \______/ \__| \________| \______/ \__|
|
||||
//
|
||||
// dqn_utest.h -- Extremely minimal unit testing framework
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// NOTE: Overview ==================================================================================
|
||||
// A super minimal testing framework, most of the logic here is the pretty
|
||||
// printing of test results.
|
||||
|
||||
// NOTE: Configuration =============================================================================
|
||||
//
|
||||
// NOTE: Configuration /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// #define DQN_UTEST_IMPLEMENTATION
|
||||
// Define this in one and only one C++ file to enable the implementation
|
||||
@ -32,11 +46,11 @@
|
||||
// Define this to a terminal color code to specify what color sucess will be
|
||||
// presented as.
|
||||
|
||||
// NOTE: Macros ====================================================================================
|
||||
// NOTE: Macros ////////////////////////////////////////////////////////////////////////////////////
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <string.>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(DQN_UTEST_RESULT_LPAD)
|
||||
#define DQN_UTEST_RESULT_LPAD 90
|
||||
@ -90,7 +104,7 @@
|
||||
"%*sAssertion Triggered\n" \
|
||||
"%*sFile: %s:%d\n" \
|
||||
"%*sExpression: [" #expr "]\n" \
|
||||
"%*sReason: " fmt "\n", \
|
||||
"%*sReason: " fmt "\n\n", \
|
||||
DQN_UTEST_SPACING * 2, \
|
||||
"", \
|
||||
DQN_UTEST_SPACING * 3, \
|
||||
@ -111,7 +125,7 @@
|
||||
(test)->state = Dqn_UTestState_TestFailed; \
|
||||
fprintf(stderr, \
|
||||
"%*sFile: %s:%d\n" \
|
||||
"%*sExpression: [" #expr "]\n", \
|
||||
"%*sExpression: [" #expr "]\n\n", \
|
||||
DQN_UTEST_SPACING * 2, \
|
||||
"", \
|
||||
file, \
|
||||
@ -121,7 +135,7 @@
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// NOTE: Header ====================================================================================
|
||||
// NOTE: Header ////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef enum Dqn_UTestState {
|
||||
Dqn_UTestState_Nil,
|
||||
Dqn_UTestState_TestBegun,
|
||||
@ -141,8 +155,9 @@ void Dqn_UTest_PrintStats(Dqn_UTest *test);
|
||||
void Dqn_UTest_BeginV(Dqn_UTest *test, char const *fmt, va_list args);
|
||||
void Dqn_UTest_Begin(Dqn_UTest *test, char const *fmt, ...);
|
||||
void Dqn_UTest_End(Dqn_UTest *test);
|
||||
#endif // DQN_UTEST_H
|
||||
|
||||
// NOTE: Implementation ============================================================================
|
||||
// NOTE: Implementation ////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DQN_UTEST_IMPLEMENTATION)
|
||||
void Dqn_UTest_PrintStats(Dqn_UTest *test)
|
||||
{
|
||||
@ -209,4 +224,3 @@ void Dqn_UTest_End(Dqn_UTest *test)
|
||||
test->state = Dqn_UTestState_Nil;
|
||||
}
|
||||
#endif // DQN_UTEST_IMPLEMENTATION
|
||||
#endif // DQN_UTEST_H
|
493
dqn_win32.h
493
dqn_win32.h
@ -1,23 +1,36 @@
|
||||
#if defined(DQN_OS_WIN32)
|
||||
#pragma comment(lib, "bcrypt")
|
||||
#pragma comment(lib, "wininet")
|
||||
#pragma comment(lib, "dbghelp")
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\
|
||||
// $$ | $\ $$ |\_$$ _|$$$\ $$ |$$ ___$$\ $$ __$$\
|
||||
// $$ |$$$\ $$ | $$ | $$$$\ $$ |\_/ $$ |\__/ $$ |
|
||||
// $$ $$ $$\$$ | $$ | $$ $$\$$ | $$$$$ / $$$$$$ |
|
||||
// $$$$ _$$$$ | $$ | $$ \$$$$ | \___$$\ $$ ____/
|
||||
// $$$ / \$$$ | $$ | $$ |\$$$ |$$\ $$ |$$ |
|
||||
// $$ / \$$ |$$$$$$\ $$ | \$$ |\$$$$$$ |$$$$$$$$\
|
||||
// \__/ \__|\______|\__| \__| \______/ \________|
|
||||
//
|
||||
// dqn_win32.h -- Windows replacement header
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(DQN_NO_WIN32_MIN_HEADER)
|
||||
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
||||
#pragma comment(lib, "bcrypt")
|
||||
#pragma comment(lib, "winhttp")
|
||||
#pragma comment(lib, "dbghelp")
|
||||
#endif
|
||||
|
||||
#if defined(DQN_NO_WIN32_MIN_HEADER) || defined(_INC_WINDOWS)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h> // LONG
|
||||
#include <bcrypt.h> // Dqn_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc
|
||||
#include <shellapi.h> // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc
|
||||
#include <winhttp.h> // WinHttp*
|
||||
#include <DbgHelp.h>
|
||||
#if !defined(DQN_NO_WINNET)
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(6553) // wininet.h|940 warning| The annotation for function 'InternetConnectA' on _Param_(8) does not apply to a value type.
|
||||
#include <wininet.h> // Dqn_Win_Net -> InternetConnect ... etc
|
||||
DQN_MSVC_WARNING_POP
|
||||
#endif // DQN_NO_WINNET
|
||||
#elif !defined(_INC_WINDOWS)
|
||||
#else
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
|
||||
|
||||
// NOTE: basetsd.h =============================================================================
|
||||
// NOTE: basetsd.h /////////////////////////////////////////////////////////////////////////////
|
||||
typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
|
||||
typedef ULONG_PTR SIZE_T, *PSIZE_T;
|
||||
typedef __int64 LONG_PTR, *PLONG_PTR;
|
||||
@ -25,7 +38,7 @@
|
||||
typedef unsigned __int64 ULONG64, *PULONG64;
|
||||
typedef unsigned __int64 DWORD64, *PDWORD64;
|
||||
|
||||
// NOTE: shared/minwindef.h ====================================================================
|
||||
// NOTE: shared/minwindef.h ////////////////////////////////////////////////////////////////////
|
||||
struct HINSTANCE__ {
|
||||
int unused;
|
||||
};
|
||||
@ -40,6 +53,8 @@
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned char UCHAR;
|
||||
typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */
|
||||
typedef void * HANDLE;
|
||||
typedef HANDLE HLOCAL;
|
||||
|
||||
#define MAX_PATH 260
|
||||
|
||||
@ -48,15 +63,15 @@
|
||||
DWORD dwHighDateTime;
|
||||
} FILETIME, *PFILETIME, *LPFILETIME;
|
||||
|
||||
// NOTE: shared/winerror.h =====================================================================
|
||||
// NOTE: shared/winerror.h /////////////////////////////////////////////////////////////////////
|
||||
// NOTE: GetModuleFileNameW
|
||||
#define ERROR_INSUFFICIENT_BUFFER 122L // dderror
|
||||
|
||||
// NOTE: um/winnls.h ===========================================================================
|
||||
// NOTE: um/winnls.h ///////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: MultiByteToWideChar
|
||||
#define CP_UTF8 65001 // UTF-8 translation
|
||||
|
||||
// NOTE: um/winnt.h ============================================================================
|
||||
// NOTE: um/winnt.h ////////////////////////////////////////////////////////////////////////////
|
||||
typedef void VOID;
|
||||
typedef __int64 LONGLONG;
|
||||
typedef unsigned __int64 ULONGLONG;
|
||||
@ -66,6 +81,7 @@
|
||||
typedef long LONG;
|
||||
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
|
||||
typedef CHAR * NPSTR, *LPSTR, *PSTR;
|
||||
typedef WCHAR * NWPSTR, *LPWSTR, *PWSTR;
|
||||
|
||||
// NOTE: VirtualAlloc: Allocation Type
|
||||
#define MEM_RESERVE 0x00002000
|
||||
@ -93,6 +109,10 @@
|
||||
#define FILE_APPEND_DATA (0x0004) // file
|
||||
|
||||
// NOTE: CreateFile/FindFirstFile
|
||||
#define FILE_SHARE_READ 0x00000001
|
||||
#define FILE_SHARE_WRITE 0x00000002
|
||||
#define FILE_SHARE_DELETE 0x00000004
|
||||
|
||||
#define FILE_ATTRIBUTE_READONLY 0x00000001
|
||||
#define FILE_ATTRIBUTE_HIDDEN 0x00000002
|
||||
#define FILE_ATTRIBUTE_SYSTEM 0x00000004
|
||||
@ -102,6 +122,11 @@
|
||||
// NOTE: STACKFRAME64
|
||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
|
||||
|
||||
// NOTE: WaitForSingleObject
|
||||
#define WAIT_TIMEOUT 258L // dderror
|
||||
#define STATUS_WAIT_0 ((DWORD )0x00000000L)
|
||||
#define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L)
|
||||
|
||||
typedef union _ULARGE_INTEGER {
|
||||
struct {
|
||||
DWORD LowPart;
|
||||
@ -230,6 +255,52 @@
|
||||
DWORD64 LastExceptionFromRip;
|
||||
} CONTEXT;
|
||||
|
||||
typedef struct _LIST_ENTRY {
|
||||
struct _LIST_ENTRY *Flink;
|
||||
struct _LIST_ENTRY *Blink;
|
||||
} LIST_ENTRY, *PLIST_ENTRY, PRLIST_ENTRY;
|
||||
|
||||
typedef struct _RTL_CRITICAL_SECTION_DEBUG {
|
||||
WORD Type;
|
||||
WORD CreatorBackTraceIndex;
|
||||
struct _RTL_CRITICAL_SECTION *CriticalSection;
|
||||
LIST_ENTRY ProcessLocksList;
|
||||
DWORD EntryCount;
|
||||
DWORD ContentionCount;
|
||||
DWORD Flags;
|
||||
WORD CreatorBackTraceIndexHigh;
|
||||
WORD Identifier;
|
||||
} RTL_CRITICAL_SECTION_DEBUG, *PRTL_CRITICAL_SECTION_DEBUG, RTL_RESOURCE_DEBUG, *PRTL_RESOURCE_DEBUG;
|
||||
|
||||
#pragma pack(push, 8)
|
||||
typedef struct _RTL_CRITICAL_SECTION {
|
||||
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
|
||||
|
||||
//
|
||||
// The following three fields control entering and exiting the critical
|
||||
// section for the resource
|
||||
//
|
||||
|
||||
LONG LockCount;
|
||||
LONG RecursionCount;
|
||||
HANDLE OwningThread; // from the thread's ClientId->UniqueThread
|
||||
HANDLE LockSemaphore;
|
||||
ULONG_PTR SpinCount; // force size on 64-bit systems when packed
|
||||
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef struct _MODLOAD_DATA {
|
||||
DWORD ssize; // size of this struct
|
||||
DWORD ssig; // signature identifying the passed data
|
||||
VOID *data; // pointer to passed data
|
||||
DWORD size; // size of passed data
|
||||
DWORD flags; // options
|
||||
} MODLOAD_DATA, *PMODLOAD_DATA;
|
||||
|
||||
#define SLMFLAG_VIRTUAL 0x1
|
||||
#define SLMFLAG_ALT_INDEX 0x2
|
||||
#define SLMFLAG_NO_SYMBOLS 0x4
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) VOID __stdcall RtlCaptureContext(CONTEXT *ContextRecord);
|
||||
@ -237,9 +308,18 @@
|
||||
__declspec(dllimport) HANDLE __stdcall GetCurrentThread(void);
|
||||
__declspec(dllimport) DWORD __stdcall SymSetOptions(DWORD SymOptions);
|
||||
__declspec(dllimport) BOOL __stdcall SymInitialize(HANDLE hProcess, const CHAR* UserSearchPath, BOOL fInvadeProcess);
|
||||
__declspec(dllimport) DWORD64 __stdcall SymLoadModuleEx(HANDLE hProcess, HANDLE hFile, CHAR const *ImageName, CHAR const *ModuleName, DWORD64 BaseOfDll, DWORD DllSize, MODLOAD_DATA *Data, DWORD Flags);
|
||||
__declspec(dllimport) BOOL __stdcall SymUnloadModule64(HANDLE hProcess, DWORD64 BaseOfDll);
|
||||
}
|
||||
|
||||
// NOTE: handleapi.h ===========================================================================
|
||||
// NOTE: shared/windef.h ////////////////////////////////////////////////////////////////////
|
||||
typedef struct tagPOINT
|
||||
{
|
||||
LONG x;
|
||||
LONG y;
|
||||
} POINT, *PPOINT, *NPPOINT, *LPPOINT;
|
||||
|
||||
// NOTE: handleapi.h ///////////////////////////////////////////////////////////////////////////
|
||||
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
|
||||
|
||||
extern "C"
|
||||
@ -247,7 +327,7 @@
|
||||
__declspec(dllimport) BOOL __stdcall CloseHandle(HANDLE hObject);
|
||||
}
|
||||
|
||||
// NOTE: consoleapi.h ===========================================================================
|
||||
// NOTE: consoleapi.h ///////////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) BOOL __stdcall WriteConsoleA(HANDLE hConsoleOutput, const VOID* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD *lpNumberOfCharsWritten, VOID *lpReserved);
|
||||
@ -257,11 +337,17 @@
|
||||
__declspec(dllimport) BOOL __stdcall GetConsoleMode(HANDLE hConsoleHandle, DWORD *lpMode);
|
||||
}
|
||||
|
||||
// NOTE: um/minwinbase.h =======================================================================
|
||||
// NOTE: um/minwinbase.h ///////////////////////////////////////////////////////////////////////
|
||||
// NOTE: FindFirstFile
|
||||
#define FIND_FIRST_EX_CASE_SENSITIVE 0x00000001
|
||||
#define FIND_FIRST_EX_LARGE_FETCH 0x00000002
|
||||
|
||||
// NOTE: WaitFor..
|
||||
#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
|
||||
#define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 )
|
||||
#define WAIT_ABANDONED ((STATUS_ABANDONED_WAIT_0 ) + 0 )
|
||||
#define WAIT_ABANDONED_0 ((STATUS_ABANDONED_WAIT_0 ) + 0 )
|
||||
|
||||
typedef enum _GET_FILEEX_INFO_LEVELS {
|
||||
GetFileExInfoStandard,
|
||||
GetFileExMaxInfoLevel
|
||||
@ -329,7 +415,9 @@
|
||||
HANDLE hEvent;
|
||||
} OVERLAPPED, *LPOVERLAPPED;
|
||||
|
||||
// NOTE: um/winbase.h ==========================================================================
|
||||
typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;
|
||||
|
||||
// NOTE: um/winbase.h //////////////////////////////////////////////////////////////////////////
|
||||
#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
|
||||
#define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 )
|
||||
|
||||
@ -344,6 +432,7 @@
|
||||
#define MOVEFILE_COPY_ALLOWED 0x00000002
|
||||
|
||||
// NOTE: FormatMessageA
|
||||
#define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100
|
||||
#define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200
|
||||
#define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
|
||||
#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
|
||||
@ -356,17 +445,18 @@
|
||||
__declspec(dllimport) BOOL __stdcall MoveFileExW (const WCHAR *lpExistingFileName, const WCHAR *lpNewFileName, DWORD dwFlags);
|
||||
__declspec(dllimport) BOOL __stdcall CopyFileW (const WCHAR *lpExistingFileName, const WCHAR *lpNewFileName, BOOL bFailIfExists);
|
||||
__declspec(dllimport) HANDLE __stdcall CreateSemaphoreA(SECURITY_ATTRIBUTES *lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, const CHAR *lpName);
|
||||
__declspec(dllimport) DWORD __stdcall FormatMessageA (DWORD dwFlags, const VOID *lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize, va_list *Arguments);
|
||||
__declspec(dllimport) DWORD __stdcall FormatMessageW (DWORD dwFlags, VOID const *lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPWSTR lpBuffer, DWORD nSize, va_list *Arguments);
|
||||
__declspec(dllimport) HLOCAL __stdcall LocalFree (HLOCAL hMem);
|
||||
}
|
||||
|
||||
// NOTE: um/stringapiset.h =====================================================================
|
||||
// NOTE: um/stringapiset.h /////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, const CHAR *lpMultiByteStr, int cbMultiByte, WCHAR *lpWideCharStr, int cchWideChar);
|
||||
__declspec(dllimport) int __stdcall WideCharToMultiByte(UINT CodePage, DWORD dwFlags, const WCHAR *lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, const CHAR *lpDefaultChar, BOOL *lpUsedDefaultChar);
|
||||
}
|
||||
|
||||
// NOTE: um/fileapi.h ==========================================================================
|
||||
// NOTE: um/fileapi.h //////////////////////////////////////////////////////////////////////////
|
||||
#define INVALID_FILE_SIZE ((DWORD)0xFFFFFFFF)
|
||||
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
|
||||
|
||||
@ -401,14 +491,14 @@
|
||||
|
||||
}
|
||||
|
||||
// NOTE: um/processenv.h =======================================================================
|
||||
// NOTE: um/processenv.h ///////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) DWORD __stdcall GetCurrentDirectoryW(DWORD nBufferLength, WCHAR *lpBuffer);
|
||||
__declspec(dllimport) HANDLE __stdcall GetStdHandle(DWORD nStdHandle);
|
||||
}
|
||||
|
||||
// NOTE: um/sysinfoapi.h =======================================================================
|
||||
// NOTE: um/sysinfoapi.h ///////////////////////////////////////////////////////////////////////
|
||||
typedef struct _SYSTEM_INFO {
|
||||
union {
|
||||
DWORD dwOemId; // Obsolete field...do not use
|
||||
@ -436,7 +526,7 @@
|
||||
__declspec(dllimport) VOID __stdcall GetLocalTime(SYSTEMTIME *lpSystemTime);
|
||||
}
|
||||
|
||||
// NOTE: shared/windef.h =======================================================================
|
||||
// NOTE: shared/windef.h ///////////////////////////////////////////////////////////////////////
|
||||
typedef struct tagRECT {
|
||||
LONG left;
|
||||
LONG top;
|
||||
@ -463,7 +553,20 @@
|
||||
|
||||
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)
|
||||
|
||||
// NOTE: um/winuser.h ==========================================================================
|
||||
// NOTE: um/winuser.h //////////////////////////////////////////////////////////////////////////
|
||||
typedef struct tagWINDOWPLACEMENT {
|
||||
UINT length;
|
||||
UINT flags;
|
||||
UINT showCmd;
|
||||
POINT ptMinPosition;
|
||||
POINT ptMaxPosition;
|
||||
RECT rcNormalPosition;
|
||||
#ifdef _MAC
|
||||
RECT rcDevice;
|
||||
#endif
|
||||
} WINDOWPLACEMENT;
|
||||
typedef WINDOWPLACEMENT *PWINDOWPLACEMENT, *LPWINDOWPLACEMENT;
|
||||
|
||||
#define SW_HIDE 0
|
||||
#define SW_NORMAL 1
|
||||
#define SW_MAXIMIZE 3
|
||||
@ -477,88 +580,262 @@
|
||||
__declspec(dllimport) BOOL __stdcall SetWindowPos (HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags);
|
||||
__declspec(dllimport) UINT __stdcall GetWindowModuleFileNameA(HWND hwnd, LPSTR pszFileName, UINT cchFileNameMax);
|
||||
__declspec(dllimport) BOOL __stdcall ShowWindow (HWND hWnd, int nCmdShow);
|
||||
__declspec(dllimport) BOOL __stdcall GetWindowPlacement (HWND hWnd, WINDOWPLACEMENT *lpwndpl);
|
||||
|
||||
}
|
||||
|
||||
// NOTE: um/wininet.h ==========================================================================
|
||||
// NOTE: um/wininet.h //////////////////////////////////////////////////////////////////////////
|
||||
typedef WORD INTERNET_PORT;
|
||||
typedef VOID *HINTERNET;
|
||||
|
||||
#define INTERNET_OPEN_TYPE_PRECONFIG 0 // use registry configuration
|
||||
#define INTERNET_INVALID_PORT_NUMBER 0 // use the protocol-specific default
|
||||
#define INTERNET_DEFAULT_FTP_PORT 21 // default for FTP servers
|
||||
// NOTE: um/winhttp.h //////////////////////////////////////////////////////////////////////////
|
||||
#define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0
|
||||
#define WINHTTP_ACCESS_TYPE_NO_PROXY 1
|
||||
#define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3
|
||||
#define WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY 4
|
||||
|
||||
#define INTERNET_DEFAULT_PORT 0 // use the protocol-specific default
|
||||
#define INTERNET_DEFAULT_HTTP_PORT 80 // " " HTTP "
|
||||
#define INTERNET_DEFAULT_HTTPS_PORT 443 // " " HTTPS "
|
||||
#define INTERNET_SERVICE_HTTP 3
|
||||
|
||||
#define INTERNET_OPTION_USERNAME 28
|
||||
#define INTERNET_OPTION_PASSWORD 29
|
||||
#define INTERNET_OPTION_USER_AGENT 41
|
||||
// NOTE: WinHttpOpen
|
||||
#define WINHTTP_FLAG_ASYNC 0x10000000 // this session is asynchronous (where supported)
|
||||
#define WINHTTP_FLAG_SECURE_DEFAULTS 0x30000000 // note that this flag also forces async
|
||||
|
||||
#define INTERNET_FLAG_NO_AUTH 0x00040000 // no automatic authentication handling
|
||||
#define INTERNET_FLAG_SECURE 0x00800000 // use PCT/SSL if applicable (HTTP)
|
||||
// NOTE: WinHttpOpenRequest
|
||||
#define WINHTTP_FLAG_SECURE 0x00800000 // use SSL if applicable (HTTPS)
|
||||
#define WINHTTP_FLAG_ESCAPE_PERCENT 0x00000004 // if escaping enabled, escape percent as well
|
||||
#define WINHTTP_FLAG_NULL_CODEPAGE 0x00000008 // assume all symbols are ASCII, use fast convertion
|
||||
#define WINHTTP_FLAG_ESCAPE_DISABLE 0x00000040 // disable escaping
|
||||
#define WINHTTP_FLAG_ESCAPE_DISABLE_QUERY 0x00000080 // if escaping enabled escape path part, but do not escape query
|
||||
#define WINHTTP_FLAG_BYPASS_PROXY_CACHE 0x00000100 // add "pragma: no-cache" request header
|
||||
#define WINHTTP_FLAG_REFRESH WINHTTP_FLAG_BYPASS_PROXY_CACHE
|
||||
#define WINHTTP_FLAG_AUTOMATIC_CHUNKING 0x00000200 // Send request without content-length header or chunked TE
|
||||
|
||||
#define HTTP_QUERY_RAW_HEADERS 21 // special: all headers as ASCIIZ
|
||||
#define HTTP_QUERY_RAW_HEADERS_CRLF 22 // special: all headers
|
||||
#define WINHTTP_NO_PROXY_NAME NULL
|
||||
#define WINHTTP_NO_PROXY_BYPASS NULL
|
||||
|
||||
#define HTTP_ADDREQ_FLAG_ADD_IF_NEW 0x10000000
|
||||
#define HTTP_ADDREQ_FLAG_ADD 0x20000000
|
||||
#define HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA 0x40000000
|
||||
#define HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
|
||||
#define HTTP_ADDREQ_FLAG_COALESCE HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA
|
||||
#define HTTP_ADDREQ_FLAG_REPLACE 0x80000000
|
||||
//
|
||||
// WINHTTP_QUERY_FLAG_NUMBER - if this bit is set in the dwInfoLevel parameter of
|
||||
// HttpQueryHeader(), then the value of the header will be converted to a number
|
||||
// before being returned to the caller, if applicable
|
||||
//
|
||||
#define WINHTTP_QUERY_FLAG_NUMBER 0x20000000
|
||||
|
||||
typedef enum {
|
||||
INTERNET_SCHEME_PARTIAL = -2,
|
||||
INTERNET_SCHEME_UNKNOWN = -1,
|
||||
INTERNET_SCHEME_DEFAULT = 0,
|
||||
INTERNET_SCHEME_FTP,
|
||||
INTERNET_SCHEME_GOPHER,
|
||||
INTERNET_SCHEME_HTTP,
|
||||
INTERNET_SCHEME_HTTPS,
|
||||
INTERNET_SCHEME_FILE,
|
||||
INTERNET_SCHEME_NEWS,
|
||||
INTERNET_SCHEME_MAILTO,
|
||||
INTERNET_SCHEME_SOCKS,
|
||||
INTERNET_SCHEME_JAVASCRIPT,
|
||||
INTERNET_SCHEME_VBSCRIPT,
|
||||
INTERNET_SCHEME_RES,
|
||||
INTERNET_SCHEME_FIRST = INTERNET_SCHEME_FTP,
|
||||
INTERNET_SCHEME_LAST = INTERNET_SCHEME_RES
|
||||
} INTERNET_SCHEME, * LPINTERNET_SCHEME;
|
||||
#define WINHTTP_QUERY_MIME_VERSION 0
|
||||
#define WINHTTP_QUERY_CONTENT_TYPE 1
|
||||
#define WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING 2
|
||||
#define WINHTTP_QUERY_CONTENT_ID 3
|
||||
#define WINHTTP_QUERY_CONTENT_DESCRIPTION 4
|
||||
#define WINHTTP_QUERY_CONTENT_LENGTH 5
|
||||
#define WINHTTP_QUERY_CONTENT_LANGUAGE 6
|
||||
#define WINHTTP_QUERY_ALLOW 7
|
||||
#define WINHTTP_QUERY_PUBLIC 8
|
||||
#define WINHTTP_QUERY_DATE 9
|
||||
#define WINHTTP_QUERY_EXPIRES 10
|
||||
#define WINHTTP_QUERY_LAST_MODIFIED 11
|
||||
#define WINHTTP_QUERY_MESSAGE_ID 12
|
||||
#define WINHTTP_QUERY_URI 13
|
||||
#define WINHTTP_QUERY_DERIVED_FROM 14
|
||||
#define WINHTTP_QUERY_COST 15
|
||||
#define WINHTTP_QUERY_LINK 16
|
||||
#define WINHTTP_QUERY_PRAGMA 17
|
||||
#define WINHTTP_QUERY_VERSION 18 // special: part of status line
|
||||
#define WINHTTP_QUERY_STATUS_CODE 19 // special: part of status line
|
||||
#define WINHTTP_QUERY_STATUS_TEXT 20 // special: part of status line
|
||||
#define WINHTTP_QUERY_RAW_HEADERS 21 // special: all headers as ASCIIZ
|
||||
#define WINHTTP_QUERY_RAW_HEADERS_CRLF 22 // special: all headers
|
||||
#define WINHTTP_QUERY_CONNECTION 23
|
||||
#define WINHTTP_QUERY_ACCEPT 24
|
||||
#define WINHTTP_QUERY_ACCEPT_CHARSET 25
|
||||
#define WINHTTP_QUERY_ACCEPT_ENCODING 26
|
||||
#define WINHTTP_QUERY_ACCEPT_LANGUAGE 27
|
||||
#define WINHTTP_QUERY_AUTHORIZATION 28
|
||||
#define WINHTTP_QUERY_CONTENT_ENCODING 29
|
||||
#define WINHTTP_QUERY_FORWARDED 30
|
||||
#define WINHTTP_QUERY_FROM 31
|
||||
#define WINHTTP_QUERY_IF_MODIFIED_SINCE 32
|
||||
#define WINHTTP_QUERY_LOCATION 33
|
||||
#define WINHTTP_QUERY_ORIG_URI 34
|
||||
#define WINHTTP_QUERY_REFERER 35
|
||||
#define WINHTTP_QUERY_RETRY_AFTER 36
|
||||
#define WINHTTP_QUERY_SERVER 37
|
||||
#define WINHTTP_QUERY_TITLE 38
|
||||
#define WINHTTP_QUERY_USER_AGENT 39
|
||||
#define WINHTTP_QUERY_WWW_AUTHENTICATE 40
|
||||
#define WINHTTP_QUERY_PROXY_AUTHENTICATE 41
|
||||
#define WINHTTP_QUERY_ACCEPT_RANGES 42
|
||||
#define WINHTTP_QUERY_SET_COOKIE 43
|
||||
#define WINHTTP_QUERY_COOKIE 44
|
||||
#define WINHTTP_QUERY_REQUEST_METHOD 45 // special: GET/POST etc.
|
||||
#define WINHTTP_QUERY_REFRESH 46
|
||||
#define WINHTTP_QUERY_CONTENT_DISPOSITION 47
|
||||
|
||||
typedef struct {
|
||||
DWORD dwStructSize; // size of this structure. Used in version check
|
||||
LPSTR lpszScheme; // pointer to scheme name
|
||||
DWORD dwSchemeLength; // length of scheme name
|
||||
INTERNET_SCHEME nScheme; // enumerated scheme type (if known)
|
||||
LPSTR lpszHostName; // pointer to host name
|
||||
DWORD dwHostNameLength; // length of host name
|
||||
INTERNET_PORT nPort; // converted port number
|
||||
LPSTR lpszUserName; // pointer to user name
|
||||
DWORD dwUserNameLength; // length of user name
|
||||
LPSTR lpszPassword; // pointer to password
|
||||
DWORD dwPasswordLength; // length of password
|
||||
LPSTR lpszUrlPath; // pointer to URL-path
|
||||
DWORD dwUrlPathLength; // length of URL-path
|
||||
LPSTR lpszExtraInfo; // pointer to extra information (e.g. ?foo or #foo)
|
||||
DWORD dwExtraInfoLength; // length of extra information
|
||||
} URL_COMPONENTSA, * LPURL_COMPONENTSA;
|
||||
// NOTE: WinHttpQueryHeaders prettifiers for optional parameters.
|
||||
#define WINHTTP_HEADER_NAME_BY_INDEX NULL
|
||||
#define WINHTTP_NO_OUTPUT_BUFFER NULL
|
||||
#define WINHTTP_NO_HEADER_INDEX NULL
|
||||
|
||||
// NOTE: Http Response Status Codes
|
||||
#define HTTP_STATUS_CONTINUE 100 // OK to continue with request
|
||||
#define HTTP_STATUS_SWITCH_PROTOCOLS 101 // server has switched protocols in upgrade header
|
||||
|
||||
#define HTTP_STATUS_OK 200 // request completed
|
||||
#define HTTP_STATUS_CREATED 201 // object created, reason = new URI
|
||||
#define HTTP_STATUS_ACCEPTED 202 // async completion (TBS)
|
||||
#define HTTP_STATUS_PARTIAL 203 // partial completion
|
||||
#define HTTP_STATUS_NO_CONTENT 204 // no info to return
|
||||
#define HTTP_STATUS_RESET_CONTENT 205 // request completed, but clear form
|
||||
#define HTTP_STATUS_PARTIAL_CONTENT 206 // partial GET fulfilled
|
||||
#define HTTP_STATUS_WEBDAV_MULTI_STATUS 207 // WebDAV Multi-Status
|
||||
|
||||
#define HTTP_STATUS_AMBIGUOUS 300 // server couldn't decide what to return
|
||||
#define HTTP_STATUS_MOVED 301 // object permanently moved
|
||||
#define HTTP_STATUS_REDIRECT 302 // object temporarily moved
|
||||
#define HTTP_STATUS_REDIRECT_METHOD 303 // redirection w/ new access method
|
||||
#define HTTP_STATUS_NOT_MODIFIED 304 // if-modified-since was not modified
|
||||
#define HTTP_STATUS_USE_PROXY 305 // redirection to proxy, location header specifies proxy to use
|
||||
#define HTTP_STATUS_REDIRECT_KEEP_VERB 307 // HTTP/1.1: keep same verb
|
||||
#define HTTP_STATUS_PERMANENT_REDIRECT 308 // Object permanently moved keep verb
|
||||
|
||||
#define HTTP_STATUS_BAD_REQUEST 400 // invalid syntax
|
||||
#define HTTP_STATUS_DENIED 401 // access denied
|
||||
#define HTTP_STATUS_PAYMENT_REQ 402 // payment required
|
||||
#define HTTP_STATUS_FORBIDDEN 403 // request forbidden
|
||||
#define HTTP_STATUS_NOT_FOUND 404 // object not found
|
||||
#define HTTP_STATUS_BAD_METHOD 405 // method is not allowed
|
||||
#define HTTP_STATUS_NONE_ACCEPTABLE 406 // no response acceptable to client found
|
||||
#define HTTP_STATUS_PROXY_AUTH_REQ 407 // proxy authentication required
|
||||
#define HTTP_STATUS_REQUEST_TIMEOUT 408 // server timed out waiting for request
|
||||
#define HTTP_STATUS_CONFLICT 409 // user should resubmit with more info
|
||||
#define HTTP_STATUS_GONE 410 // the resource is no longer available
|
||||
#define HTTP_STATUS_LENGTH_REQUIRED 411 // the server refused to accept request w/o a length
|
||||
#define HTTP_STATUS_PRECOND_FAILED 412 // precondition given in request failed
|
||||
#define HTTP_STATUS_REQUEST_TOO_LARGE 413 // request entity was too large
|
||||
#define HTTP_STATUS_URI_TOO_LONG 414 // request URI too long
|
||||
#define HTTP_STATUS_UNSUPPORTED_MEDIA 415 // unsupported media type
|
||||
#define HTTP_STATUS_RETRY_WITH 449 // retry after doing the appropriate action.
|
||||
|
||||
#define HTTP_STATUS_SERVER_ERROR 500 // internal server error
|
||||
#define HTTP_STATUS_NOT_SUPPORTED 501 // required not supported
|
||||
#define HTTP_STATUS_BAD_GATEWAY 502 // error response received from gateway
|
||||
#define HTTP_STATUS_SERVICE_UNAVAIL 503 // temporarily overloaded
|
||||
#define HTTP_STATUS_GATEWAY_TIMEOUT 504 // timed out waiting for gateway
|
||||
#define HTTP_STATUS_VERSION_NOT_SUP 505 // HTTP version not supported
|
||||
|
||||
#define HTTP_STATUS_FIRST HTTP_STATUS_CONTINUE
|
||||
#define HTTP_STATUS_LAST HTTP_STATUS_VERSION_NOT_SUP
|
||||
|
||||
#define WINHTTP_CALLBACK_STATUS_RESOLVING_NAME 0x00000001
|
||||
#define WINHTTP_CALLBACK_STATUS_NAME_RESOLVED 0x00000002
|
||||
#define WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER 0x00000004
|
||||
#define WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER 0x00000008
|
||||
#define WINHTTP_CALLBACK_STATUS_SENDING_REQUEST 0x00000010
|
||||
#define WINHTTP_CALLBACK_STATUS_REQUEST_SENT 0x00000020
|
||||
#define WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE 0x00000040
|
||||
#define WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED 0x00000080
|
||||
#define WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION 0x00000100
|
||||
#define WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED 0x00000200
|
||||
#define WINHTTP_CALLBACK_STATUS_HANDLE_CREATED 0x00000400
|
||||
#define WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING 0x00000800
|
||||
#define WINHTTP_CALLBACK_STATUS_DETECTING_PROXY 0x00001000
|
||||
#define WINHTTP_CALLBACK_STATUS_REDIRECT 0x00004000
|
||||
#define WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE 0x00008000
|
||||
#define WINHTTP_CALLBACK_STATUS_SECURE_FAILURE 0x00010000
|
||||
#define WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE 0x00020000
|
||||
#define WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE 0x00040000
|
||||
#define WINHTTP_CALLBACK_STATUS_READ_COMPLETE 0x00080000
|
||||
#define WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE 0x00100000
|
||||
#define WINHTTP_CALLBACK_STATUS_REQUEST_ERROR 0x00200000
|
||||
#define WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE 0x00400000
|
||||
|
||||
#define WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE 0x01000000
|
||||
#define WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE 0x02000000
|
||||
#define WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE 0x04000000
|
||||
#define WINHTTP_CALLBACK_STATUS_SETTINGS_WRITE_COMPLETE 0x10000000
|
||||
#define WINHTTP_CALLBACK_STATUS_SETTINGS_READ_COMPLETE 0x20000000
|
||||
|
||||
#define WINHTTP_CALLBACK_FLAG_RESOLVE_NAME (WINHTTP_CALLBACK_STATUS_RESOLVING_NAME | WINHTTP_CALLBACK_STATUS_NAME_RESOLVED)
|
||||
#define WINHTTP_CALLBACK_FLAG_CONNECT_TO_SERVER (WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER | WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER)
|
||||
#define WINHTTP_CALLBACK_FLAG_SEND_REQUEST (WINHTTP_CALLBACK_STATUS_SENDING_REQUEST | WINHTTP_CALLBACK_STATUS_REQUEST_SENT)
|
||||
#define WINHTTP_CALLBACK_FLAG_RECEIVE_RESPONSE (WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE | WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED)
|
||||
#define WINHTTP_CALLBACK_FLAG_CLOSE_CONNECTION (WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION | WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED)
|
||||
#define WINHTTP_CALLBACK_FLAG_HANDLES (WINHTTP_CALLBACK_STATUS_HANDLE_CREATED | WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING)
|
||||
#define WINHTTP_CALLBACK_FLAG_DETECTING_PROXY WINHTTP_CALLBACK_STATUS_DETECTING_PROXY
|
||||
#define WINHTTP_CALLBACK_FLAG_REDIRECT WINHTTP_CALLBACK_STATUS_REDIRECT
|
||||
#define WINHTTP_CALLBACK_FLAG_INTERMEDIATE_RESPONSE WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE
|
||||
#define WINHTTP_CALLBACK_FLAG_SECURE_FAILURE WINHTTP_CALLBACK_STATUS_SECURE_FAILURE
|
||||
#define WINHTTP_CALLBACK_FLAG_SENDREQUEST_COMPLETE WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE
|
||||
#define WINHTTP_CALLBACK_FLAG_HEADERS_AVAILABLE WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE
|
||||
#define WINHTTP_CALLBACK_FLAG_DATA_AVAILABLE WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE
|
||||
#define WINHTTP_CALLBACK_FLAG_READ_COMPLETE WINHTTP_CALLBACK_STATUS_READ_COMPLETE
|
||||
#define WINHTTP_CALLBACK_FLAG_WRITE_COMPLETE WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE
|
||||
#define WINHTTP_CALLBACK_FLAG_REQUEST_ERROR WINHTTP_CALLBACK_STATUS_REQUEST_ERROR
|
||||
|
||||
#define WINHTTP_CALLBACK_FLAG_GETPROXYFORURL_COMPLETE WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE
|
||||
|
||||
#define WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS (WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE \
|
||||
| WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE \
|
||||
| WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE \
|
||||
| WINHTTP_CALLBACK_STATUS_READ_COMPLETE \
|
||||
| WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE \
|
||||
| WINHTTP_CALLBACK_STATUS_REQUEST_ERROR \
|
||||
| WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE)
|
||||
|
||||
#define WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS 0xffffffff
|
||||
#define WINHTTP_INVALID_STATUS_CALLBACK ((WINHTTP_STATUS_CALLBACK)(-1L))
|
||||
|
||||
typedef struct _WINHTTP_EXTENDED_HEADER
|
||||
{
|
||||
union
|
||||
{
|
||||
CHAR const *pwszName;
|
||||
WCHAR const *pszName;
|
||||
};
|
||||
union
|
||||
{
|
||||
WCHAR const *pwszValue;
|
||||
CHAR const *pszValue;
|
||||
};
|
||||
} WINHTTP_EXTENDED_HEADER, *PWINHTTP_EXTENDED_HEADER;
|
||||
|
||||
typedef struct _WINHTTP_ASYNC_RESULT
|
||||
{
|
||||
DWORD *dwResult; // indicates which async API has encountered an error
|
||||
DWORD dwError; // the error code if the API failed
|
||||
} WINHTTP_ASYNC_RESULT, *LPWINHTTP_ASYNC_RESULT, *PWINHTTP_ASYNC_RESULT;
|
||||
|
||||
typedef
|
||||
VOID
|
||||
(*WINHTTP_STATUS_CALLBACK)(
|
||||
HINTERNET hInternet,
|
||||
DWORD *dwContext,
|
||||
DWORD dwInternetStatus,
|
||||
VOID *lpvStatusInformation,
|
||||
DWORD dwStatusInformationLength
|
||||
);
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) BOOL __stdcall InternetCrackUrlA (CHAR const *lpszUrl, DWORD dwUrlLength, DWORD dwFlags, URL_COMPONENTSA *lpUrlComponents);
|
||||
__declspec(dllimport) HINTERNET __stdcall InternetOpenA (CHAR const *lpszAgent, DWORD dwAccessType, CHAR const *lpszProxy, CHAR const *lpszProxyBypass, DWORD dwFlags);
|
||||
__declspec(dllimport) HINTERNET __stdcall InternetConnectA (HINTERNET hInternet, CHAR const *lpszServerName, INTERNET_PORT nServerPort, CHAR const *lpszUserName, CHAR const *lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext);
|
||||
__declspec(dllimport) BOOL __stdcall InternetSetOptionA (HINTERNET hInternet, DWORD dwOption, VOID *lpBuffer, DWORD dwBufferLength);
|
||||
__declspec(dllimport) BOOL __stdcall InternetReadFile (HINTERNET hFile, VOID *lpBuffer, DWORD dwNumberOfBytesToRead, DWORD *lpdwNumberOfBytesRead);
|
||||
__declspec(dllimport) BOOL __stdcall InternetCloseHandle (HINTERNET hInternet);
|
||||
__declspec(dllimport) HINTERNET __stdcall HttpOpenRequestA (HINTERNET hConnect, CHAR const *lpszVerb, CHAR const *lpszObjectName, CHAR const *lpszVersion, CHAR const *lpszReferrer, CHAR const *lplpszAcceptTypes, DWORD dwFlags, DWORD_PTR dwContext);
|
||||
__declspec(dllimport) BOOL __stdcall HttpSendRequestA (HINTERNET hRequest, CHAR const *lpszHeaders, DWORD dwHeadersLength, VOID *lpOptional, DWORD dwOptionalLength);
|
||||
__declspec(dllimport) BOOL __stdcall HttpAddRequestHeadersA(HINTERNET hRequest, CHAR const *lpszHeaders, DWORD dwHeadersLength, DWORD dwModifiers);
|
||||
__declspec(dllimport) BOOL __stdcall HttpQueryInfoA (HINTERNET hRequest, DWORD dwInfoLevel, VOID *lpBuffer, DWORD *lpdwBufferLength, DWORD *lpdwIndex);
|
||||
__declspec(dllimport) HINTERNET __stdcall WinHttpOpen(WCHAR const *pszAgentW, DWORD dwAccessType, WCHAR const *pszProxyW, WCHAR const *pszProxyBypassW, DWORD dwFlags);
|
||||
__declspec(dllimport) BOOL __stdcall WinHttpCloseHandle(HINTERNET hInternet);
|
||||
__declspec(dllimport) HINTERNET __stdcall WinHttpConnect(HINTERNET hSession, WCHAR const *pswzServerName, INTERNET_PORT nServerPort, DWORD dwReserved);
|
||||
__declspec(dllimport) BOOL __stdcall WinHttpReadData(HINTERNET hRequest, VOID *lpBuffer, DWORD dwNumberOfBytesToRead, DWORD *lpdwNumberOfBytesRead);
|
||||
__declspec(dllimport) HINTERNET __stdcall WinHttpOpenRequest(HINTERNET hConnect, WCHAR const *pwszVerb, WCHAR const *pwszObjectName, WCHAR const *pwszVersion, WCHAR const *pwszReferrer, WCHAR const *ppwszAcceptTypes, DWORD dwFlags);
|
||||
__declspec(dllimport) BOOL __stdcall WinHttpSendRequest(HINTERNET hRequest, WCHAR const *lpszHeaders, DWORD dwHeadersLength, VOID *lpOptional, DWORD dwOptionalLength, DWORD dwTotalLength, DWORD_PTR dwContext);
|
||||
__declspec(dllimport) DWORD __stdcall WinHttpAddRequestHeadersEx(HINTERNET hRequest, DWORD dwModifiers, ULONGLONG ullFlags, ULONGLONG ullExtra, DWORD cHeaders, WINHTTP_EXTENDED_HEADER *pHeaders);
|
||||
__declspec(dllimport) BOOL __stdcall WinHttpSetCredentials(HINTERNET hRequest, // HINTERNET handle returned by WinHttpOpenRequest.
|
||||
DWORD AuthTargets, // Only WINHTTP_AUTH_TARGET_SERVER and WINHTTP_AUTH_TARGET_PROXY are supported in this version and they are mutually exclusive
|
||||
DWORD AuthScheme, // must be one of the supported Auth Schemes returned from WinHttpQueryAuthSchemes()
|
||||
WCHAR * pwszUserName, // 1) NULL if default creds is to be used, in which case pszPassword will be ignored
|
||||
WCHAR * pwszPassword, // 1) "" == Blank Password; 2)Parameter ignored if pszUserName is NULL; 3) Invalid to pass in NULL if pszUserName is not NULL
|
||||
VOID * pAuthParams);
|
||||
__declspec(dllimport) BOOL __stdcall WinHttpQueryHeaders(HINTERNET hRequest, DWORD dwInfoLevel, WCHAR const *pwszName, VOID *lpBuffer, DWORD *lpdwBufferLength, DWORD *lpdwIndex);
|
||||
__declspec(dllimport) BOOL __stdcall WinHttpReceiveResponse(HINTERNET hRequest, VOID *lpReserved);
|
||||
__declspec(dllimport) WINHTTP_STATUS_CALLBACK __stdcall WinHttpSetStatusCallback(HINTERNET hInternet, WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags, DWORD_PTR dwReserved);
|
||||
}
|
||||
|
||||
// NOTE: um/DbgHelp.h ==========================================================================
|
||||
// NOTE: um/DbgHelp.h //////////////////////////////////////////////////////////////////////////
|
||||
#define SYMOPT_CASE_INSENSITIVE 0x00000001
|
||||
#define SYMOPT_UNDNAME 0x00000002
|
||||
#define SYMOPT_DEFERRED_LOADS 0x00000004
|
||||
@ -680,15 +957,16 @@
|
||||
__declspec(dllimport) VOID * __stdcall SymFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase);
|
||||
__declspec(dllimport) BOOL __stdcall SymGetLineFromAddrW64 (HANDLE hProcess, DWORD64 dwAddr, DWORD *pdwDisplacement, IMAGEHLP_LINEW64 *Line);
|
||||
__declspec(dllimport) DWORD64 __stdcall SymGetModuleBase64 (HANDLE hProcess, DWORD64 qwAddr);
|
||||
__declspec(dllimport) BOOL __stdcall SymRefreshModuleList (HANDLE hProcess);
|
||||
};
|
||||
|
||||
// NOTE: um/errhandlingapi.h ===================================================================
|
||||
// NOTE: um/errhandlingapi.h ///////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) DWORD __stdcall GetLastError(VOID);
|
||||
}
|
||||
|
||||
// NOTE: um/libloaderapi.h =====================================================================
|
||||
// NOTE: um/libloaderapi.h /////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) HMODULE __stdcall LoadLibraryA (const CHAR *lpLibFileName);
|
||||
@ -698,22 +976,30 @@
|
||||
__declspec(dllimport) DWORD __stdcall GetModuleFileNameW(HMODULE hModule, WCHAR *lpFilename, DWORD nSize);
|
||||
}
|
||||
|
||||
// NOTE: um/synchapi.h =========================================================================
|
||||
// NOTE: um/synchapi.h /////////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) VOID __stdcall InitializeCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
||||
__declspec(dllimport) VOID __stdcall EnterCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
||||
__declspec(dllimport) VOID __stdcall LeaveCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
||||
__declspec(dllimport) BOOL __stdcall InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount);
|
||||
__declspec(dllimport) BOOL __stdcall InitializeCriticalSectionEx (CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount, DWORD Flags);
|
||||
__declspec(dllimport) DWORD __stdcall SetCriticalSectionSpinCount (CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount);
|
||||
__declspec(dllimport) BOOL __stdcall TryEnterCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
||||
__declspec(dllimport) VOID __stdcall DeleteCriticalSection (CRITICAL_SECTION *lpCriticalSection);
|
||||
__declspec(dllimport) DWORD __stdcall WaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds);
|
||||
__declspec(dllimport) BOOL __stdcall ReleaseSemaphore (HANDLE hSemaphore, LONG lReleaseCount, LONG *lpPreviousCount);
|
||||
__declspec(dllimport) VOID __stdcall Sleep (DWORD dwMilliseconds);
|
||||
}
|
||||
|
||||
// NOTE: um/profileapi.h =======================================================================
|
||||
// NOTE: um/profileapi.h ///////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) BOOL __stdcall QueryPerformanceCounter (LARGE_INTEGER* lpPerformanceCount);
|
||||
__declspec(dllimport) BOOL __stdcall QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency);
|
||||
}
|
||||
|
||||
// NOTE: um/processthreadsapi.h ================================================================
|
||||
// NOTE: um/processthreadsapi.h ////////////////////////////////////////////////////////////////
|
||||
typedef struct _PROCESS_INFORMATION {
|
||||
HANDLE hProcess;
|
||||
HANDLE hThread;
|
||||
@ -757,7 +1043,7 @@
|
||||
|
||||
}
|
||||
|
||||
// NOTE: um/memoryapi.h ========================================================================
|
||||
// NOTE: um/memoryapi.h ////////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) VOID * __stdcall VirtualAlloc (VOID *lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
||||
@ -765,7 +1051,7 @@
|
||||
__declspec(dllimport) BOOL __stdcall VirtualFree (VOID *lpAddress, SIZE_T dwSize, DWORD dwFreeType);
|
||||
}
|
||||
|
||||
// NOTE: shared/bcrypt.h =======================================================================
|
||||
// NOTE: shared/bcrypt.h ///////////////////////////////////////////////////////////////////////
|
||||
typedef VOID *BCRYPT_ALG_HANDLE;
|
||||
typedef LONG NTSTATUS;
|
||||
|
||||
@ -775,12 +1061,17 @@
|
||||
__declspec(dllimport) NTSTATUS __stdcall BCryptGenRandom (BCRYPT_ALG_HANDLE hAlgorithm, UCHAR *pbBuffer, ULONG cbBuffer, ULONG dwFlags);
|
||||
}
|
||||
|
||||
// NOTE: um/shellapi.h =========================================================================
|
||||
// NOTE: um/shellapi.h /////////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) HINSTANCE __stdcall ShellExecuteA(HWND hwnd, CHAR const *lpOperation, CHAR const *lpFile, CHAR const *lpParameters, CHAR const *lpDirectory, INT nShowCmd);
|
||||
}
|
||||
|
||||
// NOTE: um/debugapi.h /////////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) BOOL __stdcall IsDebuggerPresent();
|
||||
}
|
||||
|
||||
DQN_MSVC_WARNING_POP
|
||||
#endif // !defined(_INC_WINDOWS)
|
||||
#endif /// defined(DQN_OS_WIN32)
|
||||
|
Loading…
Reference in New Issue
Block a user