Import latest changes from CSIGHT project

This commit is contained in:
doylet 2024-01-31 23:49:23 +11:00
parent bf413d7e57
commit 022c309e3a
42 changed files with 9227 additions and 7505 deletions

View File

@ -2,7 +2,7 @@
#define DQN_CPP_FILE_H #define DQN_CPP_FILE_H
// NOTE: Dqn_CppFile: Helper functions to generate C++ files // NOTE: Dqn_CppFile: Helper functions to generate C++ files
// ============================================================================= // /////////////////////////////////////////////////////////////////////////////
#include <stdio.h> /// printf, fputc #include <stdio.h> /// printf, fputc
#include <stdarg.h> /// va_list... #include <stdarg.h> /// va_list...
#include <assert.h> /// assert #include <assert.h> /// assert
@ -51,21 +51,21 @@ void Dqn_CppPrint(Dqn_CppFile *cpp, char const *fmt, ...);
#define Dqn_CppEnumBlock(cpp, fmt, ...) \ #define Dqn_CppEnumBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ 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_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndEnumBlock(cpp), false)) DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndEnumBlock(cpp), false))
#define Dqn_CppForBlock(cpp, fmt, ...) \ #define Dqn_CppForBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ 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_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndForBlock(cpp), false)) DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndForBlock(cpp), false))
#define Dqn_CppWhileBlock(cpp, fmt, ...) \ #define Dqn_CppWhileBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ 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_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndForBlock(cpp), false)) DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndWhileBlock(cpp), false))
#define Dqn_CppIfOrElseIfBlock(cpp, fmt, ...) \ #define Dqn_CppIfOrElseIfBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ 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, ...) \ #define Dqn_CppFuncBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ 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_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndFuncBlock(cpp), false)) DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndFuncBlock(cpp), false))
#define Dqn_CppStructBlock(cpp, fmt, ...) \ #define Dqn_CppStructBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ 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_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndStructBlock(cpp), false)) DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndStructBlock(cpp), false))
#define Dqn_CppSwitchBlock(cpp, fmt, ...) \ #define Dqn_CppSwitchBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \ 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_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndSwitchBlock(cpp), false)) 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) \ #define Dqn_CppIfChain(cpp) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppBeginIfChain(cpp), true); \ for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppBeginIfChain(cpp), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \ 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. /// increasing the indent level after the brace.
void Dqn_CppBeginBlock (Dqn_CppFile *cpp, bool append, char const *fmt, ...); 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_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. /// 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_CppBeginEnumBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "enum " fmt, ##__VA_ARGS__)
#define Dqn_CppEndEnumBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, ";\n") #define Dqn_CppEndEnumBlock(cpp) Dqn_CppEndBlock(cpp, ";\n")
#define Dqn_CppBeginForBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__) #define Dqn_CppBeginWhileBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "while (" fmt ")", ##__VA_ARGS__)
#define Dqn_CppEndForBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, "\n") #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_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_CppBeginStructBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "struct " fmt, ##__VA_ARGS__)
#define Dqn_CppEndStructBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, ";\n") #define Dqn_CppEndStructBlock(cpp) Dqn_CppEndBlock(cpp, ";\n")
#define Dqn_CppBeginSwitchBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__) #define Dqn_CppBeginSwitchBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "switch (" fmt ")", ##__VA_ARGS__)
#define Dqn_CppEndSwitchBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, "\n") #define Dqn_CppEndSwitchBlock(cpp) Dqn_CppEndBlock(cpp, "\n")
void Dqn_CppBeginIfOrElseIfBlock (Dqn_CppFile *cpp, char const *fmt, ...); 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); 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_PASTE2_(x, y) x ## y
#define DQN_CPP_TOKEN_PASTE_(x, y) DQN_CPP_TOKEN_PASTE2_(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); Dqn_CppAppendV(cpp, fmt, args);
else else
Dqn_CppPrintV(cpp, fmt, args); 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); Dqn_CppIndent(cpp);
} }
void Dqn_CppEndBlock(Dqn_CppFile *cpp) void Dqn_CppEndBlock(Dqn_CppFile *cpp, char const *ending)
{ {
Dqn_CppUnindent(cpp); Dqn_CppUnindent(cpp);
Dqn_CppPrint(cpp, "}"); Dqn_CppPrint(cpp, "}%s", ending);
} }
void Dqn_CppBeginIfOrElseIfBlock(Dqn_CppFile *cpp, char const *fmt, ...) void Dqn_CppBeginIfOrElseIfBlock(Dqn_CppFile *cpp, char const *fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
assert(cpp->if_chain_size);
if (cpp->if_chain[cpp->if_chain_size - 1] == 0) if (cpp->if_chain[cpp->if_chain_size - 1] == 0)
Dqn_CppPrint(cpp, "if"); Dqn_CppPrint(cpp, "if");
else else
@ -208,10 +220,17 @@ void Dqn_CppBeginIfOrElseIfBlock(Dqn_CppFile *cpp, char const *fmt, ...)
void Dqn_CppBeginElseBlock(Dqn_CppFile *cpp) void Dqn_CppBeginElseBlock(Dqn_CppFile *cpp)
{ {
assert(cpp->if_chain_size);
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1) if (cpp->if_chain[cpp->if_chain_size - 1] >= 1)
Dqn_CppBeginBlock(cpp, true /*append*/, " else"); 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) void Dqn_CppBeginIfChain(Dqn_CppFile *cpp)
{ {
assert(cpp->if_chain_size < sizeof(cpp->if_chain)/sizeof(cpp->if_chain[0])); 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) 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); Dqn_CppNewLine(cpp);
} }
cpp->if_chain[cpp->if_chain_size - 1] = 0;
cpp->if_chain_size--;
}
#endif // DQN_CPP_FILE_IMPLEMENTATION #endif // DQN_CPP_FILE_IMPLEMENTATION

View File

@ -4,103 +4,93 @@
#if !defined(DQN_JSON_H) #if !defined(DQN_JSON_H)
#define DQN_JSON_H #define DQN_JSON_H
// NOTE: Dqn_JSON //////////////////////////////////////////////////////////////////////////////////
// NOTE: Dqn_JSON
// -----------------------------------------------------------------------------
void *Dqn_JSON_ArenaAllocFunc (void *user_data, size_t count); void *Dqn_JSON_ArenaAllocFunc (void *user_data, size_t count);
char const *Dqn_JSON_TypeEnumCString(json_type_e type, size_t *size); 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 // NOTE: Dqn_JSON_It /////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- enum Dqn_JSONItEntryType
enum Dqn_JSONIteratorEntryType
{ {
Dqn_JSON_IteratorEntryTypeObjElement, Dqn_JSON_ItEntryTypeObjElement,
Dqn_JSON_IteratorEntryTypeObj, Dqn_JSON_ItEntryTypeObj,
Dqn_JSON_IteratorEntryTypeArrayElement, Dqn_JSON_ItEntryTypeArrayElement,
Dqn_JSON_IteratorEntryTypeArray, Dqn_JSON_ItEntryTypeArray,
Dqn_JSON_IteratorEntryTypeString, Dqn_JSON_ItEntryTypeString,
Dqn_JSON_IteratorEntryTypeNumber, Dqn_JSON_ItEntryTypeNumber,
}; };
struct Dqn_JSONIteratorEntry struct Dqn_JSONItEntry
{ {
Dqn_JSONIteratorEntryType type; Dqn_JSONItEntryType type;
void *value; void *value;
}; };
struct Dqn_JSONIterator struct Dqn_JSONIt
{ {
Dqn_JSONIteratorEntry stack[128]; Dqn_JSONItEntry stack[128];
int stack_count; int stack_count;
size_t flags; size_t flags;
}; };
// NOTE: Dqn_JSON_IteratorPush/Pop Dqn_JSONIt Dqn_JSON_LoadFileToIt(Dqn_Arena *arena, Dqn_Str8 json);
// -----------------------------------------------------------------------------
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);
// NOTE: Dqn_JSON_Iterator tree navigation // NOTE: Dqn_JSON_ItPush/Pop /////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- bool Dqn_JSON_ItPushObjElement (Dqn_JSONIt *it, json_object_element_s *element);
json_value_s *Dqn_JSON_IteratorPushCurrValue(Dqn_JSONIterator *it); bool Dqn_JSON_ItPushObj (Dqn_JSONIt *it, json_object_s *obj);
bool Dqn_JSON_IteratorNext(Dqn_JSONIterator *it); 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__) // NOTE: Dqn_JSON_It tree navigation /////////////////////////////////////////////////////////
void Dqn_JSON_IteratorErrorUnrecognisedKey_(Dqn_JSONIterator *it, Dqn_String8 file, Dqn_String8 func, Dqn_uint line); json_value_s *Dqn_JSON_ItPushCurrValue(Dqn_JSONIt *it);
bool Dqn_JSON_ItNext(Dqn_JSONIt *it);
#define Dqn_JSON_IteratorPushCurrValueIterateThenPop(it) \ #define Dqn_JSON_ItPushCurrValueIterateThenPop(it) \
for(void *DQN_UNIQUE_NAME(ptr) = Dqn_JSON_IteratorPushCurrValue(it); DQN_UNIQUE_NAME(ptr); Dqn_JSON_IteratorPop(it), DQN_UNIQUE_NAME(ptr) = nullptr) \ 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_IteratorNext(it)) while (Dqn_JSON_ItNext(it))
// NOTE: Dqn_JSON_IteratorCurr // NOTE: Dqn_JSON_ItCurr /////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- Dqn_JSONItEntry *Dqn_JSON_ItCurr(Dqn_JSONIt *it);
Dqn_JSONIteratorEntry *Dqn_JSON_IteratorCurr(Dqn_JSONIterator *it); json_value_s *Dqn_JSON_ItCurrValue(Dqn_JSONIt *it);
json_value_s *Dqn_JSON_IteratorCurrValue(Dqn_JSONIterator *it); json_object_element_s *Dqn_JSON_ItCurrObjElement(Dqn_JSONIt *it);
json_object_element_s *Dqn_JSON_IteratorCurrObjElement(Dqn_JSONIterator *it);
// NOTE: Dqn_JSON_IteratorValueIs // NOTE: Dqn_JSON_ItValueIs //////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- json_value_s *Dqn_JSON_ItValueIs(Dqn_JSONIt *it, json_type_e type);
json_value_s *Dqn_JSON_IteratorValueIs(Dqn_JSONIterator *it, json_type_e type); json_object_s *Dqn_JSON_ItValueIsObj(Dqn_JSONIt *it);
json_object_s *Dqn_JSON_IteratorValueIsObj(Dqn_JSONIterator *it); json_array_s *Dqn_JSON_ItValueIsArray(Dqn_JSONIt *it);
json_array_s *Dqn_JSON_IteratorValueIsArray(Dqn_JSONIterator *it); json_string_s *Dqn_JSON_ItValueIsString(Dqn_JSONIt *it);
json_string_s *Dqn_JSON_IteratorValueIsString(Dqn_JSONIterator *it); json_number_s *Dqn_JSON_ItValueIsNumber(Dqn_JSONIt *it);
json_number_s *Dqn_JSON_IteratorValueIsNumber(Dqn_JSONIterator *it); json_value_s *Dqn_JSON_ItValueIsBool(Dqn_JSONIt *it);
json_value_s *Dqn_JSON_IteratorValueIsBool(Dqn_JSONIterator *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 // NOTE: Dqn_JSON_ItKeyValueIs ///////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- Dqn_Str8 Dqn_JSON_ItKey(Dqn_JSONIt *it);
Dqn_String8 Dqn_JSON_IteratorKey(Dqn_JSONIterator *it); bool Dqn_JSON_ItKeyIs(Dqn_JSONIt *it, Dqn_Str8 key);
bool Dqn_JSON_IteratorKeyIs(Dqn_JSONIterator *it, Dqn_String8 key); json_object_s *Dqn_JSON_ItKeyValueIsObj(Dqn_JSONIt *it, Dqn_Str8 key);
json_object_s *Dqn_JSON_IteratorKeyValueIsObj(Dqn_JSONIterator *it, Dqn_String8 key); json_array_s *Dqn_JSON_ItKeyValueIsArray(Dqn_JSONIt *it, Dqn_Str8 key);
json_array_s *Dqn_JSON_IteratorKeyValueIsArray(Dqn_JSONIterator *it, Dqn_String8 key); json_string_s *Dqn_JSON_ItKeyValueIsString(Dqn_JSONIt *it, Dqn_Str8 key);
json_string_s *Dqn_JSON_IteratorKeyValueIsString(Dqn_JSONIterator *it, Dqn_String8 key); json_number_s *Dqn_JSON_ItKeyValueIsNumber(Dqn_JSONIt *it, Dqn_Str8 key);
json_number_s *Dqn_JSON_IteratorKeyValueIsNumber(Dqn_JSONIterator *it, Dqn_String8 key); json_value_s *Dqn_JSON_ItKeyValueIsBool(Dqn_JSONIt *it, Dqn_Str8 key);
json_value_s *Dqn_JSON_IteratorKeyValueIsBool(Dqn_JSONIterator *it, Dqn_String8 key); json_value_s *Dqn_JSON_ItKeyValueIsNull(Dqn_JSONIt *it, Dqn_Str8 key);
// NOTE: Dqn_JSON_IteratorValueTo // NOTE: Dqn_JSON_ItValueTo //////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- Dqn_Str8 Dqn_JSON_ItValueToString(Dqn_JSONIt *it);
Dqn_String8 Dqn_JSON_IteratorValueToString(Dqn_JSONIterator *it); int64_t Dqn_JSON_ItValueToI64(Dqn_JSONIt *it);
int64_t Dqn_JSON_IteratorValueToI64(Dqn_JSONIterator *it); uint64_t Dqn_JSON_ItValueToU64(Dqn_JSONIt *it);
uint64_t Dqn_JSON_IteratorValueToU64(Dqn_JSONIterator *it); bool Dqn_JSON_ItValueToBool(Dqn_JSONIt *it);
bool Dqn_JSON_IteratorValueToBool(Dqn_JSONIterator *it);
#define Dqn_JSON_IteratorErrorUnknownKeyValue(it) \ #define Dqn_JSON_ItErrorUnknownKeyValue(it) Dqn_JSON_ItErrorUnknownKeyValue_(it, DQN_CALL_SITE)
Dqn_JSON_IteratorErrorUnknownKeyValue_(it, DQN_CALL_SITE) void Dqn_JSON_ItErrorUnknownKeyValue_(Dqn_JSONIt *it, Dqn_CallSite call_site);
void Dqn_JSON_IteratorErrorUnknownKeyValue_(Dqn_JSONIterator *it, Dqn_String8 file, Dqn_String8 func, int line);
#endif // DQN_JSON_H #endif // DQN_JSON_H
#if defined(DQN_JSON_IMPLEMENTATION) #if defined(DQN_JSON_IMPLEMENTATION)
// NOTE: Dqn_JSON // NOTE: Dqn_JSON //////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count) void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count)
{ {
void *result = NULL; void *result = NULL;
@ -108,7 +98,7 @@ void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count)
return result; return result;
Dqn_Arena *arena = DQN_CAST(Dqn_Arena*)user_data; 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; 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; bool result = false;
if (lhs && Dqn_String8_IsValid(key)) { if (lhs && Dqn_Str8_HasData(key)) {
Dqn_String8 lhs_string = Dqn_String8_Init(lhs->string, lhs->string_size); Dqn_Str8 lhs_string = Dqn_Str8_Init(lhs->string, lhs->string_size);
result = Dqn_String8_Eq(lhs_string, key); result = Dqn_Str8_Eq(lhs_string, key);
} }
return result; return result;
} }
// NOTE: Dqn_JSON_Iterator_push/pop // NOTE: Dqn_JSON_It ///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- Dqn_JSONIt Dqn_JSON_LoadFileToIt(Dqn_Arena *arena, Dqn_Str8 json)
bool Dqn_JSON_IteratorPushObjElement(Dqn_JSONIterator *it, json_object_element_s *element) {
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) if (!it || !element)
return false; return false;
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); 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; 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) if (!it || !obj)
return false; return false;
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); 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; 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) if (!it || !element)
return false; return false;
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); 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; 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) if (!it || !value || json_value_as_array(value) == nullptr)
return false; return false;
DQN_ASSERT(it->stack_count < DQN_ARRAY_ICOUNT(it->stack)); 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; 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; bool result = false;
if (!it || !value) if (!it || !value)
return result; return result;
if (value->type == json_type_object) { 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) { } else if (value->type == json_type_array) {
result = Dqn_JSON_IteratorPushArray(it, value); result = Dqn_JSON_ItPushArray(it, value);
} }
return result; return result;
} }
void Dqn_JSON_IteratorPop(Dqn_JSONIterator *it) void Dqn_JSON_ItPop(Dqn_JSONIt *it)
{ {
if (!it) if (!it)
return; return;
@ -199,19 +205,18 @@ void Dqn_JSON_IteratorPop(Dqn_JSONIterator *it)
it->stack_count--; it->stack_count--;
} }
// NOTE: Dqn_JSON_Iterator json tree navigation // NOTE: Dqn_JSON_It JSON tree navigation //////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- json_value_s *Dqn_JSON_ItPushCurrValue(Dqn_JSONIt *it)
json_value_s *Dqn_JSON_IteratorPushCurrValue(Dqn_JSONIterator *it)
{ {
json_value_s *result = nullptr; json_value_s *result = nullptr;
Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it); Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
if (!curr) if (!curr)
return result; 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; json_object_element_s *element = DQN_CAST(json_object_element_s *) curr->value;
result = element->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; json_array_element_s *element = DQN_CAST(json_array_element_s *) curr->value;
result = element->value; result = element->value;
} else { } else {
@ -221,57 +226,56 @@ json_value_s *Dqn_JSON_IteratorPushCurrValue(Dqn_JSONIterator *it)
if (result->type == json_type_array) { if (result->type == json_type_array) {
json_array_s *array = json_value_as_array(result); json_array_s *array = json_value_as_array(result);
DQN_ASSERT(array); DQN_ASSERT(array);
Dqn_JSON_IteratorPushArray(it, result); Dqn_JSON_ItPushArray(it, result);
} else if (result->type == json_type_object) { } else if (result->type == json_type_object) {
json_object_s *obj = json_value_as_object(result); json_object_s *obj = json_value_as_object(result);
DQN_ASSERT(obj); DQN_ASSERT(obj);
Dqn_JSON_IteratorPushObj(it, obj); Dqn_JSON_ItPushObj(it, obj);
} }
return result; 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) if (!curr)
return false; return false;
json_object_element_s *obj_element = nullptr; json_object_element_s *obj_element = nullptr;
json_array_element_s *array_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; auto *obj = DQN_CAST(json_object_s *) curr->value;
obj_element = obj->start; 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; auto *element = DQN_CAST(json_object_element_s *) curr->value;
obj_element = element->next; obj_element = element->next;
Dqn_JSON_IteratorPop(it); Dqn_JSON_ItPop(it);
} else if (curr->type == Dqn_JSON_IteratorEntryTypeArray) { } else if (curr->type == Dqn_JSON_ItEntryTypeArray) {
auto *value = DQN_CAST(json_value_s *) curr->value; auto *value = DQN_CAST(json_value_s *) curr->value;
auto *array = json_value_as_array(value); auto *array = json_value_as_array(value);
array_element = array->start; 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; auto *element = DQN_CAST(json_array_element_s *) curr->value;
array_element = element->next; array_element = element->next;
Dqn_JSON_IteratorPop(it); Dqn_JSON_ItPop(it);
} else { } else {
Dqn_JSON_IteratorPop(it); Dqn_JSON_ItPop(it);
} }
if (obj_element) if (obj_element)
Dqn_JSON_IteratorPushObjElement(it, obj_element); Dqn_JSON_ItPushObjElement(it, obj_element);
else if (array_element) else if (array_element)
Dqn_JSON_IteratorPushArrayElement(it, array_element); Dqn_JSON_ItPushArrayElement(it, array_element);
bool result = obj_element || array_element; bool result = obj_element || array_element;
return result; return result;
} }
// NOTE: Dqn_JSON_IteratorCurr // NOTE: Dqn_JSON_ItCurr ///////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- Dqn_JSONItEntry *Dqn_JSON_ItCurr(Dqn_JSONIt *it)
Dqn_JSONIteratorEntry *Dqn_JSON_IteratorCurr(Dqn_JSONIterator *it)
{ {
Dqn_JSONIteratorEntry *result = nullptr; Dqn_JSONItEntry *result = nullptr;
if (!it || it->stack_count <= 0) if (!it || it->stack_count <= 0)
return result; return result;
@ -279,23 +283,23 @@ Dqn_JSONIteratorEntry *Dqn_JSON_IteratorCurr(Dqn_JSONIterator *it)
return result; 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; json_value_s *result = nullptr;
Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it); Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
if (!curr) if (!curr)
return result; return result;
if (curr->type == Dqn_JSON_IteratorEntryTypeObjElement) { if (curr->type == Dqn_JSON_ItEntryTypeObjElement) {
auto *element = DQN_CAST(json_object_element_s *)curr->value; auto *element = DQN_CAST(json_object_element_s *)curr->value;
result = element->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; auto *element = DQN_CAST(json_array_element_s *)curr->value;
result = element->value; result = element->value;
} else if (curr->type == Dqn_JSON_IteratorEntryTypeString || } else if (curr->type == Dqn_JSON_ItEntryTypeString ||
curr->type == Dqn_JSON_IteratorEntryTypeNumber || curr->type == Dqn_JSON_ItEntryTypeNumber ||
curr->type == Dqn_JSON_IteratorEntryTypeObj || curr->type == Dqn_JSON_ItEntryTypeObj ||
curr->type == Dqn_JSON_IteratorEntryTypeArray) curr->type == Dqn_JSON_ItEntryTypeArray)
{ {
result = DQN_CAST(json_value_s *)curr->value; result = DQN_CAST(json_value_s *)curr->value;
} }
@ -303,74 +307,79 @@ json_value_s *Dqn_JSON_IteratorCurrValue(Dqn_JSONIterator *it)
return result; 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); Dqn_JSONItEntry *curr = Dqn_JSON_ItCurr(it);
auto *result = (curr && curr->type == Dqn_JSON_IteratorEntryTypeObjElement) auto *result = (curr && curr->type == Dqn_JSON_ItEntryTypeObjElement)
? DQN_CAST(json_object_element_s *) curr->value ? DQN_CAST(json_object_element_s *) curr->value
: nullptr; : nullptr;
return result; return result;
} }
// NOTE: Dqn_JSON_IteratorValueIs // NOTE: Dqn_JSON_ItValueIs ////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- json_value_s *Dqn_JSON_ItValueIs(Dqn_JSONIt *it, json_type_e type)
json_value_s *Dqn_JSON_IteratorValueIs(Dqn_JSONIterator *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; json_value_s *result = (curr && type == curr->type) ? curr : nullptr;
return result; 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; json_object_s *result = curr ? json_value_as_object(curr) : nullptr;
return result; 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; json_array_s *result = curr ? json_value_as_array(curr) : nullptr;
return result; 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; json_string_s *result = curr ? json_value_as_string(curr) : nullptr;
return result; 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; json_number_s *result = curr ? json_value_as_number(curr) : nullptr;
return result; 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; json_value_s *result = (curr && (curr->type == json_type_true || curr->type == json_type_false)) ? curr : nullptr;
return result; 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; 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; result = curr->length;
return result; return result;
} }
// NOTE: Dqn_JSON_IteratorKeyValueIs // NOTE: Dqn_JSON_ItKeyValueIs /////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- Dqn_Str8 Dqn_JSON_ItKey(Dqn_JSONIt *it)
Dqn_String8 Dqn_JSON_IteratorKey(Dqn_JSONIterator *it)
{ {
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it); json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
Dqn_String8 result = {}; Dqn_Str8 result = {};
if (curr) { if (curr) {
result.data = DQN_CAST(char *)curr->name->string; result.data = DQN_CAST(char *)curr->name->string;
result.size = curr->name->string_size; result.size = curr->name->string_size;
@ -378,98 +387,107 @@ Dqn_String8 Dqn_JSON_IteratorKey(Dqn_JSONIterator *it)
return result; 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); bool result = Dqn_JSON_String8Cmp(curr->name, key);
return result; 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_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)) if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = json_value_as_object(curr->value); result = json_value_as_object(curr->value);
return result; 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_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)) if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = json_value_as_array(curr->value); result = json_value_as_array(curr->value);
return result; 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; json_string_s *result = nullptr;
if (curr && Dqn_JSON_String8Cmp(curr->name, key)) if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = json_value_as_string(curr->value); result = json_value_as_string(curr->value);
return result; 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; json_number_s *result = nullptr;
if (curr && Dqn_JSON_String8Cmp(curr->name, key)) if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = json_value_as_number(curr->value); result = json_value_as_number(curr->value);
return result; 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; json_value_s *result = nullptr;
if (curr && Dqn_JSON_String8Cmp(curr->name, key)) if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = curr->value->type == json_type_true || curr->value->type == json_type_false ? curr->value : nullptr; result = curr->value->type == json_type_true || curr->value->type == json_type_false ? curr->value : nullptr;
return result; return result;
} }
// NOTE: Dqn_JSON_IteratorValueTo json_value_s *Dqn_JSON_ItKeyValueIsNull(Dqn_JSONIt *it, Dqn_Str8 key)
// -----------------------------------------------------------------------------
Dqn_String8 Dqn_JSON_IteratorValueToString(Dqn_JSONIterator *it)
{ {
Dqn_String8 result = {}; json_object_element_s *curr = Dqn_JSON_ItCurrObjElement(it);
if (json_string_s *curr = Dqn_JSON_IteratorValueIsString(it)) json_value_s *result = nullptr;
result = Dqn_String8_Init(curr->string, curr->string_size); if (curr && Dqn_JSON_String8Cmp(curr->name, key))
result = curr->value->type == json_type_null ? curr->value : nullptr;
return result; 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 = {}; int64_t result = {};
if (json_number_s *curr = Dqn_JSON_IteratorValueIsNumber(it)) if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it))
result = Dqn_String8_ToI64(Dqn_String8_Init(curr->number, curr->number_size), 0 /*separator*/); result = Dqn_Str8_ToI64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value;
return result; return result;
} }
uint64_t Dqn_JSON_IteratorValueToU64(Dqn_JSONIterator *it) uint64_t Dqn_JSON_ItValueToU64(Dqn_JSONIt *it)
{ {
uint64_t result = {}; uint64_t result = {};
if (json_number_s *curr = Dqn_JSON_IteratorValueIsNumber(it)) if (json_number_s *curr = Dqn_JSON_ItValueIsNumber(it))
result = Dqn_String8_ToU64(Dqn_String8_Init(curr->number, curr->number_size), 0 /*separator*/); result = Dqn_Str8_ToU64(Dqn_Str8_Init(curr->number, curr->number_size), 0 /*separator*/).value;
return result; return result;
} }
bool Dqn_JSON_IteratorValueToBool(Dqn_JSONIterator *it) bool Dqn_JSON_ItValueToBool(Dqn_JSONIt *it)
{ {
bool result = {}; 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; result = curr->type == json_type_true;
return result; 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) if (!it)
return; return;
json_object_element_s const *curr = Dqn_JSON_IteratorCurrObjElement(it); json_object_element_s const *curr = Dqn_JSON_ItCurrObjElement(it);
if (!curr) if (!curr)
return; 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]", "Unknown key-value pair in object [loc=%zu:%zu, key=%.*s, value=%.*s]",
info->line_no, info->line_no,
info->row_no, info->row_no,
key->string_size, DQN_CAST(int)key->string_size,
key->string, key->string,
value_type_size, DQN_CAST(int)value_type_size,
value_type); value_type);
} else { } else {
Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, Dqn_Log_TypeFCallSite(Dqn_LogType_Warning,
call_site, call_site,
"Unknown key-value pair in object [key=%.*s, value=%.*s]", "Unknown key-value pair in object [key=%.*s, value=%.*s]",
key->string_size, DQN_CAST(int)key->string_size,
key->string, key->string,
value_type_size, DQN_CAST(int)value_type_size,
value_type); value_type);
} }
} }
#endif // defined(DQN_JSON_IMPLEMENTATION) #endif // defined(DQN_JSON_IMPLEMENTATION)

View File

@ -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

View File

@ -13,7 +13,7 @@ pushd Build
REM O2 Optimisation Level 2 REM O2 Optimisation Level 2
REM Oi Use CPU Intrinsics REM Oi Use CPU Intrinsics
REM Z7 Combine multi-debug files to one debug file 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 set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo

387
dqn.h
View File

@ -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 // 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 // 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 // primitives and standard library functions that are missing and or more
// appropriate for development in modern day computing (e.g. cache friendly // appropriate for development in modern day computing (e.g. allocator
// memory management, 64bit MMU, non-pessimized APIs that aren't constrained by // first-class APIs, a 64bit MMU and in general non-pessimized APIs that aren't
// the language specification and operate closer to the OS). // constrained by the language specification and operate closer to the OS).
// //
// Define DQN_IMPLEMENTATION macro in one and only one translation unit to // Define DQN_IMPLEMENTATION macro in one and only one translation unit to
// enable the implementation of this library, for example: // 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 // 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 // of this library and additional macros that can be defined to customise the
// behaviour of this library. // 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) || \ #if defined(DQN_ONLY_VARRAY) || \
defined(DQN_ONLY_SARRAY) || \ defined(DQN_ONLY_SARRAY) || \
defined(DQN_ONLY_FARRAY) || \ defined(DQN_ONLY_FARRAY) || \
defined(DQN_ONLY_SLICE) || \
defined(DQN_ONLY_DSMAP) || \ defined(DQN_ONLY_DSMAP) || \
defined(DQN_ONLY_LIST) || \ defined(DQN_ONLY_LIST) || \
defined(DQN_ONLY_FSTR8) || \ defined(DQN_ONLY_FSTR8) || \
defined(DQN_ONLY_FS) || \ defined(DQN_ONLY_FS) || \
defined(DQN_ONLY_WINNET) || \ defined(DQN_ONLY_WINNET) || \
defined(DQN_ONLY_WIN) || \ defined(DQN_ONLY_WIN) || \
defined(DQN_ONLY_SEMAPHORE) || \
defined(DQN_ONLY_THREAD) || \
defined(DQN_ONLY_V2) || \ defined(DQN_ONLY_V2) || \
defined(DQN_ONLY_V3) || \ defined(DQN_ONLY_V3) || \
defined(DQN_ONLY_V4) || \ defined(DQN_ONLY_V4) || \
@ -63,9 +190,6 @@
#if !defined(DQN_ONLY_SARRAY) #if !defined(DQN_ONLY_SARRAY)
#define DQN_NO_SARRAY #define DQN_NO_SARRAY
#endif #endif
#if !defined(DQN_ONLY_SLICE)
#define DQN_NO_SLICE
#endif
#if !defined(DQN_ONLY_DSMAP) #if !defined(DQN_ONLY_DSMAP)
#define DQN_NO_DSMAP #define DQN_NO_DSMAP
#endif #endif
@ -84,6 +208,12 @@
#if !defined(DQN_ONLY_WIN) #if !defined(DQN_ONLY_WIN)
#define DQN_NO_WIN #define DQN_NO_WIN
#endif #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) #if !defined(DQN_ONLY_V2)
#define DQN_NO_V2 #define DQN_NO_V2
#endif #endif
@ -110,222 +240,65 @@
#endif #endif
#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" #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" #include "dqn_external.h"
#if defined(DQN_PLATFORM_WIN32)
#include "dqn_win32.h" #include "dqn_win32.h"
#endif
// NOTE: Additional Configuration #include "dqn_allocator.h"
// - Define this to stop this library from defining a minimal subset of Win32 #include "dqn_thread_context.h"
// 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
#include "dqn_debug.h" #include "dqn_debug.h"
#include "dqn_string.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_containers.h" #include "dqn_containers.h"
#if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64)
// NOTE: Additional Configuration #elif defined(DQN_PLATFORM_WIN32)
// - Override the default break into the active debugger function. By default #include "dqn_os_win32.h"
// we use __debugbreak() on Windows and raise(SIGTRAP) on other platforms. #else
// #error Please define a platform e.g. 'DQN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
// DQN_DEBUG_BREAK #endif
//
// - 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
#include "dqn_os.h" #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" #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" #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_helpers.h"
#include "dqn_type_info.h"
#endif // DQN_H #endif // DQN_H
#if defined(DQN_IMPLEMENTATION) #if defined(DQN_IMPLEMENTATION)
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// /$$$$$$\ $$\ $$\ $$$$$$$\ $$\
// \_$$ _|$$$\ $$$ |$$ __$$\ $$ |
// $$ | $$$$\ $$$$ |$$ | $$ |$$ |
// $$ | $$\$$\$$ $$ |$$$$$$$ |$$ |
// $$ | $$ \$$$ $$ |$$ ____/ $$ |
// $$ | $$ |\$ /$$ |$$ | $$ |
// $$$$$$\ $$ | \_/ $$ |$$ | $$$$$$$$\
// \______|\__| \__|\__| \________|
//
// Implementation
//
////////////////////////////////////////////////////////////////////////////////////////////////////
#include "dqn_base.cpp" #include "dqn_base.cpp"
#include "dqn_thread_context.cpp"
#include "dqn_external.cpp" #include "dqn_external.cpp"
#include "dqn_memory.cpp" #include "dqn_allocator.cpp"
#include "dqn_debug.cpp" #include "dqn_debug.cpp"
#include "dqn_strings.cpp" #include "dqn_string.cpp"
#include "dqn_containers.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_os.cpp"
#include "dqn_math.cpp" #include "dqn_math.cpp"
#include "dqn_hash.cpp" #include "dqn_hash.cpp"
#include "dqn_helpers.cpp" #include "dqn_helpers.cpp"
#include "dqn_unit_tests.cpp"
#include "dqn_docs.cpp"
#endif // DQN_IMPLEMENTATION #endif // DQN_IMPLEMENTATION

BIN
dqn.rdbg

Binary file not shown.

497
dqn_allocator.cpp Normal file
View 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
View 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, ...);

View File

@ -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_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN)
#if defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG) #if defined(DQN_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
#include <cpuid.h> #include <cpuid.h>
#endif #endif
Dqn_CPUIDRegisters Dqn_CPUID(int function_id) DQN_API Dqn_CPUIDRegisters Dqn_CPUID(int function_id)
{ {
Dqn_CPUIDRegisters result = {}; Dqn_CPUIDRegisters result = {};
#if defined(DQN_COMPILER_MSVC) #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) #endif // !defined(DQN_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN)
// NOTE: [$ALLO] Dqn_Allocator ===================================================================== // NOTE: [$TMUT] Dqn_TicketMutex ///////////////////////////////////////////////////////////////////
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 ===================================================================
DQN_API void Dqn_TicketMutex_Begin(Dqn_TicketMutex *mutex) DQN_API void Dqn_TicketMutex_Begin(Dqn_TicketMutex *mutex)
{ {
unsigned int ticket = Dqn_Atomic_AddU32(&mutex->ticket, 1); 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
#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_API Dqn_PrintStyle Dqn_Print_StyleColour(uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold)
{ {
Dqn_PrintStyle result = {}; 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); DQN_ASSERT(std_handle == Dqn_PrintStd_Out || std_handle == Dqn_PrintStd_Err);
#if defined(DQN_OS_WIN32) #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_out_print_handle = nullptr;
DQN_THREAD_LOCAL void *std_err_print_handle = nullptr; DQN_THREAD_LOCAL void *std_err_print_handle = nullptr;
DQN_THREAD_LOCAL bool std_out_print_to_console = false; 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; 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; void *print_handle = std_out_print_handle;
bool print_to_console = std_out_print_to_console; bool print_to_console = std_out_print_to_console;
if (std_handle == Dqn_PrintStd_Err) { 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; 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); DQN_ASSERT(string.size < DQN_CAST(unsigned long)-1);
unsigned long bytes_written = 0; (void)bytes_written; unsigned long bytes_written = 0; (void)bytes_written;
if (print_to_console) { 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, ...) DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...)
{ {
va_list args; va_list args;
@ -191,10 +174,27 @@ DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style,
va_end(args); 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) 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]; char buffer[STB_SPRINTF_MIN];
STB_SPRINTF_DECORATE(vsprintfcb)(Dqn_Print_VSPrintfChunker_, DQN_CAST(void *)DQN_CAST(uintptr_t)std_handle, buffer, fmt, args); 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) 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]; DQN_THREAD_LOCAL char buffer[32];
buffer[0] = 0; buffer[0] = 0;
Dqn_Str8 result = {}; Dqn_Str8 result = {};
result.size = STB_SPRINTF_DECORATE(snprintf)(buffer, result.size = DQN_SNPRINTF(buffer,
DQN_ARRAY_UCOUNT(buffer), DQN_ARRAY_UCOUNT(buffer),
"\x1b[%d;2;%u;%u;%um", "\x1b[%d;2;%u;%u;%um",
colour == Dqn_PrintESCColour_Fg ? 38 : 48, colour == Dqn_PrintESCColour_Fg ? 38 : 48,
@ -273,17 +273,14 @@ DQN_API Dqn_Str8 Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour colour, uint32_t
return result; return result;
} }
// NOTE: [$LLOG] Dqn_Log ========================================================================== // NOTE: [$LLOG] Dqn_Log ///////////////////////////////////////////////////////////////////////////
DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Allocator allocator, DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Arena *arena,
bool colour, bool colour,
Dqn_Str8 type, Dqn_Str8 type,
int log_type, int log_type,
Dqn_CallSite call_site, Dqn_CallSite call_site,
DQN_FMT_ATTRIB char const *fmt, DQN_FMT_ATTRIB char const *fmt,
va_list args) va_list args)
{
Dqn_usize header_size_no_ansi_codes = 0;
Dqn_Str8 header = {};
{ {
DQN_LOCAL_PERSIST Dqn_usize max_type_length = 0; DQN_LOCAL_PERSIST Dqn_usize max_type_length = 0;
max_type_length = DQN_MAX(max_type_length, type.size); 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_Str8 file_name = Dqn_Str8_FileNameFromPath(call_site.file);
Dqn_DateHMSTimeStr8 const time = Dqn_Date_LocalTimeHMSStr8Now(); Dqn_OSDateTimeStr8 const time = Dqn_OS_DateLocalTimeStr8Now();
header = Dqn_Str8_InitF(allocator, Dqn_Str8 header = Dqn_Str8_InitF(arena,
"%.*s " // date "%.*s " // date
"%.*s " // hms "%.*s " // hms
"%.*s" // colour "%.*s" // colour
@ -314,30 +311,27 @@ DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Allocator allocator,
"%*s" // type padding "%*s" // type padding
"%.*s" // reset "%.*s" // reset
" %.*s" // file name " %.*s" // file name
":%05u ", // line number ":%05I32u " // 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(int)time.date_size - 2, time.date + 2, // date
DQN_CAST(uint32_t)colour_esc.size, colour_esc.data, // colour DQN_CAST(int)time.hms_size, time.hms, // hms
DQN_CAST(uint32_t)bold_esc.size, bold_esc.data, // bold DQN_STR_FMT(colour_esc), // colour
DQN_CAST(uint32_t)type.size, type.data, // type DQN_STR_FMT(bold_esc), // bold
DQN_CAST(uint32_t)type_padding, "", // type padding DQN_STR_FMT(type), // type
DQN_CAST(uint32_t)reset_esc.size, reset_esc.data, // reset DQN_CAST(int)type_padding, "", // type padding
DQN_CAST(uint32_t)file_name.size, file_name.data, // file name DQN_STR_FMT(reset_esc), // reset
DQN_STR_FMT(file_name), // file name
call_site.line); // line number 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 ======================================================================== // NOTE: Header padding ////////////////////////////////////////////////////////////////////////
Dqn_usize header_padding = 0;
{
DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0; DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0;
max_header_length = DQN_MAX(max_header_length, header_size_no_ansi_codes); 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 =================================================================== // NOTE: Construct final log ///////////////////////////////////////////////////////////////////
Dqn_Str8 user_msg = Dqn_Str8_InitFV(allocator, fmt, args); Dqn_Str8 user_msg = Dqn_Str8_InitFV(arena, fmt, args);
Dqn_Str8 result = Dqn_Str8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No); 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_MEMCPY(result.data, header.data, header.size);
DQN_MEMSET(result.data + header.size, ' ', header_padding); DQN_MEMSET(result.data + header.size, ' ', header_padding);
DQN_MEMCPY(result.data + header.size + header_padding, user_msg.data, user_msg.size); 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)log_type;
(void)user_data; (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); Dqn_TicketMutex_Begin(&lib->log_file_mutex);
if (lib->log_to_file && !lib->log_file.handle && lib->log_file.error_size == 0) { if (lib->log_to_file && !lib->log_file.handle && lib->log_file.error_size == 0) {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 log_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir)); Dqn_Str8 log_path = Dqn_OS_PathConvertF(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); lib->log_file = Dqn_OS_OpenFile(log_path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_AppendOnly);
} }
Dqn_TicketMutex_End(&lib->log_file_mutex); Dqn_TicketMutex_End(&lib->log_file_mutex);
// NOTE: Generate the log header =========================================== // NOTE: Generate the log header ///////////////////////////////////////////
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 log_line = Dqn_Log_MakeStr8(scratch.allocator, !lib->log_no_colour, type, log_type, call_site, fmt, args); 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_Print_StdLn(Dqn_PrintStd_Out, log_line);
Dqn_TicketMutex_Begin(&lib->log_file_mutex); Dqn_TicketMutex_Begin(&lib->log_file_mutex);
Dqn_Fs_WriteFile(&lib->log_file, log_line); Dqn_OS_WriteFile(&lib->log_file, log_line);
Dqn_Fs_WriteFile(&lib->log_file, DQN_STR8("\n")); Dqn_OS_WriteFile(&lib->log_file, DQN_STR8("\n"));
Dqn_TicketMutex_End(&lib->log_file_mutex); Dqn_TicketMutex_End(&lib->log_file_mutex);
} }

View File

@ -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_STRINGIFY(x) #x
#define DQN_TOKEN_COMBINE2(x, y) x ## y #define DQN_TOKEN_COMBINE2(x, y) x ## y
#define DQN_TOKEN_COMBINE(x, y) DQN_TOKEN_COMBINE2(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 // NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER
#if defined(_MSC_VER) #if defined(_MSC_VER)
#if defined(__clang__) #if defined(__clang__)
@ -39,14 +61,31 @@
#if defined(_WIN32) #if defined(_WIN32)
#define DQN_OS_WIN32 #define DQN_OS_WIN32
#elif defined(__linux__) #elif defined(__gnu_linux__) || defined(__linux__)
#define DQN_OS_UNIX #define DQN_OS_UNIX
#endif #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) #if defined(__aarch64__) || defined(_M_ARM64)
#define DQN_PLATFORM_ARM64 #define DQN_PLATFORM_ARM64
#elif defined(__EMSCRIPTEN__) #elif defined(__EMSCRIPTEN__)
#define DQN_PLATFORM_EMSCRIPTEN #define DQN_PLATFORM_EMSCRIPTEN
#elif defined(DQN_OS_WIN32)
#define DQN_PLATFORM_WIN32
#else
#define DQN_PLATFORM_POSIX
#endif
#endif #endif
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL) #if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
@ -81,33 +120,23 @@
#define DQN_GCC_WARNING_POP #define DQN_GCC_WARNING_POP
#endif #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_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_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_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_IsPowerOfTwo(value) ((((uintptr_t)(value)) & (((uintptr_t)(value)) - 1)) == 0)
#define Dqn_IsPowerOfTwoAligned(value, pot) ((((uintptr_t)value) & (((uintptr_t)pot) - 1)) == 0) #define Dqn_IsPowerOfTwoAligned(value, pot) ((((uintptr_t)value) & (((uintptr_t)pot) - 1)) == 0)
// NOTE: Alloc Macros ============================================================================== // NOTE: String.h Dependencies /////////////////////////////////////////////////////////////////////
#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 =====================================================================
#if !defined(DQN_MEMCPY) || !defined(DQN_MEMSET) || !defined(DQN_MEMCMP) || !defined(DQN_MEMMOVE) #if !defined(DQN_MEMCPY) || !defined(DQN_MEMSET) || !defined(DQN_MEMCMP) || !defined(DQN_MEMMOVE)
#include <string.h> #include <string.h>
#if !defined(DQN_MEMCPY) #if !defined(DQN_MEMCPY)
@ -124,10 +153,12 @@
#endif #endif
#endif #endif
// NOTE: Math.h Dependencies ======================================================================= // NOTE: Math.h Dependencies ///////////////////////////////////////////////////////////////////////
#if !defined(DQN_SQRTF) || !defined(DQN_SINF) || !defined(DQN_COSF) || !defined(DQN_TANF) #if !defined(DQN_SQRTF) || !defined(DQN_SINF) || !defined(DQN_COSF) || !defined(DQN_TANF)
#include <math.h> #include <math.h>
#if !defined(DQN_SQRTF)
#define DQN_SQRTF(val) sqrtf(val) #define DQN_SQRTF(val) sqrtf(val)
#endif
#if !defined(DQN_SINF) #if !defined(DQN_SINF)
#define DQN_SINF(val) sinf(val) #define DQN_SINF(val) sinf(val)
#endif #endif
@ -139,11 +170,7 @@
#endif #endif
#endif #endif
#if !defined(DQN_OS_WIN32) // NOTE: Math //////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h> // exit()
#endif
// NOTE: Math Macros ===============================================================================
#define DQN_PI 3.14159265359f #define DQN_PI 3.14159265359f
#define DQN_DEGREE_TO_RADIAN(degrees) ((degrees) * (DQN_PI / 180.0f)) #define DQN_DEGREE_TO_RADIAN(degrees) ((degrees) * (DQN_PI / 180.0f))
@ -163,7 +190,7 @@
b = temp; \ b = temp; \
} while (0) } while (0)
// NOTE: Function/Variable Annotations ============================================================= // NOTE: Function/Variable Annotations /////////////////////////////////////////////////////////////
#if defined(DQN_STATIC_API) #if defined(DQN_STATIC_API)
#define DQN_API static #define DQN_API static
#else #else
@ -180,86 +207,88 @@
#define DQN_FORCE_INLINE inline __attribute__((always_inline)) #define DQN_FORCE_INLINE inline __attribute__((always_inline))
#endif #endif
// NOTE: Size Macros =============================================================================== // NOTE: Size //////////////////////////////////////////////////////////////////////////////////////
#define DQN_ISIZEOF(val) DQN_CAST(ptrdiff_t)sizeof(val) #define DQN_ISIZEOF(val) DQN_CAST(ptrdiff_t)sizeof(val)
#define DQN_ARRAY_UCOUNT(array) (sizeof(array)/(sizeof((array)[0]))) #define DQN_ARRAY_UCOUNT(array) (sizeof(array)/(sizeof((array)[0])))
#define DQN_ARRAY_ICOUNT(array) (Dqn_isize)DQN_ARRAY_UCOUNT(array) #define DQN_ARRAY_ICOUNT(array) (Dqn_isize)DQN_ARRAY_UCOUNT(array)
#define DQN_CHAR_COUNT(string) (sizeof(string) - 1) #define DQN_CHAR_COUNT(string) (sizeof(string) - 1)
// NOTE: SI Byte Macros ============================================================================ // NOTE: SI Byte ///////////////////////////////////////////////////////////////////////////////////
#define DQN_BYTES(val) (val) #define DQN_BYTES(val) (val)
#define DQN_KILOBYTES(val) (1024ULL * DQN_BYTES(val)) #define DQN_KILOBYTES(val) (1024ULL * DQN_BYTES(val))
#define DQN_MEGABYTES(val) (1024ULL * DQN_KILOBYTES(val)) #define DQN_MEGABYTES(val) (1024ULL * DQN_KILOBYTES(val))
#define DQN_GIGABYTES(val) (1024ULL * DQN_MEGABYTES(val)) #define DQN_GIGABYTES(val) (1024ULL * DQN_MEGABYTES(val))
// NOTE: Time Macros =============================================================================== // NOTE: Time //////////////////////////////////////////////////////////////////////////////////////
#define DQN_SECONDS_TO_MS(val) ((val) * 1000) #define DQN_SECONDS_TO_MS(val) ((val) * 1000)
#define DQN_MINS_TO_S(val) ((val) * 60ULL) #define DQN_MINS_TO_S(val) ((val) * 60ULL)
#define DQN_HOURS_TO_S(val) (DQN_MINS_TO_S(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_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(DQN_DEBUG_BREAK)
#if defined(NDEBUG) #if defined(NDEBUG)
#define DQN_DEBUG_BREAK #define DQN_DEBUG_BREAK
#else #else
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL) #if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
#define DQN_DEBUG_BREAK __debugbreak() #define DQN_DEBUG_BREAK __debugbreak()
#elif defined(DQN_COMPILER_CLANG) #elif DQN_HAS_BUILTIN(__builtin_debugtrap)
#define DQN_DEBUG_BREAK __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> #include <signal.h>
#if defined(SIGTRAP)
#define DQN_DEBUG_BREAK raise(SIGTRAP) #define DQN_DEBUG_BREAK raise(SIGTRAP)
#elif #else
#error "Unhandled compiler" #define DQN_DEBUG_BREAK raise(SIGABRT)
#endif
#endif #endif
#endif #endif
#endif #endif
#if !defined(DQN_MEMSET_BYTE) // NOTE: Assert Macros /////////////////////////////////////////////////////////////////////////////
#define DQN_MEMSET_BYTE 0
#endif
// NOTE: Assert Macros =============================================================================
#define DQN_HARD_ASSERT(expr) DQN_HARD_ASSERTF(expr, "") #define DQN_HARD_ASSERT(expr) DQN_HARD_ASSERTF(expr, "")
#define DQN_HARD_ASSERTF(expr, fmt, ...) \ #define DQN_HARD_ASSERTF(expr, fmt, ...) \
do { \
if (!(expr)) { \ if (!(expr)) { \
Dqn_Log_ErrorF("Hard assert triggered [" #expr "]. " fmt, ##__VA_ARGS__); \ Dqn_Str8 stack_trace_ = Dqn_StackTrace_WalkStr8CRT(128 /*limit*/, 2 /*skip*/); \
Dqn_StackTrace_Print(128 /*limit*/); \ 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; \ DQN_DEBUG_BREAK; \
} } \
} while (0)
#if defined(DQN_NO_ASSERT) #if defined(DQN_NO_ASSERT)
#define DQN_ASSERTF(...) #define DQN_ASSERTF(...)
#define DQN_ASSERT(...) #define DQN_ASSERT(...)
#else #else
#define DQN_ASSERT(expr) DQN_ASSERTF(expr, "") #define DQN_ASSERT(expr) DQN_ASSERTF((expr), "")
#define DQN_ASSERTF(expr, fmt, ...) \ #define DQN_ASSERTF(expr, fmt, ...) \
do { \
if (!(expr)) { \ if (!(expr)) { \
Dqn_Log_ErrorF("Assert triggered [" #expr "]. " fmt, ##__VA_ARGS__); \ Dqn_Str8 stack_trace_ = Dqn_StackTrace_WalkStr8CRT(128 /*limit*/, 2 /*skip*/); \
Dqn_StackTrace_Print(128 /*limit*/); \ Dqn_Log_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DQN_STR_FMT(stack_trace_), \
##__VA_ARGS__); \
DQN_DEBUG_BREAK; \ DQN_DEBUG_BREAK; \
} } \
} while (0)
#endif #endif
#define DQN_INVALID_CODE_PATHF(fmt, ...) DQN_ASSERTF(0, fmt, ##__VA_ARGS__) #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") #define DQN_INVALID_CODE_PATH DQN_INVALID_CODE_PATHF("Invalid code path triggered")
// NOTE: Check macro =============================================================================== // 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
#define DQN_CHECK(expr) DQN_CHECKF(expr, "") #define DQN_CHECK(expr) DQN_CHECKF(expr, "")
#if defined(DQN_NO_CHECK_BREAK) #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)) ((expr) ? true : (Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, ## __VA_ARGS__), false))
#else #else
#define DQN_CHECKF(expr, fmt, ...) \ #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 #endif
// NOTE: Zero initialisation macro ================================================================= // NOTE: Zero initialisation macro /////////////////////////////////////////////////////////////////
#if defined(__cplusplus) #if defined(__cplusplus)
#define DQN_ZERO_INIT {} #define DQN_ZERO_INIT {}
#else #else
#define DQN_ZERO_INIT {0} #define DQN_ZERO_INIT {0}
#endif #endif
// NOTE: Defer Macro =============================================================================== // NOTE: Defer Macro ///////////////////////////////////////////////////////////////////////////////
#if defined(__cplusplus) #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> template <typename Procedure>
struct Dqn_Defer struct Dqn_Defer
{ {
@ -316,7 +331,7 @@ struct Dqn_DeferHelper
DQN_UNIQUE_NAME(once); \ DQN_UNIQUE_NAME(once); \
end, DQN_UNIQUE_NAME(once) = false) end, DQN_UNIQUE_NAME(once) = false)
// NOTE: [$TYPE] Types ============================================================================= // NOTE: [$TYPE] Types /////////////////////////////////////////////////////////////////////////////
typedef intptr_t Dqn_isize; typedef intptr_t Dqn_isize;
typedef uintptr_t Dqn_usize; typedef uintptr_t Dqn_usize;
typedef intptr_t Dqn_isize; typedef intptr_t Dqn_isize;
@ -350,7 +365,6 @@ struct Dqn_Str8
char *end () { return data + size; } char *end () { return data + size; }
}; };
#if !defined(DQN_NO_SLICE)
template <typename T> struct Dqn_Slice // A pointer and length container of data template <typename T> struct Dqn_Slice // A pointer and length container of data
{ {
T *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 *begin() const { return data; }
T const *end () const { return data + size; } T const *end () const { return data + size; }
}; };
#endif
// NOTE: [$CALL] Dqn_CallSite ====================================================================== // NOTE: [$CALL] Dqn_CallSite //////////////////////////////////////////////////////////////////////
struct Dqn_CallSite struct Dqn_CallSite
{ {
Dqn_Str8 file; Dqn_Str8 file;
Dqn_Str8 function; Dqn_Str8 function;
unsigned int line; uint32_t line;
}; };
#define DQN_CALL_SITE Dqn_CallSite{DQN_STR8(__FILE__), DQN_STR8(__func__), __LINE__} #define DQN_CALL_SITE Dqn_CallSite{DQN_STR8(__FILE__), DQN_STR8(__func__), __LINE__}
// NOTE: [$INTR] Intrinsics ======================================================================== // 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: Dqn_Atomic_Add/Exchange return the previous value store in the target // NOTE: Dqn_Atomic_Add/Exchange return the previous value store in the target
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL) #if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
#include <intrin.h> #include <intrin.h>
@ -422,118 +421,21 @@ struct Dqn_CallSite
#error "Compiler not supported" #error "Compiler not supported"
#endif #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) #if !defined(DQN_PLATFORM_ARM64)
struct Dqn_CPUIDRegisters 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 #endif // DQN_PLATFORM_ARM64
// NOTE: [$TMUT] Dqn_TicketMutex =================================================================== // 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
struct Dqn_TicketMutex struct Dqn_TicketMutex
{ {
unsigned int volatile ticket; // The next ticket to give out to the thread taking the mutex 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 unsigned int volatile serving; // The ticket ID to block the mutex on until it is returned
}; };
void Dqn_TicketMutex_Begin (Dqn_TicketMutex *mutex); // NOTE: [$PRIN] Dqn_Print /////////////////////////////////////////////////////////////////////////
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 =========================================================================
enum Dqn_PrintStd enum Dqn_PrintStd
{ {
Dqn_PrintStd_Out, Dqn_PrintStd_Out,
@ -559,12 +461,45 @@ enum Dqn_PrintESCColour
Dqn_PrintESCColour_Bg, 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_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_StyleColourU32 (uint32_t rgb, Dqn_PrintBold bold);
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold (); 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(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_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) #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_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_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); #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_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_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); 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_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); 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 ===================================================================== // NOTE: ANSI Formatting Codes /////////////////////////////////////////////////////////////////////
Dqn_Str8 Dqn_Print_ESCColourStr8 (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b); DQN_API 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); 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_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) #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_ESCBold "\x1b[1m"
#define Dqn_Print_ESCResetStr8 DQN_STR8(Dqn_Print_ESCReset) #define Dqn_Print_ESCResetStr8 DQN_STR8(Dqn_Print_ESCReset)
#define Dqn_Print_ESCBoldStr8 DQN_STR8(Dqn_Print_ESCBold) #define Dqn_Print_ESCBoldStr8 DQN_STR8(Dqn_Print_ESCBold)
// NOTE: [$LLOG] Dqn_Log ///////////////////////////////////////////////////////////////////////////
// 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
#define Dqn_LogTypeColourU32_Info 0x00'87'ff'ff // Blue #define Dqn_LogTypeColourU32_Info 0x00'87'ff'ff // Blue
#define Dqn_LogTypeColourU32_Warning 0xff'ff'00'ff // Yellow #define Dqn_LogTypeColourU32_Warning 0xff'ff'00'ff // Yellow
#define Dqn_LogTypeColourU32_Error 0xff'00'00'ff // Red #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_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_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_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_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_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_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_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_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_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_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_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__) #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_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_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_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, ...); 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
}

View File

@ -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) #if !defined(DQN_NO_DSMAP)
// NOTE: [$DMAP] Dqn_DSMap ========================================================================= // NOTE: [$DMAP] Dqn_DSMap /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash(uint64_t u64) DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash(uint64_t u64)
{ {
Dqn_DSMapKey result = {}; Dqn_DSMapKey result = {};

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,27 @@
#if !defined(DQN_CPP_BUILD_H) #if !defined(DQN_CPP_BUILD_H)
#define 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 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 input_file_path;
Dqn_Str8 output_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_OBJ = DQN_STR8(".obj");
Dqn_Str8 const DQN_CPP_BUILD_OBJ_SUFFIX_O = DQN_STR8(".o"); Dqn_Str8 const DQN_CPP_BUILD_OBJ_SUFFIX_O = DQN_STR8(".o");
enum Dqn_CPPBuildCompiler enum Dqn_CPPBuildFlagsStyle
{ {
Dqn_CPPBuildCompiler_MSVC, Dqn_CPPBuildFlagsStyle_MSVC,
Dqn_CPPBuildCompiler_GCC, Dqn_CPPBuildFlagsStyle_GCC,
Dqn_CPPBuildFlagsStyle_CLANG,
};
enum Dqn_CPPBuildAppendCompilerToCommand
{
Dqn_CPPBuildAppendCompilerToCommand_No,
Dqn_CPPBuildAppendCompilerToCommand_Yes,
}; };
struct Dqn_CPPBuildContext 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_Str8 compile_file_obj_suffix;
Dqn_Slice<Dqn_CPPBuildCompileFile> compile_files; Dqn_Slice<Dqn_CPPBuildCompileFile> compile_files;
Dqn_Slice<Dqn_Str8> compile_flags; Dqn_Slice<Dqn_Str8> compile_flags;
@ -46,17 +74,18 @@ enum Dqn_CPPBuildMode
Dqn_CPPBuildMode_CacheBuild, 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 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); DQN_API void Dqn_CPPBuild_ExecOrAbort (Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode);
#endif // DQN_CPP_BUILD_H #endif // DQN_CPP_BUILD_H
#if defined(DQN_CPP_BUILD_IMPLEMENTATION) #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 ================================= // NOTE: Check if object files are newer than the source files /////////////////////////////////
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
Dqn_Str8 result = {}; Dqn_Slice<Dqn_Str8> result = {};
Dqn_Slice<Dqn_CPPBuildCompileFile> dirtied_compile_files = build_context.compile_files; Dqn_Slice<Dqn_CPPBuildCompileFile> dirtied_compile_files = build_context.compile_files;
if (mode == Dqn_CPPBuildMode_CacheBuild) { 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 // NOTE: Create the object file path
Dqn_Str8 file_stem = Dqn_Str8_FileNameNoExtension(file.input_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; Dqn_Str8 obj_file_path = obj_file_name;
if (build_context.build_dir.size) 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_OSPathInfo file_info = Dqn_OS_PathInfo(file.input_file_path);
Dqn_FsInfo obj_file_info = Dqn_Fs_GetInfo(obj_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) if (obj_file_info.last_write_time_in_s >= file_info.last_write_time_in_s)
continue; continue;
@ -96,88 +125,77 @@ DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, D
return result; return result;
} }
// NOTE: Build the command line invocation ===================================================== // NOTE: Build the command line invocation /////////////////////////////////////////////////////
Dqn_Str8Builder builder = {}; Dqn_Str8Builder builder = {};
builder.allocator = allocator; builder.arena = scratch.arena;
DQN_FOR_UINDEX (index, build_context.compile_flags.size) { Dqn_Str8Builder_AppendRefArray(&builder, build_context.compile_flags);
Dqn_Str8 flag = build_context.compile_flags.data[index];
if (index)
Dqn_Str8Builder_AppendF(&builder, " ");
Dqn_Str8Builder_AppendRef(&builder, flag);
}
DQN_FOR_UINDEX(index, build_context.include_dirs.size) { DQN_FOR_UINDEX(index, build_context.include_dirs.size) {
Dqn_Str8 include_dir = build_context.include_dirs.data[index]; Dqn_Str8 include_dir = build_context.include_dirs.data[index];
if (builder.count) Dqn_Str8Builder_AppendRef(&builder, DQN_STR8("-I"));
Dqn_Str8Builder_AppendF(&builder, " "); Dqn_Str8Builder_AppendRef(&builder, include_dir);
Dqn_Str8Builder_AppendF(&builder, "/I %.*s", DQN_STR_FMT(include_dir));
} }
DQN_FOR_UINDEX(index, dirtied_compile_files.size) { DQN_FOR_UINDEX(index, dirtied_compile_files.size) {
Dqn_CPPBuildCompileFile file = dirtied_compile_files.data[index]; Dqn_CPPBuildCompileFile file = dirtied_compile_files.data[index];
Dqn_Str8 obj_file = {}; if (Dqn_Str8_HasData(file.output_file_path)) {
if (builder.count) switch (build_context.flags_style) {
Dqn_Str8Builder_AppendF(&builder, " "); case Dqn_CPPBuildFlagsStyle_MSVC: {
Dqn_Str8Builder_AppendF(&builder, "-Fo%.*s", DQN_STR_FMT(file.output_file_path));
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));
} break; } break;
case Dqn_CPPBuildCompiler_GCC: { case Dqn_CPPBuildFlagsStyle_GCC: /*FALLTHRU*/
Dqn_Str8Builder_AppendF(&builder, "-o %.*s ", DQN_STR_FMT(file.output_file_path)); case Dqn_CPPBuildFlagsStyle_CLANG: {
Dqn_Str8Builder_AppendF (&builder, "-o");
Dqn_Str8Builder_AppendRef(&builder, file.output_file_path);
} break; } break;
} }
} }
DQN_FOR_UINDEX (flag_index, file.flags.size) { // TODO(doyle): Check if the file exists, error if it doesn't
Dqn_Str8 flag = file.flags.data[flag_index];
Dqn_Str8Builder_AppendF(&builder, "%s%.*s", flag_index ? " " : "", DQN_STR_FMT(flag));
}
if (file.flags.size) Dqn_Str8Builder_AppendRefArray(&builder, file.prefix_flags);
Dqn_Str8Builder_AppendF(&builder, " ");
Dqn_Str8Builder_AppendRef(&builder, file.input_file_path); 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_Str8Builder_AppendRefArray(&builder, build_context.link_flags);
Dqn_Str8 file = build_context.link_flags.data[index]; result = Dqn_Str8Builder_BuildSlice(&builder, arena);
if (builder.count)
Dqn_Str8Builder_AppendF(&builder, " ");
Dqn_Str8Builder_AppendRef(&builder, file);
}
result = Dqn_Str8Builder_Build(&builder, allocator);
return result; 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_API Dqn_CPPBuildAsyncResult Dqn_CPPBuild_Async(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode)
{ {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.allocator); Dqn_Slice<Dqn_Str8> cmd_line = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.arena);
Dqn_CPPBuildAsyncResult result = {}; Dqn_CPPBuildAsyncResult result = {};
if (!cmd.size) if (!cmd_line.size)
return result; return result;
if (!Dqn_Fs_MakeDir(build_context.build_dir)) { if (!Dqn_OS_DirMake(build_context.build_dir)) {
result.status = Dqn_CPPBuildStatus_BuildDirectoryFailedToBeMade; result.status = Dqn_CPPBuildStatus_BuildDirectoryFailedToBeMade;
return result; 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; return result;
} }
void Dqn_CPPBuild_ExecOrAbort(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode) 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)); Dqn_Log_ErrorF("Failed to make build dir '%.*s'", DQN_STR_FMT(build_context.build_dir));
exit(-1); exit(-1);
} }
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.allocator); Dqn_Slice<Dqn_Str8> cmd_line = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.arena);
Dqn_OS_ExecOrAbort(cmd, build_context.build_dir); Dqn_OS_ExecOrAbort(cmd_line, build_context.build_dir);
} }
#endif // DQN_CPP_BUILD_IMPLEMENTATION #endif // DQN_CPP_BUILD_IMPLEMENTATION

View File

@ -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) 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); __asan_poison_memory_region(ptr, size);
if (DQN_ASAN_VET_POISON) { if (DQN_ASAN_VET_POISON) {
DQN_HARD_ASSERT(__asan_address_is_poisoned(ptr)); 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) 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); __asan_unpoison_memory_region(ptr, size);
if (DQN_ASAN_VET_POISON) { if (DQN_ASAN_VET_POISON) {
DQN_HARD_ASSERT(__asan_region_is_poisoned((void *)ptr, size) == 0); 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(); HANDLE thread = GetCurrentThread();
result.process = GetCurrentProcess(); 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); SymSetOptions(SYMOPT_LOAD_LINES);
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) { 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_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)); 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.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat; frame.AddrStack.Mode = AddrModeFlat;
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); Dqn_FArray<uint64_t, 256> raw_frames = {};
Dqn_List<uint64_t> raw_frames = Dqn_List_Init<uint64_t>(scratch.arena, 32 /*chunk size*/); while (raw_frames.size < limit) {
while (raw_frames.count < limit) {
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64,
result.process, result.process,
thread, 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. // NOTE: It might be useful one day to use frame.AddrReturn.Offset.
// If AddrPC.Offset == AddrReturn.Offset then we can detect recursion. // 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); Dqn_TicketMutex_End(&mutex);
result.base_addr = Dqn_Arena_NewArray(arena, uint64_t, raw_frames.count, Dqn_ZeroMem_No); result.base_addr = Dqn_Arena_NewArray(arena, uint64_t, raw_frames.size, Dqn_ZeroMem_No);
for (Dqn_ListChunk<uint64_t> *chunk = raw_frames.head; chunk; chunk = chunk->next) { result.size = DQN_CAST(uint16_t)raw_frames.size;
DQN_MEMCPY(result.base_addr + result.size, chunk->data, sizeof(*chunk->data) * chunk->count); DQN_MEMCPY(result.base_addr, raw_frames.data, raw_frames.size * sizeof(raw_frames.data[0]));
result.size += DQN_CAST(uint16_t)chunk->count;
}
#else #else
(void)limit; (void)arena; (void)limit; (void)arena;
#endif #endif
return result; 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; bool result = false;
if (!it || !walk || !walk->base_addr || !walk->process) if (!it || !walk || !walk->base_addr || !walk->process)
@ -105,53 +156,43 @@ DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *
return result; 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) Dqn_Str8 result {};
// NOTE: Get line+filename ===================================================================== if (!walk || !arena)
return result;
// TODO: Why does zero-initialising this with `line = {};` cause Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
// SymGetLineFromAddr64 function to fail once we are at Dqn_Str8Builder builder = {};
// __scrt_commain_main_seh and hit BaseThreadInitThunk frame? The builder.arena = scratch.arena;
// line and file number are still valid in the result which we use, so, Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
// we silently ignore this error. result = Dqn_Str8Builder_Build(&builder, arena);
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
return result; 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_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames(Dqn_Arena *arena, uint16_t limit)
{ {
Dqn_Slice<Dqn_StackTraceFrame> result = {}; Dqn_Slice<Dqn_StackTraceFrame> result = {};
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena); if (!arena)
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, limit); return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, limit);
if (!walk.size) if (!walk.size)
return result; return result;
@ -163,138 +204,173 @@ DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames(Dqn_Arena *arena
return result; 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_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); Dqn_Slice<Dqn_StackTraceFrame> stack_trace = Dqn_StackTrace_GetFrames(scratch.arena, limit);
for (Dqn_StackTraceFrame &frame : stack_trace) 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)); 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 ========================================================================= DQN_API void Dqn_StackTrace_ReloadSymbols()
#if defined(DQN_LEAK_TRACING) {
DQN_API void Dqn_Debug_TrackAlloc_(Dqn_Str8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted) #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) if (!ptr)
return; 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); Dqn_TicketMutex_Begin(&g_dqn_library->alloc_table_mutex);
g_dqn_library->alloc_tracking_disabled = true;
DQN_DEFER { DQN_DEFER {
g_dqn_library->alloc_tracking_disabled = false;
Dqn_TicketMutex_End(&g_dqn_library->alloc_table_mutex); 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. // 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. // TODO: Add API for always making the item but exposing a var to indicate if the item was newly created or it
Dqn_DSMap<Dqn_AllocRecord> *alloc_table = &g_dqn_library->alloc_table; // already existed.
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(alloc_table, DQN_CAST(uintptr_t)ptr); Dqn_Str8 stack_trace = Dqn_StackTrace_WalkStr8CRTNoScratch(128, 3 /*skip*/);
Dqn_AllocRecord *alloc = Dqn_DSMap_Find(alloc_table, key); Dqn_DSMap<Dqn_DebugAlloc> *alloc_table = &g_dqn_library->alloc_table;
if (alloc) { Dqn_DSMapResult<Dqn_DebugAlloc> alloc_entry = Dqn_DSMap_MakeKeyU64(alloc_table, DQN_CAST(uint64_t) ptr);
if ((alloc->flags & Dqn_AllocRecordFlag_Freed) == 0) { Dqn_DebugAlloc *alloc = alloc_entry.value;
Dqn_Str8 alloc_stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size); if (alloc_entry.found) {
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); if ((alloc->flags & Dqn_DebugAllocFlag_Freed) == 0) {
Dqn_Str8 clean_stack_trace = Dqn_Str8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size); 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( DQN_HARD_ASSERTF(
alloc->flags & Dqn_AllocRecordFlag_Freed, alloc->flags & Dqn_DebugAllocFlag_Freed,
"\n\nThis pointer is already in the leak tracker, however it has not " "This pointer is already in the leak tracker, however it has not "
"been freed yet. This same pointer is being ask to be tracked " "been freed yet. This same pointer is being ask to be tracked "
"twice in the allocation table, e.g. one if its previous free " "twice in the allocation table, e.g. one if its previous free "
"calls has not being marked freed with an equivalent call to " "calls has not being marked freed with an equivalent call to "
"Dqn_Debug_TrackDealloc()\n" "Dqn_Debug_TrackDealloc()\n"
"\n" "\n"
"The pointer (0x%p) originally allocated %_$$zu at:\n" "The pointer (0x%p) originally allocated %.*s at:\n"
"\n" "\n"
"%.*s" "%.*s\n"
"\n" "\n"
"The pointer is being allocated again at:\n" "The pointer is allocating %.*s again at:\n"
"%.*s" "\n"
"%.*s\n"
, ,
ptr, alloc->size, ptr, DQN_STR_FMT(alloc_size),
DQN_STR_FMT(alloc_clean_stack_trace), DQN_STR_FMT(alloc->stack_trace),
DQN_STR_FMT(clean_stack_trace)); DQN_STR_FMT(new_alloc_size),
DQN_STR_FMT(stack_trace));
} }
// NOTE: Pointer was reused, clean up the prior entry // NOTE: Pointer was reused, clean up the prior entry
free(alloc->stack_trace); free(alloc->stack_trace.data);
free(alloc->freed_stack_trace); free(alloc->freed_stack_trace.data);
*alloc = {}; *alloc = {};
} else {
alloc = Dqn_DSMap_Make(alloc_table, key, /*found*/ nullptr);
} }
alloc->ptr = ptr; alloc->ptr = ptr;
alloc->size = size; alloc->size = size;
alloc->stack_trace = stack_trace.data; alloc->stack_trace = stack_trace;
alloc->stack_trace_size = DQN_CAST(uint16_t)stack_trace.size; alloc->flags |= leak_permitted ? Dqn_DebugAllocFlag_LeakPermitted : 0;
// 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;
} }
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; return;
Dqn_TicketMutex_Begin(&g_dqn_library->alloc_table_mutex); Dqn_TicketMutex_Begin(&g_dqn_library->alloc_table_mutex);
DQN_DEFER { Dqn_TicketMutex_End(&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_Str8 stack_trace = Dqn_StackTrace_WalkStr8CRTNoScratch(128, 3 /*skip*/);
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(alloc_table, DQN_CAST(uintptr_t)ptr); Dqn_DSMap<Dqn_DebugAlloc> *alloc_table = &g_dqn_library->alloc_table;
Dqn_AllocRecord *alloc = Dqn_DSMap_Find(alloc_table, key); Dqn_DSMapResult<Dqn_DebugAlloc> alloc_entry = Dqn_DSMap_FindKeyU64(alloc_table, DQN_CAST(uintptr_t) ptr);
DQN_HARD_ASSERTF(alloc_entry.found,
DQN_HARD_ASSERTF(alloc, "Allocated pointer can not be removed as it does not exist in the " "Allocated pointer can not be removed as it does not exist in the "
"allocation table. When this memory was allocated, the pointer was " "allocation table. When this memory was allocated, the pointer was "
"not added to the allocation table [ptr=%p]", "not added to the allocation table [ptr=%p]",
ptr); ptr);
if (alloc->flags & Dqn_AllocRecordFlag_Freed) { Dqn_DebugAlloc *alloc = alloc_entry.value;
Dqn_Str8 alloc_stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size); if (alloc->flags & Dqn_DebugAllocFlag_Freed) {
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 freed_size = Dqn_U64ToByteSizeStr8(alloc_table->arena, alloc->freed_size, Dqn_U64ByteSizeType_Auto);
DQN_HARD_ASSERTF((alloc->flags & Dqn_DebugAllocFlag_Freed) == 0,
Dqn_Str8 alloc_freed_stack_trace = Dqn_Str8_Init(alloc->freed_stack_trace, alloc->freed_stack_trace_size); "Double free detected, pointer to free was already marked "
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 "
"as freed. Either the pointer was reallocated but not " "as freed. Either the pointer was reallocated but not "
"traced, or, the pointer was freed twice.\n" "traced, or, the pointer was freed twice.\n"
"\n" "\n"
"The pointer (0x%p) originally allocated %_$$zu at:\n" "The pointer (0x%p) originally allocated %.*s at:\n"
"\n" "\n"
"%.*s" "%.*s\n"
"\n" "\n"
"The pointer was freed at:\n" "The pointer was freed at:\n"
"\n" "\n"
"%.*s" "%.*s\n"
"\n" "\n"
"The pointer is being freed again at:\n" "The pointer is being freed again at:\n"
"%.*s" "\n"
"%.*s\n"
, ,
ptr, alloc->freed_size, ptr, DQN_STR_FMT(freed_size),
DQN_STR_FMT(alloc_clean_stack_trace), DQN_STR_FMT(alloc->stack_trace),
DQN_STR_FMT(alloc_freed_clean_stack_trace), DQN_STR_FMT(alloc->freed_stack_trace),
DQN_STR_FMT(dealloc_stack_trace)); DQN_STR_FMT(stack_trace));
} }
alloc->flags |= Dqn_AllocRecordFlag_Freed; DQN_ASSERT(!Dqn_Str8_HasData(alloc->freed_stack_trace));
alloc->freed_size = alloc->size; alloc->flags |= Dqn_DebugAllocFlag_Freed;
alloc->freed_stack_trace = stack_trace.data; alloc->freed_stack_trace = stack_trace;
alloc->freed_stack_trace_size = DQN_CAST(uint16_t)stack_trace.size;
} }
DQN_API void Dqn_Debug_DumpLeaks() DQN_API void Dqn_Debug_DumpLeaks()
@ -302,25 +378,24 @@ DQN_API void Dqn_Debug_DumpLeaks()
uint64_t leak_count = 0; uint64_t leak_count = 0;
uint64_t leaked_bytes = 0; uint64_t leaked_bytes = 0;
for (Dqn_usize index = 1; index < g_dqn_library->alloc_table.occupied; index++) { 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_DSMapSlot<Dqn_DebugAlloc> *slot = g_dqn_library->alloc_table.slots + index;
Dqn_AllocRecord *alloc = &slot->value; Dqn_DebugAlloc *alloc = &slot->value;
bool alloc_leaked = (alloc->flags & Dqn_AllocRecordFlag_Freed) == 0; bool alloc_leaked = (alloc->flags & Dqn_DebugAllocFlag_Freed) == 0;
bool leak_permitted = (alloc->flags & Dqn_AllocRecordFlag_LeakPermitted); bool leak_permitted = (alloc->flags & Dqn_DebugAllocFlag_LeakPermitted);
if (alloc_leaked && !leak_permitted) { if (alloc_leaked && !leak_permitted) {
leaked_bytes += alloc->size; leaked_bytes += alloc->size;
leak_count++; leak_count++;
Dqn_Str8 alloc_size = Dqn_U64ToByteSizeStr8(g_dqn_library->alloc_table.arena, alloc->size, Dqn_U64ByteSizeType_Auto);
Dqn_Str8 stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size); Dqn_Log_WarningF("Pointer (0x%p) leaked %.*s at:\n"
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"
"%.*s", "%.*s",
alloc->ptr, alloc->size, alloc->ptr, DQN_STR_FMT(alloc_size),
DQN_STR_FMT(clean_stack_trace)); DQN_STR_FMT(alloc->stack_trace));
} }
} }
if (leak_count) { 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

View File

@ -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) #if !defined(DQN_ASAN_POISON)
#define DQN_ASAN_POISON 0 #define DQN_ASAN_POISON 0
#endif #endif
@ -7,6 +29,7 @@
#endif #endif
#define DQN_ASAN_POISON_ALIGNMENT 8 #define DQN_ASAN_POISON_ALIGNMENT 8
#if !defined(DQN_ASAN_POISON_GUARD_SIZE) #if !defined(DQN_ASAN_POISON_GUARD_SIZE)
#define DQN_ASAN_POISON_GUARD_SIZE 128 #define DQN_ASAN_POISON_GUARD_SIZE 128
#endif #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" "ASAN poison guard size must be a power-of-two and aligned to ASAN's alignment"
"requirement (8 bytes)"); "requirement (8 bytes)");
// NOTE: MSVC does not support the feature detection macro for instance so we #if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
// 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__)
#include <sanitizer/asan_interface.h> #include <sanitizer/asan_interface.h>
#endif #endif
DQN_API void Dqn_ASAN_PoisonMemoryRegion(void const volatile *ptr, Dqn_usize size); // NOTE: [$STKT] Dqn_StackTrace ////////////////////////////////////////////////////////////////////
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).
struct Dqn_StackTraceFrame struct Dqn_StackTraceFrame
{ {
uint64_t address; uint64_t address;
@ -88,45 +69,52 @@ struct Dqn_StackTraceWalkResultIterator
uint16_t index; uint16_t index;
}; };
DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk (Dqn_Arena *arena, uint16_t limit); // NOTE: [$DEBG] Dqn_Debug /////////////////////////////////////////////////////////////////////////
DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *it, Dqn_StackTraceWalkResult *walk); enum Dqn_DebugAllocFlag
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
{ {
Dqn_AllocRecordFlag_Freed = 1 << 0, Dqn_DebugAllocFlag_Freed = 1 << 0,
Dqn_AllocRecordFlag_LeakPermitted = 1 << 1, Dqn_DebugAllocFlag_LeakPermitted = 1 << 1,
}; };
struct Dqn_AllocRecord struct Dqn_DebugAlloc
{ {
void *ptr; // Pointer to the allocation being tracked void *ptr; // 8 Pointer to the allocation being tracked
Dqn_usize size; // Size of the allocation Dqn_usize size; // 16 Size of the allocation
Dqn_usize freed_size; // Store the size of the allocation when it is freed Dqn_usize freed_size; // 24 Store the size of the allocation when it is freed
char *stack_trace; // Stack trace at the point of allocation Dqn_Str8 stack_trace; // 40 Stack trace at the point of allocation
char *freed_stack_trace; // Stack trace of where the allocation was freed Dqn_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
uint16_t stack_trace_size; // Size of the `stack_trace` uint16_t flags; // 72 Bit flags from `Dqn_DebugAllocFlag`
uint16_t freed_stack_trace_size; // Size of `freed_stack_trace`
uint16_t flags; // Bit flags from `Dqn_AllocRecordFlag`
char padding[2];
}; };
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 " "We aim to keep the allocation record as light as possible as "
"memory tracking can get expensive. Enforce that there is no " "memory tracking can get expensive. Enforce that there is no "
"unexpected padding."); "unexpected padding.");
#if defined(DQN_LEAK_TRACING) // NOTE: [$ASAN] Dqn_Asan //////////////////////////////////////////////////////////////////////////
#define Dqn_Debug_TrackAlloc(ptr, size, leak_permitted) Dqn_Debug_TrackAlloc_ (Dqn_Str8_InitCString8(b_stacktrace_get_string()), ptr, size, leak_permitted) DQN_API void Dqn_ASAN_PoisonMemoryRegion (void const volatile *ptr, Dqn_usize size);
#define Dqn_Debug_TrackDealloc(ptr) Dqn_Debug_TrackDealloc_(Dqn_Str8_InitCString8(b_stacktrace_get_string()), ptr) 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); // NOTE: [$STKT] Dqn_StackTrace ////////////////////////////////////////////////////////////////////
DQN_API void Dqn_Debug_TrackDealloc_(Dqn_Str8 stack_trace, void *ptr); 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 (); DQN_API void Dqn_Debug_DumpLeaks ();
#else #else
#define Dqn_Debug_TrackAlloc(...) #define Dqn_Debug_TrackAlloc(ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
#define Dqn_Debug_TrackDealloc(...) #define Dqn_Debug_TrackDealloc(ptr) do { (void)ptr; } while (0)
#define Dqn_Debug_DumpLeaks(...) #define Dqn_Debug_DumpLeaks() do { } while (0)
#endif #endif

1091
dqn_docs.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 #define STB_SPRINTF_IMPLEMENTATION
#ifdef 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); return (stbsp__uint32)(sn - s);
} }
#if defined(__clang__) #if defined(__clang__)
__attribute__((no_sanitize("undefined"))) __attribute__((no_sanitize("undefined")))
#endif #endif
@ -1160,7 +1174,7 @@ done:
#undef stbsp__flush_cb #undef stbsp__flush_cb
#undef stbsp__cb_buf_clamp #undef stbsp__cb_buf_clamp
// ============================================================================ // ////////////////////////////////////////////////////////////////////////////
// wrapper functions // wrapper functions
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) 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); return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
} }
// ======================================================================= // ///////////////////////////////////////////////////////////////////////
// low level float utility functions // low level float utility functions
#ifndef STB_SPRINTF_NOFLOAT #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. 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)

View File

@ -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) #if defined(DQN_OS_UNIX) || defined(DQN_PLATFORM_EMSCRIPTEN)
#include <errno.h> // errno #include <errno.h> // errno
#include <fcntl.h> // O_RDONLY ... etc #include <fcntl.h> // O_RDONLY ... etc
@ -18,9 +38,26 @@
#endif #endif
#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 // 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 // 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 // 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. in 16K.
API: API:
==== ////
int stbsp_sprintf( char * buf, char const * fmt, ... ) int stbsp_sprintf( char * buf, char const * fmt, ... )
int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
Convert an arg list into a buffer. stbsp_snprintf always returns 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. Set the comma and period characters to use.
FLOATS/DOUBLES: FLOATS/DOUBLES:
=============== ///////////////
This code uses a internal float->ascii conversion method that uses This code uses a internal float->ascii conversion method that uses
doubles with error correction (double-doubles, for ~105 bits of doubles with error correction (double-doubles, for ~105 bits of
precision). This conversion is round-trip perfect - that is, an atof 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. and you'll save 4K of code space.
64-BIT INTS: 64-BIT INTS:
============ ////////////
This library also supports 64-bit integers and you can use MSVC style or 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 GCC style indicators (%I64d or %lld). It supports the C99 specifiers
for size_t and ptr_diff_t (%jd %zd) as well. for size_t and ptr_diff_t (%jd %zd) as well.
EXTRAS: EXTRAS:
======= ///////
Like some GCCs, for integers and floats, you can use a ' (single quote) 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 specifier and commas will be inserted on the thousands: "%'d" on 12345
would print 12,345. 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. integers in binary: "%b" for 256 would print 100.
PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): 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) "%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) "%24d" across all 32-bit ints (4.5x/4.2x faster)
"%x" across all 32-bit ints (4.5x/3.8x 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 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); STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);
#endif // STB_SPRINTF_H_INCLUDE #endif // STB_SPRINTF_H_INCLUDE
#endif // !defined(DQN_USE_STD_PRINTF)

View File

@ -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/ // 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) 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; return result;
} }
// NOTE: [$MMUR] Dqn_MurmurHash3 =================================================================== // NOTE: [$MMUR] Dqn_MurmurHash3 ///////////////////////////////////////////////////////////////////
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL) #if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
#define DQN_MMH3_ROTL32(x, y) _rotl(x, y) #define DQN_MMH3_ROTL32(x, y) _rotl(x, y)
#define DQN_MMH3_ROTL64(x, y) _rotl64(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; result.e[1] = h2;
return result; return result;
} }

View File

@ -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) #if !defined(DQN_FNV1A32_SEED)
#define DQN_FNV1A32_SEED 2166136261U #define DQN_FNV1A32_SEED 2166136261U
#endif #endif
@ -15,22 +27,16 @@
#define DQN_FNV1A64_SEED 14695981039346656037ULL #define DQN_FNV1A64_SEED 14695981039346656037ULL
#endif #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 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 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 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); DQN_API uint64_t Dqn_FNV1A64_Iterate (void const *bytes, Dqn_usize size, uint64_t hash);
// NOTE: [$MMUR] Dqn_MurmurHash3 =================================================================== // 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]; };
DQN_API uint32_t Dqn_MurmurHash3_x86U32 (void const *key, int len, uint32_t seed); 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); 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]) #define Dqn_MurmurHash3_x64U128AsU64(key, len, seed) (Dqn_MurmurHash3_x64U128(key, len, seed).e[0])

View File

@ -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_MULTIPLIER_64 6364136223846793005ULL
#define DQN_PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL #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) DQN_API uint32_t Dqn_PCG32_Next(Dqn_PCG32 *rng)
{ {
uint64_t state = rng->state; 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; 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) DQN_API void Dqn_PCG32_Advance(Dqn_PCG32 *rng, uint64_t delta)
{ {
uint64_t cur_mult = DQN_PCG_DEFAULT_MULTIPLIER_64; 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) #if !defined(DQN_NO_JSON_BUILDER)
// NOTE: [$JSON] Dqn_JSONBuilder =================================================================== // NOTE: [$JSON] Dqn_JSONBuilder ///////////////////////////////////////////////////////////////////
DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init(Dqn_Allocator allocator, int spaces_per_indent) DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init(Dqn_Arena *arena, int spaces_per_indent)
{ {
Dqn_JSONBuilder result = {}; Dqn_JSONBuilder result = {};
result.spaces_per_indent = spaces_per_indent; result.spaces_per_indent = spaces_per_indent;
result.string_builder.allocator = allocator; result.string_builder.arena = arena;
return result; 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; 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; Dqn_JSONBuilderItem item = Dqn_JSONBuilderItem_KeyValue;
if (value.size == 1) { if (value.size == 1) {
if (value.data[0] == '{' || value.data[0] == '[') { if (value.data[0] == '{' || value.data[0] == '[')
item = Dqn_JSONBuilderItem_OpenContainer; item = Dqn_JSONBuilderItem_OpenContainer;
} else if (value.data[0] == '}' || value.data[0] == ']') { else if (value.data[0] == '}' || value.data[0] == ']')
item = Dqn_JSONBuilderItem_CloseContainer; item = Dqn_JSONBuilderItem_CloseContainer;
} }
}
bool adding_to_container_with_items = item != Dqn_JSONBuilderItem_CloseContainer && bool adding_to_container_with_items =
(builder->last_item == Dqn_JSONBuilderItem_KeyValue || item != Dqn_JSONBuilderItem_CloseContainer && (builder->last_item == Dqn_JSONBuilderItem_KeyValue ||
builder->last_item == Dqn_JSONBuilderItem_CloseContainer); builder->last_item == Dqn_JSONBuilderItem_CloseContainer);
uint8_t prefix_size = 0; 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) { if (key.size) {
Dqn_Str8Builder_AppendF(&builder->string_builder, Dqn_Str8Builder_AppendF(&builder->string_builder,
"%.*s%*c\"%.*s\": %.*s", "%.*s%*c\"%.*s\": %.*s",
prefix_size, prefix, prefix_size,
spaces, ' ', prefix,
spaces,
' ',
DQN_STR_FMT(key), DQN_STR_FMT(key),
DQN_STR_FMT(value)); DQN_STR_FMT(value));
} else { } else {
Dqn_Str8Builder_AppendF(&builder->string_builder, Dqn_Str8Builder_AppendF(
"%.*s%*c%.*s", &builder->string_builder, "%.*s%*c%.*s", prefix_size, prefix, spaces, ' ', DQN_STR_FMT(value));
prefix_size, prefix,
spaces, ' ',
DQN_STR_FMT(value));
} }
if (item == Dqn_JSONBuilderItem_OpenContainer) 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_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_Scratch scratch = Dqn_Scratch_Get(builder->string_builder.arena);
Dqn_Str8 value = Dqn_Str8_InitFV(scratch.allocator, value_fmt, args); Dqn_Str8 value = Dqn_Str8_InitFV(scratch.arena, value_fmt, args);
Dqn_JSONBuilder_KeyValue(builder, key, value); 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]; char float_fmt[16];
if (decimal_places > 0) { if (decimal_places > 0) {
// NOTE: Emit the format string "%.<decimal_places>f" i.e. %.1f // 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 { } else {
// NOTE: Emit the format string "%f" // 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]; char fmt[32];
if (key.size) if (key.size)
STB_SPRINTF_DECORATE(snprintf)(fmt, sizeof(fmt), "\"%%.*s\": %s", float_fmt); DQN_SNPRINTF(fmt, sizeof(fmt), "\"%%.*s\": %s", float_fmt);
else 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); 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) #endif // !defined(DQN_NO_JSON_BUILDER)
#if !defined(DQN_NO_BIN) #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_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)); 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; result.size += DQN_CAST(int8_t) prefix.size;
char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x"; 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; result.size += DQN_CAST(uint8_t) size;
DQN_ASSERT(result.size < DQN_ARRAY_UCOUNT(result.data)); 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; 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 = {}; Dqn_Str8 prefix = {};
if (!(flags & Dqn_BinHexU64Str8Flags_No0xPrefix)) 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"; char const *fmt = (flags & Dqn_BinHexU64Str8Flags_UppercaseHex) ? "%I64X" : "%I64x";
Dqn_usize required_size = Dqn_CStr8_FSize(fmt, number) + prefix.size; 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); DQN_MEMCPY(result.data, prefix.data, prefix.size);
int space = DQN_CAST(int) DQN_MAX((result.size - prefix.size) + 1, 0); /*null-terminator*/ 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; 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; Dqn_Str8 real_hex = Dqn_Str8_TrimPrefix(Dqn_Str8_TrimPrefix(hex, DQN_STR8("0x")), DQN_STR8("0X"));
char const *trim_hex = hex; Dqn_usize max_hex_size = sizeof(uint64_t) * 2 /*hex chars per byte*/;
if (trim_size >= 2) { DQN_ASSERT(real_hex.size <= max_hex_size);
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_usize size = DQN_MIN(max_hex_size, real_hex.size);
uint64_t result = 0; uint64_t result = 0;
Dqn_usize max_size = DQN_MIN(size, 8 /*bytes*/ * 2 /*hex chars per byte*/); for (Dqn_usize index = 0; index < size; index++) {
for (Dqn_usize hex_index = 0; hex_index < max_size; hex_index++) { char ch = real_hex.data[index];
char ch = trim_hex[hex_index];
if (!Dqn_Char_IsHex(ch)) if (!Dqn_Char_IsHex(ch))
break; break;
uint8_t val = Dqn_Char_HexToU8(ch); 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; 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; 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) 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) { if (result) {
bool converted = Dqn_Bin_BytesToHexBuffer(src, size, result, size * 2); bool converted = Dqn_Bin_BytesToHexBuffer(src, size, result, size * 2);
DQN_ASSERT(converted); DQN_ASSERT(converted);
@ -371,9 +379,7 @@ DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes(char const *hex, Dqn_usize hex_size,
return result; return result;
Dqn_usize trim_size = 0; Dqn_usize trim_size = 0;
char const *trim_hex = Dqn_Bin_HexBufferTrim0x(hex, char const *trim_hex = Dqn_Bin_HexBufferTrim0x(hex, hex_size, &trim_size);
hex_size,
&trim_size);
// NOTE: Trimmed hex can be "0xf" -> "f" or "0xAB" -> "AB" // 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 // 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; return result;
} }
result = Dqn_Bin_HexBufferToBytesUnchecked(trim_hex, result = Dqn_Bin_HexBufferToBytesUnchecked(trim_hex, trim_size, dest, dest_size);
trim_size,
dest,
dest_size);
return result; 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; Dqn_usize result = 0;
unsigned char *dest_u8 = DQN_CAST(unsigned char *) dest; unsigned char *dest_u8 = DQN_CAST(unsigned char *) dest;
for (Dqn_usize hex_index = 0; for (Dqn_usize hex_index = 0; hex_index < hex_size; hex_index += 2, result += 1) {
hex_index < hex_size;
hex_index += 2, result += 1)
{
char hex01 = hex[hex_index]; char hex01 = hex[hex_index];
char hex02 = (hex_index + 1 < hex_size) ? hex[hex_index + 1] : 0; 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) #endif // !defined(DQN_NO_BIN)
// NOTE: [$BITS] Dqn_Bit =========================================================================== // NOTE: [$BITS] Dqn_Bit ///////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_Bit_UnsetInplace(uint64_t *flags, uint64_t bitfield) DQN_API void Dqn_Bit_UnsetInplace(Dqn_usize *flags, Dqn_usize bitfield)
{ {
*flags = (*flags & ~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); *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); auto result = DQN_CAST(bool)((bits & bits_to_set) == bits_to_set);
return result; 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); auto result = !Dqn_Bit_IsSet(bits, bits_to_check);
return result; return result;
} }
// NOTE: [$SAFE] Dqn_Safe ========================================================================== // NOTE: [$SAFE] Dqn_Safe //////////////////////////////////////////////////////////////////////////
DQN_API int64_t Dqn_Safe_AddI64(int64_t a, int64_t b) 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; 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; 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 // Both operands are unsigned and the lowest rank operand will be promoted to
// match the highest rank operand. // match the highest rank operand.
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt(uint64_t val) 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; return result;
} }
// NOTE: Dqn_Safe_SaturateCastISizeToI* // NOTE: Dqn_Safe_SaturateCastISizeToI*
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Both operands are signed so the lowest rank operand will be promoted to // 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; return result;
} }
// NOTE: [$MISC] Misc ============================================================================== // NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, DQN_FMT_ATTRIB char const *fmt, ...) DQN_API int Dqn_FmtBuffer3DotTruncate(char *buffer, int size, DQN_FMT_ATTRIB char const *fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); 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); int result = DQN_MAX(DQN_MIN(size_required, size - 1), 0);
if (result == size - 1) { if (result == size - 1) {
buffer[size - 2] = '.'; buffer[size - 2] = '.';
@ -842,7 +852,8 @@ DQN_API Dqn_U64Str8 Dqn_U64ToStr8(uint64_t val, char separator)
// NOTE: Reverse the string // NOTE: Reverse the string
DQN_MSVC_WARNING_PUSH DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE(6293) // Ill-defined for-loop 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--) { for (Dqn_usize temp_index = temp.size - 1; temp_index < temp.size; temp_index--) {
char ch = temp.data[temp_index]; char ch = temp.data[temp_index];
result.data[result.size++] = ch; result.data[result.size++] = ch;
@ -853,7 +864,105 @@ DQN_API Dqn_U64Str8 Dqn_U64ToStr8(uint64_t val, char separator)
return result; 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_Library *g_dqn_library;
DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init) 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; g_dqn_library = &default_instance;
} }
// NOTE: Init check =========================================================================== // NOTE: Init check ///////////////////////////////////////////////////////////////////////////
Dqn_Library *result = g_dqn_library; Dqn_Library *result = g_dqn_library;
Dqn_TicketMutex_Begin(&result->lib_mutex); 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) if (result->lib_init)
return result; return result;
result->lib_init = true;
// NOTE: Query OS page size ==================================================================== // NOTE: Query OS info /////////////////////////////////////////////////////////////////////////
{ {
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
SYSTEM_INFO system_info = {}; SYSTEM_INFO system_info = {};
GetSystemInfo(&system_info); GetSystemInfo(&system_info);
result->os_page_size = system_info.dwPageSize; result->os_page_size = system_info.dwPageSize;
result->os_alloc_granularity = system_info.dwAllocationGranularity; result->os_alloc_granularity = system_info.dwAllocationGranularity;
QueryPerformanceFrequency(&result->win32_qpc_frequency);
#else #else
// TODO(doyle): Get the proper page size from the OS. // TODO(doyle): Get the proper page size from the OS.
result->os_page_size = DQN_KILOBYTES(4); result->os_page_size = DQN_KILOBYTES(4);
@ -886,60 +999,77 @@ DQN_API Dqn_Library *Dqn_Library_Init(Dqn_LibraryOnInit on_init)
#endif #endif
} }
// NOTE Initialise fields ======================================================================
// NOTE Initialise fields //////////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_PROFILER) #if !defined(DQN_NO_PROFILER)
result->profiler = &result->profiler_default_instance; result->profiler = &result->profiler_default_instance;
#endif #endif
result->lib_init = true; // NOTE: BEGIN IMPORTANT ORDER OF STATEMENTS ///////////////////////////////////////////////////
Dqn_ArenaCatalog_Init(&result->arena_catalog, &result->arena); #if defined(DQN_LEAK_TRACKING)
result->exe_dir = Dqn_OS_EXEDir(&result->arena); // NOTE: Setup the allocation table with allocation tracking turned off on
// the arena we're using to initialise the table.
// NOTE: Leak tracing ========================================================================== result->alloc_table_arena.flags |= Dqn_ArenaFlag_NoAllocTrack;
result->alloc_table = Dqn_DSMap_Init<Dqn_DebugAlloc>(&result->alloc_table_arena, 4096);
#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;
}
#endif #endif
// NOTE: Print out init features =============================================================== result->arena = Dqn_Arena_InitSize(0, 0, Dqn_ArenaFlag_AllocCanLeak);
if (on_init == Dqn_LibraryOnInit_LogFeatures) { result->pool = Dqn_ChunkPool_Init(&result->arena, /*align*/ 0);
Dqn_Log_DebugF("Dqn Library initialised:\n"); Dqn_ArenaCatalog_Init(&result->arena_catalog, &result->pool);
Dqn_ArenaCatalog_AddF(&result->arena_catalog, &result->arena, "Dqn Library");
// 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
#if defined(DQN_LEAK_TRACING) #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 #endif
#if !defined(DQN_NO_PROFILER) #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 #endif
// TODO(doyle): Add stacktrace feature log // 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; return result;
} }
@ -990,8 +1120,10 @@ DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_Str8 file_path)
Dqn_DateHMSTimeStr now = Dqn_Date_HMSLocalTimeStrNow(); Dqn_DateHMSTimeStr now = Dqn_Date_HMSLocalTimeStrNow();
fprintf(file, fprintf(file,
"Time=%.*s %.*s | Thread Context Arenas | Count=%d\n", "Time=%.*s %.*s | Thread Context Arenas | Count=%d\n",
now.date_size, now.date, now.date_size,
now.hms_size, now.hms, now.date,
now.hms_size,
now.hms,
g_dqn_library->thread_context_arena_stats_count); g_dqn_library->thread_context_arena_stats_count);
// NOTE: Write the cumulative thread arena data // 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) #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) #if !defined(DQN_NO_PROFILER)
// NOTE: [$PROF] Dqn_Profiler ====================================================================== // NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
Dqn_ProfilerZoneScope::Dqn_ProfilerZoneScope(Dqn_Str8 name, uint16_t anchor_index) Dqn_ProfilerZoneScope::Dqn_ProfilerZoneScope(Dqn_Str8 name, uint16_t anchor_index)
{ {
zone = Dqn_Profiler_BeginZoneWithIndex(name, 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) Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer buffer)
{ {
uint8_t offset = buffer == Dqn_ProfilerAnchorBuffer_Back ? 0 : 1; 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]; Dqn_ProfilerAnchor *result = g_dqn_library->profiler->anchors[anchor_buffer];
return result; return result;
} }
@ -1081,7 +1224,9 @@ void Dqn_Profiler_SwapAnchorBuffer()
{ {
g_dqn_library->profiler->active_anchor_buffer++; g_dqn_library->profiler->active_anchor_buffer++;
Dqn_ProfilerAnchor *anchors = Dqn_Profiler_AnchorBuffer(Dqn_ProfilerAnchorBuffer_Back); 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) 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; uint64_t tsc_inclusive = anchor->tsc_inclusive;
Dqn_f64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DQN_CAST(Dqn_f64) tsc_per_second; Dqn_f64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DQN_CAST(Dqn_f64) tsc_per_second;
if (tsc_exclusive == tsc_inclusive) { if (tsc_exclusive == tsc_inclusive) {
Dqn_Print_LnF("%.*s[%u]: %.1fms", Dqn_Print_LnF("%.*s[%u]: %.1fms", DQN_STR_FMT(anchor->name), anchor->hit_count, tsc_exclusive_milliseconds);
DQN_STR_FMT(anchor->name),
anchor->hit_count,
tsc_exclusive_milliseconds);
} else { } else {
Dqn_f64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DQN_CAST(Dqn_f64) tsc_per_second; Dqn_f64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DQN_CAST(Dqn_f64) tsc_per_second;
Dqn_Print_LnF("%.*s[%u]: %.1f/%.1fms", 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) #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);
}

View File

@ -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; }; 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) #if !defined(DQN_NO_JSON_BUILDER)
// NOTE: [$JSON] Dqn_JSONBuilder =================================================================== // 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>'
enum Dqn_JSONBuilderItem enum Dqn_JSONBuilderItem
{ {
Dqn_JSONBuilderItem_Empty, Dqn_JSONBuilderItem_Empty,
@ -71,138 +46,10 @@ struct Dqn_JSONBuilder
int spaces_per_indent; // The number of spaces per indent level int spaces_per_indent; // The number of spaces per indent level
Dqn_JSONBuilderItem last_item; 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) #endif // !defined(DQN_NO_JSON_BUIDLER)
#if !defined(DQN_NO_BIN) #if !defined(DQN_NO_BIN)
// NOTE: [$BHEX] Dqn_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.
struct Dqn_BinHexU64Str8 struct Dqn_BinHexU64Str8
{ {
char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/]; 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_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string
Dqn_BinHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex 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) #endif // !defined(DQN_NO_BIN)
// NOTE: [$BSEA] Dqn_BinarySearch ================================================================== // NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
template <typename T> template <typename T>
using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs); using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
template <typename T> template <typename T>
bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
// TODO(doyle): Implement lower/upper bound searching
enum Dqn_BinarySearchType enum Dqn_BinarySearchType
{ {
// Index of the match. If no match is found, found is set to false and the // 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). // [last index + 1] of the array).
Dqn_BinarySearchType_Match, 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 // item is found or the array is empty, then, the index is set to the array
// size and found is set to `false`. // 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, 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 // item is found or the array is empty, then, the index is set to the array
// size and found is set to `false`. // 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, Dqn_BinarySearchType_UpperBound,
}; };
@ -270,287 +107,45 @@ struct Dqn_BinarySearchResult
Dqn_usize index; Dqn_usize index;
}; };
template <typename T> // NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
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.
struct Dqn_U64Str8 struct Dqn_U64Str8
{ {
char data[27+1]; // NOTE(dqn): 27 is the maximum size of uint64_t including a separtor char data[27+1]; // NOTE(dqn): 27 is the maximum size of uint64_t including a separtor
uint8_t size; uint8_t size;
}; };
DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, DQN_FMT_ATTRIB char const *fmt, ...); enum Dqn_U64ByteSizeType
DQN_API Dqn_U64Str8 Dqn_U64ToStr8 (uint64_t val, char separator); {
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) #if !defined(DQN_NO_PROFILER)
// NOTE: [$PROF] Dqn_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.
#if !defined(DQN_PROFILER_ANCHOR_BUFFER_SIZE) #if !defined(DQN_PROFILER_ANCHOR_BUFFER_SIZE)
#define DQN_PROFILER_ANCHOR_BUFFER_SIZE 128 #define DQN_PROFILER_ANCHOR_BUFFER_SIZE 128
#endif #endif
@ -601,81 +196,68 @@ struct Dqn_Profiler
uint8_t active_anchor_buffer; uint8_t active_anchor_buffer;
uint16_t parent_zone; 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) #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 // Book-keeping data for the library and allow customisation of certain features
// provided. // 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 struct Dqn_Library
{ {
bool lib_init; // True if the library has been initialised via `Dqn_Library_Init` bool lib_init; // True if the library has been initialised via `Dqn_Library_Init`
Dqn_TicketMutex lib_mutex; Dqn_TicketMutex lib_mutex;
Dqn_Str8 exe_dir; // The directory of the current executable Dqn_Str8 exe_dir; // The directory of the current executable
Dqn_Arena arena; Dqn_Arena arena;
Dqn_ChunkPool pool; // Uses 'arena' for malloc-like allocations
Dqn_ArenaCatalog arena_catalog; 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 Dqn_LogProc * log_callback; // Set this pointer to override the logging routine
void * log_user_data; void * log_user_data;
bool log_to_file; // Output logs to file as well as standard out 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 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 bool log_no_colour; // Disable colours in the logging output
// NOTE: Leak Tracing //////////////////////////////////////////////////////////////////////////
// NOTE: Leak Tracing ========================================================================== #if defined(DQN_LEAK_TRACKING)
Dqn_DSMap<Dqn_DebugAlloc> alloc_table;
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;
Dqn_TicketMutex alloc_table_mutex; Dqn_TicketMutex alloc_table_mutex;
Dqn_DSMap<Dqn_AllocRecord> alloc_table; Dqn_Arena alloc_table_arena;
#endif #endif
// NOTE: Win32 /////////////////////////////////////////////////////////////////////////////////
// NOTE: OS ====================================================================================
#if defined(DQN_OS_WIN32) #if defined(DQN_OS_WIN32)
LARGE_INTEGER win32_qpc_frequency; LARGE_INTEGER win32_qpc_frequency;
Dqn_TicketMutex win32_bcrypt_rng_mutex; Dqn_TicketMutex win32_bcrypt_rng_mutex;
void * win32_bcrypt_rng_handle; void * win32_bcrypt_rng_handle;
#endif #endif
bool win32_sym_initialised;
// NOTE: OS ////////////////////////////////////////////////////////////////////////////////////
uint32_t os_page_size; uint32_t os_page_size;
uint32_t os_alloc_granularity; uint32_t os_alloc_granularity;
// NOTE: Profiler //////////////////////////////////////////////////////////////////////////////
// NOTE: Profiler ==============================================================================
#if !defined(DQN_NO_PROFILER) #if !defined(DQN_NO_PROFILER)
Dqn_Profiler *profiler; Dqn_Profiler *profiler;
Dqn_Profiler profiler_default_instance; Dqn_Profiler profiler_default_instance;
@ -688,7 +270,174 @@ enum Dqn_LibraryOnInit
Dqn_LibraryOnInit_LogFeatures, 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 Dqn_Library * Dqn_Library_Init (Dqn_LibraryOnInit on_init);
DQN_API void Dqn_Library_SetPointer (Dqn_Library *library); DQN_API void Dqn_Library_SetPointer (Dqn_Library *library);
#if !defined(DQN_NO_PROFILER) #if !defined(DQN_NO_PROFILER)
@ -696,4 +445,58 @@ DQN_API void Dqn_Library_SetProfiler (Dqn_Profiler *profil
#endif #endif
DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data); DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data);
DQN_API void Dqn_Library_DumpThreadContextArenaStat (Dqn_Str8 file_path); 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;
}

View File

@ -1,5 +1,20 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$\ $$\ $$$$$$\ $$$$$$$$\ $$\ $$\
// $$$\ $$$ |$$ __$$\\__$$ __|$$ | $$ |
// $$$$\ $$$$ |$$ / $$ | $$ | $$ | $$ |
// $$\$$\$$ $$ |$$$$$$$$ | $$ | $$$$$$$$ |
// $$ \$$$ $$ |$$ __$$ | $$ | $$ __$$ |
// $$ |\$ /$$ |$$ | $$ | $$ | $$ | $$ |
// $$ | \_/ $$ |$$ | $$ | $$ | $$ | $$ |
// \__| \__|\__| \__| \__| \__| \__|
//
// dqn_math.cpp
//
////////////////////////////////////////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_V2) #if !defined(DQN_NO_V2)
// NOTE: [$VEC2] Vector2 =========================================================================== // NOTE: [$VEC2] Vector2 ///////////////////////////////////////////////////////////////////////////
// NOTE: Dqn_V2I // NOTE: Dqn_V2I
DQN_API bool operator!=(Dqn_V2I lhs, Dqn_V2I rhs) 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; 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 // NOTE: Dqn_V2U16
DQN_API bool operator!=(Dqn_V2U16 lhs, Dqn_V2U16 rhs) 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; 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_API Dqn_V2 operator-(Dqn_V2 lhs, Dqn_V2 rhs)
{ {
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs.x, lhs.y - rhs.y); Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs.x, lhs.y - rhs.y);
return result; 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_API Dqn_V2 operator-(Dqn_V2 lhs, Dqn_f32 rhs)
{ {
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs, lhs.y - rhs); Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs, lhs.y - rhs);
return result; 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; return result;
} }
// NOTE: Dqn_V2 operator+ //////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 operator+(Dqn_V2 lhs, Dqn_V2 rhs) 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); Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs.x, lhs.y + rhs.y);
return result; 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_API Dqn_V2 operator+(Dqn_V2 lhs, Dqn_f32 rhs)
{ {
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs, lhs.y + rhs); Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs, lhs.y + rhs);
return result; 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_API Dqn_V2 operator*(Dqn_V2 lhs, Dqn_V2 rhs)
{ {
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x * rhs.x, lhs.y * rhs.y); Dqn_V2 result = Dqn_V2_InitNx2(lhs.x * rhs.x, lhs.y * rhs.y);
return result; 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_API Dqn_V2 operator*(Dqn_V2 lhs, Dqn_f32 rhs)
{ {
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x * rhs, lhs.y * 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; return result;
} }
// NOTE: Dqn_V2 operator/ //////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 operator/(Dqn_V2 lhs, Dqn_V2 rhs) 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); Dqn_V2 result = Dqn_V2_InitNx2(lhs.x / rhs.x, lhs.y / rhs.y);
return result; 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_API Dqn_V2 operator/(Dqn_V2 lhs, Dqn_f32 rhs)
{ {
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x / rhs, lhs.y / 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; return result;
} }
// NOTE: Dqn_V2 operator*/ /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, Dqn_V2 rhs) DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, Dqn_V2 rhs)
{ {
lhs = lhs * rhs; lhs = lhs * rhs;
return lhs; 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) DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, Dqn_f32 rhs)
{ {
lhs = lhs * rhs; lhs = lhs * rhs;
@ -393,12 +473,19 @@ DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, int32_t rhs)
return lhs; return lhs;
} }
// NOTE: Dqn_V2 operator// /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, Dqn_V2 rhs) DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, Dqn_V2 rhs)
{ {
lhs = lhs / rhs; lhs = lhs / rhs;
return lhs; 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) DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, Dqn_f32 rhs)
{ {
lhs = lhs / rhs; lhs = lhs / rhs;
@ -411,30 +498,56 @@ DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, int32_t rhs)
return lhs; return lhs;
} }
// NOTE: Dqn_V2 operator-/ /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, Dqn_V2 rhs) DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, Dqn_V2 rhs)
{ {
lhs = lhs - rhs; lhs = lhs - rhs;
return lhs; 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) DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, Dqn_f32 rhs)
{ {
lhs = lhs - rhs; lhs = lhs - rhs;
return lhs; 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) DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, Dqn_V2 rhs)
{ {
lhs = lhs + rhs; lhs = lhs + rhs;
return lhs; 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) DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, Dqn_f32 rhs)
{ {
lhs = lhs + rhs; lhs = lhs + rhs;
return lhs; 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_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)); 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) 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` // Scalar projection calculates the signed distance between `b` and `a`
// where `a` is a unit vector then, the dot product calculates the projection // 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 // 2 objects. One of the vectors must be normalised (e.g. turned into a unit
// vector). // vector).
// //
// NOTE: Vector projection ===================================================================== // NOTE: Vector projection /////////////////////////////////////////////////////////////////////
// //
// Vector projection calculates the exact X,Y coordinates of where `b` meets // Vector projection calculates the exact X,Y coordinates of where `b` meets
// `a` when it was projected. This is calculated by multipying the // `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) #endif // !defined(DQN_NO_V2)
#if !defined(DQN_NO_V3) #if !defined(DQN_NO_V3)
// NOTE: [$VEC3] Vector3 =========================================================================== // NOTE: [$VEC3] Vector3 ///////////////////////////////////////////////////////////////////////////
DQN_API bool operator!=(Dqn_V3 lhs, Dqn_V3 rhs) DQN_API bool operator!=(Dqn_V3 lhs, Dqn_V3 rhs)
{ {
bool result = !(lhs == rhs); bool result = !(lhs == rhs);
@ -739,7 +852,7 @@ DQN_API Dqn_V3 Dqn_V3_Normalise(Dqn_V3 a)
#endif // !defined(DQN_NO_V3) #endif // !defined(DQN_NO_V3)
#if !defined(DQN_NO_V4) #if !defined(DQN_NO_V4)
// NOTE: [$VEC4] Vector4 =========================================================================== // NOTE: [$VEC4] Vector4 ///////////////////////////////////////////////////////////////////////////
DQN_API bool operator!=(Dqn_V4 lhs, Dqn_V4 rhs) DQN_API bool operator!=(Dqn_V4 lhs, Dqn_V4 rhs)
{ {
bool result = !(lhs == 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) #endif // !defined(DQN_NO_V4)
#if !defined(DQN_NO_M4) #if !defined(DQN_NO_M4)
// NOTE: [$MAT4] Dqn_M4 ============================================================================ // NOTE: [$MAT4] Dqn_M4 ////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_M4 Dqn_M4_Identity() DQN_API Dqn_M4 Dqn_M4_Identity()
{ {
Dqn_M4 result = 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_API Dqn_M4 Dqn_M4_Transpose(Dqn_M4 mat)
{ {
Dqn_M4 result; Dqn_M4 result = {};
for (int col = 0; col < 4; col++) for (int col = 0; col < 4; col++)
for (int row = 0; row < 4; row++) for (int row = 0; row < 4; row++)
result.columns[col][row] = mat.columns[row][col]; 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
#endif // !defined(DQN_M4) #endif // !defined(DQN_M4)
// NOTE: [$M2x3] Dqn_M2x3 ========================================================================== // 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)
{ {
bool result = DQN_MEMCMP(lhs.e, rhs.e, sizeof(lhs.e[0]) * DQN_ARRAY_UCOUNT(lhs.e)) == 0; 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; 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) // 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`. // != 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 | // | 1 |
Dqn_V2 result = {{ Dqn_V2 result = {{
m1.e[0]*v2.x + m1.e[1]*v2.y + m1.e[2], // a*x + b*y + c*1 m1.e[0]*x + m1.e[1]*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[3]*x + m1.e[4]*y + m1.e[5], // d*x + e*y + f*1
}}; }};
return result; 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) #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) DQN_API bool operator==(const Dqn_Rect& lhs, const Dqn_Rect& rhs)
{ {
bool result = (lhs.pos == rhs.pos) && (lhs.size == rhs.size); 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) #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) 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; 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_API Dqn_V2 Dqn_Lerp_V2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b)
{ {
Dqn_V2 result = {}; Dqn_V2 result = {};

View File

@ -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_PUSH
DQN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union DQN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
#if !defined(DQN_NO_V2) #if !defined(DQN_NO_V2)
// NOTE: [$VEC2] Vector2 =========================================================================== // NOTE: [$VEC2] Vector2 ///////////////////////////////////////////////////////////////////////////
union Dqn_V2I union Dqn_V2I
{ {
struct { int32_t x, y; }; struct { int32_t x, y; };
@ -10,6 +34,111 @@ union Dqn_V2I
int32_t data[2]; 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_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_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}} #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);
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 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);
struct { uint16_t x, y; }; DQN_API Dqn_V2I Dqn_V2I_Abs (Dqn_V2I a);
struct { uint16_t w, h; };
uint16_t data[2];
};
#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_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)}} #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);
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_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_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)}} #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 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_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_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, 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_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, Dqn_f32 rhs);
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, int32_t 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_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, Dqn_f32 rhs);
DQN_API Dqn_V2 operator/ (Dqn_V2 lhs, int32_t 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_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, Dqn_f32 rhs);
DQN_API Dqn_V2 & operator*= (Dqn_V2& lhs, int32_t 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_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, Dqn_f32 rhs);
DQN_API Dqn_V2 & operator/= (Dqn_V2& lhs, int32_t 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_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_Max (Dqn_V2 a, Dqn_V2 b);
DQN_API Dqn_V2 Dqn_V2_Abs (Dqn_V2 a); 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_V2 Dqn_V2_Reflect (Dqn_V2 in, Dqn_V2 surface);
DQN_API Dqn_f32 Dqn_V2_Area (Dqn_V2 a); DQN_API Dqn_f32 Dqn_V2_Area (Dqn_V2 a);
#endif // !defined(DQN_NO_V2) #endif // !defined(DQN_NO_V2)
#if !defined(DQN_NO_V3) #if !defined(DQN_NO_V3)
// NOTE: [$VEC3] Vector3 =========================================================================== // NOTE: [$VEC3] Vector3 ///////////////////////////////////////////////////////////////////////////
union Dqn_V3
{
struct { Dqn_f32 x, y, z; };
struct { Dqn_f32 r, g, b; };
Dqn_f32 data[3];
};
#define Dqn_V3_InitNx1(x) DQN_LITERAL(Dqn_V3){{(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}} #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_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)}} #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, 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_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_LengthSq (Dqn_V3 a);
DQN_API Dqn_f32 Dqn_V3_Length (Dqn_V3 a); DQN_API Dqn_f32 Dqn_V3_Length (Dqn_V3 a);
DQN_API Dqn_V3 Dqn_V3_Normalise (Dqn_V3 a); DQN_API Dqn_V3 Dqn_V3_Normalise (Dqn_V3 a);
#endif // !defined(DQN_NO_V3) #endif // !defined(DQN_NO_V3)
#if !defined(DQN_NO_V4) #if !defined(DQN_NO_V4)
// NOTE: [$VEC4] Vector4 =========================================================================== // 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];
};
#define Dqn_V4_InitNx1(x) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}} #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_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}} #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); 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);
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) #endif // !defined(DQN_NO_V4)
#if !defined(DQN_NO_M4) #if !defined(DQN_NO_M4)
// NOTE: [$MAT4] Dqn_M4 ============================================================================ // NOTE: [$MAT4] Dqn_M4 ////////////////////////////////////////////////////////////////////////////
// NOTE: Column major matrix
struct Dqn_M4
{
Dqn_f32 columns[4][4];
};
DQN_API Dqn_f32 Dqn_V4Dot (Dqn_V4 a, Dqn_V4 b); 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_Identity ();
DQN_API Dqn_M4 Dqn_M4_ScaleF (Dqn_f32 x, Dqn_f32 y, Dqn_f32 z); 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); 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_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_MulF (Dqn_M4 lhs, Dqn_f32 rhs);
DQN_API Dqn_M4 Dqn_M4_DivF (Dqn_M4 lhs, Dqn_f32 rhs); DQN_API Dqn_M4 Dqn_M4_DivF (Dqn_M4 lhs, Dqn_f32 rhs);
#if !defined(DQN_NO_FSTR8) #if !defined(DQN_NO_FSTR8)
DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString (Dqn_M4 mat); DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString (Dqn_M4 mat);
#endif #endif
#endif // !defined(DQN_M4) #endif // !defined(DQN_NO_M4)
// NOTE: [$M2x3] Dqn_M2x3 //////////////////////////////////////////////////////////////////////////
union Dqn_M2x3
{
Dqn_f32 e[6];
Dqn_f32 row[2][3];
};
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 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 (); 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_Scale (Dqn_V2 scale);
DQN_API Dqn_M2x3 Dqn_M2x3_Rotate (Dqn_f32 radians); 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_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); 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_RECT)
#if defined(DQN_NO_V2) // NOTE: [$RECT] Dqn_Rect //////////////////////////////////////////////////////////////////////////
#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;
};
#define Dqn_Rect_InitV2x2(pos, size) DQN_LITERAL(Dqn_Rect){(pos), (size)} #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 bool operator== (const Dqn_Rect& lhs, const Dqn_Rect& rhs);
DQN_API Dqn_V2 Dqn_Rect_Center (Dqn_Rect rect); 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_BottomLeft (Dqn_Rect rect);
DQN_API Dqn_V2 Dqn_Rect_BottomRight (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_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_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); 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_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) #define Dqn_Rect_CutBottomNoClip(rect, amount) Dqn_Rect_CutBottomClip(rect, amount, Dqn_RectCutClip_No)
enum Dqn_RectCutSide DQN_API Dqn_Rect Dqn_RectCut_Cut (Dqn_RectCut rect_cut, Dqn_V2 size, Dqn_RectCutClip clip);
{
Dqn_RectCutSide_Left,
Dqn_RectCutSide_Right,
Dqn_RectCutSide_Top,
Dqn_RectCutSide_Bottom,
};
struct Dqn_RectCut
{
Dqn_Rect* rect;
Dqn_RectCutSide side;
};
#define Dqn_RectCut_Init(rect, side) DQN_LITERAL(Dqn_RectCut){rect, side} #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_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_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_Top(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Top}
#define Dqn_RectCut_Bottom(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Bottom} #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) #endif // !defined(DQN_NO_RECT)
// NOTE: [$MATH] Other /////////////////////////////////////////////////////////////////////////////
// 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)`
};
DQN_API Dqn_RaycastLineIntersectV2Result Dqn_Raycast_LineIntersectV2(Dqn_V2 origin_a, Dqn_V2 dir_a, Dqn_V2 origin_b, Dqn_V2 dir_b); 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_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_API Dqn_f32 Dqn_Lerp_F32 (Dqn_f32 a, Dqn_f32 t, Dqn_f32 b);
DQN_MSVC_WARNING_POP

View File

@ -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;
}

View File

@ -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, ...);

View File

@ -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) Dqn_OSDateTimeStr8 result = {};
ExitProcess(exit_code); result.hms_size = DQN_CAST(uint8_t) DQN_SNPRINTF(result.hms,
#else DQN_ARRAY_ICOUNT(result.hms),
exit(exit_code); "%02hhu%c%02hhu%c%02hhu",
#endif 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_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8Now(char date_separator, char hms_separator)
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle)
{ {
Dqn_OSExecResult result = {}; Dqn_OSDateTime time = Dqn_OS_DateLocalTimeNow();
if (!handle.process || handle.os_error_code) { Dqn_OSDateTimeStr8 result = Dqn_OS_DateLocalTimeStr8(time, date_separator, hms_separator);
result.os_error_code = handle.os_error_code;
return result; return result;
} }
#if defined(DQN_OS_WIN32) DQN_API Dqn_Str8 Dqn_OS_EXEDir(Dqn_Arena *arena)
DWORD exec_result = WaitForSingleObject(handle.process, INFINITE); {
if (exec_result == WAIT_FAILED) { Dqn_Str8 result = {};
result.os_error_code = GetLastError(); 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; return result;
} }
DWORD exit_status; DQN_API Dqn_f64 Dqn_OS_PerfCounterS(uint64_t begin, uint64_t end)
if (!GetExitCodeProcess(handle.process, &exit_status)) { {
result.os_error_code = GetLastError(); uint64_t frequency = Dqn_OS_PerfCounterFrequency();
uint64_t ticks = end - begin;
Dqn_f64 result = ticks / DQN_CAST(Dqn_f64)frequency;
return result; return result;
} }
result.exit_code = exit_status; DQN_API Dqn_f64 Dqn_OS_PerfCounterMs(uint64_t begin, uint64_t end)
CloseHandle(handle.process); {
#elif defined(DQN_PLATFORM_EMSCRIPTEN) uint64_t frequency = Dqn_OS_PerfCounterFrequency();
DQN_ASSERTF(false, "Unsupported operation"); uint64_t ticks = end - begin;
#else 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 (;;) { for (;;) {
int status = 0; Dqn_Str8BinarySplitResult delimiter = Dqn_Str8_BinarySplitArray(path, delimiter_array, DQN_ARRAY_UCOUNT(delimiter_array));
if (waitpid(DQN_CAST(pid_t)handle.process, &status, 0) < 0) { for (; delimiter.lhs.data; delimiter = Dqn_Str8_BinarySplitArray(delimiter.rhs, delimiter_array, DQN_ARRAY_UCOUNT(delimiter_array))) {
result.os_error_code = errno; 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; break;
} }
if (WIFEXITED(status)) { return true;
result.exit_code = WEXITSTATUS(status);
break;
} }
if (WIFSIGNALLED(status)) { DQN_API bool Dqn_OS_PathAdd(Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path)
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_OSExecAsyncHandle result = {}; Dqn_Str8 copy = Dqn_Str8_Copy(arena, path);
if (cmd.size == 0) bool result = Dqn_Str8_HasData(copy) ? true : Dqn_OS_PathAddRef(arena, fs_path, copy);
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();
return result; return result;
} }
CloseHandle(proc_info.hThread); DQN_API bool Dqn_OS_PathAddF(Dqn_Arena *arena, Dqn_OSPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...)
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_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); Dqn_OSExecResult result = Dqn_OS_ExecWait(async_handle);
return result; 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) { 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) { 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); Dqn_OS_Exit(result.os_error_code);
} }
if (result.exit_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); 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
View File

@ -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 struct Dqn_OSExecAsyncHandle
{ {
uint32_t os_error_code; uint32_t os_error_code;
uint32_t exit_code;
void *process; void *process;
}; };
@ -11,8 +183,200 @@ struct Dqn_OSExecResult
uint32_t exit_code; 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 void Dqn_OS_Exit (uint32_t exit_code);
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait (Dqn_OSExecAsyncHandle handle); 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_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir);
DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Str8 cmd, 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_Str8 cmd, 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
View 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

File diff suppressed because it is too large Load Diff

52
dqn_os_win32.h Normal file
View 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);

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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, ...) DQN_API Dqn_usize Dqn_CStr8_FSize(DQN_FMT_ATTRIB char const *fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); 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); va_end(args);
return result; 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_list args_copy;
va_copy(args_copy, args); 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); va_end(args_copy);
return result; return result;
} }
@ -38,7 +53,22 @@ DQN_API Dqn_usize Dqn_CStr16_Size(wchar_t const *src)
return result; 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_API Dqn_Str8 Dqn_Str8_InitCStr8(char const *src)
{ {
Dqn_usize size = Dqn_CStr8_Size(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) 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) if (!result)
return 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_API Dqn_Str8 Dqn_Str8_Slice(Dqn_Str8 string, Dqn_usize offset, Dqn_usize size)
{ {
Dqn_Str8 result = Dqn_Str8_Init(string.data, 0); Dqn_Str8 result = Dqn_Str8_Init(string.data, 0);
if (!Dqn_Str8_IsValid(result)) if (!Dqn_Str8_HasData(string))
return result; return result;
Dqn_usize capped_offset = DQN_MIN(offset, string.size); 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_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
{ {
Dqn_Str8BinarySplitResult result = {}; Dqn_Str8BinarySplitResult result = {};
if (!Dqn_Str8_IsValid(string) || !find || find_size == 0) if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
return result; return result;
result.lhs = string; 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_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
{ {
Dqn_Str8BinarySplitResult result = {}; Dqn_Str8BinarySplitResult result = {};
if (!Dqn_Str8_IsValid(string) || !find || find_size == 0) if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
return result; return result;
result.lhs = string; result.lhs = string;
@ -147,17 +177,17 @@ DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverse(Dqn_Str8 string, D
return result; 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. 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; return result;
Dqn_Str8BinarySplitResult split = {}; Dqn_Str8BinarySplitResult split = {};
Dqn_Str8 first = string; Dqn_Str8 first = string;
do { do {
split = Dqn_Str8_BinarySplit(first, delimiter); split = Dqn_Str8_BinarySplit(first, delimiter);
if (split.lhs.size) { if (split.lhs.size || mode == Dqn_Str8SplitIncludeEmptyStrings_Yes) {
if (splits && result < splits_count) if (splits && result < splits_count)
splits[result] = split.lhs; splits[result] = split.lhs;
result++; result++;
@ -168,15 +198,13 @@ DQN_API Dqn_usize Dqn_Str8_Split(Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *
return result; return result;
} }
DQN_API Dqn_Str8SplitAllocResult Dqn_Str8_SplitAlloc(Dqn_Allocator allocator, DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc(Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode)
Dqn_Str8 string,
Dqn_Str8 delimiter)
{ {
Dqn_Str8SplitAllocResult result = {}; Dqn_Slice<Dqn_Str8> result = {};
Dqn_usize splits_required = Dqn_Str8_Split(string, delimiter, /*splits*/ nullptr, /*count*/ 0); Dqn_usize splits_required = Dqn_Str8_Split(string, delimiter, /*splits*/ nullptr, /*count*/ 0, mode);
result.data = Dqn_Allocator_NewArray(allocator, Dqn_Str8, splits_required, Dqn_ZeroMem_No); result.data = Dqn_Arena_NewArray(arena, Dqn_Str8, splits_required, Dqn_ZeroMem_No);
if (result.data) { 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); DQN_ASSERT(splits_required == result.size);
} }
return result; 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_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
{ {
Dqn_Str8FindResult result = {}; Dqn_Str8FindResult result = {};
if (!Dqn_Str8_IsValid(string) || !find || find_size == 0) if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
return result; return result;
for (Dqn_usize index = 0; !result.found && index < string.size; index++) { 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; 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 (!segment_size || !Dqn_Str8_HasData(src)) {
if (result_size > segment_size) Dqn_Str8 result = Dqn_Str8_Copy(arena, src);
result_size += (src.size / segment_size) - 1; // NOTE: No segment on the first chunk. 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_usize write_index = 0;
DQN_FOR_UINDEX(src_index, src.size) { DQN_FOR_UINDEX(src_index, src.size) {
result.data[write_index++] = src.data[src_index]; 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; result.data[write_index++] = segment_char;
segment_counter++;
}
DQN_ASSERTF(write_index <= result.size, "result.size=%zu, write_index=%zu", result.size, write_index); 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; 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) 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_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround(Dqn_Str8 string)
{ {
Dqn_Str8 result = string; Dqn_Str8 result = string;
if (!Dqn_Str8_IsValid(string)) if (!Dqn_Str8_HasData(string))
return result; return result;
char const *start = string.data; 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_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark(Dqn_Str8 string)
{ {
Dqn_Str8 result = string; Dqn_Str8 result = string;
if (!Dqn_Str8_IsValid(result)) if (!Dqn_Str8_HasData(result))
return result; return result;
// TODO(dqn): This is little endian // 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_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")};
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverseArray(path, separators, DQN_ARRAY_UCOUNT(separators)); 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; return result;
} }
@ -400,17 +468,26 @@ DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension(Dqn_Str8 path)
return result; 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) DQN_API Dqn_Str8ToU64Result Dqn_Str8_ToU64(Dqn_Str8 string, char separator)
{ {
// NOTE: Argument check // NOTE: Argument check
Dqn_Str8ToU64Result result = {}; Dqn_Str8ToU64Result result = {};
if (!Dqn_Str8_IsValid(string)) if (!Dqn_Str8_HasData(string)) {
result.success = true;
return result; return result;
}
// NOTE: Sanitize input/output // NOTE: Sanitize input/output
Dqn_Str8 trim_string = Dqn_Str8_TrimWhitespaceAround(string); Dqn_Str8 trim_string = Dqn_Str8_TrimWhitespaceAround(string);
if (trim_string.size == 0) { if (trim_string.size == 0) {
result.success = false; result.success = true;
return result; return result;
} }
@ -446,13 +523,15 @@ DQN_API Dqn_Str8ToI64Result Dqn_Str8_ToI64(Dqn_Str8 string, char separator)
{ {
// NOTE: Argument check // NOTE: Argument check
Dqn_Str8ToI64Result result = {}; Dqn_Str8ToI64Result result = {};
if (!Dqn_Str8_IsValid(string)) if (!Dqn_Str8_HasData(string)) {
result.success = true;
return result; return result;
}
// NOTE: Sanitize input/output // NOTE: Sanitize input/output
Dqn_Str8 trim_string = Dqn_Str8_TrimWhitespaceAround(string); Dqn_Str8 trim_string = Dqn_Str8_TrimWhitespaceAround(string);
if (trim_string.size == 0) { if (trim_string.size == 0) {
result.success = false; result.success = true;
return result; return result;
} }
@ -492,18 +571,18 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string,
Dqn_Str8 find, Dqn_Str8 find,
Dqn_Str8 replace, Dqn_Str8 replace,
Dqn_usize start_index, Dqn_usize start_index,
Dqn_Allocator allocator, Dqn_Arena *arena,
Dqn_Str8EqCase eq_case) Dqn_Str8EqCase eq_case)
{ {
Dqn_Str8 result = {}; Dqn_Str8 result = {};
if (!Dqn_Str8_IsValid(string) || !Dqn_Str8_IsValid(find) || find.size > string.size || find.size == 0 || string.size == 0) { if (!Dqn_Str8_HasData(string) || !Dqn_Str8_HasData(find) || find.size > string.size || find.size == 0 || string.size == 0) {
result = Dqn_Str8_Copy(allocator, string); result = Dqn_Str8_Copy(arena, string);
return result; return result;
} }
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context); Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
Dqn_Str8Builder string_builder = {}; Dqn_Str8Builder string_builder = {};
string_builder.allocator = scratch.allocator; string_builder.arena = scratch.arena;
Dqn_usize max = string.size - find.size; Dqn_usize max = string.size - find.size;
Dqn_usize head = start_index; 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) { if (string_builder.string_size == 0) {
// NOTE: No replacement possible, so we just do a full-copy // NOTE: No replacement possible, so we just do a full-copy
result = Dqn_Str8_Copy(allocator, string); result = Dqn_Str8_Copy(arena, string);
} else { } else {
Dqn_Str8 remainder = Dqn_Str8_Init(string.data + head, string.size - head); Dqn_Str8 remainder = Dqn_Str8_Init(string.data + head, string.size - head);
Dqn_Str8Builder_AppendRef(&string_builder, remainder); Dqn_Str8Builder_AppendRef(&string_builder, remainder);
result = Dqn_Str8Builder_Build(&string_builder, allocator); result = Dqn_Str8Builder_Build(&string_builder, arena);
} }
return result; 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; return result;
} }
DQN_API void Dqn_Str8_Remove(Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size) 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; return;
char *end = string->data + string->size; char *end = string->data + string->size;
@ -573,16 +652,16 @@ DQN_API bool operator!=(Dqn_Str8 const &lhs, Dqn_Str8 const &rhs)
} }
#endif #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_list va;
va_start(va, fmt); 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); va_end(va);
return result; 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 = {}; Dqn_Str8 result = {};
if (!fmt) 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); Dqn_usize size = Dqn_CStr8_FVSize(fmt, args);
if (size) { if (size) {
result = Dqn_Str8_Allocate(allocator, size, Dqn_ZeroMem_No); result = Dqn_Str8_Alloc(arena, size, Dqn_ZeroMem_No);
if (Dqn_Str8_IsValid(result)) if (Dqn_Str8_HasData(result))
STB_SPRINTF_DECORATE(vsnprintf)(result.data, Dqn_Safe_SaturateCastISizeToInt(size + 1 /*null-terminator*/), fmt, args); DQN_VSNPRINTF(result.data, Dqn_Safe_SaturateCastISizeToInt(size + 1 /*null-terminator*/), fmt, args);
} }
return result; 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 = {}; 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) if (result.data)
result.size = size; result.size = size;
return result; 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 = {}; Dqn_Str8 result = {};
if (!string) if (!string)
return result; return result;
result = Dqn_Str8_Allocate(allocator, size, Dqn_ZeroMem_No); result = Dqn_Str8_Alloc(arena, size, Dqn_ZeroMem_No);
if (Dqn_Str8_IsValid(result)) { if (Dqn_Str8_HasData(result)) {
DQN_MEMCPY(result.data, string, size); DQN_MEMCPY(result.data, string, size);
result.data[size] = 0; result.data[size] = 0;
} }
return result; 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; return result;
} }
// NOTE: [$STRB] Dqn_Str8Builder ================================================================ // NOTE: [$STRB] Dqn_Str8Builder ////////////////////////////////////////////////////////////////
DQN_API bool Dqn_Str8Builder_AppendRef(Dqn_Str8Builder *builder, Dqn_Str8 string) 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) if (!builder || !string.data || string.size <= 0)
return false; 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) if (!link)
return false; return false;
@ -647,25 +730,31 @@ DQN_API bool Dqn_Str8Builder_AppendRef(Dqn_Str8Builder *builder, Dqn_Str8 string
builder->tail = link; builder->tail = link;
builder->count++; builder->count++;
builder->string_size += string.size; builder->string_size += string.size;
}
return true; 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); for (Dqn_Str8 string : array) {
bool result = Dqn_Str8Builder_AppendRef(builder, copy); Dqn_Str8 copy = Dqn_Str8_Copy(builder->arena, string);
return result; 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_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) if (string.size == 0)
return true; return true;
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(builder->arena);
bool result = Dqn_Str8Builder_AppendRef(builder, string); bool result = Dqn_Str8Builder_AppendRef(builder, string);
if (!result) if (!result)
Dqn_Allocator_Dealloc(builder->allocator, string.data, string.size + 1); Dqn_Arena_TempMemEnd(temp_mem);
return result; return result;
} }
@ -678,13 +767,27 @@ DQN_API bool Dqn_Str8Builder_AppendF(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB ch
return result; 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; Dqn_Str8 result = DQN_ZERO_INIT;
if (!builder || builder->string_size <= 0 || builder->count <= 0) if (!builder || builder->string_size <= 0 || builder->count <= 0)
return result; 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) if (!result.data)
return result; return result;
@ -698,7 +801,46 @@ DQN_API Dqn_Str8 Dqn_Str8Builder_Build(Dqn_Str8Builder const *builder, Dqn_Alloc
return result; 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) DQN_API bool Dqn_Char_IsAlphabet(char ch)
{ {
bool result = (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); bool result = (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
@ -766,7 +908,7 @@ DQN_API char Dqn_Char_ToLower(char ch)
return result; return result;
} }
// NOTE: [$UTFX] Dqn_UTF =========================================================================== // NOTE: [$UTFX] Dqn_UTF ///////////////////////////////////////////////////////////////////////////
DQN_API int Dqn_UTF8_EncodeCodepoint(uint8_t utf8[4], uint32_t codepoint) 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/ // 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 | // 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; utf8[0] = DQN_CAST(uint8_t) codepoint;
return 1; return 1;
} }
if (codepoint <= 0b0111'1111'1111) if (codepoint <= 0b0111'1111'1111) {
{
utf8[0] = (0b1100'0000 | ((codepoint >> 6) & 0b01'1111)); // x utf8[0] = (0b1100'0000 | ((codepoint >> 6) & 0b01'1111)); // x
utf8[1] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // y utf8[1] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // y
return 2; return 2;
} }
if (codepoint <= 0b1111'1111'1111'1111) if (codepoint <= 0b1111'1111'1111'1111) {
{
utf8[0] = (0b1110'0000 | ((codepoint >> 12) & 0b00'1111)); // x utf8[0] = (0b1110'0000 | ((codepoint >> 12) & 0b00'1111)); // x
utf8[1] = (0b1000'0000 | ((codepoint >> 6) & 0b11'1111)); // y utf8[1] = (0b1000'0000 | ((codepoint >> 6) & 0b11'1111)); // y
utf8[2] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // z utf8[2] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // z
return 3; 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[0] = (0b1111'0000 | ((codepoint >> 18) & 0b00'0111)); // x
utf8[1] = (0b1000'0000 | ((codepoint >> 12) & 0b11'1111)); // y utf8[1] = (0b1000'0000 | ((codepoint >> 12) & 0b11'1111)); // y
utf8[2] = (0b1000'0000 | ((codepoint >> 6) & 0b11'1111)); // z 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+10000U+10FFFF | // 1101'10xx'xxxx'xxxx 1101'11yy'yyyy'yyyy | xxxx'xxxx'xxyy'yyyy'yyyy + 0x10000 | U+10000U+10FFFF |
// ----------------------------------------+------------------------------------+------------------+ // ----------------------------------------+------------------------------------+------------------+
if (codepoint <= 0b1111'1111'1111'1111) if (codepoint <= 0b1111'1111'1111'1111) {
{
utf16[0] = DQN_CAST(uint16_t) codepoint; utf16[0] = DQN_CAST(uint16_t) codepoint;
return 1; return 1;
} }
if (codepoint <= 0b1111'1111'1111'1111'1111) if (codepoint <= 0b1111'1111'1111'1111'1111) {
{
uint32_t surrogate_codepoint = codepoint + 0x10000; uint32_t surrogate_codepoint = codepoint + 0x10000;
utf16[0] = 0b1101'1000'0000'0000 | ((surrogate_codepoint >> 10) & 0b11'1111'1111); // x 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 utf16[1] = 0b1101'1100'0000'0000 | ((surrogate_codepoint >> 0) & 0b11'1111'1111); // y

388
dqn_string.h Normal file
View 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)

View File

@ -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
View 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
View 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
View 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;
};

View File

@ -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 #define DQN_UTEST_IMPLEMENTATION
#include "dqn_utest.h" #include "dqn_utest.h"
@ -25,28 +5,22 @@ static Dqn_UTest Dqn_Test_Arena()
{ {
Dqn_UTest test = {}; Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_Arena") { 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") { DQN_UTEST_TEST("Reused memory is zeroed out") {
uint8_t alignment = 1; uint8_t alignment = 1;
Dqn_usize alloc_size = DQN_KILOBYTES(128); 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 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 // NOTE: Allocate 128 kilobytes, fill it with garbage, then reset the arena
uintptr_t first_ptr_address = 0; 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); void *ptr = Dqn_Arena_Alloc(&arena, alloc_size, alignment, Dqn_ZeroMem_Yes);
first_ptr_address = DQN_CAST(uintptr_t)ptr; first_ptr_address = DQN_CAST(uintptr_t)ptr;
DQN_MEMSET(ptr, 'z', alloc_size); DQN_MEMSET(ptr, 'z', alloc_size);
Dqn_Arena_EndTempMemory(temp_mem, false /*cancel*/); Dqn_Arena_TempMemEnd(temp_mem);
} }
// NOTE: Reallocate 128 kilobytes // NOTE: Reallocate 128 kilobytes
@ -58,99 +32,60 @@ static Dqn_UTest Dqn_Test_Arena()
// NOTE: Check that the bytes are set to 0 // NOTE: Check that the bytes are set to 0
for (Dqn_usize i = 0; i < alloc_size; i++) for (Dqn_usize i = 0; i < alloc_size; i++)
DQN_UTEST_ASSERT(&test, ptr[i] == 0); DQN_UTEST_ASSERT(&test, ptr[i] == 0);
Dqn_Arena_Free(&arena);
} }
DQN_UTEST_TEST("Test arena grows naturally, 1mb + 4mb") { DQN_UTEST_TEST("Test arena grows naturally, 1mb + 4mb") {
Dqn_Arena arena = {};
// NOTE: Allocate 1mb, then 4mb, this should force the arena to grow // 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_Arena arena = Dqn_Arena_InitSize(DQN_MEGABYTES(2), DQN_MEGABYTES(2), Dqn_ArenaFlag_Nil);
char *ptr_4mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(4), 1 /*align*/, Dqn_ZeroMem_Yes); 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_1mb);
DQN_UTEST_ASSERT(&test, ptr_4mb); DQN_UTEST_ASSERT(&test, ptr_4mb);
Dqn_MemBlock const *block_1mb = arena.head; Dqn_ArenaBlock const *block_4mb_begin = arena.curr;
char const *block_1mb_begin = DQN_CAST(char *)block_1mb->data; char const *block_4mb_end = DQN_CAST(char *)block_4mb_begin + block_4mb_begin->reserve;
char const *block_1mb_end = DQN_CAST(char *)block_1mb->data + block_1mb->size;
Dqn_MemBlock const *block_4mb = arena.curr; Dqn_ArenaBlock const *block_1mb_begin = block_4mb_begin->prev;
char const *block_4mb_begin = DQN_CAST(char *)block_4mb->data; DQN_UTEST_ASSERTF(&test, block_1mb_begin, "New block should have been allocated");
char const *block_4mb_end = DQN_CAST(char *)block_4mb->data + block_4mb->size; 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, block_1mb_begin != block_4mb_begin, "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_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 >= block_4mb_begin && ptr_4mb <= block_4mb_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_ASSERT (&test, arena.curr == arena.tail);
DQN_UTEST_ASSERT (&test, arena.curr != arena.head);
Dqn_Arena_Free(&arena);
} }
DQN_UTEST_TEST("Test arena grows naturally, 1mb, temp memory 4mb") { 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 // 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_1mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
DQN_UTEST_ASSERT(&test, ptr_1mb); 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_UTEST_ASSERT(&test, ptr_4mb);
Dqn_MemBlock const *block_1mb = arena.head; Dqn_ArenaBlock const *block_4mb_begin = arena.curr;
char const *block_1mb_begin = DQN_CAST(char *)block_1mb->data; char const *block_4mb_end = DQN_CAST(char *) block_4mb_begin + block_4mb_begin->reserve;
char const *block_1mb_end = DQN_CAST(char *)block_1mb->data + block_1mb->size;
Dqn_MemBlock const *block_4mb = arena.curr; Dqn_ArenaBlock const *block_1mb_begin = block_4mb_begin->prev;
char const *block_4mb_begin = DQN_CAST(char *)block_4mb->data; char const *block_1mb_end = DQN_CAST(char *) block_1mb_begin + block_1mb_begin->reserve;
char const *block_4mb_end = DQN_CAST(char *)block_4mb->data + block_4mb->size;
DQN_UTEST_ASSERTF(&test, block_1mb != block_4mb, "New block should have been allocated and linked"); DQN_UTEST_ASSERTF(&test, block_1mb_begin != block_4mb_begin, "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_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 >= block_4mb_begin && ptr_4mb <= block_4mb_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_ASSERT (&test, arena.curr == arena.tail);
DQN_UTEST_ASSERT (&test, arena.curr != arena.head);
} }
Dqn_Arena_EndTempMemory(temp_memory, false /*cancel*/); Dqn_Arena_TempMemEnd(temp_memory);
DQN_UTEST_ASSERT (&test, arena.curr->prev == nullptr);
DQN_UTEST_ASSERT (&test, arena.curr == arena.head); 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));
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);
} }
} }
return test; return test;
@ -158,7 +93,7 @@ static Dqn_UTest Dqn_Test_Arena()
static Dqn_UTest Dqn_Test_Bin() 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 test = {};
DQN_UTEST_GROUP(test, "Dqn_Bin") { DQN_UTEST_GROUP(test, "Dqn_Bin") {
DQN_UTEST_TEST("Convert 0x123") { DQN_UTEST_TEST("Convert 0x123") {
@ -553,11 +488,12 @@ static Dqn_UTest Dqn_Test_DSMap()
{ {
Dqn_UTest test = {}; Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_DSMap") { 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; uint32_t const MAP_SIZE = 64;
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(MAP_SIZE); Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE);
DQN_DEFER { Dqn_DSMap_Deinit(&map); }; DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); };
DQN_UTEST_TEST("Find non-existent value") { DQN_UTEST_TEST("Find non-existent value") {
uint64_t *value = Dqn_DSMap_FindKeyStr8(&map, DQN_STR8("Foo")).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; 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; uint32_t const MAP_SIZE = 64;
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(MAP_SIZE); Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE);
DQN_DEFER { Dqn_DSMap_Deinit(&map); }; DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); };
DQN_UTEST_TEST("%.*s: Test growing", DQN_STR_FMT(prefix)) { DQN_UTEST_TEST("%.*s: Test growing", DQN_STR_FMT(prefix)) {
uint64_t map_start_size = map.size; uint64_t map_start_size = map.size;
@ -761,51 +698,51 @@ static Dqn_UTest Dqn_Test_Fs()
Dqn_UTest test = {}; Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_Fs") { DQN_UTEST_GROUP(test, "Dqn_Fs") {
DQN_UTEST_TEST("Make directory recursive \"abcd/efgh\"") { 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_OS_DirMake(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_OS_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_OS_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_OS_FileExists(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_OS_FileExists(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_OS_PathDelete(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_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 // NOTE: Write step
Dqn_Str8 const SRC_FILE = DQN_STR8("dqn_test_file"); 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, write_result);
DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(SRC_FILE)); DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(SRC_FILE));
// NOTE: Read step // NOTE: Read step
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 read_file = Dqn_Fs_Read(SRC_FILE, scratch.allocator); Dqn_Str8 read_file = Dqn_OS_ReadAll(SRC_FILE, scratch.arena);
DQN_UTEST_ASSERTF(&test, Dqn_Str8_IsValid(read_file), "Failed to load file"); 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, 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)); 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 // NOTE: Copy step
Dqn_Str8 const COPY_FILE = DQN_STR8("dqn_test_file_copy"); 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, copy_result);
DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(COPY_FILE)); DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(COPY_FILE));
// NOTE: Move step // NOTE: Move step
Dqn_Str8 const MOVE_FILE = DQN_STR8("dqn_test_file_move"); 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, move_result);
DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(MOVE_FILE)); DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(MOVE_FILE));
DQN_UTEST_ASSERTF(&test, Dqn_Fs_Exists(COPY_FILE) == false, "Moving a file should remove the original"); DQN_UTEST_ASSERTF(&test, Dqn_OS_FileExists(COPY_FILE) == false, "Moving a file should remove the original");
// NOTE: Delete step // NOTE: Delete step
Dqn_b32 delete_src_file = Dqn_Fs_Delete(SRC_FILE); Dqn_b32 delete_src_file = Dqn_OS_PathDelete(SRC_FILE);
Dqn_b32 delete_moved_file = Dqn_Fs_Delete(MOVE_FILE); Dqn_b32 delete_moved_file = Dqn_OS_PathDelete(MOVE_FILE);
DQN_UTEST_ASSERT(&test, delete_src_file); DQN_UTEST_ASSERT(&test, delete_src_file);
DQN_UTEST_ASSERT(&test, delete_moved_file); DQN_UTEST_ASSERT(&test, delete_moved_file);
// NOTE: Deleting non-existent file fails // NOTE: Deleting non-existent file fails
Dqn_b32 delete_non_existent_src_file = Dqn_Fs_Delete(SRC_FILE); Dqn_b32 delete_non_existent_src_file = Dqn_OS_PathDelete(SRC_FILE);
Dqn_b32 delete_non_existent_moved_file = Dqn_Fs_Delete(MOVE_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_moved_file == false);
DQN_UTEST_ASSERT(&test, delete_non_existent_src_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) 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); Dqn_Str8 input_hex = Dqn_Hex_BytesToStr8Arena(scratch.arena, input.data, input.size);
switch(hash_type) switch(hash_type)
@ -1203,10 +1140,10 @@ static Dqn_UTest Dqn_Test_OS()
} }
DQN_UTEST_TEST("Query executable directory") { 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_Str8 result = Dqn_OS_EXEDir(scratch.arena);
DQN_UTEST_ASSERT(&test, Dqn_Str8_IsValid(result)); DQN_UTEST_ASSERT(&test, Dqn_Str8_HasData(result));
DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(result), "result(%zu): %.*s", result.size, DQN_STR_FMT(result)); DQN_UTEST_ASSERTF(&test, Dqn_OS_DirExists(result), "result(%zu): %.*s", result.size, DQN_STR_FMT(result));
} }
DQN_UTEST_TEST("Dqn_OS_PerfCounterNow") { DQN_UTEST_TEST("Dqn_OS_PerfCounterNow") {
@ -1225,11 +1162,11 @@ static Dqn_UTest Dqn_Test_OS()
uint64_t b = Dqn_OS_PerfCounterNow(); uint64_t b = Dqn_OS_PerfCounterNow();
Dqn_f64 s = Dqn_OS_PerfCounterS(a, b); Dqn_f64 s = Dqn_OS_PerfCounterS(a, b);
Dqn_f64 ms = Dqn_OS_PerfCounterMs(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_f64 ns = Dqn_OS_PerfCounterNs(a, b);
DQN_UTEST_ASSERTF(&test, s <= ms, "s: %f, ms: %f", s, ms); 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, ms <= us, "ms: %f, us: %f", ms, us);
DQN_UTEST_ASSERTF(&test, micro_s <= ns, "micro_s: %f, ns: %f", micro_s, ns); 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_UTEST_TEST("Initialise with format string") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 string = Dqn_Str8_InitF(scratch.allocator, "%s", "AB"); 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.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[0] == 'A', "string[0]: %c", string.data[0]);
DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]); 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_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 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.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[0] == 'A', "copy[0]: %c", copy.data[0]);
DQN_UTEST_ASSERTF(&test, copy.data[1] == 'B', "copy[1]: %c", copy.data[1]); 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_UTEST_TEST("Allocate string from arena") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 string = Dqn_Str8_Allocate(scratch.allocator, 2, Dqn_ZeroMem_No); Dqn_Str8 string = Dqn_Str8_Alloc(scratch.arena, 2, Dqn_ZeroMem_No);
DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64u", string.size); 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)); 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_UTEST_TEST("Is all digits fails on non-digit string") {
Dqn_b32 result = Dqn_Str8_IsAll(DQN_STR8("@123string"), Dqn_Str8IsAll_Digits); Dqn_b32 result = Dqn_Str8_IsAll(DQN_STR8("@123string"), Dqn_Str8IsAll_Digits);
DQN_UTEST_ASSERT(&test, result == false); DQN_UTEST_ASSERT(&test, result == false);
@ -1448,10 +1384,10 @@ static Dqn_UTest Dqn_Test_Str8()
DQN_UTEST_ASSERT(&test, result == false); 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"; char const buf[] = "@123string";
Dqn_b32 result = Dqn_Str8_IsAll(Dqn_Str8_Init(buf, 0), Dqn_Str8IsAll_Digits); 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") { 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_UTEST_TEST("To I64: Convert null string") {
Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(Dqn_Str8_Init(nullptr, 5), 0); 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_ASSERT(&test, result.value == 0);
} }
DQN_UTEST_TEST("To I64: Convert empty string") { DQN_UTEST_TEST("To I64: Convert empty string") {
Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(DQN_STR8(""), 0); 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); 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_UTEST_TEST("To U64: Convert nullptr") {
Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(Dqn_Str8_Init(nullptr, 5), 0); 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_ASSERTF(&test, result.value == 0, "result: %I64u", result.value);
} }
DQN_UTEST_TEST("To U64: Convert empty string") { DQN_UTEST_TEST("To U64: Convert empty string") {
Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(DQN_STR8(""), 0); 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); 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 test = {};
DQN_UTEST_GROUP(test, "Dqn_VArray") { DQN_UTEST_GROUP(test, "Dqn_VArray") {
{ {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_VArray<uint32_t> array = Dqn_VArray_InitByteSize<uint32_t>(DQN_KILOBYTES(64), 0);
Dqn_VArray<uint32_t> array = Dqn_VArray_InitByteSize<uint32_t>(scratch.arena, DQN_KILOBYTES(64)); DQN_DEFER {
Dqn_VArray_Deinit(&array);
};
DQN_UTEST_TEST("Test adding an array of items to the 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}; 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_MSVC_WARNING_POP
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_VArray<UnalignedObject> array = Dqn_VArray_InitByteSize<UnalignedObject>(DQN_KILOBYTES(64), 0);
Dqn_VArray<UnalignedObject> array = Dqn_VArray_InitByteSize<UnalignedObject>(scratch.arena, DQN_KILOBYTES(64)); DQN_DEFER {
Dqn_VArray_Deinit(&array);
};
// NOTE: Verify that the items returned from the data array are // NOTE: Verify that the items returned from the data array are
// contiguous in memory. // contiguous in memory.
@ -1840,54 +1779,29 @@ static Dqn_UTest Dqn_Test_VArray()
static Dqn_UTest Dqn_Test_Win() static Dqn_UTest Dqn_Test_Win()
{ {
Dqn_UTest test = {}; Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_Win") { DQN_UTEST_GROUP(test, "OS Win32") {
DQN_UTEST_TEST("Str8 to Str16 size required") { Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
int result = Dqn_Win_Str8ToStr16Buffer(DQN_STR8("a"), nullptr, 0); Dqn_Str8 input8 = DQN_STR8("String");
DQN_UTEST_ASSERTF(&test, result == 1, "Size returned: %d. This size should not include the null-terminator", result); Dqn_Str16 input16 = Dqn_Str16{(wchar_t *)(L"String"), sizeof(L"String") / sizeof(L"String"[0]) - 1};
}
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_TEST("Str8 to Str16") { DQN_UTEST_TEST("Str8 to Str16") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Str16 result = Dqn_Win_Str8ToStr16(scratch.arena, input8);
Dqn_Str8 const INPUT = DQN_STR8("String"); DQN_UTEST_ASSERT(&test, result == input16);
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_UTEST_TEST("Str16 to Str8: No null-terminate") { DQN_UTEST_TEST("Str16 to Str8") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Str8 result = Dqn_Win_Str16ToStr8(scratch.arena, input16);
Dqn_Str16 INPUT = DQN_STR16(L"String"); DQN_UTEST_ASSERT(&test, result == input8);
int size_required = Dqn_Win_Str16ToStr8Buffer(INPUT, nullptr, 0); }
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); 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); 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}; 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); 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_ASSERT(&test, DQN_MEMCMP(EXPECTED, string, sizeof(EXPECTED)) == 0);
} }
DQN_UTEST_TEST("Str8 to Str16 arena") { DQN_UTEST_TEST("Str16 to Str8: Arena null terminates string") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr); Dqn_Str8 string8 = Dqn_Win_Str16ToStr8(scratch.arena, input16);
Dqn_Str8 const INPUT = DQN_STR8("String"); int size_returned = Dqn_Win_Str16ToStr8Buffer(input16, nullptr, 0);
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);
char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 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); 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; 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_UTest tests[] =
{ {
Dqn_Test_Arena(), 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); 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) #if defined(DQN_TEST_WITH_MAIN)

View File

@ -1,11 +1,25 @@
#if !defined(DQN_UTEST_H) #if !defined(DQN_UTEST_H)
#define 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 // A super minimal testing framework, most of the logic here is the pretty
// printing of test results. // printing of test results.
//
// NOTE: Configuration ============================================================================= // NOTE: Configuration /////////////////////////////////////////////////////////////////////////////
// //
// #define DQN_UTEST_IMPLEMENTATION // #define DQN_UTEST_IMPLEMENTATION
// Define this in one and only one C++ file to enable the 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 // Define this to a terminal color code to specify what color sucess will be
// presented as. // presented as.
// NOTE: Macros ==================================================================================== // NOTE: Macros ////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <assert.h> #include <assert.h>
#include <string.> #include <string.h>
#if !defined(DQN_UTEST_RESULT_LPAD) #if !defined(DQN_UTEST_RESULT_LPAD)
#define DQN_UTEST_RESULT_LPAD 90 #define DQN_UTEST_RESULT_LPAD 90
@ -90,7 +104,7 @@
"%*sAssertion Triggered\n" \ "%*sAssertion Triggered\n" \
"%*sFile: %s:%d\n" \ "%*sFile: %s:%d\n" \
"%*sExpression: [" #expr "]\n" \ "%*sExpression: [" #expr "]\n" \
"%*sReason: " fmt "\n", \ "%*sReason: " fmt "\n\n", \
DQN_UTEST_SPACING * 2, \ DQN_UTEST_SPACING * 2, \
"", \ "", \
DQN_UTEST_SPACING * 3, \ DQN_UTEST_SPACING * 3, \
@ -111,7 +125,7 @@
(test)->state = Dqn_UTestState_TestFailed; \ (test)->state = Dqn_UTestState_TestFailed; \
fprintf(stderr, \ fprintf(stderr, \
"%*sFile: %s:%d\n" \ "%*sFile: %s:%d\n" \
"%*sExpression: [" #expr "]\n", \ "%*sExpression: [" #expr "]\n\n", \
DQN_UTEST_SPACING * 2, \ DQN_UTEST_SPACING * 2, \
"", \ "", \
file, \ file, \
@ -121,7 +135,7 @@
} \ } \
} while (0) } while (0)
// NOTE: Header ==================================================================================== // NOTE: Header ////////////////////////////////////////////////////////////////////////////////////
typedef enum Dqn_UTestState { typedef enum Dqn_UTestState {
Dqn_UTestState_Nil, Dqn_UTestState_Nil,
Dqn_UTestState_TestBegun, 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_BeginV(Dqn_UTest *test, char const *fmt, va_list args);
void Dqn_UTest_Begin(Dqn_UTest *test, char const *fmt, ...); void Dqn_UTest_Begin(Dqn_UTest *test, char const *fmt, ...);
void Dqn_UTest_End(Dqn_UTest *test); void Dqn_UTest_End(Dqn_UTest *test);
#endif // DQN_UTEST_H
// NOTE: Implementation ============================================================================ // NOTE: Implementation ////////////////////////////////////////////////////////////////////////////
#if defined(DQN_UTEST_IMPLEMENTATION) #if defined(DQN_UTEST_IMPLEMENTATION)
void Dqn_UTest_PrintStats(Dqn_UTest *test) void Dqn_UTest_PrintStats(Dqn_UTest *test)
{ {
@ -209,4 +224,3 @@ void Dqn_UTest_End(Dqn_UTest *test)
test->state = Dqn_UTestState_Nil; test->state = Dqn_UTestState_Nil;
} }
#endif // DQN_UTEST_IMPLEMENTATION #endif // DQN_UTEST_IMPLEMENTATION
#endif // DQN_UTEST_H

View File

@ -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 <bcrypt.h> // Dqn_OS_SecureRNGBytes -> BCryptOpenAlgorithmProvider ... etc
#include <shellapi.h> // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc #include <shellapi.h> // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc
#include <winhttp.h> // WinHttp*
#include <DbgHelp.h> #include <DbgHelp.h>
#if !defined(DQN_NO_WINNET) #else
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)
DQN_MSVC_WARNING_PUSH DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union 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 unsigned __int64 ULONG_PTR, *PULONG_PTR;
typedef ULONG_PTR SIZE_T, *PSIZE_T; typedef ULONG_PTR SIZE_T, *PSIZE_T;
typedef __int64 LONG_PTR, *PLONG_PTR; typedef __int64 LONG_PTR, *PLONG_PTR;
@ -25,7 +38,7 @@
typedef unsigned __int64 ULONG64, *PULONG64; typedef unsigned __int64 ULONG64, *PULONG64;
typedef unsigned __int64 DWORD64, *PDWORD64; typedef unsigned __int64 DWORD64, *PDWORD64;
// NOTE: shared/minwindef.h ==================================================================== // NOTE: shared/minwindef.h ////////////////////////////////////////////////////////////////////
struct HINSTANCE__ { struct HINSTANCE__ {
int unused; int unused;
}; };
@ -40,6 +53,8 @@
typedef unsigned char BYTE; typedef unsigned char BYTE;
typedef unsigned char UCHAR; typedef unsigned char UCHAR;
typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */ typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */
typedef void * HANDLE;
typedef HANDLE HLOCAL;
#define MAX_PATH 260 #define MAX_PATH 260
@ -48,15 +63,15 @@
DWORD dwHighDateTime; DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME; } FILETIME, *PFILETIME, *LPFILETIME;
// NOTE: shared/winerror.h ===================================================================== // NOTE: shared/winerror.h /////////////////////////////////////////////////////////////////////
// NOTE: GetModuleFileNameW // NOTE: GetModuleFileNameW
#define ERROR_INSUFFICIENT_BUFFER 122L // dderror #define ERROR_INSUFFICIENT_BUFFER 122L // dderror
// NOTE: um/winnls.h =========================================================================== // NOTE: um/winnls.h ///////////////////////////////////////////////////////////////////////////
// NOTE: MultiByteToWideChar // NOTE: MultiByteToWideChar
#define CP_UTF8 65001 // UTF-8 translation #define CP_UTF8 65001 // UTF-8 translation
// NOTE: um/winnt.h ============================================================================ // NOTE: um/winnt.h ////////////////////////////////////////////////////////////////////////////
typedef void VOID; typedef void VOID;
typedef __int64 LONGLONG; typedef __int64 LONGLONG;
typedef unsigned __int64 ULONGLONG; typedef unsigned __int64 ULONGLONG;
@ -66,6 +81,7 @@
typedef long LONG; typedef long LONG;
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
typedef CHAR * NPSTR, *LPSTR, *PSTR; typedef CHAR * NPSTR, *LPSTR, *PSTR;
typedef WCHAR * NWPSTR, *LPWSTR, *PWSTR;
// NOTE: VirtualAlloc: Allocation Type // NOTE: VirtualAlloc: Allocation Type
#define MEM_RESERVE 0x00002000 #define MEM_RESERVE 0x00002000
@ -93,6 +109,10 @@
#define FILE_APPEND_DATA (0x0004) // file #define FILE_APPEND_DATA (0x0004) // file
// NOTE: CreateFile/FindFirstFile // 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_READONLY 0x00000001
#define FILE_ATTRIBUTE_HIDDEN 0x00000002 #define FILE_ATTRIBUTE_HIDDEN 0x00000002
#define FILE_ATTRIBUTE_SYSTEM 0x00000004 #define FILE_ATTRIBUTE_SYSTEM 0x00000004
@ -102,6 +122,11 @@
// NOTE: STACKFRAME64 // NOTE: STACKFRAME64
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8) #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 { typedef union _ULARGE_INTEGER {
struct { struct {
DWORD LowPart; DWORD LowPart;
@ -230,6 +255,52 @@
DWORD64 LastExceptionFromRip; DWORD64 LastExceptionFromRip;
} CONTEXT; } 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" extern "C"
{ {
__declspec(dllimport) VOID __stdcall RtlCaptureContext(CONTEXT *ContextRecord); __declspec(dllimport) VOID __stdcall RtlCaptureContext(CONTEXT *ContextRecord);
@ -237,9 +308,18 @@
__declspec(dllimport) HANDLE __stdcall GetCurrentThread(void); __declspec(dllimport) HANDLE __stdcall GetCurrentThread(void);
__declspec(dllimport) DWORD __stdcall SymSetOptions(DWORD SymOptions); __declspec(dllimport) DWORD __stdcall SymSetOptions(DWORD SymOptions);
__declspec(dllimport) BOOL __stdcall SymInitialize(HANDLE hProcess, const CHAR* UserSearchPath, BOOL fInvadeProcess); __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) #define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
extern "C" extern "C"
@ -247,7 +327,7 @@
__declspec(dllimport) BOOL __stdcall CloseHandle(HANDLE hObject); __declspec(dllimport) BOOL __stdcall CloseHandle(HANDLE hObject);
} }
// NOTE: consoleapi.h =========================================================================== // NOTE: consoleapi.h ///////////////////////////////////////////////////////////////////////////
extern "C" extern "C"
{ {
__declspec(dllimport) BOOL __stdcall WriteConsoleA(HANDLE hConsoleOutput, const VOID* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD *lpNumberOfCharsWritten, VOID *lpReserved); __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); __declspec(dllimport) BOOL __stdcall GetConsoleMode(HANDLE hConsoleHandle, DWORD *lpMode);
} }
// NOTE: um/minwinbase.h ======================================================================= // NOTE: um/minwinbase.h ///////////////////////////////////////////////////////////////////////
// NOTE: FindFirstFile // NOTE: FindFirstFile
#define FIND_FIRST_EX_CASE_SENSITIVE 0x00000001 #define FIND_FIRST_EX_CASE_SENSITIVE 0x00000001
#define FIND_FIRST_EX_LARGE_FETCH 0x00000002 #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 { typedef enum _GET_FILEEX_INFO_LEVELS {
GetFileExInfoStandard, GetFileExInfoStandard,
GetFileExMaxInfoLevel GetFileExMaxInfoLevel
@ -329,7 +415,9 @@
HANDLE hEvent; HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED; } OVERLAPPED, *LPOVERLAPPED;
// NOTE: um/winbase.h ========================================================================== typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;
// NOTE: um/winbase.h //////////////////////////////////////////////////////////////////////////
#define WAIT_FAILED ((DWORD)0xFFFFFFFF) #define WAIT_FAILED ((DWORD)0xFFFFFFFF)
#define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 ) #define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 )
@ -344,6 +432,7 @@
#define MOVEFILE_COPY_ALLOWED 0x00000002 #define MOVEFILE_COPY_ALLOWED 0x00000002
// NOTE: FormatMessageA // NOTE: FormatMessageA
#define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100
#define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200 #define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200
#define FORMAT_MESSAGE_FROM_HMODULE 0x00000800 #define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000 #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 MoveFileExW (const WCHAR *lpExistingFileName, const WCHAR *lpNewFileName, DWORD dwFlags);
__declspec(dllimport) BOOL __stdcall CopyFileW (const WCHAR *lpExistingFileName, const WCHAR *lpNewFileName, BOOL bFailIfExists); __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) 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" extern "C"
{ {
__declspec(dllimport) int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, const CHAR *lpMultiByteStr, int cbMultiByte, WCHAR *lpWideCharStr, int cchWideChar); __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); __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_SIZE ((DWORD)0xFFFFFFFF)
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
@ -401,14 +491,14 @@
} }
// NOTE: um/processenv.h ======================================================================= // NOTE: um/processenv.h ///////////////////////////////////////////////////////////////////////
extern "C" extern "C"
{ {
__declspec(dllimport) DWORD __stdcall GetCurrentDirectoryW(DWORD nBufferLength, WCHAR *lpBuffer); __declspec(dllimport) DWORD __stdcall GetCurrentDirectoryW(DWORD nBufferLength, WCHAR *lpBuffer);
__declspec(dllimport) HANDLE __stdcall GetStdHandle(DWORD nStdHandle); __declspec(dllimport) HANDLE __stdcall GetStdHandle(DWORD nStdHandle);
} }
// NOTE: um/sysinfoapi.h ======================================================================= // NOTE: um/sysinfoapi.h ///////////////////////////////////////////////////////////////////////
typedef struct _SYSTEM_INFO { typedef struct _SYSTEM_INFO {
union { union {
DWORD dwOemId; // Obsolete field...do not use DWORD dwOemId; // Obsolete field...do not use
@ -436,7 +526,7 @@
__declspec(dllimport) VOID __stdcall GetLocalTime(SYSTEMTIME *lpSystemTime); __declspec(dllimport) VOID __stdcall GetLocalTime(SYSTEMTIME *lpSystemTime);
} }
// NOTE: shared/windef.h ======================================================================= // NOTE: shared/windef.h ///////////////////////////////////////////////////////////////////////
typedef struct tagRECT { typedef struct tagRECT {
LONG left; LONG left;
LONG top; LONG top;
@ -463,7 +553,20 @@
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4) #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_HIDE 0
#define SW_NORMAL 1 #define SW_NORMAL 1
#define SW_MAXIMIZE 3 #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) 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) UINT __stdcall GetWindowModuleFileNameA(HWND hwnd, LPSTR pszFileName, UINT cchFileNameMax);
__declspec(dllimport) BOOL __stdcall ShowWindow (HWND hWnd, int nCmdShow); __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 WORD INTERNET_PORT;
typedef VOID *HINTERNET; typedef VOID *HINTERNET;
#define INTERNET_OPEN_TYPE_PRECONFIG 0 // use registry configuration // NOTE: um/winhttp.h //////////////////////////////////////////////////////////////////////////
#define INTERNET_INVALID_PORT_NUMBER 0 // use the protocol-specific default #define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0
#define INTERNET_DEFAULT_FTP_PORT 21 // default for FTP servers #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_HTTP_PORT 80 // " " HTTP "
#define INTERNET_DEFAULT_HTTPS_PORT 443 // " " HTTPS " #define INTERNET_DEFAULT_HTTPS_PORT 443 // " " HTTPS "
#define INTERNET_SERVICE_HTTP 3
#define INTERNET_OPTION_USERNAME 28 // NOTE: WinHttpOpen
#define INTERNET_OPTION_PASSWORD 29 #define WINHTTP_FLAG_ASYNC 0x10000000 // this session is asynchronous (where supported)
#define INTERNET_OPTION_USER_AGENT 41 #define WINHTTP_FLAG_SECURE_DEFAULTS 0x30000000 // note that this flag also forces async
#define INTERNET_FLAG_NO_AUTH 0x00040000 // no automatic authentication handling // NOTE: WinHttpOpenRequest
#define INTERNET_FLAG_SECURE 0x00800000 // use PCT/SSL if applicable (HTTP) #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 WINHTTP_NO_PROXY_NAME NULL
#define HTTP_QUERY_RAW_HEADERS_CRLF 22 // special: all headers #define WINHTTP_NO_PROXY_BYPASS NULL
#define HTTP_ADDREQ_FLAG_ADD_IF_NEW 0x10000000 //
#define HTTP_ADDREQ_FLAG_ADD 0x20000000 // WINHTTP_QUERY_FLAG_NUMBER - if this bit is set in the dwInfoLevel parameter of
#define HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA 0x40000000 // HttpQueryHeader(), then the value of the header will be converted to a number
#define HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON 0x01000000 // before being returned to the caller, if applicable
#define HTTP_ADDREQ_FLAG_COALESCE HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA //
#define HTTP_ADDREQ_FLAG_REPLACE 0x80000000 #define WINHTTP_QUERY_FLAG_NUMBER 0x20000000
typedef enum { #define WINHTTP_QUERY_MIME_VERSION 0
INTERNET_SCHEME_PARTIAL = -2, #define WINHTTP_QUERY_CONTENT_TYPE 1
INTERNET_SCHEME_UNKNOWN = -1, #define WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING 2
INTERNET_SCHEME_DEFAULT = 0, #define WINHTTP_QUERY_CONTENT_ID 3
INTERNET_SCHEME_FTP, #define WINHTTP_QUERY_CONTENT_DESCRIPTION 4
INTERNET_SCHEME_GOPHER, #define WINHTTP_QUERY_CONTENT_LENGTH 5
INTERNET_SCHEME_HTTP, #define WINHTTP_QUERY_CONTENT_LANGUAGE 6
INTERNET_SCHEME_HTTPS, #define WINHTTP_QUERY_ALLOW 7
INTERNET_SCHEME_FILE, #define WINHTTP_QUERY_PUBLIC 8
INTERNET_SCHEME_NEWS, #define WINHTTP_QUERY_DATE 9
INTERNET_SCHEME_MAILTO, #define WINHTTP_QUERY_EXPIRES 10
INTERNET_SCHEME_SOCKS, #define WINHTTP_QUERY_LAST_MODIFIED 11
INTERNET_SCHEME_JAVASCRIPT, #define WINHTTP_QUERY_MESSAGE_ID 12
INTERNET_SCHEME_VBSCRIPT, #define WINHTTP_QUERY_URI 13
INTERNET_SCHEME_RES, #define WINHTTP_QUERY_DERIVED_FROM 14
INTERNET_SCHEME_FIRST = INTERNET_SCHEME_FTP, #define WINHTTP_QUERY_COST 15
INTERNET_SCHEME_LAST = INTERNET_SCHEME_RES #define WINHTTP_QUERY_LINK 16
} INTERNET_SCHEME, * LPINTERNET_SCHEME; #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 { // NOTE: WinHttpQueryHeaders prettifiers for optional parameters.
DWORD dwStructSize; // size of this structure. Used in version check #define WINHTTP_HEADER_NAME_BY_INDEX NULL
LPSTR lpszScheme; // pointer to scheme name #define WINHTTP_NO_OUTPUT_BUFFER NULL
DWORD dwSchemeLength; // length of scheme name #define WINHTTP_NO_HEADER_INDEX NULL
INTERNET_SCHEME nScheme; // enumerated scheme type (if known)
LPSTR lpszHostName; // pointer to host name // NOTE: Http Response Status Codes
DWORD dwHostNameLength; // length of host name #define HTTP_STATUS_CONTINUE 100 // OK to continue with request
INTERNET_PORT nPort; // converted port number #define HTTP_STATUS_SWITCH_PROTOCOLS 101 // server has switched protocols in upgrade header
LPSTR lpszUserName; // pointer to user name
DWORD dwUserNameLength; // length of user name #define HTTP_STATUS_OK 200 // request completed
LPSTR lpszPassword; // pointer to password #define HTTP_STATUS_CREATED 201 // object created, reason = new URI
DWORD dwPasswordLength; // length of password #define HTTP_STATUS_ACCEPTED 202 // async completion (TBS)
LPSTR lpszUrlPath; // pointer to URL-path #define HTTP_STATUS_PARTIAL 203 // partial completion
DWORD dwUrlPathLength; // length of URL-path #define HTTP_STATUS_NO_CONTENT 204 // no info to return
LPSTR lpszExtraInfo; // pointer to extra information (e.g. ?foo or #foo) #define HTTP_STATUS_RESET_CONTENT 205 // request completed, but clear form
DWORD dwExtraInfoLength; // length of extra information #define HTTP_STATUS_PARTIAL_CONTENT 206 // partial GET fulfilled
} URL_COMPONENTSA, * LPURL_COMPONENTSA; #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" extern "C"
{ {
__declspec(dllimport) BOOL __stdcall InternetCrackUrlA (CHAR const *lpszUrl, DWORD dwUrlLength, DWORD dwFlags, URL_COMPONENTSA *lpUrlComponents); __declspec(dllimport) HINTERNET __stdcall WinHttpOpen(WCHAR const *pszAgentW, DWORD dwAccessType, WCHAR const *pszProxyW, WCHAR const *pszProxyBypassW, DWORD dwFlags);
__declspec(dllimport) HINTERNET __stdcall InternetOpenA (CHAR const *lpszAgent, DWORD dwAccessType, CHAR const *lpszProxy, CHAR const *lpszProxyBypass, DWORD dwFlags); __declspec(dllimport) BOOL __stdcall WinHttpCloseHandle(HINTERNET hInternet);
__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) HINTERNET __stdcall WinHttpConnect(HINTERNET hSession, WCHAR const *pswzServerName, INTERNET_PORT nServerPort, DWORD dwReserved);
__declspec(dllimport) BOOL __stdcall InternetSetOptionA (HINTERNET hInternet, DWORD dwOption, VOID *lpBuffer, DWORD dwBufferLength); __declspec(dllimport) BOOL __stdcall WinHttpReadData(HINTERNET hRequest, VOID *lpBuffer, DWORD dwNumberOfBytesToRead, DWORD *lpdwNumberOfBytesRead);
__declspec(dllimport) BOOL __stdcall InternetReadFile (HINTERNET hFile, 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 InternetCloseHandle (HINTERNET hInternet); __declspec(dllimport) BOOL __stdcall WinHttpSendRequest(HINTERNET hRequest, WCHAR const *lpszHeaders, DWORD dwHeadersLength, VOID *lpOptional, DWORD dwOptionalLength, DWORD dwTotalLength, DWORD_PTR dwContext);
__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) DWORD __stdcall WinHttpAddRequestHeadersEx(HINTERNET hRequest, DWORD dwModifiers, ULONGLONG ullFlags, ULONGLONG ullExtra, DWORD cHeaders, WINHTTP_EXTENDED_HEADER *pHeaders);
__declspec(dllimport) BOOL __stdcall HttpSendRequestA (HINTERNET hRequest, CHAR const *lpszHeaders, DWORD dwHeadersLength, VOID *lpOptional, DWORD dwOptionalLength); __declspec(dllimport) BOOL __stdcall WinHttpSetCredentials(HINTERNET hRequest, // HINTERNET handle returned by WinHttpOpenRequest.
__declspec(dllimport) BOOL __stdcall HttpAddRequestHeadersA(HINTERNET hRequest, CHAR const *lpszHeaders, DWORD dwHeadersLength, DWORD dwModifiers); DWORD AuthTargets, // Only WINHTTP_AUTH_TARGET_SERVER and WINHTTP_AUTH_TARGET_PROXY are supported in this version and they are mutually exclusive
__declspec(dllimport) BOOL __stdcall HttpQueryInfoA (HINTERNET hRequest, DWORD dwInfoLevel, VOID *lpBuffer, DWORD *lpdwBufferLength, DWORD *lpdwIndex); 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_CASE_INSENSITIVE 0x00000001
#define SYMOPT_UNDNAME 0x00000002 #define SYMOPT_UNDNAME 0x00000002
#define SYMOPT_DEFERRED_LOADS 0x00000004 #define SYMOPT_DEFERRED_LOADS 0x00000004
@ -680,15 +957,16 @@
__declspec(dllimport) VOID * __stdcall SymFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase); __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) BOOL __stdcall SymGetLineFromAddrW64 (HANDLE hProcess, DWORD64 dwAddr, DWORD *pdwDisplacement, IMAGEHLP_LINEW64 *Line);
__declspec(dllimport) DWORD64 __stdcall SymGetModuleBase64 (HANDLE hProcess, DWORD64 qwAddr); __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" extern "C"
{ {
__declspec(dllimport) DWORD __stdcall GetLastError(VOID); __declspec(dllimport) DWORD __stdcall GetLastError(VOID);
} }
// NOTE: um/libloaderapi.h ===================================================================== // NOTE: um/libloaderapi.h /////////////////////////////////////////////////////////////////////
extern "C" extern "C"
{ {
__declspec(dllimport) HMODULE __stdcall LoadLibraryA (const CHAR *lpLibFileName); __declspec(dllimport) HMODULE __stdcall LoadLibraryA (const CHAR *lpLibFileName);
@ -698,22 +976,30 @@
__declspec(dllimport) DWORD __stdcall GetModuleFileNameW(HMODULE hModule, WCHAR *lpFilename, DWORD nSize); __declspec(dllimport) DWORD __stdcall GetModuleFileNameW(HMODULE hModule, WCHAR *lpFilename, DWORD nSize);
} }
// NOTE: um/synchapi.h ========================================================================= // NOTE: um/synchapi.h /////////////////////////////////////////////////////////////////////////
extern "C" 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) DWORD __stdcall WaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds);
__declspec(dllimport) BOOL __stdcall ReleaseSemaphore (HANDLE hSemaphore, LONG lReleaseCount, LONG *lpPreviousCount); __declspec(dllimport) BOOL __stdcall ReleaseSemaphore (HANDLE hSemaphore, LONG lReleaseCount, LONG *lpPreviousCount);
__declspec(dllimport) VOID __stdcall Sleep (DWORD dwMilliseconds); __declspec(dllimport) VOID __stdcall Sleep (DWORD dwMilliseconds);
} }
// NOTE: um/profileapi.h ======================================================================= // NOTE: um/profileapi.h ///////////////////////////////////////////////////////////////////////
extern "C" extern "C"
{ {
__declspec(dllimport) BOOL __stdcall QueryPerformanceCounter (LARGE_INTEGER* lpPerformanceCount); __declspec(dllimport) BOOL __stdcall QueryPerformanceCounter (LARGE_INTEGER* lpPerformanceCount);
__declspec(dllimport) BOOL __stdcall QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency); __declspec(dllimport) BOOL __stdcall QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency);
} }
// NOTE: um/processthreadsapi.h ================================================================ // NOTE: um/processthreadsapi.h ////////////////////////////////////////////////////////////////
typedef struct _PROCESS_INFORMATION { typedef struct _PROCESS_INFORMATION {
HANDLE hProcess; HANDLE hProcess;
HANDLE hThread; HANDLE hThread;
@ -757,7 +1043,7 @@
} }
// NOTE: um/memoryapi.h ======================================================================== // NOTE: um/memoryapi.h ////////////////////////////////////////////////////////////////////////
extern "C" extern "C"
{ {
__declspec(dllimport) VOID * __stdcall VirtualAlloc (VOID *lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); __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); __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 VOID *BCRYPT_ALG_HANDLE;
typedef LONG NTSTATUS; typedef LONG NTSTATUS;
@ -775,12 +1061,17 @@
__declspec(dllimport) NTSTATUS __stdcall BCryptGenRandom (BCRYPT_ALG_HANDLE hAlgorithm, UCHAR *pbBuffer, ULONG cbBuffer, ULONG dwFlags); __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" extern "C"
{ {
__declspec(dllimport) HINSTANCE __stdcall ShellExecuteA(HWND hwnd, CHAR const *lpOperation, CHAR const *lpFile, CHAR const *lpParameters, CHAR const *lpDirectory, INT nShowCmd); __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 DQN_MSVC_WARNING_POP
#endif // !defined(_INC_WINDOWS) #endif // !defined(_INC_WINDOWS)
#endif /// defined(DQN_OS_WIN32)