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
// NOTE: Dqn_CppFile: Helper functions to generate C++ files
// =============================================================================
// /////////////////////////////////////////////////////////////////////////////
#include <stdio.h> /// printf, fputc
#include <stdarg.h> /// va_list...
#include <assert.h> /// assert
@ -51,21 +51,21 @@ void Dqn_CppPrint(Dqn_CppFile *cpp, char const *fmt, ...);
#define Dqn_CppEnumBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
(Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__), true); \
(Dqn_CppBeginEnumBlock(cpp, fmt, ##__VA_ARGS__), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndEnumBlock(cpp), false))
#define Dqn_CppForBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
(Dqn_CppBeginBlock(cpp, false /*append*/, "for (" fmt ")", ##__VA_ARGS__), true); \
(Dqn_CppBeginForBLock(cpp, fmt, ##__VA_ARGS__), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndForBlock(cpp), false))
#define Dqn_CppWhileBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
(Dqn_CppBeginBlock(cpp, false /*append*/, "while (" fmt ")", ##__VA_ARGS__), true); \
(Dqn_CppBeginWhileBlock(cpp, fmt, ##__VA_ARGS__), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndForBlock(cpp), false))
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndWhileBlock(cpp), false))
#define Dqn_CppIfOrElseIfBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
@ -81,22 +81,28 @@ void Dqn_CppPrint(Dqn_CppFile *cpp, char const *fmt, ...);
#define Dqn_CppFuncBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
(Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__), true); \
(Dqn_CppBeginFuncBlock(cpp, fmt, ##__VA_ARGS__), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndFuncBlock(cpp), false))
#define Dqn_CppStructBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
(Dqn_CppBeginBlock(cpp, false /*append*/, "struct " fmt, ##__VA_ARGS__), true); \
(Dqn_CppBeginStructBlock(cpp, fmt, ##__VA_ARGS__), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndStructBlock(cpp), false))
#define Dqn_CppSwitchBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
(Dqn_CppBeginBlock(cpp, false /*append*/, "switch (" fmt ")", ##__VA_ARGS__), true); \
(Dqn_CppBeginSwitchBlock(cpp, fmt, ##__VA_ARGS__), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndSwitchBlock(cpp), false))
#define Dqn_CppBlock(cpp, ending, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
(Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndBlock(cpp, ending), false))
#define Dqn_CppIfChain(cpp) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppBeginIfChain(cpp), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
@ -106,29 +112,32 @@ void Dqn_CppPrint(Dqn_CppFile *cpp, char const *fmt, ...);
/// increasing the indent level after the brace.
void Dqn_CppBeginBlock (Dqn_CppFile *cpp, bool append, char const *fmt, ...);
void Dqn_CppBeginBlockV(Dqn_CppFile *cpp, bool append, char const *fmt, va_list args);
void Dqn_CppEndBlock (Dqn_CppFile *cpp);
void Dqn_CppEndBlock (Dqn_CppFile *cpp, char const *ending);
/// Begin/End a block, specifically for the following language constructs.
#define Dqn_CppBeginEnumBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
#define Dqn_CppEndEnumBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, ";\n")
#define Dqn_CppBeginEnumBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "enum " fmt, ##__VA_ARGS__)
#define Dqn_CppEndEnumBlock(cpp) Dqn_CppEndBlock(cpp, ";\n")
#define Dqn_CppBeginForBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
#define Dqn_CppEndForBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, "\n")
#define Dqn_CppBeginWhileBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "while (" fmt ")", ##__VA_ARGS__)
#define Dqn_CppEndWhileBlock(cpp) Dqn_CppEndBlock(cpp, "\n")
#define Dqn_CppBeginForBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "for (" fmt ")", ##__VA_ARGS__)
#define Dqn_CppEndForBlock(cpp) Dqn_CppEndBlock(cpp, "\n")
#define Dqn_CppBeginFuncBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
#define Dqn_CppEndFuncBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, "\n")
#define Dqn_CppEndFuncBlock(cpp) Dqn_CppEndBlock(cpp, "\n")
#define Dqn_CppBeginStructBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
#define Dqn_CppEndStructBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, ";\n")
#define Dqn_CppBeginStructBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "struct " fmt, ##__VA_ARGS__)
#define Dqn_CppEndStructBlock(cpp) Dqn_CppEndBlock(cpp, ";\n")
#define Dqn_CppBeginSwitchBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
#define Dqn_CppEndSwitchBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(cpp, "\n")
#define Dqn_CppBeginSwitchBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, "switch (" fmt ")", ##__VA_ARGS__)
#define Dqn_CppEndSwitchBlock(cpp) Dqn_CppEndBlock(cpp, "\n")
void Dqn_CppBeginIfOrElseIfBlock (Dqn_CppFile *cpp, char const *fmt, ...);
#define Dqn_CppEndIfOrElseIfBlock(cpp) Dqn_CppEndBlock(cpp)
#define Dqn_CppEndIfOrElseIfBlock(cpp) Dqn_CppEndBlock(cpp, "")
void Dqn_CppBeginElseBlock (Dqn_CppFile *cpp);
#define Dqn_CppEndElseBlock(cpp) Dqn_CppEndBlock(cpp)
void Dqn_CppEndElseBlock (Dqn_CppFile *cpp);
#define DQN_CPP_TOKEN_PASTE2_(x, y) x ## y
#define DQN_CPP_TOKEN_PASTE_(x, y) DQN_CPP_TOKEN_PASTE2_(x, y)
@ -179,20 +188,23 @@ void Dqn_CppBeginBlockV(Dqn_CppFile *cpp, bool append, char const *fmt, va_list
Dqn_CppAppendV(cpp, fmt, args);
else
Dqn_CppPrintV(cpp, fmt, args);
Dqn_CppAppend(cpp, " {\n");
bool empty_fmt = fmt == nullptr || strlen(fmt) == 0;
Dqn_CppAppend(cpp, "%s{\n", empty_fmt ? "" : " ");
Dqn_CppIndent(cpp);
}
void Dqn_CppEndBlock(Dqn_CppFile *cpp)
void Dqn_CppEndBlock(Dqn_CppFile *cpp, char const *ending)
{
Dqn_CppUnindent(cpp);
Dqn_CppPrint(cpp, "}");
Dqn_CppPrint(cpp, "}%s", ending);
}
void Dqn_CppBeginIfOrElseIfBlock(Dqn_CppFile *cpp, char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
assert(cpp->if_chain_size);
if (cpp->if_chain[cpp->if_chain_size - 1] == 0)
Dqn_CppPrint(cpp, "if");
else
@ -208,10 +220,17 @@ void Dqn_CppBeginIfOrElseIfBlock(Dqn_CppFile *cpp, char const *fmt, ...)
void Dqn_CppBeginElseBlock(Dqn_CppFile *cpp)
{
assert(cpp->if_chain_size);
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1)
Dqn_CppBeginBlock(cpp, true /*append*/, " else");
}
void Dqn_CppEndElseBlock(Dqn_CppFile *cpp)
{
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1)
Dqn_CppEndBlock(cpp, "");
}
void Dqn_CppBeginIfChain(Dqn_CppFile *cpp)
{
assert(cpp->if_chain_size < sizeof(cpp->if_chain)/sizeof(cpp->if_chain[0]));
@ -220,8 +239,12 @@ void Dqn_CppBeginIfChain(Dqn_CppFile *cpp)
void Dqn_CppEndIfChain(Dqn_CppFile *cpp)
{
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1)
assert(cpp->if_chain_size);
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1) {
Dqn_CppNewLine(cpp);
}
cpp->if_chain[cpp->if_chain_size - 1] = 0;
cpp->if_chain_size--;
}
#endif // DQN_CPP_FILE_IMPLEMENTATION

View File

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

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 Oi Use CPU Intrinsics
REM Z7 Combine multi-debug files to one debug file
set common_flags=-D DQN_TEST_WITH_MAIN -I %script_dir% %script_dir%\Misc\dqn_unit_tests.cpp
set common_flags=-D DQN_TEST_WITH_MAIN -D DQN_IMPLEMENTATION -D DQN_USE_STD_PRINTF /Tp %script_dir%\dqn.h
set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo

389
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
// from STB libraries for ease of integration and use. It defines a bunch of
// primitives and standard library functions that are missing and or more
// appropriate for development in modern day computing (e.g. cache friendly
// memory management, 64bit MMU, non-pessimized APIs that aren't constrained by
// the language specification and operate closer to the OS).
// appropriate for development in modern day computing (e.g. allocator
// first-class APIs, a 64bit MMU and in general non-pessimized APIs that aren't
// constrained by the language specification and operate closer to the OS).
//
// Define DQN_IMPLEMENTATION macro in one and only one translation unit to
// enable the implementation of this library, for example:
@ -15,7 +32,7 @@
//
// Additionally, this library supports including/excluding specific sections
// of the library by using #define on the name of the section. These names are
// documented in the section table of contents at the #define column, for
// documented in the section table of contents at the #define column, for
// example:
//
// #define DQN_ONLY_VARRAY
@ -33,18 +50,128 @@
// Below is a table of contents that describes what you can find in each header
// of this library and additional macros that can be defined to customise the
// behaviour of this library.
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\
// $$ __$$\ $$ __$$\\__$$ __|\_$$ _|$$ __$$\ $$$\ $$ |$$ __$$\
// $$ / $$ |$$ | $$ | $$ | $$ | $$ / $$ |$$$$\ $$ |$$ / \__|
// $$ | $$ |$$$$$$$ | $$ | $$ | $$ | $$ |$$ $$\$$ |\$$$$$$\
// $$ | $$ |$$ ____/ $$ | $$ | $$ | $$ |$$ \$$$$ | \____$$\
// $$ | $$ |$$ | $$ | $$ | $$ | $$ |$$ |\$$$ |$$\ $$ |
// $$$$$$ |$$ | $$ | $$$$$$\ $$$$$$ |$$ | \$$ |\$$$$$$ |
// \______/ \__| \__| \______| \______/ \__| \__| \______/
//
// Options -- Compile time build customisation
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// - Override these routines from the CRT by redefining them. By default we wrap
// the CRT functions from <strings.h> and <math.h>, e.g:
//
// #define DQN_MEMCPY(dest, src, count) memcpy(dest, src, value)
// #define DQN_MEMSET(dest, value, count) memset(dest, value, count)
// #define DQN_MEMCMP(lhs, rhs, count) memcpy(lhs, rhs, count)
// #define DQN_MEMMOVE(dest, src, count) memmove(dest, src, count)
// #define DQN_SQRTF(val) sqrtf(val)
// #define DQN_SINF(val) sinf(val)
// #define DQN_COSF(val) cosf(val)
// #define DQN_TANF(val) tanf(val)
//
// - Redefine 'DQN_API' to change the prefix of all declared functions in the library
//
// #define DQN_API
//
// - Define 'DQN_STATIC_API' to apply 'static' to all function definitions and
// disable external linkage to other translation units by redefining 'DQN_API' to
// 'static'.
//
// #define DQN_STATIC_API
//
// - Turn all assertion macros to no-ops except for hard asserts (which are
// always enabled and represent unrecoverable errors in the library).
//
// #define DQN_NO_ASSERT
//
// - Augment DQN_CHECK(expr) macro's behaviour. By default when the check fails a
// debug break is emitted. If this macro is defined, the check will not trigger
// a debug break.
//
// #define DQN_NO_CHECK_BREAK
//
// - Define this macro to enable memory leak tracking on arena's that are
// configured to track allocations.
//
// Allocations are stored in a global hash-table and their respective stack
// traces for the allocation location. Memory leaks can be dumped at the end
// of the program or some epoch by calling Dqn_Library_DumpLeaks()
//
// #define DQN_LEAK_TRACKING
//
// - Define this to revert to the family of printf functions from <stdio.h>
// instead of using stb_sprintf in this library. stb_sprintf is 5-6x faster
// than printf with a smaller binary footprint and has deterministic behaviour
// across all supported platforms.
//
// #define DQN_USE_STD_PRINTF
//
// However, if you are compiling with ASAN on MSVC, MSVC's implementation of
// __declspec(no_sanitize_address) is unable to suppress warnings in some
// individual functions in stb's implementation causing ASAN to trigger. This
// library will error on compilation if it detects this is the case and is
// being compiled with STB sprintf.
//
// - Define this to stop this library from defining a minimal subset of Win32
// prototypes and definitions in this file. You should use this macro if you
// intend to #include <Windows.h> yourself to avoid symbol conflicts with
// the redefined declarations in this library.
//
// #define DQN_NO_WIN32_MIN_HEADER
//
// - Define this to stop this library from defining STB_SPRINTF_IMPLEMENTATION.
// Useful if another library uses and includes "stb_sprintf.h"
//
// #define DQN_STB_SPRINTF_HEADER_ONLY
//
// - Override the default break into the active debugger function. By default
// we use __debugbreak() on Windows and raise(SIGTRAP) on other platforms.
//
// #define DQN_DEBUG_BREAK
//
// - Define this macro to 1 to enable poisoning of memory from arenas when ASAN
// `-fsanitize=address` is enabled. Enabling this will detect memory overwrite
// by padding allocated before and after with poisoned memory which will raise
// a use-after-poison in ASAN on read/write. This is a no-op if the library is
// not compiled with ASAN.
//
// #define DQN_ASAN_POISON 1
//
// - Define this macro 1 to enable sanity checks for manually poisoned memory in
// this library when ASAN `-fsanitize=address` is enabled. These sanity checks
// ensure that memory from arenas are correctly un/poisoned when pointers are
// allocated and returned to the memory arena's. This is a no-op if we are not
// compiled with ASAN or `DQN_ASAN_POISON` is not set to `1`.
//
// #define DQN_ASAN_VET_POISON 1
//
// - Define this macro to the size of the guard memory reserved before and after
// allocations made that are poisoned to protect against out-of-bounds memory
// accesses. By default the library sets the guard to 128 bytes.
//
// #define DQN_ASAN_POISON_GUARD_SIZE 128
//
#if !defined(DQN_H)
#if defined(DQN_ONLY_VARRAY) || \
defined(DQN_ONLY_SARRAY) || \
defined(DQN_ONLY_FARRAY) || \
defined(DQN_ONLY_SLICE) || \
defined(DQN_ONLY_DSMAP) || \
defined(DQN_ONLY_LIST) || \
defined(DQN_ONLY_FSTR8) || \
defined(DQN_ONLY_FS) || \
defined(DQN_ONLY_WINNET) || \
defined(DQN_ONLY_WIN) || \
defined(DQN_ONLY_SEMAPHORE) || \
defined(DQN_ONLY_THREAD) || \
defined(DQN_ONLY_V2) || \
defined(DQN_ONLY_V3) || \
defined(DQN_ONLY_V4) || \
@ -63,9 +190,6 @@
#if !defined(DQN_ONLY_SARRAY)
#define DQN_NO_SARRAY
#endif
#if !defined(DQN_ONLY_SLICE)
#define DQN_NO_SLICE
#endif
#if !defined(DQN_ONLY_DSMAP)
#define DQN_NO_DSMAP
#endif
@ -84,6 +208,12 @@
#if !defined(DQN_ONLY_WIN)
#define DQN_NO_WIN
#endif
#if !defined(DQN_ONLY_SEMAPHORE)
#define DQN_NO_SEMAPHORE
#endif
#if !defined(DQN_ONLY_THREAD)
#define DQN_NO_THREAD
#endif
#if !defined(DQN_ONLY_V2)
#define DQN_NO_V2
#endif
@ -110,222 +240,65 @@
#endif
#endif
// NOTE: Table of Contents =========================================================================
// Index | #define Label | Description
// NOTE: C Headers =================================================================================
#include <stdarg.h> // | | va_list
#include <stdio.h> // | | fprintf, FILE, stdout, stderr
#include <stdint.h> // | | [u]int_*, ...
#include <limits.h> // | | [U]INT_MAX, ...
// NOTE: Dqn_Base ==================================================================================
// [$CMAC] Compiler macros | | Macros for the compiler
// [$MACR] Macros | | Define macros used in the library
// [$TYPE] Types | | Basic types and typedefs
// [$INTR] Intrinsics | | Atomics, cpuid, ticket mutex
// [$CALL] Dqn_CallSite | | Source code location/tracing
// [$TMUT] Dqn_TicketMutex | | Userland mutex via spinlocking atomics
// [$ALLO] Dqn_Allocator | | Generic allocator interface
// [$PRIN] Dqn_Print | | Console printing
// [$LLOG] Dqn_Log | | Console logging macros
// NOTE: Additional Configuration
// - Override the default heap-allocation routine that is called when the
// default Dqn_Allocator is used by #define-ing. By default we use the OS's
// virtual memory allocators (e.g. VirtualAlloc on Windows and mmap on Linux).
//
// DQN_ALLOC(size)
// DQN_DEALLOC(ptr, size)
//
// - Override the byte-manipulation routines by #define-ing. By default we use
// <strings.h>
//
// DQN_MEMCPY(dest, src, count)
// DQN_MEMSET(dest, value, count)
// DQN_MEMCMP(lhs, rhs, count)
// DQN_MEMMOVE(dest, src, count)
//
// - Override these math functions. By default we use <math.h>
//
// DQN_SQRTF(val)
// DQN_SINF(val)
// DQN_COSF(val)
// DQN_TANF(val)
//
// - Change the prefix to all declared functions in the library by #define-ing.
//
// DQN_API
//
// - Apply static to all function definitions and disable external linkage to
// other translation units by #define-ing. This macro is only used if DQN_API
// is not overriden.
//
// DQN_STATIC_API
//
// - Turn all assertion macros to no-ops except for hard asserts (which are
// always enabled and represent unrecoverable errors in the library).
//
// DQN_NO_ASSERT
//
// - Augment DQN_CHECK(expr) macro's behaviour. By default it will trigger a
// debugger break when when the expression evalutes false otherwise by
// #define-ing this macro it will evaluate to false and DQN_CHECK is usually
// used in a if branch to recover gracefully from the failed condition.
//
// DQN_NO_CHECK_BREAK
#include "dqn_base.h"
// NOTE: Dqn_External ==============================================================================
// [$OS_H] OS Headers | | Headers from the operating system
// [$STBS] stb_sprintf | | Portable sprintf
#include "dqn_external.h"
#if defined(DQN_PLATFORM_WIN32)
#include "dqn_win32.h"
// NOTE: Additional Configuration
// - Define this to stop this library from defining a minimal subset of Win32
// prototypes and definitions in this file. You should use this macro if you
// intend to #include <Windows.h> yourself to avoid symbol conflicts with
// the redefined declarations in this library.
//
// DQN_NO_WIN32_MIN_HEADER
//
// - Define this to stop this library from defining STB_SPRINTF_IMPLEMENTATION.
// Useful if another library uses and includes "stb_sprintf.h"
//
// DQN_STB_SPRINTF_HEADER_ONLY
// NOTE: Dqn_Memory ================================================================================
// [$VMEM] Dqn_VMem | | Virtual memory allocation
// [$MEMB] Dqn_MemBlock | | Virtual memory blocks
// [$AREN] Dqn_Arena | | Growing bump allocator
// [$ACAT] Dqn_ArenaCatalog | | Collate, create & manage arenas in a catalog
#include "dqn_memory.h"
// NOTE: Dqn_Debug =================================================================================
// [$DEBM] Debug Macros | |
// [$ASAN] Dqn_Asan | | Helpers to manually poison memory using ASAN
// [$STKT] Dqn_StackTrace | | Create stack traces
// [$DEBG] Dqn_Debug | | Debugging tools/helpers
#endif
#include "dqn_allocator.h"
#include "dqn_thread_context.h"
#include "dqn_debug.h"
// NOTE: Dqn_Strings ===============================================================================
// [$CSTR] Dqn_CStr8 | | C-string helpers
// [$STR8] Dqn_Str8 | | Pointer and length strings
// [$FSTR] Dqn_FStr8 | DQN_FSTr8 | Fixed-size strings
// [$STRB] Dqn_Str8Builder | |
// [$CHAR] Dqn_Char | | Character ascii/digit.. helpers
// [$UTFX] Dqn_UTF | | Unicode helpers
#include "dqn_strings.h"
// NOTE: Dqn_Containers ============================================================================
// [$CARR] Dqn_CArray | | Basic operations on C arrays for VArray/SArray/FArray to reuse
// [$VARR] Dqn_VArray | DQN_VARRAY | Array backed by virtual memory arena
// [$SARR] Dqn_SArray | DQN_SARRAY | Array that are allocated but cannot resize
// [$FARR] Dqn_FArray | DQN_FARRAY | Fixed-size arrays
// [$SLIC] Dqn_Slice | DQN_SLICE | Pointe and length container of data
// [$DMAP] Dqn_DSMap | DQN_DSMAP | Hashtable, 70% max load, PoT size, linear probe, chain repair
// [$LIST] Dqn_List | DQN_LIST | Chunked linked lists, append only
#include "dqn_string.h"
#include "dqn_containers.h"
// NOTE: Additional Configuration
// - Override the default break into the active debugger function. By default
// we use __debugbreak() on Windows and raise(SIGTRAP) on other platforms.
//
// DQN_DEBUG_BREAK
//
// - Change the byte that DQN_MEMSET will clear memory with. By default this
// is set to 0. Some of the API's in this library accept a Dqn_ZeroMem enum
// which scrubs memory with this #define-d value.
//
// DQN_MEMSET_BYTE
//
// - Define this macro to enable emory leak tracking when requesting memory
// from the OS via this library. For example calls to Dqn_VMem_Reserve or
// DQN_ALLOC are recorded to the leak table.
//
// Allocations are stored in a global hash-table and their respective stack
// traces for the allocation location. Memory leaks can be dumped at the end
// of the program or some epoch by calling Dqn_Library_DumpLeaks()
//
// You may mark sections of your program as allowed to leak memory by setting
// the arena's or Dqn_Library's runtime struct `allocs_are_allowed_to_leak`
// flag.
//
// DQN_LEAK_TRACING
//
// - Define this macro to 1 to enable poisoning of memory from arenas when ASAN
// `-fsanitize=address` is enabled. Enabling this will detect memory overwrite
// by padding allocated before and after with poisoned memory which will raise
// a use-after-poison in ASAN on read/write. This is a no-op if the library is
// not compiled with ASAN.
//
// DQN_ASAN_POISON 1
//
// - Define this macro 1 to enable sanity checks for manually poisoned memory in
// this library when ASAN `-fsanitize=address` is enabled. These sanity checks
// ensure that memory from arenas are correctly un/poisoned when pointers are
// allocated and returned to the memory arena's. This is a no-op if we are not
// compiled with ASAN or `DQN_ASAN_POISON` is not set to `1`.
//
// DQN_ASAN_VET_POISON 1
//
// - Define this macro to the size of the guard memory reserved before and after
// allocations made that are poisoned to protect against out-of-bounds memory
// accesses. By default the library sets the guard to 128 bytes.
//
// DQN_ASAN_POISON_GUARD_SIZE 128
// NOTE: Dqn_Platform ==============================================================================
// [$FSYS] Dqn_Fs | DQN_FS | Filesystem helpers
// [$DATE] Dqn_Date | | Date-time helpers
// [$WIND] Dqn_Win | | Windows OS helpers
// [$WINN] Dqn_WinNet | DQN_WINNET | Windows internet download/query helpers
// [$OSYS] Dqn_OS | DQN_WIN | Operating-system APIs
// [$TCTX] Dqn_ThreadContext | | Per-thread data structure e.g. temp arenas
#include "dqn_platform.h"
// NOTE: Dqn_OS ====================================================================================
// [$EXEC] Dqn_OSExec | | Execute programs programatically
#if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64)
#elif defined(DQN_PLATFORM_WIN32)
#include "dqn_os_win32.h"
#else
#error Please define a platform e.g. 'DQN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
#endif
#include "dqn_os.h"
// NOTE: Dqn_Math ==================================================================================
// [$VEC2] Dqn_V2, V2i | DQN_V2 |
// [$VEC3] Dqn_V3, V3i | DQN_V3 |
// [$VEC4] Dqn_V4, V4i | DQN_V4 |
// [$MAT4] Dqn_M4 | DQN_M4 |
// [$RECT] Dqn_Rect | DQN_RECT |
// [$MATH] Other | |
#include "dqn_math.h"
// NOTE: Dqn_Hash ==================================================================================
// [$FNV1] Dqn_FNV1A | | Hash(x) -> 32/64bit via FNV1a
// [$MMUR] Dqn_MurmurHash3 | | Hash(x) -> 32/128bit via MurmurHash3
#include "dqn_hash.h"
// NOTE: Dqn_Helpers ===============================================================================
// [$PCG3] Dqn_PCG32 | | RNG from the PCG family
// [$JSON] Dqn_JSONBuilder | DQN_JSON_BUILDER | Construct json output
// [$BHEX] Dqn_Bin | DQN_BIN | Binary <-> hex helpers
// [$BSEA] Dqn_BinarySearch | | Binary search
// [$BITS] Dqn_Bit | | Bitset manipulation
// [$SAFE] Dqn_Safe | | Safe arithmetic, casts, asserts
// [$MISC] Misc | | Uncategorised helper functions
// [$DLIB] Dqn_Library | | Globally shared runtime data for this library
// [$PROF] Dqn_Profiler | DQN_PROFILER | Profiler that measures using a timestamp counter
#include "dqn_helpers.h"
#include "dqn_type_info.h"
#endif // DQN_H
#if defined(DQN_IMPLEMENTATION)
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// /$$$$$$\ $$\ $$\ $$$$$$$\ $$\
// \_$$ _|$$$\ $$$ |$$ __$$\ $$ |
// $$ | $$$$\ $$$$ |$$ | $$ |$$ |
// $$ | $$\$$\$$ $$ |$$$$$$$ |$$ |
// $$ | $$ \$$$ $$ |$$ ____/ $$ |
// $$ | $$ |\$ /$$ |$$ | $$ |
// $$$$$$\ $$ | \_/ $$ |$$ | $$$$$$$$\
// \______|\__| \__|\__| \________|
//
// Implementation
//
////////////////////////////////////////////////////////////////////////////////////////////////////
#include "dqn_base.cpp"
#include "dqn_thread_context.cpp"
#include "dqn_external.cpp"
#include "dqn_memory.cpp"
#include "dqn_allocator.cpp"
#include "dqn_debug.cpp"
#include "dqn_strings.cpp"
#include "dqn_string.cpp"
#include "dqn_containers.cpp"
#include "dqn_platform.cpp"
#if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64)
#include "dqn_os_posix.cpp"
#elif defined(DQN_PLATFORM_WIN32)
#include "dqn_os_win32.cpp"
#else
#error Please define a platform e.g. 'DQN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
#endif
#include "dqn_os.cpp"
#include "dqn_math.cpp"
#include "dqn_hash.cpp"
#include "dqn_helpers.cpp"
#include "dqn_unit_tests.cpp"
#include "dqn_docs.cpp"
#endif // DQN_IMPLEMENTATION

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_COMPILER_GCC) || defined(DQN_COMPILER_CLANG)
#include <cpuid.h>
#endif
Dqn_CPUIDRegisters Dqn_CPUID(int function_id)
DQN_API Dqn_CPUIDRegisters Dqn_CPUID(int function_id)
{
Dqn_CPUIDRegisters result = {};
#if defined(DQN_COMPILER_MSVC)
@ -18,28 +33,7 @@ Dqn_CPUIDRegisters Dqn_CPUID(int function_id)
}
#endif // !defined(DQN_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN)
// NOTE: [$ALLO] Dqn_Allocator =====================================================================
DQN_API void *Dqn_Allocator_Alloc(Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem)
{
void *result = NULL;
if (allocator.alloc) {
result = allocator.alloc(size, align, zero_mem, allocator.user_context);
} else {
result = DQN_ALLOC(size);
}
return result;
}
DQN_API void Dqn_Allocator_Dealloc(Dqn_Allocator allocator, void *ptr, size_t size)
{
if (allocator.dealloc) {
allocator.dealloc(ptr, size, allocator.user_context);
} else {
DQN_DEALLOC(ptr, size);
}
}
// NOTE: [$TMUT] Dqn_TicketMutex ===================================================================
// NOTE: [$TMUT] Dqn_TicketMutex ///////////////////////////////////////////////////////////////////
DQN_API void Dqn_TicketMutex_Begin(Dqn_TicketMutex *mutex)
{
unsigned int ticket = Dqn_Atomic_AddU32(&mutex->ticket, 1);
@ -82,7 +76,7 @@ DQN_API bool Dqn_TicketMutex_CanLock(Dqn_TicketMutex const *mutex, Dqn_uint tick
#endif
#endif
// NOTE: [$PRIN] Dqn_Print =========================================================================
// NOTE: [$PRIN] Dqn_Print /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour(uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold)
{
Dqn_PrintStyle result = {};
@ -115,7 +109,7 @@ DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_Str8 string)
DQN_ASSERT(std_handle == Dqn_PrintStd_Out || std_handle == Dqn_PrintStd_Err);
#if defined(DQN_OS_WIN32)
// NOTE: Get the output handles from kernel ====================================================
// NOTE: Get the output handles from kernel ////////////////////////////////////////////////////
DQN_THREAD_LOCAL void *std_out_print_handle = nullptr;
DQN_THREAD_LOCAL void *std_err_print_handle = nullptr;
DQN_THREAD_LOCAL bool std_out_print_to_console = false;
@ -130,7 +124,7 @@ DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_Str8 string)
std_err_print_to_console = GetConsoleMode(std_err_print_handle, &mode) != 0;
}
// NOTE: Select the output handle ==============================================================
// NOTE: Select the output handle //////////////////////////////////////////////////////////////
void *print_handle = std_out_print_handle;
bool print_to_console = std_out_print_to_console;
if (std_handle == Dqn_PrintStd_Err) {
@ -138,7 +132,7 @@ DQN_API void Dqn_Print_Std(Dqn_PrintStd std_handle, Dqn_Str8 string)
print_to_console = std_err_print_to_console;
}
// NOTE: Write the string ======================================================================
// NOTE: Write the string //////////////////////////////////////////////////////////////////////
DQN_ASSERT(string.size < DQN_CAST(unsigned long)-1);
unsigned long bytes_written = 0; (void)bytes_written;
if (print_to_console) {
@ -164,17 +158,6 @@ DQN_API void Dqn_Print_StdStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, D
}
}
DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int len)
{
Dqn_Str8 string = {};
string.data = DQN_CAST(char *)buf;
string.size = len;
Dqn_PrintStd std_handle = DQN_CAST(Dqn_PrintStd)DQN_CAST(uintptr_t)user;
Dqn_Print_Std(std_handle, string);
return (char *)buf;
}
DQN_API void Dqn_Print_StdF(Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...)
{
va_list args;
@ -191,10 +174,27 @@ DQN_API void Dqn_Print_StdFStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style,
va_end(args);
}
#if !defined(DQN_USE_STD_PRINTF)
DQN_FILE_SCOPE char *Dqn_Print_VSPrintfChunker_(const char *buf, void *user, int len)
{
Dqn_Str8 string = {};
string.data = DQN_CAST(char *)buf;
string.size = len;
Dqn_PrintStd std_handle = DQN_CAST(Dqn_PrintStd)DQN_CAST(uintptr_t)user;
Dqn_Print_Std(std_handle, string);
return (char *)buf;
}
#endif
DQN_API void Dqn_Print_StdFV(Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, va_list args)
{
#if defined(DQN_USE_STD_PRINTF)
vfprintf(std_handle == Dqn_PrintStd_Out ? stdout : stderr, fmt, args);
#else
char buffer[STB_SPRINTF_MIN];
STB_SPRINTF_DECORATE(vsprintfcb)(Dqn_Print_VSPrintfChunker_, DQN_CAST(void *)DQN_CAST(uintptr_t)std_handle, buffer, fmt, args);
#endif
}
DQN_API void Dqn_Print_StdFVStyle(Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, va_list args)
@ -255,26 +255,26 @@ DQN_API Dqn_Str8 Dqn_Print_ESCColourStr8(Dqn_PrintESCColour colour, uint8_t r, u
DQN_THREAD_LOCAL char buffer[32];
buffer[0] = 0;
Dqn_Str8 result = {};
result.size = STB_SPRINTF_DECORATE(snprintf)(buffer,
DQN_ARRAY_UCOUNT(buffer),
"\x1b[%d;2;%u;%u;%um",
colour == Dqn_PrintESCColour_Fg ? 38 : 48,
r, g, b);
result.size = DQN_SNPRINTF(buffer,
DQN_ARRAY_UCOUNT(buffer),
"\x1b[%d;2;%u;%u;%um",
colour == Dqn_PrintESCColour_Fg ? 38 : 48,
r, g, b);
result.data = buffer;
return result;
}
DQN_API Dqn_Str8 Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour colour, uint32_t value)
{
uint8_t r = DQN_CAST(uint8_t)(value >> 24);
uint8_t g = DQN_CAST(uint8_t)(value >> 16);
uint8_t b = DQN_CAST(uint8_t)(value >> 8);
uint8_t r = DQN_CAST(uint8_t)(value >> 24);
uint8_t g = DQN_CAST(uint8_t)(value >> 16);
uint8_t b = DQN_CAST(uint8_t)(value >> 8);
Dqn_Str8 result = Dqn_Print_ESCColourStr8(colour, r, g, b);
return result;
}
// NOTE: [$LLOG] Dqn_Log ==========================================================================
DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Allocator allocator,
// NOTE: [$LLOG] Dqn_Log ///////////////////////////////////////////////////////////////////////////
DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Arena *arena,
bool colour,
Dqn_Str8 type,
int log_type,
@ -282,64 +282,58 @@ DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Allocator allocator,
DQN_FMT_ATTRIB char const *fmt,
va_list args)
{
Dqn_usize header_size_no_ansi_codes = 0;
Dqn_Str8 header = {};
{
DQN_LOCAL_PERSIST Dqn_usize max_type_length = 0;
max_type_length = DQN_MAX(max_type_length, type.size);
int type_padding = DQN_CAST(int)(max_type_length - type.size);
DQN_LOCAL_PERSIST Dqn_usize max_type_length = 0;
max_type_length = DQN_MAX(max_type_length, type.size);
int type_padding = DQN_CAST(int)(max_type_length - type.size);
Dqn_Str8 colour_esc = {};
Dqn_Str8 bold_esc = {};
Dqn_Str8 reset_esc = {};
if (colour) {
bold_esc = Dqn_Print_ESCBoldStr8;
reset_esc = Dqn_Print_ESCResetStr8;
switch (log_type) {
case Dqn_LogType_Debug: break;
case Dqn_LogType_Info: colour_esc = Dqn_Print_ESCColourFgU32Str8(Dqn_LogTypeColourU32_Info); break;
case Dqn_LogType_Warning: colour_esc = Dqn_Print_ESCColourFgU32Str8(Dqn_LogTypeColourU32_Warning); break;
case Dqn_LogType_Error: colour_esc = Dqn_Print_ESCColourFgU32Str8(Dqn_LogTypeColourU32_Error); break;
}
Dqn_Str8 colour_esc = {};
Dqn_Str8 bold_esc = {};
Dqn_Str8 reset_esc = {};
if (colour) {
bold_esc = Dqn_Print_ESCBoldStr8;
reset_esc = Dqn_Print_ESCResetStr8;
switch (log_type) {
case Dqn_LogType_Debug: break;
case Dqn_LogType_Info: colour_esc = Dqn_Print_ESCColourFgU32Str8(Dqn_LogTypeColourU32_Info); break;
case Dqn_LogType_Warning: colour_esc = Dqn_Print_ESCColourFgU32Str8(Dqn_LogTypeColourU32_Warning); break;
case Dqn_LogType_Error: colour_esc = Dqn_Print_ESCColourFgU32Str8(Dqn_LogTypeColourU32_Error); break;
}
Dqn_Str8 file_name = Dqn_Str8_FileNameFromPath(call_site.file);
Dqn_DateHMSTimeStr8 const time = Dqn_Date_LocalTimeHMSStr8Now();
header = Dqn_Str8_InitF(allocator,
"%.*s " // date
"%.*s " // hms
"%.*s" // colour
"%.*s" // bold
"%.*s" // type
"%*s" // type padding
"%.*s" // reset
" %.*s" // file name
":%05u ", // line number
DQN_CAST(uint32_t)time.date_size - 2, time.date + 2, // date
DQN_CAST(uint32_t)time.hms_size, time.hms, // hms
DQN_CAST(uint32_t)colour_esc.size, colour_esc.data, // colour
DQN_CAST(uint32_t)bold_esc.size, bold_esc.data, // bold
DQN_CAST(uint32_t)type.size, type.data, // type
DQN_CAST(uint32_t)type_padding, "", // type padding
DQN_CAST(uint32_t)reset_esc.size, reset_esc.data, // reset
DQN_CAST(uint32_t)file_name.size, file_name.data, // file name
call_site.line); // line number
header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetStr8.size;
}
// NOTE: Header padding ========================================================================
Dqn_usize header_padding = 0;
{
DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0;
max_header_length = DQN_MAX(max_header_length, header_size_no_ansi_codes);
header_padding = max_header_length - header_size_no_ansi_codes;
}
Dqn_Str8 file_name = Dqn_Str8_FileNameFromPath(call_site.file);
Dqn_OSDateTimeStr8 const time = Dqn_OS_DateLocalTimeStr8Now();
Dqn_Str8 header = Dqn_Str8_InitF(arena,
"%.*s " // date
"%.*s " // hms
"%.*s" // colour
"%.*s" // bold
"%.*s" // type
"%*s" // type padding
"%.*s" // reset
" %.*s" // file name
":%05I32u " // line number
,
DQN_CAST(int)time.date_size - 2, time.date + 2, // date
DQN_CAST(int)time.hms_size, time.hms, // hms
DQN_STR_FMT(colour_esc), // colour
DQN_STR_FMT(bold_esc), // bold
DQN_STR_FMT(type), // type
DQN_CAST(int)type_padding, "", // type padding
DQN_STR_FMT(reset_esc), // reset
DQN_STR_FMT(file_name), // file name
call_site.line); // line number
Dqn_usize header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetStr8.size;
// NOTE: Construct final log ===================================================================
Dqn_Str8 user_msg = Dqn_Str8_InitFV(allocator, fmt, args);
Dqn_Str8 result = Dqn_Str8_Allocate(allocator, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No);
DQN_MEMCPY(result.data, header.data, header.size);
DQN_MEMSET(result.data + header.size, ' ', header_padding);
// NOTE: Header padding ////////////////////////////////////////////////////////////////////////
DQN_LOCAL_PERSIST Dqn_usize max_header_length = 0;
max_header_length = DQN_MAX(max_header_length, header_size_no_ansi_codes);
Dqn_usize header_padding = max_header_length - header_size_no_ansi_codes;
// NOTE: Construct final log ///////////////////////////////////////////////////////////////////
Dqn_Str8 user_msg = Dqn_Str8_InitFV(arena, fmt, args);
Dqn_Str8 result = Dqn_Str8_Alloc(arena, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No);
DQN_MEMCPY(result.data, header.data, header.size);
DQN_MEMSET(result.data + header.size, ' ', header_padding);
DQN_MEMCPY(result.data + header.size + header_padding, user_msg.data, user_msg.size);
return result;
}
@ -350,25 +344,25 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_Str8 type, int log_type, void *user_d
(void)log_type;
(void)user_data;
// NOTE: Open log file for appending if requested ==========================
// NOTE: Open log file for appending if requested //////////////////////////
Dqn_TicketMutex_Begin(&lib->log_file_mutex);
if (lib->log_to_file && !lib->log_file.handle && lib->log_file.error_size == 0) {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str8 log_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir));
lib->log_file = Dqn_Fs_OpenFile(log_path, Dqn_FsFileOpen_CreateAlways, Dqn_FsFileAccess_AppendOnly);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 log_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir));
lib->log_file = Dqn_OS_OpenFile(log_path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_AppendOnly);
}
Dqn_TicketMutex_End(&lib->log_file_mutex);
// NOTE: Generate the log header ===========================================
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str8 log_line = Dqn_Log_MakeStr8(scratch.allocator, !lib->log_no_colour, type, log_type, call_site, fmt, args);
// NOTE: Generate the log header ///////////////////////////////////////////
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 log_line = Dqn_Log_MakeStr8(scratch.arena, !lib->log_no_colour, type, log_type, call_site, fmt, args);
// NOTE: Print log =========================================================
// NOTE: Print log /////////////////////////////////////////////////////////
Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line);
Dqn_TicketMutex_Begin(&lib->log_file_mutex);
Dqn_Fs_WriteFile(&lib->log_file, log_line);
Dqn_Fs_WriteFile(&lib->log_file, DQN_STR8("\n"));
Dqn_OS_WriteFile(&lib->log_file, log_line);
Dqn_OS_WriteFile(&lib->log_file, DQN_STR8("\n"));
Dqn_TicketMutex_End(&lib->log_file_mutex);
}

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_TOKEN_COMBINE2(x, y) x ## y
#define DQN_TOKEN_COMBINE(x, y) DQN_TOKEN_COMBINE2(x, y)
// NOTE: [$CMAC] Compiler macros ===================================================================
// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER
#if defined(_MSC_VER)
#if defined(__clang__)
@ -39,14 +61,31 @@
#if defined(_WIN32)
#define DQN_OS_WIN32
#elif defined(__linux__)
#elif defined(__gnu_linux__) || defined(__linux__)
#define DQN_OS_UNIX
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#define DQN_PLATFORM_ARM64
#elif defined(__EMSCRIPTEN__)
#define DQN_PLATFORM_EMSCRIPTEN
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#if !defined(DQN_OS_WIN32)
#include <stdlib.h> // exit()
#endif
#if !defined(DQN_PLATFORM_EMSCRIPTEN) && \
!defined(DQN_PLATFORM_POSIX) && \
!defined(DQN_PLATFORM_WIN32)
#if defined(__aarch64__) || defined(_M_ARM64)
#define DQN_PLATFORM_ARM64
#elif defined(__EMSCRIPTEN__)
#define DQN_PLATFORM_EMSCRIPTEN
#elif defined(DQN_OS_WIN32)
#define DQN_PLATFORM_WIN32
#else
#define DQN_PLATFORM_POSIX
#endif
#endif
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
@ -81,33 +120,23 @@
#define DQN_GCC_WARNING_POP
#endif
// NOTE: [$MACR] Macros ============================================================================
// NOTE: MSVC does not support the feature detection macro for instance so we
// compile it out
#if defined(__has_feature)
#define DQN_HAS_FEATURE(expr) __has_feature(expr)
#else
#define DQN_HAS_FEATURE(expr) 0
#endif
#define DQN_FOR_UINDEX(index, size) for (Dqn_usize index = 0; index < size; index++)
#define DQN_FOR_IINDEX(index, size) for (Dqn_isize index = 0; index < size; index++)
#define Dqn_AlignUpPowerOfTwo(value, pot) (((uintptr_t)(value) + ((uintptr_t)(pot) - 1)) & ~((uintptr_t)(pot) - 1))
#define Dqn_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ((uintptr_t)(pot) - 1))
#define Dqn_AlignDownPowerOfTwo(value, pot) ((uintptr_t)(value) & ~((uintptr_t)(pot) - 1))
#define Dqn_IsPowerOfTwo(value) ((((uintptr_t)(value)) & (((uintptr_t)(value)) - 1)) == 0)
#define Dqn_IsPowerOfTwoAligned(value, pot) ((((uintptr_t)value) & (((uintptr_t)pot) - 1)) == 0)
// NOTE: Alloc Macros ==============================================================================
#if !defined(DQN_ALLOC)
#if defined(DQN_PLATFORM_EMSCRIPTEN)
#define DQN_ALLOC(size) malloc(size)
#else
#define DQN_ALLOC(size) Dqn_VMem_Reserve(size, Dqn_VMemCommit_Yes, Dqn_VMemPage_ReadWrite)
#endif
#endif
#if !defined(DQN_DEALLOC)
#if defined(DQN_PLATFORM_EMSCRIPTEN)
#define DQN_DEALLOC(ptr, size) free(ptr)
#else
#define DQN_DEALLOC(ptr, size) Dqn_VMem_Release(ptr, size)
#endif
#endif
// NOTE: String.h Dependencies =====================================================================
// NOTE: String.h Dependencies /////////////////////////////////////////////////////////////////////
#if !defined(DQN_MEMCPY) || !defined(DQN_MEMSET) || !defined(DQN_MEMCMP) || !defined(DQN_MEMMOVE)
#include <string.h>
#if !defined(DQN_MEMCPY)
@ -124,10 +153,12 @@
#endif
#endif
// NOTE: Math.h Dependencies =======================================================================
// NOTE: Math.h Dependencies ///////////////////////////////////////////////////////////////////////
#if !defined(DQN_SQRTF) || !defined(DQN_SINF) || !defined(DQN_COSF) || !defined(DQN_TANF)
#include <math.h>
#define DQN_SQRTF(val) sqrtf(val)
#if !defined(DQN_SQRTF)
#define DQN_SQRTF(val) sqrtf(val)
#endif
#if !defined(DQN_SINF)
#define DQN_SINF(val) sinf(val)
#endif
@ -139,11 +170,7 @@
#endif
#endif
#if !defined(DQN_OS_WIN32)
#include <stdlib.h> // exit()
#endif
// NOTE: Math Macros ===============================================================================
// NOTE: Math //////////////////////////////////////////////////////////////////////////////////////
#define DQN_PI 3.14159265359f
#define DQN_DEGREE_TO_RADIAN(degrees) ((degrees) * (DQN_PI / 180.0f))
@ -163,7 +190,7 @@
b = temp; \
} while (0)
// NOTE: Function/Variable Annotations =============================================================
// NOTE: Function/Variable Annotations /////////////////////////////////////////////////////////////
#if defined(DQN_STATIC_API)
#define DQN_API static
#else
@ -180,86 +207,88 @@
#define DQN_FORCE_INLINE inline __attribute__((always_inline))
#endif
// NOTE: Size Macros ===============================================================================
// NOTE: Size //////////////////////////////////////////////////////////////////////////////////////
#define DQN_ISIZEOF(val) DQN_CAST(ptrdiff_t)sizeof(val)
#define DQN_ARRAY_UCOUNT(array) (sizeof(array)/(sizeof((array)[0])))
#define DQN_ARRAY_ICOUNT(array) (Dqn_isize)DQN_ARRAY_UCOUNT(array)
#define DQN_CHAR_COUNT(string) (sizeof(string) - 1)
// NOTE: SI Byte Macros ============================================================================
#define DQN_BYTES(val) (val)
// NOTE: SI Byte ///////////////////////////////////////////////////////////////////////////////////
#define DQN_BYTES(val) (val)
#define DQN_KILOBYTES(val) (1024ULL * DQN_BYTES(val))
#define DQN_MEGABYTES(val) (1024ULL * DQN_KILOBYTES(val))
#define DQN_GIGABYTES(val) (1024ULL * DQN_MEGABYTES(val))
// NOTE: Time Macros ===============================================================================
// NOTE: Time //////////////////////////////////////////////////////////////////////////////////////
#define DQN_SECONDS_TO_MS(val) ((val) * 1000)
#define DQN_MINS_TO_S(val) ((val) * 60ULL)
#define DQN_HOURS_TO_S(val) (DQN_MINS_TO_S(val) * 60ULL)
#define DQN_DAYS_TO_S(val) (DQN_HOURS_TO_S(val) * 24ULL)
#define DQN_YEARS_TO_S(val) (DQN_DAYS_TO_S(val) * 365ULL)
#define DQN_MINS_TO_S(val) ((val) * 60ULL)
#define DQN_HOURS_TO_S(val) (DQN_MINS_TO_S(val) * 60ULL)
#define DQN_DAYS_TO_S(val) (DQN_HOURS_TO_S(val) * 24ULL)
#define DQN_WEEKS_TO_S(val) (DQN_DAYS_TO_S(val) * 7ULL)
#define DQN_YEARS_TO_S(val) (DQN_WEEKS_TO_S(val) * 52ULL)
// NOTE: Debug Break ===============================================================================
#if defined(__has_builtin)
#define DQN_HAS_BUILTIN(expr) __has_builtin(expr)
#else
#define DQN_HAS_BUILTIN(expr) 0
#endif
// NOTE: Debug Break ///////////////////////////////////////////////////////////////////////////////
#if !defined(DQN_DEBUG_BREAK)
#if defined(NDEBUG)
#define DQN_DEBUG_BREAK
#else
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
#define DQN_DEBUG_BREAK __debugbreak()
#elif defined(DQN_COMPILER_CLANG)
#elif DQN_HAS_BUILTIN(__builtin_debugtrap)
#define DQN_DEBUG_BREAK __builtin_debugtrap()
#elif defined(DQN_COMPILER_CLANG) || defined(DQN_COMPILER_GCC)
#elif DQN_HAS_BUILTIN(__builtin_trap) || defined(DQN_COMPILER_GCC)
#define DQN_DEBUG_BREAK __builtin_trap()
#else
#include <signal.h>
#define DQN_DEBUG_BREAK raise(SIGTRAP)
#elif
#error "Unhandled compiler"
#if defined(SIGTRAP)
#define DQN_DEBUG_BREAK raise(SIGTRAP)
#else
#define DQN_DEBUG_BREAK raise(SIGABRT)
#endif
#endif
#endif
#endif
#if !defined(DQN_MEMSET_BYTE)
#define DQN_MEMSET_BYTE 0
#endif
// NOTE: Assert Macros =============================================================================
// NOTE: Assert Macros /////////////////////////////////////////////////////////////////////////////
#define DQN_HARD_ASSERT(expr) DQN_HARD_ASSERTF(expr, "")
#define DQN_HARD_ASSERTF(expr, fmt, ...) \
if (!(expr)) { \
Dqn_Log_ErrorF("Hard assert triggered [" #expr "]. " fmt, ##__VA_ARGS__); \
Dqn_StackTrace_Print(128 /*limit*/); \
DQN_DEBUG_BREAK; \
}
#define DQN_HARD_ASSERTF(expr, fmt, ...) \
do { \
if (!(expr)) { \
Dqn_Str8 stack_trace_ = Dqn_StackTrace_WalkStr8CRT(128 /*limit*/, 2 /*skip*/); \
Dqn_Log_ErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DQN_STR_FMT(stack_trace_), \
##__VA_ARGS__); \
DQN_DEBUG_BREAK; \
} \
} while (0)
#if defined(DQN_NO_ASSERT)
#define DQN_ASSERTF(...)
#define DQN_ASSERT(...)
#else
#define DQN_ASSERT(expr) DQN_ASSERTF(expr, "")
#define DQN_ASSERTF(expr, fmt, ...) \
if (!(expr)) { \
Dqn_Log_ErrorF("Assert triggered [" #expr "]. " fmt, ##__VA_ARGS__); \
Dqn_StackTrace_Print(128 /*limit*/); \
DQN_DEBUG_BREAK; \
}
#define DQN_ASSERT(expr) DQN_ASSERTF((expr), "")
#define DQN_ASSERTF(expr, fmt, ...) \
do { \
if (!(expr)) { \
Dqn_Str8 stack_trace_ = Dqn_StackTrace_WalkStr8CRT(128 /*limit*/, 2 /*skip*/); \
Dqn_Log_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DQN_STR_FMT(stack_trace_), \
##__VA_ARGS__); \
DQN_DEBUG_BREAK; \
} \
} while (0)
#endif
#define DQN_INVALID_CODE_PATHF(fmt, ...) DQN_ASSERTF(0, fmt, ##__VA_ARGS__)
#define DQN_INVALID_CODE_PATH DQN_INVALID_CODE_PATHF("Invalid code path triggered")
// NOTE: Check macro ===============================================================================
// Check the expression trapping in debug, whilst in release- trapping is
// removed and the expression is evaluated as if it were a normal 'if' branch.
//
// This allows handling of the condition gracefully when compiled out but traps
// to notify the developer in builds when it's compiled in.
#if 0
bool flag = true;
if (DQN_CHECKF(flag, "Flag was false!")) {
// This branch will execute!
} else {
// Prints "Flag was false!"
}
#endif
// NOTE: Check macro ///////////////////////////////////////////////////////////////////////////////
#define DQN_CHECK(expr) DQN_CHECKF(expr, "")
#if defined(DQN_NO_CHECK_BREAK)
@ -267,32 +296,18 @@
((expr) ? true : (Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, ## __VA_ARGS__), false))
#else
#define DQN_CHECKF(expr, fmt, ...) \
((expr) ? true : (Dqn_Log_TypeFCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, ## __VA_ARGS__), DQN_DEBUG_BREAK, false))
((expr) ? true : (Dqn_Log_TypeFCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, ## __VA_ARGS__), Dqn_StackTrace_Print(128 /*limit*/), DQN_DEBUG_BREAK, false))
#endif
// NOTE: Zero initialisation macro =================================================================
// NOTE: Zero initialisation macro /////////////////////////////////////////////////////////////////
#if defined(__cplusplus)
#define DQN_ZERO_INIT {}
#else
#define DQN_ZERO_INIT {0}
#endif
// NOTE: Defer Macro ===============================================================================
// NOTE: Defer Macro ///////////////////////////////////////////////////////////////////////////////
#if defined(__cplusplus)
#if 0
#include <stdio.h>
int main()
{
DQN_DEFER { printf("Three ..\n"); };
printf("One ..\n");
printf("Two ..\n");
// One ..
// Two ..
// Three ..
return 0;
}
#endif
template <typename Procedure>
struct Dqn_Defer
{
@ -316,7 +331,7 @@ struct Dqn_DeferHelper
DQN_UNIQUE_NAME(once); \
end, DQN_UNIQUE_NAME(once) = false)
// NOTE: [$TYPE] Types =============================================================================
// NOTE: [$TYPE] Types /////////////////////////////////////////////////////////////////////////////
typedef intptr_t Dqn_isize;
typedef uintptr_t Dqn_usize;
typedef intptr_t Dqn_isize;
@ -350,7 +365,6 @@ struct Dqn_Str8
char *end () { return data + size; }
};
#if !defined(DQN_NO_SLICE)
template <typename T> struct Dqn_Slice // A pointer and length container of data
{
T *data;
@ -361,32 +375,17 @@ template <typename T> struct Dqn_Slice // A pointer and length container of data
T const *begin() const { return data; }
T const *end () const { return data + size; }
};
#endif
// NOTE: [$CALL] Dqn_CallSite ======================================================================
// NOTE: [$CALL] Dqn_CallSite //////////////////////////////////////////////////////////////////////
struct Dqn_CallSite
{
Dqn_Str8 file;
Dqn_Str8 function;
unsigned int line;
Dqn_Str8 file;
Dqn_Str8 function;
uint32_t line;
};
#define DQN_CALL_SITE Dqn_CallSite{DQN_STR8(__FILE__), DQN_STR8(__func__), __LINE__}
// NOTE: [$INTR] Intrinsics ========================================================================
// Platform agnostic functions for CPU level instructions like atomics, barriers
// and timestamp counters.
//
// NOTE: API
// @proc Dqn_Atomic_SetValue64, Dqn_Atomic_SetValue32
// @desc Atomically set the value into the target using an atomic compare and
// swap.
// @param[in,out] target The target pointer to set atomically
// @param[in] value The value to set atomically into the target
// @return The value that was last stored in the target
// @proc Dqn_CPUID
// Execute 'CPUID' instruction to query the capabilities of the current CPU.
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
// NOTE: Dqn_Atomic_Add/Exchange return the previous value store in the target
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
#include <intrin.h>
@ -422,6 +421,176 @@ struct Dqn_CallSite
#error "Compiler not supported"
#endif
#if !defined(DQN_PLATFORM_ARM64)
struct Dqn_CPUIDRegisters
{
Dqn_uint array[4]; // Values from 'CPUID' instruction for each register (EAX, EBX, ECX, EDX)
};
#endif // DQN_PLATFORM_ARM64
// NOTE: [$TMUT] Dqn_TicketMutex ///////////////////////////////////////////////////////////////////
struct Dqn_TicketMutex
{
unsigned int volatile ticket; // The next ticket to give out to the thread taking the mutex
unsigned int volatile serving; // The ticket ID to block the mutex on until it is returned
};
// NOTE: [$PRIN] Dqn_Print /////////////////////////////////////////////////////////////////////////
enum Dqn_PrintStd
{
Dqn_PrintStd_Out,
Dqn_PrintStd_Err,
};
enum Dqn_PrintBold
{
Dqn_PrintBold_No,
Dqn_PrintBold_Yes,
};
struct Dqn_PrintStyle
{
Dqn_PrintBold bold;
bool colour;
uint8_t r, g, b;
};
enum Dqn_PrintESCColour
{
Dqn_PrintESCColour_Fg,
Dqn_PrintESCColour_Bg,
};
// NOTE: [$LLOG] Dqn_Log ///////////////////////////////////////////////////////////////////////////
enum Dqn_LogType
{
Dqn_LogType_Debug,
Dqn_LogType_Info,
Dqn_LogType_Warning,
Dqn_LogType_Error,
Dqn_LogType_Count,
};
typedef void Dqn_LogProc(Dqn_Str8 type,
int log_type,
void *user_data,
Dqn_CallSite call_site,
DQN_FMT_ATTRIB char const *fmt,
va_list va);
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64 (uint64_t volatile *target, uint64_t value);
DQN_FORCE_INLINE long Dqn_Atomic_SetValue32 (long volatile *target, long value);
#if !defined(DQN_PLATFORM_ARM64)
DQN_API Dqn_CPUIDRegisters Dqn_CPUID (int function_id);
#endif
// NOTE: [$TMUT] Dqn_TicketMutex ///////////////////////////////////////////////////////////////////
DQN_API void Dqn_TicketMutex_Begin (Dqn_TicketMutex *mutex);
DQN_API void Dqn_TicketMutex_End (Dqn_TicketMutex *mutex);
DQN_API Dqn_uint Dqn_TicketMutex_MakeTicket (Dqn_TicketMutex *mutex);
DQN_API void Dqn_TicketMutex_BeginTicket (Dqn_TicketMutex const *mutex, Dqn_uint ticket);
DQN_API bool Dqn_TicketMutex_CanLock (Dqn_TicketMutex const *mutex, Dqn_uint ticket);
// NOTE: [$PRIN] Dqn_Print /////////////////////////////////////////////////////////////////////////
// NOTE: Print Style ///////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour (uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold);
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32 (uint32_t rgb, Dqn_PrintBold bold);
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold ();
// NOTE: Print Macros //////////////////////////////////////////////////////////////////////////////
#define Dqn_Print(string) Dqn_Print_Std(Dqn_PrintStd_Out, string)
#define Dqn_Print_F(fmt, ...) Dqn_Print_StdF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
#define Dqn_Print_FV(fmt, args) Dqn_Print_StdFV(Dqn_PrintStd_Out, fmt, args)
#define Dqn_Print_Style(style, string) Dqn_Print_StdStyle(Dqn_PrintStd_Out, style, string)
#define Dqn_Print_FStyle(style, fmt, ...) Dqn_Print_StdFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__)
#define Dqn_Print_FVStyle(style, fmt, args, ...) Dqn_Print_StdFVStyle(Dqn_PrintStd_Out, style, fmt, args)
#define Dqn_Print_Ln(string) Dqn_Print_StdLn(Dqn_PrintStd_Out, string)
#define Dqn_Print_LnF(fmt, ...) Dqn_Print_StdLnF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
#define Dqn_Print_LnFV(fmt, args) Dqn_Print_StdLnFV(Dqn_PrintStd_Out, fmt, args)
#define Dqn_Print_LnStyle(style, string) Dqn_Print_StdLnStyle(Dqn_PrintStd_Out, style, string);
#define Dqn_Print_LnFStyle(style, fmt, ...) Dqn_Print_StdLnFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__);
#define Dqn_Print_LnFVStyle(style, fmt, args) Dqn_Print_StdLnFVStyle(Dqn_PrintStd_Out, style, fmt, args);
#define Dqn_Print_Err(string) Dqn_Print_Std(Dqn_PrintStd_Err, string)
#define Dqn_Print_ErrF(fmt, ...) Dqn_Print_StdF(Dqn_PrintStd_Err, fmt, ## __VA_ARGS__)
#define Dqn_Print_ErrFV(fmt, args) Dqn_Print_StdFV(Dqn_PrintStd_Err, fmt, args)
#define Dqn_Print_ErrStyle(style, string) Dqn_Print_StdStyle(Dqn_PrintStd_Err, style, string)
#define Dqn_Print_ErrFStyle(style, fmt, ...) Dqn_Print_StdFStyle(Dqn_PrintStd_Err, style, fmt, ## __VA_ARGS__)
#define Dqn_Print_ErrFVStyle(style, fmt, args, ...) Dqn_Print_StdFVStyle(Dqn_PrintStd_Err, style, fmt, args)
#define Dqn_Print_ErrLn(string) Dqn_Print_StdLn(Dqn_PrintStd_Err, string)
#define Dqn_Print_ErrLnF(fmt, ...) Dqn_Print_StdLnF(Dqn_PrintStd_Err, fmt, ## __VA_ARGS__)
#define Dqn_Print_ErrLnFV(fmt, args) Dqn_Print_StdLnFV(Dqn_PrintStd_Err, fmt, args)
#define Dqn_Print_ErrLnStyle(style, string) Dqn_Print_StdLnStyle(Dqn_PrintStd_Err, style, string);
#define Dqn_Print_ErrLnFStyle(style, fmt, ...) Dqn_Print_StdLnFStyle(Dqn_PrintStd_Err, style, fmt, ## __VA_ARGS__);
#define Dqn_Print_ErrLnFVStyle(style, fmt, args) Dqn_Print_StdLnFVStyle(Dqn_PrintStd_Err, style, fmt, args);
// NOTE: Print /////////////////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_Str8 string);
DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API void Dqn_Print_StdStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_Str8 string);
DQN_API void Dqn_Print_StdFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, ...);
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_StdLn (Dqn_PrintStd std_handle, Dqn_Str8 string);
DQN_API void Dqn_Print_StdLnF (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API void Dqn_Print_StdLnFV (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_Str8 string);
DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, va_list args);
// NOTE: ANSI Formatting Codes /////////////////////////////////////////////////////////////////////
DQN_API Dqn_Str8 Dqn_Print_ESCColourStr8 (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b);
DQN_API Dqn_Str8 Dqn_Print_ESCColourU32Str8 (Dqn_PrintESCColour colour, uint32_t value);
#define Dqn_Print_ESCColourFgStr8(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Fg, r, g, b)
#define Dqn_Print_ESCColourBgStr8(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Bg, r, g, b)
#define Dqn_Print_ESCColourFg(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Fg, r, g, b).data
#define Dqn_Print_ESCColourBg(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Bg, r, g, b).data
#define Dqn_Print_ESCColourFgU32Str8(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Fg, value)
#define Dqn_Print_ESCColourBgU32Str8(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Bg, value)
#define Dqn_Print_ESCColourFgU32(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Fg, value).data
#define Dqn_Print_ESCColourBgU32(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Bg, value).data
#define Dqn_Print_ESCReset "\x1b[0m"
#define Dqn_Print_ESCBold "\x1b[1m"
#define Dqn_Print_ESCResetStr8 DQN_STR8(Dqn_Print_ESCReset)
#define Dqn_Print_ESCBoldStr8 DQN_STR8(Dqn_Print_ESCBold)
// NOTE: [$LLOG] Dqn_Log ///////////////////////////////////////////////////////////////////////////
#define Dqn_LogTypeColourU32_Info 0x00'87'ff'ff // Blue
#define Dqn_LogTypeColourU32_Warning 0xff'ff'00'ff // Yellow
#define Dqn_LogTypeColourU32_Error 0xff'00'00'ff // Red
#define Dqn_Log_DebugF(fmt, ...) Dqn_Log_TypeFCallSite (Dqn_LogType_Debug, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
#define Dqn_Log_InfoF(fmt, ...) Dqn_Log_TypeFCallSite (Dqn_LogType_Info, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
#define Dqn_Log_WarningF(fmt, ...) Dqn_Log_TypeFCallSite (Dqn_LogType_Warning, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
#define Dqn_Log_ErrorF(fmt, ...) Dqn_Log_TypeFCallSite (Dqn_LogType_Error, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
#define Dqn_Log_DebugFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Debug, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_InfoFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Info, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_WarningFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_ErrorFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_TypeFV(type, fmt, args) Dqn_Log_TypeFVCallSite(type, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_TypeF(type, fmt, ...) Dqn_Log_TypeFCallSite (type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
#define Dqn_Log_FV(type, fmt, args) Dqn_Log_FVCallSite (type, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_F(type, fmt, ...) Dqn_Log_FCallSite (type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
DQN_API Dqn_Str8 Dqn_Log_MakeStr8 (struct Dqn_Arena *arena, bool colour, Dqn_Str8 type, int log_type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API void Dqn_Log_TypeFVCallSite (Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va);
DQN_API void Dqn_Log_TypeFCallSite (Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API void Dqn_Log_FVCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va);
DQN_API void Dqn_Log_FCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...);
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64(uint64_t volatile *target, uint64_t value)
{
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
@ -453,233 +622,3 @@ DQN_FORCE_INLINE long Dqn_Atomic_SetValue32(long volatile *target, long value)
#error Unsupported compiler
#endif
}
#if !defined(DQN_PLATFORM_ARM64)
struct Dqn_CPUIDRegisters
{
Dqn_uint array[4]; ///< Values from 'CPUID' instruction for each register (EAX, EBX, ECX, EDX)
};
Dqn_CPUIDRegisters Dqn_CPUID(int function_id);
#endif // DQN_PLATFORM_ARM64
// NOTE: [$TMUT] Dqn_TicketMutex ===================================================================
//
// A mutex implemented using an atomic compare and swap on tickets handed out
// for each critical section.
//
// This mutex serves ticket in order and will block all other threads until the
// tickets are returned in order. The thread with the oldest ticket that has
// not been returned has right of way to execute, all other threads will be
// blocked in an atomic compare and swap loop. block execution by going into an
// atomic
//
// When a thread is blocked by this mutex, a spinlock intrinsic `_mm_pause` is
// used to yield the CPU and reduce spinlock on the thread. This mutex is not
// ideal for long blocking operations. This mutex does not issue any syscalls
// and relies entirely on atomic instructions.
//
// NOTE: API
//
// @proc Dqn_TicketMutex_Begin, End
// @desc Lock and unlock the mutex respectively
// @proc Dqn_TicketMutex_MakeTicket
// @desc Allocate the next available ticket from the mutex for locking using
// Dqn_TicketMutex_BeginTicket().
// @param[in] mutex The mutex
#if 0
Dqn_TicketMutex mutex = {};
unsigned int ticket = Dqn_TicketMutex_MakeTicket(&mutex);
Dqn_TicketMutex_BeginTicket(&mutex, ticket); // Blocking call until we attain the lock
Dqn_TicketMutex_End(&mutex);
#endif
// @proc Dqn_TicketMutex_BeginTicket
// @desc Lock the mutex using the given ticket if possible, otherwise block
// waiting until the mutex can be locked.
// @proc Dqn_TicketMutex_CanLock
// @desc Determine if the mutex can be locked using the given ticket number
struct Dqn_TicketMutex
{
unsigned int volatile ticket; // The next ticket to give out to the thread taking the mutex
unsigned int volatile serving; // The ticket ID to block the mutex on until it is returned
};
void Dqn_TicketMutex_Begin (Dqn_TicketMutex *mutex);
void Dqn_TicketMutex_End (Dqn_TicketMutex *mutex);
Dqn_uint Dqn_TicketMutex_MakeTicket (Dqn_TicketMutex *mutex);
void Dqn_TicketMutex_BeginTicket(Dqn_TicketMutex const *mutex, Dqn_uint ticket);
bool Dqn_TicketMutex_CanLock (Dqn_TicketMutex const *mutex, Dqn_uint ticket);
// NOTE: [$ALLO] Dqn_Allocator =====================================================================
typedef void *Dqn_Allocator_AllocProc(size_t size, uint8_t align, Dqn_ZeroMem zero_mem, void *user_context);
typedef void Dqn_Allocator_DeallocProc(void *ptr, size_t size, void *user_context);
struct Dqn_Allocator
{
void *user_context; // User assigned pointer that is passed into the allocator functions
Dqn_Allocator_AllocProc *alloc; // Memory allocating routine
Dqn_Allocator_DeallocProc *dealloc; // Memory deallocating routine
};
// NOTE: Macros ====================================================================================
#define Dqn_Allocator_NewArray(allocator, Type, count, zero_mem) (Type *)Dqn_Allocator_Alloc(allocator, sizeof(Type) * count, alignof(Type), zero_mem)
#define Dqn_Allocator_New(allocator, Type, zero_mem) (Type *)Dqn_Allocator_Alloc(allocator, sizeof(Type), alignof(Type), zero_mem)
// NOTE: API =======================================================================================
void *Dqn_Allocator_Alloc (Dqn_Allocator allocator, size_t size, uint8_t align, Dqn_ZeroMem zero_mem);
void Dqn_Allocator_Dealloc(Dqn_Allocator allocator, void *ptr, size_t size);
// NOTE: [$PRIN] Dqn_Print =========================================================================
enum Dqn_PrintStd
{
Dqn_PrintStd_Out,
Dqn_PrintStd_Err,
};
enum Dqn_PrintBold
{
Dqn_PrintBold_No,
Dqn_PrintBold_Yes,
};
struct Dqn_PrintStyle
{
Dqn_PrintBold bold;
bool colour;
uint8_t r, g, b;
};
enum Dqn_PrintESCColour
{
Dqn_PrintESCColour_Fg,
Dqn_PrintESCColour_Bg,
};
// NOTE: Print Style ===============================================================================
DQN_API Dqn_PrintStyle Dqn_Print_StyleColour (uint8_t r, uint8_t g, uint8_t b, Dqn_PrintBold bold);
DQN_API Dqn_PrintStyle Dqn_Print_StyleColourU32 (uint32_t rgb, Dqn_PrintBold bold);
DQN_API Dqn_PrintStyle Dqn_Print_StyleBold ();
// NOTE: Print Standard Out ========================================================================
#define Dqn_Print(string) Dqn_Print_Std(Dqn_PrintStd_Out, string)
#define Dqn_Print_F(fmt, ...) Dqn_Print_StdF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
#define Dqn_Print_FV(fmt, args) Dqn_Print_StdFV(Dqn_PrintStd_Out, fmt, args)
#define Dqn_Print_Style(style, string) Dqn_Print_StdStyle(Dqn_PrintStd_Out, style, string)
#define Dqn_Print_FStyle(style, fmt, ...) Dqn_Print_StdFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__)
#define Dqn_Print_FVStyle(style, fmt, args, ...) Dqn_Print_StdFVStyle(Dqn_PrintStd_Out, style, fmt, args)
#define Dqn_Print_Ln(string) Dqn_Print_StdLn(Dqn_PrintStd_Out, string)
#define Dqn_Print_LnF(fmt, ...) Dqn_Print_StdLnF(Dqn_PrintStd_Out, fmt, ## __VA_ARGS__)
#define Dqn_Print_LnFV(fmt, args) Dqn_Print_StdLnFV(Dqn_PrintStd_Out, fmt, args)
#define Dqn_Print_LnStyle(style, string) Dqn_Print_StdLnStyle(Dqn_PrintStd_Out, style, string);
#define Dqn_Print_LnFStyle(style, fmt, ...) Dqn_Print_StdLnFStyle(Dqn_PrintStd_Out, style, fmt, ## __VA_ARGS__);
#define Dqn_Print_LnFVStyle(style, fmt, args) Dqn_Print_StdLnFVStyle(Dqn_PrintStd_Out, style, fmt, args);
#define Dqn_Print_Err(string) Dqn_Print_Std(Dqn_PrintStd_Err, string)
#define Dqn_Print_ErrF(fmt, ...) Dqn_Print_StdF(Dqn_PrintStd_Err, fmt, ## __VA_ARGS__)
#define Dqn_Print_ErrFV(fmt, args) Dqn_Print_StdFV(Dqn_PrintStd_Err, fmt, args)
#define Dqn_Print_ErrStyle(style, string) Dqn_Print_StdStyle(Dqn_PrintStd_Err, style, string)
#define Dqn_Print_ErrFStyle(style, fmt, ...) Dqn_Print_StdFStyle(Dqn_PrintStd_Err, style, fmt, ## __VA_ARGS__)
#define Dqn_Print_ErrFVStyle(style, fmt, args, ...) Dqn_Print_StdFVStyle(Dqn_PrintStd_Err, style, fmt, args)
#define Dqn_Print_ErrLn(string) Dqn_Print_StdLn(Dqn_PrintStd_Err, string)
#define Dqn_Print_ErrLnF(fmt, ...) Dqn_Print_StdLnF(Dqn_PrintStd_Err, fmt, ## __VA_ARGS__)
#define Dqn_Print_ErrLnFV(fmt, args) Dqn_Print_StdLnFV(Dqn_PrintStd_Err, fmt, args)
#define Dqn_Print_ErrLnStyle(style, string) Dqn_Print_StdLnStyle(Dqn_PrintStd_Err, style, string);
#define Dqn_Print_ErrLnFStyle(style, fmt, ...) Dqn_Print_StdLnFStyle(Dqn_PrintStd_Err, style, fmt, ## __VA_ARGS__);
#define Dqn_Print_ErrLnFVStyle(style, fmt, args) Dqn_Print_StdLnFVStyle(Dqn_PrintStd_Err, style, fmt, args);
// NOTE: Print =====================================================================================
DQN_API void Dqn_Print_Std (Dqn_PrintStd std_handle, Dqn_Str8 string);
DQN_API void Dqn_Print_StdF (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API void Dqn_Print_StdFV (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API void Dqn_Print_StdStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_Str8 string);
DQN_API void Dqn_Print_StdFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, ...);
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_StdLn (Dqn_PrintStd std_handle, Dqn_Str8 string);
DQN_API void Dqn_Print_StdLnF (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API void Dqn_Print_StdLnFV (Dqn_PrintStd std_handle, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API void Dqn_Print_StdLnStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, Dqn_Str8 string);
DQN_API void Dqn_Print_StdLnFStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API void Dqn_Print_StdLnFVStyle (Dqn_PrintStd std_handle, Dqn_PrintStyle style, DQN_FMT_ATTRIB char const *fmt, va_list args);
// NOTE: ANSI Formatting Codes =====================================================================
Dqn_Str8 Dqn_Print_ESCColourStr8 (Dqn_PrintESCColour colour, uint8_t r, uint8_t g, uint8_t b);
Dqn_Str8 Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour colour, uint32_t value);
#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_ESCColourFg(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Fg, r, g, b).data
#define Dqn_Print_ESCColourBg(r, g, b) Dqn_Print_ESCColourStr8(Dqn_PrintESCColour_Bg, r, g, b).data
#define Dqn_Print_ESCColourFgU32Str8(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Fg, value)
#define Dqn_Print_ESCColourBgU32Str8(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Bg, value)
#define Dqn_Print_ESCColourFgU32(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Fg, value).data
#define Dqn_Print_ESCColourBgU32(value) Dqn_Print_ESCColourU32Str8(Dqn_PrintESCColour_Bg, value).data
#define Dqn_Print_ESCReset "\x1b[0m"
#define Dqn_Print_ESCBold "\x1b[1m"
#define Dqn_Print_ESCResetStr8 DQN_STR8(Dqn_Print_ESCReset)
#define Dqn_Print_ESCBoldStr8 DQN_STR8(Dqn_Print_ESCBold)
// NOTE: [$LLOG] Dqn_Log ==========================================================================
// NOTE: API
// @proc Dqn_LogProc
// @desc The logging procedure of the library. Users can override the default
// logging function by setting the logging function pointer in Dqn_Library.
// This function will be invoked every time a log is recorded using the
// following functions.
//
// @param[in] log_type This value is one of the Dqn_LogType values if the log
// was generated from one of the default categories. -1 if the log is not from
// one of the default categories.
enum Dqn_LogType
{
Dqn_LogType_Debug,
Dqn_LogType_Info,
Dqn_LogType_Warning,
Dqn_LogType_Error,
Dqn_LogType_Count,
};
/// RGBA
#define Dqn_LogTypeColourU32_Info 0x00'87'ff'ff // Blue
#define Dqn_LogTypeColourU32_Warning 0xff'ff'00'ff // Yellow
#define Dqn_LogTypeColourU32_Error 0xff'00'00'ff // Red
typedef void Dqn_LogProc(Dqn_Str8 type, int log_type, void *user_data, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va);
#define Dqn_Log_DebugF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Debug, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
#define Dqn_Log_InfoF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Info, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
#define Dqn_Log_WarningF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
#define Dqn_Log_ErrorF(fmt, ...) Dqn_Log_TypeFCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
#define Dqn_Log_DebugFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Debug, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_InfoFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Info, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_WarningFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Warning, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_ErrorFV(fmt, args) Dqn_Log_TypeFVCallSite(Dqn_LogType_Error, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_TypeFV(type, fmt, args) Dqn_Log_TypeFVCallSite(type, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_TypeF(type, fmt, ...) Dqn_Log_TypeFCallSite(type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
#define Dqn_Log_FV(type, fmt, args) Dqn_Log_FVCallSite(type, DQN_CALL_SITE, fmt, args)
#define Dqn_Log_F(type, fmt, ...) Dqn_Log_FCallSite(type, DQN_CALL_SITE, fmt, ## __VA_ARGS__)
DQN_API Dqn_Str8 Dqn_Log_MakeStr8 (Dqn_Allocator allocator, bool colour, Dqn_Str8 type, int log_type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API void Dqn_Log_TypeFVCallSite(Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va);
DQN_API void Dqn_Log_TypeFCallSite (Dqn_LogType type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API void Dqn_Log_FVCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va);
DQN_API void Dqn_Log_FCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...);

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)
// NOTE: [$DMAP] Dqn_DSMap =========================================================================
// NOTE: [$DMAP] Dqn_DSMap /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_DSMapKey Dqn_DSMap_KeyU64NoHash(uint64_t u64)
{
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)
#define DQN_CPP_BUILD_H
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$\ $$$$$$$\ $$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$\ $$$$$$$\
// $$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ $$ | $$ |\_$$ _|$$ | $$ __$$\
// $$ / \__|$$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |
// $$ | $$$$$$$ |$$$$$$$ | $$$$$$$\ |$$ | $$ | $$ | $$ | $$ | $$ |
// $$ | $$ ____/ $$ ____/ $$ __$$\ $$ | $$ | $$ | $$ | $$ | $$ |
// $$ | $$\ $$ | $$ | $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |
// \$$$$$$ |$$ | $$ | $$$$$$$ |\$$$$$$ |$$$$$$\ $$$$$$$$\ $$$$$$$ |
// \______/ \__| \__| \_______/ \______/ \______|\________|\_______/
//
// dqn_cppbuild.h -- Helper functions to make build scripts in C++
//
////////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h> // exit
struct Dqn_CPPBuildCompileFile
{
Dqn_Slice<Dqn_Str8> flags;
Dqn_Slice<Dqn_Str8> prefix_flags;
Dqn_Slice<Dqn_Str8> suffix_flags;
Dqn_Str8 input_file_path;
Dqn_Str8 output_file_path;
};
@ -11,15 +29,25 @@ struct Dqn_CPPBuildCompileFile
Dqn_Str8 const DQN_CPP_BUILD_OBJ_SUFFIX_OBJ = DQN_STR8(".obj");
Dqn_Str8 const DQN_CPP_BUILD_OBJ_SUFFIX_O = DQN_STR8(".o");
enum Dqn_CPPBuildCompiler
enum Dqn_CPPBuildFlagsStyle
{
Dqn_CPPBuildCompiler_MSVC,
Dqn_CPPBuildCompiler_GCC,
Dqn_CPPBuildFlagsStyle_MSVC,
Dqn_CPPBuildFlagsStyle_GCC,
Dqn_CPPBuildFlagsStyle_CLANG,
};
enum Dqn_CPPBuildAppendCompilerToCommand
{
Dqn_CPPBuildAppendCompilerToCommand_No,
Dqn_CPPBuildAppendCompilerToCommand_Yes,
};
struct Dqn_CPPBuildContext
{
Dqn_CPPBuildCompiler compiler;
// Dictates the type of compiler flags the functions may append to the
// build command line
Dqn_CPPBuildFlagsStyle flags_style;
Dqn_Str8 compile_file_obj_suffix;
Dqn_Slice<Dqn_CPPBuildCompileFile> compile_files;
Dqn_Slice<Dqn_Str8> compile_flags;
@ -46,17 +74,18 @@ enum Dqn_CPPBuildMode
Dqn_CPPBuildMode_CacheBuild,
};
DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Allocator allocator);
DQN_API Dqn_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 Dqn_Slice<Dqn_Str8> Dqn_CPPBuild_ToCommandLine (Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Arena *arena);
DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLineStr8(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Arena *arena);
DQN_API Dqn_CPPBuildAsyncResult Dqn_CPPBuild_Async (Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode);
DQN_API void Dqn_CPPBuild_ExecOrAbort (Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode);
#endif // DQN_CPP_BUILD_H
#if defined(DQN_CPP_BUILD_IMPLEMENTATION)
DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Allocator allocator)
DQN_API Dqn_Slice<Dqn_Str8> Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Arena *arena)
{
// NOTE: Check if object files are newer than the source files =================================
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context);
Dqn_Str8 result = {};
// NOTE: Check if object files are newer than the source files /////////////////////////////////
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
Dqn_Slice<Dqn_Str8> result = {};
Dqn_Slice<Dqn_CPPBuildCompileFile> dirtied_compile_files = build_context.compile_files;
if (mode == Dqn_CPPBuildMode_CacheBuild) {
@ -77,15 +106,15 @@ DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, D
// NOTE: Create the object file path
Dqn_Str8 file_stem = Dqn_Str8_FileNameNoExtension(file.input_file_path);
obj_file_name = Dqn_Str8_InitF(scratch.allocator, "%.*s%.*s", DQN_STR_FMT(file_stem), DQN_STR_FMT(compile_file_obj_suffix));
obj_file_name = Dqn_Str8_InitF(scratch.arena, "%.*s%.*s", DQN_STR_FMT(file_stem), DQN_STR_FMT(compile_file_obj_suffix));
}
Dqn_Str8 obj_file_path = obj_file_name;
if (build_context.build_dir.size)
obj_file_path = Dqn_FsPath_ConvertF(scratch.arena, "%.*s/%.*s", DQN_STR_FMT(build_context.build_dir), DQN_STR_FMT(obj_file_name));
obj_file_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s/%.*s", DQN_STR_FMT(build_context.build_dir), DQN_STR_FMT(obj_file_name));
Dqn_FsInfo file_info = Dqn_Fs_GetInfo(file.input_file_path);
Dqn_FsInfo obj_file_info = Dqn_Fs_GetInfo(obj_file_path);
Dqn_OSPathInfo file_info = Dqn_OS_PathInfo(file.input_file_path);
Dqn_OSPathInfo obj_file_info = Dqn_OS_PathInfo(obj_file_path);
if (obj_file_info.last_write_time_in_s >= file_info.last_write_time_in_s)
continue;
@ -96,88 +125,77 @@ DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLine(Dqn_CPPBuildContext build_context, D
return result;
}
// NOTE: Build the command line invocation =====================================================
// NOTE: Build the command line invocation /////////////////////////////////////////////////////
Dqn_Str8Builder builder = {};
builder.allocator = allocator;
DQN_FOR_UINDEX (index, build_context.compile_flags.size) {
Dqn_Str8 flag = build_context.compile_flags.data[index];
if (index)
Dqn_Str8Builder_AppendF(&builder, " ");
Dqn_Str8Builder_AppendRef(&builder, flag);
}
builder.arena = scratch.arena;
Dqn_Str8Builder_AppendRefArray(&builder, build_context.compile_flags);
DQN_FOR_UINDEX (index, build_context.include_dirs.size) {
DQN_FOR_UINDEX(index, build_context.include_dirs.size) {
Dqn_Str8 include_dir = build_context.include_dirs.data[index];
if (builder.count)
Dqn_Str8Builder_AppendF(&builder, " ");
Dqn_Str8Builder_AppendF(&builder, "/I %.*s", DQN_STR_FMT(include_dir));
Dqn_Str8Builder_AppendRef(&builder, DQN_STR8("-I"));
Dqn_Str8Builder_AppendRef(&builder, include_dir);
}
DQN_FOR_UINDEX (index, dirtied_compile_files.size) {
DQN_FOR_UINDEX(index, dirtied_compile_files.size) {
Dqn_CPPBuildCompileFile file = dirtied_compile_files.data[index];
Dqn_Str8 obj_file = {};
if (builder.count)
Dqn_Str8Builder_AppendF(&builder, " ");
if (file.output_file_path.size) {
switch (build_context.compiler) {
case Dqn_CPPBuildCompiler_MSVC: {
Dqn_Str8Builder_AppendF(&builder, "/Fo%.*s ", DQN_STR_FMT(file.output_file_path));
if (Dqn_Str8_HasData(file.output_file_path)) {
switch (build_context.flags_style) {
case Dqn_CPPBuildFlagsStyle_MSVC: {
Dqn_Str8Builder_AppendF(&builder, "-Fo%.*s", DQN_STR_FMT(file.output_file_path));
} break;
case Dqn_CPPBuildCompiler_GCC: {
Dqn_Str8Builder_AppendF(&builder, "-o %.*s ", DQN_STR_FMT(file.output_file_path));
case Dqn_CPPBuildFlagsStyle_GCC: /*FALLTHRU*/
case Dqn_CPPBuildFlagsStyle_CLANG: {
Dqn_Str8Builder_AppendF (&builder, "-o");
Dqn_Str8Builder_AppendRef(&builder, file.output_file_path);
} break;
}
}
DQN_FOR_UINDEX (flag_index, file.flags.size) {
Dqn_Str8 flag = file.flags.data[flag_index];
Dqn_Str8Builder_AppendF(&builder, "%s%.*s", flag_index ? " " : "", DQN_STR_FMT(flag));
}
// TODO(doyle): Check if the file exists, error if it doesn't
if (file.flags.size)
Dqn_Str8Builder_AppendF(&builder, " ");
Dqn_Str8Builder_AppendRefArray(&builder, file.prefix_flags);
Dqn_Str8Builder_AppendRef(&builder, file.input_file_path);
Dqn_Str8Builder_AppendRefArray(&builder, file.suffix_flags);
}
DQN_FOR_UINDEX (index, build_context.link_flags.size) {
Dqn_Str8 file = build_context.link_flags.data[index];
if (builder.count)
Dqn_Str8Builder_AppendF(&builder, " ");
Dqn_Str8Builder_AppendRef(&builder, file);
}
result = Dqn_Str8Builder_Build(&builder, allocator);
Dqn_Str8Builder_AppendRefArray(&builder, build_context.link_flags);
result = Dqn_Str8Builder_BuildSlice(&builder, arena);
return result;
}
DQN_API Dqn_Str8 Dqn_CPPBuild_ToCommandLineStr8(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode, Dqn_Arena *arena)
{
Dqn_Slice<Dqn_Str8> cmd_line = Dqn_CPPBuild_ToCommandLine(build_context, mode, arena);
Dqn_Str8 result = Dqn_Slice_Str8Render(arena, cmd_line, DQN_STR8(" ") /*separator*/);
return result;
}
DQN_API Dqn_CPPBuildAsyncResult Dqn_CPPBuild_Async(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode)
{
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.allocator);
Dqn_CPPBuildAsyncResult result = {};
if (!cmd.size)
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Slice<Dqn_Str8> cmd_line = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.arena);
Dqn_CPPBuildAsyncResult result = {};
if (!cmd_line.size)
return result;
if (!Dqn_Fs_MakeDir(build_context.build_dir)) {
if (!Dqn_OS_DirMake(build_context.build_dir)) {
result.status = Dqn_CPPBuildStatus_BuildDirectoryFailedToBeMade;
return result;
}
result.async_handle = Dqn_OS_ExecAsync(cmd, build_context.build_dir);
result.async_handle = Dqn_OS_ExecAsync(cmd_line, build_context.build_dir);
return result;
}
void Dqn_CPPBuild_ExecOrAbort(Dqn_CPPBuildContext build_context, Dqn_CPPBuildMode mode)
{
if (!Dqn_Fs_MakeDir(build_context.build_dir)) {
if (!Dqn_OS_DirMake(build_context.build_dir)) {
Dqn_Log_ErrorF("Failed to make build dir '%.*s'", DQN_STR_FMT(build_context.build_dir));
exit(-1);
}
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str8 cmd = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.allocator);
Dqn_OS_ExecOrAbort(cmd, build_context.build_dir);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Slice<Dqn_Str8> cmd_line = Dqn_CPPBuild_ToCommandLine(build_context, mode, scratch.arena);
Dqn_OS_ExecOrAbort(cmd_line, build_context.build_dir);
}
#endif // DQN_CPP_BUILD_IMPLEMENTATION

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)
{
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
if (!ptr || !size)
return;
#if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
__asan_poison_memory_region(ptr, size);
if (DQN_ASAN_VET_POISON) {
DQN_HARD_ASSERT(__asan_address_is_poisoned(ptr));
@ -14,7 +32,10 @@ DQN_API void Dqn_ASAN_PoisonMemoryRegion(void const volatile *ptr, Dqn_usize siz
DQN_API void Dqn_ASAN_UnpoisonMemoryRegion(void const volatile *ptr, Dqn_usize size)
{
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
if (!ptr || !size)
return;
#if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
__asan_unpoison_memory_region(ptr, size);
if (DQN_ASAN_VET_POISON) {
DQN_HARD_ASSERT(__asan_region_is_poisoned((void *)ptr, size) == 0);
@ -31,17 +52,18 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t
if (!arena)
return result;
static Dqn_TicketMutex mutex = {};
static Dqn_TicketMutex mutex = {};
Dqn_TicketMutex_Begin(&mutex);
HANDLE thread = GetCurrentThread();
result.process = GetCurrentProcess();
for (static bool init = false; !init; init = true) {
if (!g_dqn_library->win32_sym_initialised) {
g_dqn_library->win32_sym_initialised = true;
SymSetOptions(SYMOPT_LOAD_LINES);
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
Dqn_Log_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DQN_STR_FMT(error.msg));
}
}
@ -57,10 +79,8 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena);
Dqn_List<uint64_t> raw_frames = Dqn_List_Init<uint64_t>(scratch.arena, 32 /*chunk size*/);
while (raw_frames.count < limit) {
Dqn_FArray<uint64_t, 256> raw_frames = {};
while (raw_frames.size < limit) {
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64,
result.process,
thread,
@ -75,22 +95,53 @@ DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk(Dqn_Arena *arena, uint16_t
// NOTE: It might be useful one day to use frame.AddrReturn.Offset.
// If AddrPC.Offset == AddrReturn.Offset then we can detect recursion.
Dqn_List_Add(&raw_frames, frame.AddrPC.Offset);
Dqn_FArray_Add(&raw_frames, frame.AddrPC.Offset);
}
Dqn_TicketMutex_End(&mutex);
result.base_addr = Dqn_Arena_NewArray(arena, uint64_t, raw_frames.count, Dqn_ZeroMem_No);
for (Dqn_ListChunk<uint64_t> *chunk = raw_frames.head; chunk; chunk = chunk->next) {
DQN_MEMCPY(result.base_addr + result.size, chunk->data, sizeof(*chunk->data) * chunk->count);
result.size += DQN_CAST(uint16_t)chunk->count;
}
result.base_addr = Dqn_Arena_NewArray(arena, uint64_t, raw_frames.size, Dqn_ZeroMem_No);
result.size = DQN_CAST(uint16_t)raw_frames.size;
DQN_MEMCPY(result.base_addr, raw_frames.data, raw_frames.size * sizeof(raw_frames.data[0]));
#else
(void)limit; (void)arena;
#endif
return result;
}
DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *it, Dqn_StackTraceWalkResult *walk)
DQN_API Dqn_Str8 Dqn_StackTrace_WalkStr8CRT(uint16_t limit, uint16_t skip)
{
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_StackTraceWalkResult walk_result = Dqn_StackTrace_Walk(scratch.arena, limit);
Dqn_Str8 result = Dqn_StackTrace_WalkResultStr8CRT(&walk_result, skip);
return result;
}
static void Dqn_StackTrace_AddWalkToStr8Builder_(Dqn_StackTraceWalkResult const *walk, Dqn_Str8Builder *builder, Dqn_usize skip)
{
Dqn_StackTraceRawFrame raw_frame = {};
raw_frame.process = walk->process;
for (Dqn_usize index = skip; index < walk->size; index++) {
raw_frame.base_addr = walk->base_addr[index];
Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(builder->arena, raw_frame);
Dqn_Str8Builder_AppendF(builder, "%.*s(%I64u): %.*s%s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name), (index == walk->size - 1) ? "" : "\n");
}
}
DQN_API Dqn_Str8 Dqn_StackTrace_WalkStr8CRTNoScratch(uint16_t limit, uint16_t skip)
{
Dqn_Arena arena = {};
arena.flags |= Dqn_ArenaFlag_NoAllocTrack;
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(&arena, limit);
Dqn_Str8Builder builder = {};
builder.arena = &arena;
Dqn_StackTrace_AddWalkToStr8Builder_(&walk, &builder, skip);
Dqn_Str8 result = Dqn_Str8Builder_BuildCRT(&builder);
Dqn_Arena_Deinit(&arena);
return result;
}
DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *it, Dqn_StackTraceWalkResult const *walk)
{
bool result = false;
if (!it || !walk || !walk->base_addr || !walk->process)
@ -105,53 +156,43 @@ DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *
return result;
}
DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame(Dqn_Arena *arena, Dqn_StackTraceRawFrame raw_frame)
DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8(Dqn_Arena *arena, Dqn_StackTraceWalkResult const *walk, uint16_t skip)
{
#if defined(DQN_OS_WIN32)
// NOTE: Get line+filename =====================================================================
Dqn_Str8 result {};
if (!walk || !arena)
return result;
// TODO: Why does zero-initialising this with `line = {};` cause
// SymGetLineFromAddr64 function to fail once we are at
// __scrt_commain_main_seh and hit BaseThreadInitThunk frame? The
// line and file number are still valid in the result which we use, so,
// we silently ignore this error.
IMAGEHLP_LINEW64 line;
line.SizeOfStruct = sizeof(line);
DWORD line_displacement = 0;
SymGetLineFromAddrW64(raw_frame.process, raw_frame.base_addr, &line_displacement, &line);
// NOTE: Get function name =====================================================================
alignas(SYMBOL_INFOW) char buffer[sizeof(SYMBOL_INFOW) + (MAX_SYM_NAME * sizeof(wchar_t))] = {};
SYMBOL_INFOW *symbol = DQN_CAST(SYMBOL_INFOW *)buffer;
symbol->SizeOfStruct = sizeof(*symbol);
symbol->MaxNameLen = sizeof(buffer) - sizeof(*symbol);
uint64_t symbol_displacement = 0; // Offset to the beginning of the symbol to the address
SymFromAddrW(raw_frame.process, raw_frame.base_addr, &symbol_displacement, symbol);
// NOTE: Construct result ======================================================================
Dqn_Str16 file_name16 = Dqn_Str16{line.FileName, Dqn_CStr16_Size(line.FileName)};
Dqn_Str16 function_name16 = Dqn_Str16{symbol->Name, symbol->NameLen};
Dqn_StackTraceFrame result = {};
result.address = raw_frame.base_addr;
result.line_number = line.LineNumber;
result.file_name = Dqn_Win_Str16ToStr8(arena, file_name16);
result.function_name = Dqn_Win_Str16ToStr8(arena, function_name16);
#else
Dqn_StackTraceFrame result = {};
#endif
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
Dqn_Str8Builder builder = {};
builder.arena = scratch.arena;
Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
result = Dqn_Str8Builder_Build(&builder, arena);
return result;
}
DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8CRT(Dqn_StackTraceWalkResult const *walk, uint16_t skip)
{
Dqn_Str8 result {};
if (!walk)
return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8Builder builder = {};
builder.arena = scratch.arena;
Dqn_StackTrace_AddWalkToStr8Builder_(walk, &builder, skip);
result = Dqn_Str8Builder_BuildCRT(&builder);
return result;
}
DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames(Dqn_Arena *arena, uint16_t limit)
{
Dqn_Slice<Dqn_StackTraceFrame> result = {};
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(arena);
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, limit);
if (!arena)
return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, limit);
if (!walk.size)
return result;
@ -163,138 +204,173 @@ DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames(Dqn_Arena *arena
return result;
}
DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame(Dqn_Arena *arena, Dqn_StackTraceRawFrame raw_frame)
{
#if defined(DQN_OS_WIN32)
// NOTE: Get line+filename /////////////////////////////////////////////////////////////////////
// TODO: Why does zero-initialising this with `line = {};` cause
// SymGetLineFromAddr64 function to fail once we are at
// __scrt_commain_main_seh and hit BaseThreadInitThunk frame? The
// line and file number are still valid in the result which we use, so,
// we silently ignore this error.
IMAGEHLP_LINEW64 line;
line.SizeOfStruct = sizeof(line);
DWORD line_displacement = 0;
if (!SymGetLineFromAddrW64(raw_frame.process, raw_frame.base_addr, &line_displacement, &line)) {
line = {};
}
// NOTE: Get function name /////////////////////////////////////////////////////////////////////
alignas(SYMBOL_INFOW) char buffer[sizeof(SYMBOL_INFOW) + (MAX_SYM_NAME * sizeof(wchar_t))] = {};
SYMBOL_INFOW *symbol = DQN_CAST(SYMBOL_INFOW *)buffer;
symbol->SizeOfStruct = sizeof(*symbol);
symbol->MaxNameLen = sizeof(buffer) - sizeof(*symbol);
uint64_t symbol_displacement = 0; // Offset to the beginning of the symbol to the address
SymFromAddrW(raw_frame.process, raw_frame.base_addr, &symbol_displacement, symbol);
// NOTE: Construct result //////////////////////////////////////////////////////////////////////
Dqn_Str16 file_name16 = Dqn_Str16{line.FileName, Dqn_CStr16_Size(line.FileName)};
Dqn_Str16 function_name16 = Dqn_Str16{symbol->Name, symbol->NameLen};
Dqn_StackTraceFrame result = {};
result.address = raw_frame.base_addr;
result.line_number = line.LineNumber;
result.file_name = Dqn_Win_Str16ToStr8(arena, file_name16);
result.function_name = Dqn_Win_Str16ToStr8(arena, function_name16);
if (!Dqn_Str8_HasData(result.function_name))
result.function_name = DQN_STR8("<unknown function>");
if (!Dqn_Str8_HasData(result.file_name))
result.file_name = DQN_STR8("<unknown file>");
#else
Dqn_StackTraceFrame result = {};
#endif
return result;
}
DQN_API void Dqn_StackTrace_Print(uint16_t limit)
{
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Slice<Dqn_StackTraceFrame> stack_trace = Dqn_StackTrace_GetFrames(scratch.arena, limit);
for (Dqn_StackTraceFrame& frame : stack_trace)
for (Dqn_StackTraceFrame &frame : stack_trace)
Dqn_Print_ErrLnF("%.*s(%I64u): %.*s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name));
}
// NOTE: [$DEBG] Dqn_Debug =========================================================================
#if defined(DQN_LEAK_TRACING)
DQN_API void Dqn_Debug_TrackAlloc_(Dqn_Str8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted)
DQN_API void Dqn_StackTrace_ReloadSymbols()
{
#if defined(DQN_OS_WIN32)
HANDLE process = GetCurrentProcess();
SymRefreshModuleList(process);
#endif
}
// NOTE: [$DEBG] Dqn_Debug /////////////////////////////////////////////////////////////////////////
#if defined(DQN_LEAK_TRACKING)
DQN_API void Dqn_Debug_TrackAlloc(void *ptr, Dqn_usize size, bool leak_permitted)
{
if (!ptr)
return;
if (g_dqn_library->alloc_tracking_disabled)
return;
// NOTE: In this function we can create alloc records and hence it's
// possible for the alloc table to resize. This can cause a nested call
// into the tracking alloc function and dead-lock. We don't
// care about tracking these alloc records for the alloc table itself so we
// disable alloc tracking for the duration of this function.
// TODO(doyle): @robust This is not thread safe.
Dqn_TicketMutex_Begin(&g_dqn_library->alloc_table_mutex);
g_dqn_library->alloc_tracking_disabled = true;
DQN_DEFER {
g_dqn_library->alloc_tracking_disabled = false;
Dqn_TicketMutex_End(&g_dqn_library->alloc_table_mutex);
};
// NOTE: If the entry was not added, we are reusing a pointer that has been freed.
// TODO: Add API for always making the item but exposing a var to indicate if the item was newly created or it already existed.
Dqn_DSMap<Dqn_AllocRecord> *alloc_table = &g_dqn_library->alloc_table;
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(alloc_table, DQN_CAST(uintptr_t)ptr);
Dqn_AllocRecord *alloc = Dqn_DSMap_Find(alloc_table, key);
if (alloc) {
if ((alloc->flags & Dqn_AllocRecordFlag_Freed) == 0) {
Dqn_Str8 alloc_stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size);
Dqn_Str8 alloc_clean_stack_trace = Dqn_Str8_Slice(alloc_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_stack_trace.size);
Dqn_Str8 clean_stack_trace = Dqn_Str8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size);
// TODO: Add API for always making the item but exposing a var to indicate if the item was newly created or it
// already existed.
Dqn_Str8 stack_trace = Dqn_StackTrace_WalkStr8CRTNoScratch(128, 3 /*skip*/);
Dqn_DSMap<Dqn_DebugAlloc> *alloc_table = &g_dqn_library->alloc_table;
Dqn_DSMapResult<Dqn_DebugAlloc> alloc_entry = Dqn_DSMap_MakeKeyU64(alloc_table, DQN_CAST(uint64_t) ptr);
Dqn_DebugAlloc *alloc = alloc_entry.value;
if (alloc_entry.found) {
if ((alloc->flags & Dqn_DebugAllocFlag_Freed) == 0) {
Dqn_Str8 alloc_size = Dqn_U64ToByteSizeStr8(alloc_table->arena, alloc->size, Dqn_U64ByteSizeType_Auto);
Dqn_Str8 new_alloc_size = Dqn_U64ToByteSizeStr8(alloc_table->arena, size, Dqn_U64ByteSizeType_Auto);
DQN_HARD_ASSERTF(
alloc->flags & Dqn_AllocRecordFlag_Freed,
"\n\nThis pointer is already in the leak tracker, however it has not "
alloc->flags & Dqn_DebugAllocFlag_Freed,
"This pointer is already in the leak tracker, however it has not "
"been freed yet. This same pointer is being ask to be tracked "
"twice in the allocation table, e.g. one if its previous free "
"calls has not being marked freed with an equivalent call to "
"Dqn_Debug_TrackDealloc()\n"
"\n"
"The pointer (0x%p) originally allocated %_$$zu at:\n"
"The pointer (0x%p) originally allocated %.*s at:\n"
"\n"
"%.*s"
"%.*s\n"
"\n"
"The pointer is being allocated again at:\n"
"%.*s"
"The pointer is allocating %.*s again at:\n"
"\n"
"%.*s\n"
,
ptr, alloc->size,
DQN_STR_FMT(alloc_clean_stack_trace),
DQN_STR_FMT(clean_stack_trace));
ptr, DQN_STR_FMT(alloc_size),
DQN_STR_FMT(alloc->stack_trace),
DQN_STR_FMT(new_alloc_size),
DQN_STR_FMT(stack_trace));
}
// NOTE: Pointer was reused, clean up the prior entry
free(alloc->stack_trace);
free(alloc->freed_stack_trace);
free(alloc->stack_trace.data);
free(alloc->freed_stack_trace.data);
*alloc = {};
} else {
alloc = Dqn_DSMap_Make(alloc_table, key, /*found*/ nullptr);
}
alloc->ptr = ptr;
alloc->size = size;
alloc->stack_trace = stack_trace.data;
alloc->stack_trace_size = DQN_CAST(uint16_t)stack_trace.size;
// TODO(doyle): @robust The global flag is not multi-thread safe
if (leak_permitted || g_dqn_library->alloc_is_allowed_to_leak)
alloc->flags |= Dqn_AllocRecordFlag_LeakPermitted;
alloc->ptr = ptr;
alloc->size = size;
alloc->stack_trace = stack_trace;
alloc->flags |= leak_permitted ? Dqn_DebugAllocFlag_LeakPermitted : 0;
}
DQN_API void Dqn_Debug_TrackDealloc_(Dqn_Str8 stack_trace, void *ptr)
DQN_API void Dqn_Debug_TrackDealloc(void *ptr)
{
if (!ptr || g_dqn_library->alloc_tracking_disabled)
if (!ptr)
return;
Dqn_TicketMutex_Begin(&g_dqn_library->alloc_table_mutex);
DQN_DEFER { Dqn_TicketMutex_End(&g_dqn_library->alloc_table_mutex); };
Dqn_DSMap<Dqn_AllocRecord> *alloc_table = &g_dqn_library->alloc_table;
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(alloc_table, DQN_CAST(uintptr_t)ptr);
Dqn_AllocRecord *alloc = Dqn_DSMap_Find(alloc_table, key);
Dqn_Str8 stack_trace = Dqn_StackTrace_WalkStr8CRTNoScratch(128, 3 /*skip*/);
Dqn_DSMap<Dqn_DebugAlloc> *alloc_table = &g_dqn_library->alloc_table;
Dqn_DSMapResult<Dqn_DebugAlloc> alloc_entry = Dqn_DSMap_FindKeyU64(alloc_table, DQN_CAST(uintptr_t) ptr);
DQN_HARD_ASSERTF(alloc_entry.found,
"Allocated pointer can not be removed as it does not exist in the "
"allocation table. When this memory was allocated, the pointer was "
"not added to the allocation table [ptr=%p]",
ptr);
DQN_HARD_ASSERTF(alloc, "Allocated pointer can not be removed as it does not exist in the "
"allocation table. When this memory was allocated, the pointer was "
"not added to the allocation table [ptr=%p]",
ptr);
if (alloc->flags & Dqn_AllocRecordFlag_Freed) {
Dqn_Str8 alloc_stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size);
Dqn_Str8 alloc_clean_stack_trace = Dqn_Str8_Slice(alloc_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_stack_trace.size);
Dqn_Str8 alloc_freed_stack_trace = Dqn_Str8_Init(alloc->freed_stack_trace, alloc->freed_stack_trace_size);
Dqn_Str8 alloc_freed_clean_stack_trace = Dqn_Str8_Slice(alloc_freed_stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, alloc_freed_stack_trace.size);
Dqn_Str8 dealloc_stack_trace = Dqn_Str8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size);
DQN_HARD_ASSERTF((alloc->flags & Dqn_AllocRecordFlag_Freed) == 0,
"\n\nDouble free detected, pointer to free was already marked "
" as freed. Either the pointer was reallocated but not"
" traced, or, the pointer was freed twice.\n"
Dqn_DebugAlloc *alloc = alloc_entry.value;
if (alloc->flags & Dqn_DebugAllocFlag_Freed) {
Dqn_Str8 freed_size = Dqn_U64ToByteSizeStr8(alloc_table->arena, alloc->freed_size, Dqn_U64ByteSizeType_Auto);
DQN_HARD_ASSERTF((alloc->flags & Dqn_DebugAllocFlag_Freed) == 0,
"Double free detected, pointer to free was already marked "
"as freed. Either the pointer was reallocated but not "
"traced, or, the pointer was freed twice.\n"
"\n"
"The pointer (0x%p) originally allocated %_$$zu at:\n"
"The pointer (0x%p) originally allocated %.*s at:\n"
"\n"
"%.*s"
"%.*s\n"
"\n"
"The pointer was freed at:\n"
"\n"
"%.*s"
"%.*s\n"
"\n"
"The pointer is being freed again at:\n"
"%.*s"
"\n"
"%.*s\n"
,
ptr, alloc->freed_size,
DQN_STR_FMT(alloc_clean_stack_trace),
DQN_STR_FMT(alloc_freed_clean_stack_trace),
DQN_STR_FMT(dealloc_stack_trace));
ptr, DQN_STR_FMT(freed_size),
DQN_STR_FMT(alloc->stack_trace),
DQN_STR_FMT(alloc->freed_stack_trace),
DQN_STR_FMT(stack_trace));
}
alloc->flags |= Dqn_AllocRecordFlag_Freed;
alloc->freed_size = alloc->size;
alloc->freed_stack_trace = stack_trace.data;
alloc->freed_stack_trace_size = DQN_CAST(uint16_t)stack_trace.size;
DQN_ASSERT(!Dqn_Str8_HasData(alloc->freed_stack_trace));
alloc->flags |= Dqn_DebugAllocFlag_Freed;
alloc->freed_stack_trace = stack_trace;
}
DQN_API void Dqn_Debug_DumpLeaks()
@ -302,25 +378,24 @@ DQN_API void Dqn_Debug_DumpLeaks()
uint64_t leak_count = 0;
uint64_t leaked_bytes = 0;
for (Dqn_usize index = 1; index < g_dqn_library->alloc_table.occupied; index++) {
Dqn_DSMapSlot<Dqn_AllocRecord> *slot = g_dqn_library->alloc_table.slots + index;
Dqn_AllocRecord *alloc = &slot->value;
bool alloc_leaked = (alloc->flags & Dqn_AllocRecordFlag_Freed) == 0;
bool leak_permitted = (alloc->flags & Dqn_AllocRecordFlag_LeakPermitted);
Dqn_DSMapSlot<Dqn_DebugAlloc> *slot = g_dqn_library->alloc_table.slots + index;
Dqn_DebugAlloc *alloc = &slot->value;
bool alloc_leaked = (alloc->flags & Dqn_DebugAllocFlag_Freed) == 0;
bool leak_permitted = (alloc->flags & Dqn_DebugAllocFlag_LeakPermitted);
if (alloc_leaked && !leak_permitted) {
leaked_bytes += alloc->size;
leak_count++;
Dqn_Str8 stack_trace = Dqn_Str8_Init(alloc->stack_trace, alloc->stack_trace_size);
Dqn_Str8 clean_stack_trace = Dqn_Str8_Slice(stack_trace, g_dqn_library->stack_trace_offset_to_our_call_stack, stack_trace.size);
Dqn_Log_WarningF("Pointer (0x%p) leaked %_$$zu at:\n"
Dqn_Str8 alloc_size = Dqn_U64ToByteSizeStr8(g_dqn_library->alloc_table.arena, alloc->size, Dqn_U64ByteSizeType_Auto);
Dqn_Log_WarningF("Pointer (0x%p) leaked %.*s at:\n"
"%.*s",
alloc->ptr, alloc->size,
DQN_STR_FMT(clean_stack_trace));
alloc->ptr, DQN_STR_FMT(alloc_size),
DQN_STR_FMT(alloc->stack_trace));
}
}
if (leak_count) {
Dqn_Log_WarningF("There were %I64u leaked allocations totalling %_$$I64u", leak_count, leaked_bytes);
Dqn_Str8 leak_size = Dqn_U64ToByteSizeStr8(&g_dqn_library->arena, leaked_bytes, Dqn_U64ByteSizeType_Auto);
Dqn_Log_WarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DQN_STR_FMT(leak_size));
}
}
#endif // defined(DQN_LEAK_TRACING)
#endif // DQN_LEAK_TRACKING

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)
#define DQN_ASAN_POISON 0
#endif
@ -7,6 +29,7 @@
#endif
#define DQN_ASAN_POISON_ALIGNMENT 8
#if !defined(DQN_ASAN_POISON_GUARD_SIZE)
#define DQN_ASAN_POISON_GUARD_SIZE 128
#endif
@ -14,53 +37,11 @@ static_assert(Dqn_IsPowerOfTwoAligned(DQN_ASAN_POISON_GUARD_SIZE, DQN_ASAN_POISO
"ASAN poison guard size must be a power-of-two and aligned to ASAN's alignment"
"requirement (8 bytes)");
// NOTE: MSVC does not support the feature detection macro for instance so we
// compile it out
#if !defined(__has_feature)
#define __has_feature(x) 0
#endif
// NOTE: [$ASAN] Dqn_Asan ==========================================================================
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
#if DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
#include <sanitizer/asan_interface.h>
#endif
DQN_API void Dqn_ASAN_PoisonMemoryRegion(void const volatile *ptr, Dqn_usize size);
DQN_API void Dqn_ASAN_UnpoisonMemoryRegion(void const volatile *ptr, Dqn_usize size);
// NOTE: [$STKT] Dqn_StackTrace ====================================================================
// Create a stack trace at the calling site that these functions are invoked
// from.
//
// NOTE: API =======================================================================================
// @proc Dqn_StackTrace_Walk
// @desc This functions generates the stack trace as a series of U64's that
// represent the base address of the functions on the call-stack at the point
// of execution. These functions are stored in order from the current
// executing function first and the most ancestor function last in the walk.
// @proc Dqn_StackTrace_WalkResultIterate
// @desc Create an iterator to iterate the walk result and produce
// `Dqn_StackTraceRawFrame` from each level of the call-stack. These frames
// can then be converted into `Dqn_StackTraceFrame` which is a human readable
// representation of the frame.
#if 0
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, 128 /*limit*/);
for (Dqn_StackTraceWalkResultIterator it = {}; Dqn_StackTrace_WalkResultIterate(&it, &walk); ) {
Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(scratch.arena, it.raw_frame);
Dqn_Print_LnF("%.*s(%I64u): %.*s", DQN_STRING_FMT(frame.file_name), frame.line_number, DQN_STRING_FMT(frame.function_name));
}
#endif
// @proc Dqn_StackTrace_GetFrames
// @desc Create a stack trace at the point of execution and return the frames
// converted into its human readable format.
// @proc Dqn_StackTrace_RawFrameToFrame
// @desc Convert a raw frame from a stack trace walk into the human readable
// format (e.g. with function names, line number and file name).
// NOTE: [$STKT] Dqn_StackTrace ////////////////////////////////////////////////////////////////////
struct Dqn_StackTraceFrame
{
uint64_t address;
@ -88,45 +69,52 @@ struct Dqn_StackTraceWalkResultIterator
uint16_t index;
};
DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk (Dqn_Arena *arena, uint16_t limit);
DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *it, Dqn_StackTraceWalkResult *walk);
DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames (Dqn_Arena *arena, uint16_t limit);
DQN_API void Dqn_StackTrace_Print (uint16_t limit);
DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame (Dqn_Arena *arena, Dqn_StackTraceRawFrame raw_frame);
// NOTE: [$DEBG] Dqn_Debug =========================================================================
enum Dqn_AllocRecordFlag
// NOTE: [$DEBG] Dqn_Debug /////////////////////////////////////////////////////////////////////////
enum Dqn_DebugAllocFlag
{
Dqn_AllocRecordFlag_Freed = 1 << 0,
Dqn_AllocRecordFlag_LeakPermitted = 1 << 1,
Dqn_DebugAllocFlag_Freed = 1 << 0,
Dqn_DebugAllocFlag_LeakPermitted = 1 << 1,
};
struct Dqn_AllocRecord
struct Dqn_DebugAlloc
{
void *ptr; // Pointer to the allocation being tracked
Dqn_usize size; // Size of the allocation
Dqn_usize freed_size; // Store the size of the allocation when it is freed
char *stack_trace; // Stack trace at the point of allocation
char *freed_stack_trace; // Stack trace of where the allocation was freed
uint16_t stack_trace_size; // Size of the `stack_trace`
uint16_t freed_stack_trace_size; // Size of `freed_stack_trace`
uint16_t flags; // Bit flags from `Dqn_AllocRecordFlag`
char padding[2];
void *ptr; // 8 Pointer to the allocation being tracked
Dqn_usize size; // 16 Size of the allocation
Dqn_usize freed_size; // 24 Store the size of the allocation when it is freed
Dqn_Str8 stack_trace; // 40 Stack trace at the point of allocation
Dqn_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
uint16_t flags; // 72 Bit flags from `Dqn_DebugAllocFlag`
};
static_assert(sizeof(Dqn_AllocRecord) == 48 || sizeof(Dqn_AllocRecord) == 28, // NOTE: 64 bit vs 32 bit pointers respectively
static_assert(sizeof(Dqn_DebugAlloc) == 64 || sizeof(Dqn_DebugAlloc) == 32, // NOTE: 64 bit vs 32 bit pointers respectively
"We aim to keep the allocation record as light as possible as "
"memory tracking can get expensive. Enforce that there is no "
"unexpected padding.");
#if defined(DQN_LEAK_TRACING)
#define Dqn_Debug_TrackAlloc(ptr, size, leak_permitted) Dqn_Debug_TrackAlloc_ (Dqn_Str8_InitCString8(b_stacktrace_get_string()), ptr, size, leak_permitted)
#define Dqn_Debug_TrackDealloc(ptr) Dqn_Debug_TrackDealloc_(Dqn_Str8_InitCString8(b_stacktrace_get_string()), ptr)
// NOTE: [$ASAN] Dqn_Asan //////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_ASAN_PoisonMemoryRegion (void const volatile *ptr, Dqn_usize size);
DQN_API void Dqn_ASAN_UnpoisonMemoryRegion (void const volatile *ptr, Dqn_usize size);
DQN_API void Dqn_Debug_TrackAlloc_(Dqn_Str8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted);
DQN_API void Dqn_Debug_TrackDealloc_(Dqn_Str8 stack_trace, void *ptr);
DQN_API void Dqn_Debug_DumpLeaks();
// NOTE: [$STKT] Dqn_StackTrace ////////////////////////////////////////////////////////////////////
DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk (Dqn_Arena *arena, uint16_t limit);
DQN_API Dqn_Str8 Dqn_StackTrace_WalkStr8CRT (uint16_t limit, uint16_t skip);
DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *it, Dqn_StackTraceWalkResult const *walk);
DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8 (Dqn_Arena *arena, Dqn_StackTraceWalkResult const *walk, uint16_t skip);
DQN_API Dqn_Str8 Dqn_StackTrace_WalkResultStr8CRT(Dqn_StackTraceWalkResult const *walk, uint16_t skip);
DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames (Dqn_Arena *arena, uint16_t limit);
DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame (Dqn_Arena *arena, Dqn_StackTraceRawFrame raw_frame);
DQN_API void Dqn_StackTrace_Print (uint16_t limit);
DQN_API void Dqn_StackTrace_ReloadSymbols ();
// NOTE: [$DEBG] Dqn_Debug /////////////////////////////////////////////////////////////////////////
#if defined(DQN_LEAK_TRACKING)
DQN_API void Dqn_Debug_TrackAlloc (void *ptr, Dqn_usize size, bool alloc_can_leak);
DQN_API void Dqn_Debug_TrackDealloc (void *ptr);
DQN_API void Dqn_Debug_DumpLeaks ();
#else
#define Dqn_Debug_TrackAlloc(...)
#define Dqn_Debug_TrackDealloc(...)
#define Dqn_Debug_DumpLeaks(...)
#define Dqn_Debug_TrackAlloc(ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
#define Dqn_Debug_TrackDealloc(ptr) do { (void)ptr; } while (0)
#define Dqn_Debug_DumpLeaks() do { } while (0)
#endif

1091
dqn_docs.cpp Normal file

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
#ifdef STB_SPRINTF_IMPLEMENTATION
@ -132,7 +147,6 @@ static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uin
return (stbsp__uint32)(sn - s);
}
#if defined(__clang__)
__attribute__((no_sanitize("undefined")))
#endif
@ -1160,7 +1174,7 @@ done:
#undef stbsp__flush_cb
#undef stbsp__cb_buf_clamp
// ============================================================================
// ////////////////////////////////////////////////////////////////////////////
// wrapper functions
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...)
@ -1264,7 +1278,7 @@ STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt,
return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
}
// =======================================================================
// ///////////////////////////////////////////////////////////////////////
// low level float utility functions
#ifndef STB_SPRINTF_NOFLOAT
@ -1694,4 +1708,4 @@ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/
#endif // DQN_STB_SPRINTF_HEADER_ONLY
#endif // !defined(DQN_USE_STD_PRINTF) && !defined(DQN_STB_SPRINTF_HEADER_ONLY)

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)
#include <errno.h> // errno
#include <fcntl.h> // O_RDONLY ... etc
@ -18,21 +38,38 @@
#endif
#endif
// NOTE: [$STBS] stb_sprintf =======================================================================
#if defined(DQN_COMPILER_MSVC)
// NOTE: stb_sprintf assumes c-string literals are 4 byte aligned which is
// always true, however, reading past the end of a string whose size is not
// a multiple of 4 is UB causing ASAN to complain. This is practically safe
// and guaranteed by all compilers so we mute this.
//
// ==12072==ERROR: AddressSanitizer: global-buffer-overflow on address
// READ of size 4 at 0x7ff6f442a0d8 thread T0
// #0 0x7ff6f42d3be8 in stbsp_vsprintfcb C:\Home\Code\dqn\dqn_external.cpp:199
#define STBSP__ASAN __declspec(no_sanitize_address)
#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.
// NOTE: stb_sprintf assumes c-string literals are 4 byte aligned which is
// always true, however, reading past the end of a string whose size is not
// a multiple of 4 is UB causing ASAN to complain. This is practically safe
// and guaranteed by all compilers so we mute this.
//
// ==12072==ERROR: AddressSanitizer: global-buffer-overflow on address
// READ of size 4 at 0x7ff6f442a0d8 thread T0
// #0 0x7ff6f42d3be8 in stbsp_vsprintfcb C:\Home\Code\dqn\dqn_external.cpp:199
#define STBSP__ASAN __declspec(no_sanitize_address)
#endif
// stb_sprintf - v1.10 - public domain snprintf() implementation
// originally by Jeff Roberts / RAD Game Tools, 2015/10/20
// http://github.com/nothings/stb
@ -96,7 +133,7 @@ As a comparison, when using MSVC static libs, calling sprintf drags
in 16K.
API:
====
////
int stbsp_sprintf( char * buf, char const * fmt, ... )
int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
Convert an arg list into a buffer. stbsp_snprintf always returns
@ -119,7 +156,7 @@ void stbsp_set_separators( char comma, char period )
Set the comma and period characters to use.
FLOATS/DOUBLES:
===============
///////////////
This code uses a internal float->ascii conversion method that uses
doubles with error correction (double-doubles, for ~105 bits of
precision). This conversion is round-trip perfect - that is, an atof
@ -134,13 +171,13 @@ If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
and you'll save 4K of code space.
64-BIT INTS:
============
////////////
This library also supports 64-bit integers and you can use MSVC style or
GCC style indicators (%I64d or %lld). It supports the C99 specifiers
for size_t and ptr_diff_t (%jd %zd) as well.
EXTRAS:
=======
///////
Like some GCCs, for integers and floats, you can use a ' (single quote)
specifier and commas will be inserted on the thousands: "%'d" on 12345
would print 12,345.
@ -157,7 +194,7 @@ In addition to octal and hexadecimal conversions, you can print
integers in binary: "%b" for 256 would print 100.
PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
===================================================================
///////////////////////////////////////////////////////////////////
"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
"%24d" across all 32-bit ints (4.5x/4.2x faster)
"%x" across all 32-bit ints (4.5x/3.8x faster)
@ -248,5 +285,4 @@ STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char c
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);
#endif // STB_SPRINTF_H_INCLUDE
#endif // !defined(DQN_USE_STD_PRINTF)

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/
DQN_API uint32_t Dqn_FNV1A32_Iterate(void const *bytes, Dqn_usize size, uint32_t hash)
{
@ -28,7 +43,7 @@ DQN_API uint64_t Dqn_FNV1A64_Hash(void const *bytes, Dqn_usize size)
return result;
}
// NOTE: [$MMUR] Dqn_MurmurHash3 ===================================================================
// NOTE: [$MMUR] Dqn_MurmurHash3 ///////////////////////////////////////////////////////////////////
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
#define DQN_MMH3_ROTL32(x, y) _rotl(x, y)
#define DQN_MMH3_ROTL64(x, y) _rotl64(x, y)
@ -244,4 +259,3 @@ DQN_API Dqn_MurmurHash3 Dqn_MurmurHash3_x64U128(void const *key, int len, uint32
result.e[1] = h2;
return result;
}

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)
#define DQN_FNV1A32_SEED 2166136261U
#endif
@ -15,24 +27,18 @@
#define DQN_FNV1A64_SEED 14695981039346656037ULL
#endif
DQN_API uint32_t Dqn_FNV1A32_Hash (void const *bytes, Dqn_usize size);
DQN_API uint64_t Dqn_FNV1A64_Hash (void const *bytes, Dqn_usize size);
DQN_API uint32_t Dqn_FNV1A32_Iterate(void const *bytes, Dqn_usize size, uint32_t hash);
DQN_API uint64_t Dqn_FNV1A64_Iterate(void const *bytes, Dqn_usize size, uint64_t hash);
// NOTE: [$MMUR] Dqn_MurmurHash3 ===================================================================
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author (Austin Appleby) hereby disclaims copyright to this source
// code.
//
// Note - The x86 and x64 versions do _not_ produce the same results, as the
// algorithms are optimized for their respective platforms. You can still
// compile and run any of them on any platform, but your performance with the
// non-native version will be less than optimal.
// NOTE: [$MMUR] Dqn_MurmurHash3 ///////////////////////////////////////////////////////////////////
struct Dqn_MurmurHash3 { uint64_t e[2]; };
DQN_API uint32_t Dqn_MurmurHash3_x86U32 (void const *key, int len, uint32_t seed);
DQN_API Dqn_MurmurHash3 Dqn_MurmurHash3_x64U128(void const *key, int len, uint32_t seed);
#define Dqn_MurmurHash3_x64U128AsU64(key, len, seed) (Dqn_MurmurHash3_x64U128(key, len, seed).e[0])
#define Dqn_MurmurHash3_x64U128AsU32(key, len, seed) (DQN_CAST(uint32_t)Dqn_MurmurHash3_x64U128(key, len, seed).e[0])
// NOTE: [$FNV1] Dqn_FNV1A /////////////////////////////////////////////////////////////////////////
DQN_API uint32_t Dqn_FNV1A32_Hash (void const *bytes, Dqn_usize size);
DQN_API uint64_t Dqn_FNV1A64_Hash (void const *bytes, Dqn_usize size);
DQN_API uint32_t Dqn_FNV1A32_Iterate (void const *bytes, Dqn_usize size, uint32_t hash);
DQN_API uint64_t Dqn_FNV1A64_Iterate (void const *bytes, Dqn_usize size, uint64_t hash);
// NOTE: [$MMUR] Dqn_MurmurHash3 ///////////////////////////////////////////////////////////////////
DQN_API uint32_t Dqn_MurmurHash3_x86U32 (void const *key, int len, uint32_t seed);
DQN_API Dqn_MurmurHash3 Dqn_MurmurHash3_x64U128 (void const *key, int len, uint32_t seed);
#define Dqn_MurmurHash3_x64U128AsU64(key, len, seed) (Dqn_MurmurHash3_x64U128(key, len, seed).e[0])
#define Dqn_MurmurHash3_x64U128AsU32(key, len, seed) (DQN_CAST(uint32_t)Dqn_MurmurHash3_x64U128(key, len, seed).e[0])

File diff suppressed because it is too large Load Diff

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; };
DQN_API uint32_t Dqn_PCG32_Next (Dqn_PCG32 *rng);
DQN_API uint64_t Dqn_PCG32_Next64 (Dqn_PCG32 *rng);
DQN_API uint32_t Dqn_PCG32_Range (Dqn_PCG32 *rng, uint32_t low, uint32_t high);
DQN_API Dqn_f32 Dqn_PCG32_NextF32(Dqn_PCG32 *rng);
DQN_API Dqn_f64 Dqn_PCG32_NextF64(Dqn_PCG32 *rng);
DQN_API void Dqn_PCG32_Seed (Dqn_PCG32 *rng, uint64_t seed);
DQN_API void Dqn_PCG32_Advance(Dqn_PCG32 *rng, uint64_t delta);
#if !defined(DQN_NO_JSON_BUILDER)
// NOTE: [$JSON] Dqn_JSONBuilder ===================================================================
// Basic helper class to construct JSON output to a string
// TODO(dqn): We need to write tests for this
//
// NOTE: API =======================================================================================
// @proc Dqn_JSONBuilder_Build
// @desc Convert the internal JSON buffer in the builder into a string.
// @param[in] arena The allocator to use to build the string
// @proc Dqn_JSONBuilder_KeyValue, Dqn_JSONBuilder_KeyValueF
// @desc Add a JSON key value pair untyped. The value is emitted directly
// without checking the contents of value.
//
// All other functions internally call into this function which is the main
// workhorse of the builder.
// @proc Dqn_JSON_Builder_ObjectEnd
// @desc End a JSON object in the builder, generates internally a '}' string
// @proc Dqn_JSON_Builder_ArrayEnd
// @desc End a JSON array in the builder, generates internally a ']' string
// @proc Dqn_JSONBuilder_LiteralNamed
// @desc Add a named JSON key-value object whose value is directly written to
// the following '"<key>": <value>' (e.g. useful for emitting the 'null'
// value)
// @proc Dqn_JSONBuilder_U64Named, Dqn_JSONBuilder_U64,
// Dqn_JSONBuilder_I64Named, Dqn_JSONBuilder_I64,
// Dqn_JSONBuilder_F64Named, Dqn_JSONBuilder_F64,
// Dqn_JSONBuilder_BoolNamed, Dqn_JSONBuilder_Bool,
// @desc Add the named JSON data type as a key-value object. Generates
// internally the string '"<key>": <value>'
// NOTE: [$JSON] Dqn_JSONBuilder ///////////////////////////////////////////////////////////////////
enum Dqn_JSONBuilderItem
{
Dqn_JSONBuilderItem_Empty,
@ -71,138 +46,10 @@ struct Dqn_JSONBuilder
int spaces_per_indent; // The number of spaces per indent level
Dqn_JSONBuilderItem last_item;
};
#define Dqn_JSONBuilder_Object(builder) \
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBegin(builder), \
Dqn_JSONBuilder_ObjectEnd(builder))
#define Dqn_JSONBuilder_ObjectNamed(builder, name) \
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBeginNamed(builder, name), \
Dqn_JSONBuilder_ObjectEnd(builder))
#define Dqn_JSONBuilder_Array(builder) \
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBegin(builder), \
Dqn_JSONBuilder_ArrayEnd(builder))
#define Dqn_JSONBuilder_ArrayNamed(builder, name) \
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBeginNamed(builder, name), \
Dqn_JSONBuilder_ArrayEnd(builder))
DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init (Dqn_Allocator allocator, int spaces_per_indent);
DQN_API Dqn_Str8 Dqn_JSONBuilder_Build (Dqn_JSONBuilder const *builder, Dqn_Allocator allocator);
DQN_API void Dqn_JSONBuilder_KeyValue (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
DQN_API void Dqn_JSONBuilder_KeyValueF (Dqn_JSONBuilder *builder, Dqn_Str8 key, char const *value_fmt, ...);
DQN_API void Dqn_JSONBuilder_ObjectBeginNamed(Dqn_JSONBuilder *builder, Dqn_Str8 name);
DQN_API void Dqn_JSONBuilder_ObjectEnd (Dqn_JSONBuilder *builder);
DQN_API void Dqn_JSONBuilder_ArrayBeginNamed (Dqn_JSONBuilder *builder, Dqn_Str8 name);
DQN_API void Dqn_JSONBuilder_ArrayEnd (Dqn_JSONBuilder *builder);
DQN_API void Dqn_JSONBuilder_Str8Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
DQN_API void Dqn_JSONBuilder_LiteralNamed (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
DQN_API void Dqn_JSONBuilder_U64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, uint64_t value);
DQN_API void Dqn_JSONBuilder_I64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, int64_t value);
DQN_API void Dqn_JSONBuilder_F64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, double value, int decimal_places);
DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBuilder *builder, Dqn_Str8 key, bool value);
#define Dqn_JSONBuilder_ObjectBegin(builder) Dqn_JSONBuilder_ObjectBeginNamed(builder, DQN_STRING8(""))
#define Dqn_JSONBuilder_ArrayBegin(builder) Dqn_JSONBuilder_ArrayBeginNamed(builder, DQN_STRING8(""))
#define Dqn_JSONBuilder_Str8(builder, value) Dqn_JSONBuilder_Str8Named(builder, DQN_STRING8(""), value)
#define Dqn_JSONBuilder_Literal(builder, value) Dqn_JSONBuilder_LiteralNamed(builder, DQN_STRING8(""), value)
#define Dqn_JSONBuilder_U64(builder, value) Dqn_JSONBuilder_U64Named(builder, DQN_STRING8(""), value)
#define Dqn_JSONBuilder_I64(builder, value) Dqn_JSONBuilder_I64Named(builder, DQN_STRING8(""), value)
#define Dqn_JSONBuilder_F64(builder, value) Dqn_JSONBuilder_F64Named(builder, DQN_STRING8(""), value)
#define Dqn_JSONBuilder_Bool(builder, value) Dqn_JSONBuilder_BoolNamed(builder, DQN_STRING8(""), value)
#endif // !defined(DQN_NO_JSON_BUIDLER)
#if !defined(DQN_NO_BIN)
// NOTE: [$BHEX] Dqn_Bin ===========================================================================
// NOTE: API =======================================================================================
// @proc Dqn_Bin_U64ToHexU64Str8
// @desc Convert a 64 bit number to a hex string
// @param[in] number Number to convert to hexadecimal representation
// @param[in] flags Bit flags from Dqn_BinHexU64Str8Flags to customise the
// output of the hexadecimal string.
// @return The hexadecimal representation of the number. This string is always
// null-terminated.
// @proc Dqn_Bin_U64ToHex
// @copybrief Dqn_Bin_U64ToHexU64Str8
// @param[in] allocator The memory allocator to use for the memory of the
// hexadecimal string.
// @copyparams Dqn_Bin_U64ToHexU64Str8
// @proc Dqn_Bin_HexBufferToU64
// @desc Convert a hexadecimal string a 64 bit value.
// Asserts if the hex string is too big to be converted into a 64 bit number.
// @proc Dqn_Bin_HexToU64
// @copydoc Dqn_Bin_HexToU64
// @proc Dqn_Bin_BytesToHexBuffer
// @desc Convert a binary buffer into its hex representation into dest.
//
// The dest buffer must be large enough to contain the hex representation, i.e.
// atleast (src_size * 2).
//
// @return True if the conversion into the dest buffer was successful, false
// otherwise (e.g. invalid arguments).
// @proc Dqn_Bin_BytesToHexBufferArena
// @desc Convert a series of bytes into a string
// @return A null-terminated hex string, null pointer if allocation failed
// @proc Dqn_Bin_BytesToHexArena
// @copydoc Dqn_Bin_BytesToHexBufferArena
// @return A hex string, the string is invalid if conversion failed.
// @proc Dqn_Bin_HexBufferToBytes
// @desc Convert a hex string into binary at `dest`.
//
// The dest buffer must be large enough to contain the binary representation,
// i.e. atleast ceil(hex_size / 2). This function will strip whitespace,
// leading 0x/0X prefix from the string before conversion.
//
// @param[in] hex The hexadecimal string to convert
// @param[in] hex_size Size of the hex buffer. This function can handle an odd
// size hex string i.e. "fff" produces 0xfff0.
// @param[out] dest Buffer to write the bytes to
// @param[out] dest_size Maximum number of bytes to write to dest
//
// @return The number of bytes written to `dest_size`, this value will *never*
// be greater than `dest_size`.
// @proc Dqn_Bin_HexToBytes
// @desc Str8 variant of @see Dqn_Bin_HexBufferToBytes
// @proc Dqn_Bin_Str8HexBufferToBytesUnchecked
// @desc Unchecked variant of @see Dqn_Bin_HexBufferToBytes
//
// This function skips some string checks, it assumes the hex is a valid hex
// stream and that the arguments are valid e.g. no trimming or 0x prefix
// stripping is performed
// @proc Dqn_Bin_Str8
// @desc Str8 variant of @see Dqn_Bin_HexBufferToBytesUnchecked
// @proc Dqn_Bin_HexBufferToBytesArena
// Dynamic allocating variant of @see Dqn_Bin_HexBufferToBytesUnchecked
//
// @param[in] arena The arena to allocate the bytes from
// @param[in] hex Hex string to convert into bytes
// @param[in] size Size of the hex string
// @param[out] real_size The size of the buffer returned by the function
//
// @return The byte representation of the hex string.
// @proc Dqn_Bin_HexToBytesArena
// @copybrief Dqn_Bin_HexBufferToBytesArena
//
// @param[in] arena The arena to allocate the bytes from
// @param[in] hex Hex string to convert into bytes
//
// @return The byte representation of the hex string.
// NOTE: [$BHEX] Dqn_Bin ///////////////////////////////////////////////////////////////////////////
struct Dqn_BinHexU64Str8
{
char data[2 /*0x*/ + 16 /*hex*/ + 1 /*null-terminator*/];
@ -214,37 +61,15 @@ enum Dqn_BinHexU64Str8Flags
Dqn_BinHexU64Str8Flags_No0xPrefix = 1 << 0, /// Remove the 0x prefix from the string
Dqn_BinHexU64Str8Flags_UppercaseHex = 1 << 1, /// Use uppercase ascii characters for hex
};
DQN_API char const * Dqn_Bin_HexBufferTrim0x (char const *hex, Dqn_usize size, Dqn_usize *real_size);
DQN_API Dqn_Str8 Dqn_Bin_HexTrim0x (Dqn_Str8 string);
DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8 (uint64_t number, uint32_t flags);
DQN_API Dqn_Str8 Dqn_Bin_U64ToHex (Dqn_Allocator allocator, uint64_t number, uint32_t flags);
DQN_API uint64_t Dqn_Bin_HexBufferToU64 (char const *hex, Dqn_usize size);
DQN_API uint64_t Dqn_Bin_HexToU64 (Dqn_Str8 hex);
DQN_API Dqn_Str8 Dqn_Bin_BytesToHexArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
DQN_API char * Dqn_Bin_BytesToHexBufferArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
DQN_API bool Dqn_Bin_BytesToHexBuffer (void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size);
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked(char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
DQN_API char * Dqn_Bin_HexBufferToBytesArena (Dqn_Arena *arena, char const *hex, Dqn_usize hex_size, Dqn_usize *real_size);
DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
DQN_API Dqn_usize Dqn_Bin_HexToBytes (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
DQN_API Dqn_Str8 Dqn_Bin_HexToBytesArena (Dqn_Arena *arena, Dqn_Str8 hex);
#endif // !defined(DQN_NO_BIN)
// NOTE: [$BSEA] Dqn_BinarySearch ==================================================================
// NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
template <typename T>
using Dqn_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs);
template <typename T>
bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
// TODO(doyle): Implement lower/upper bound searching
enum Dqn_BinarySearchType
{
// Index of the match. If no match is found, found is set to false and the
@ -253,14 +78,26 @@ enum Dqn_BinarySearchType
// [last index + 1] of the array).
Dqn_BinarySearchType_Match,
// Index of the first element in the array that is `<= find`. If no such
// Index of the first element in the array that is `element >= find`. If no such
// item is found or the array is empty, then, the index is set to the array
// size and found is set to `false`.
//
// For example:
// int array[] = {0, 1, 2, 3, 4, 5};
// Dqn_BinarySearchResult result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4, Dqn_BinarySearchType_LowerBound);
// printf("%zu\n", result.index); // Prints index '4'
Dqn_BinarySearchType_LowerBound,
// Index of the first element in the array that is `> find`. If no such
// Index of the first element in the array that is `element > find`. If no such
// item is found or the array is empty, then, the index is set to the array
// size and found is set to `false`.
//
// For example:
// int array[] = {0, 1, 2, 3, 4, 5};
// Dqn_BinarySearchResult result = Dqn_BinarySearch(array, DQN_ARRAY_UCOUNT(array), 4, Dqn_BinarySearchType_UpperBound);
// printf("%zu\n", result.index); // Prints index '5'
Dqn_BinarySearchType_UpperBound,
};
@ -270,287 +107,45 @@ struct Dqn_BinarySearchResult
Dqn_usize index;
};
template <typename T>
Dqn_BinarySearchResult Dqn_BinarySearch(T const *array,
Dqn_usize array_size,
T const &find,
Dqn_BinarySearchType type = Dqn_BinarySearchType_Match,
Dqn_BinarySearchLessThanProc<T> less_than = Dqn_BinarySearch_DefaultLessThan);
template <typename T>
bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
{
bool result = lhs < rhs;
return result;
}
template <typename T>
Dqn_BinarySearchResult
Dqn_BinarySearch(T const *array,
Dqn_usize array_size,
T const &find,
Dqn_BinarySearchType type,
Dqn_BinarySearchLessThanProc<T> less_than)
{
Dqn_BinarySearchResult result = {};
if (!array || array_size <= 0)
return result;
T const *end = array + array_size;
T const *first = array;
T const *last = end;
while (first != last) {
Dqn_usize count = last - first;
T const *it = first + (count / 2);
bool advance_first = false;
if (type == Dqn_BinarySearchType_UpperBound)
advance_first = !less_than(find, it[0]);
else
advance_first = less_than(it[0], find);
if (advance_first)
first = it + 1;
else
last = it;
}
switch (type) {
case Dqn_BinarySearchType_Match: {
result.found = first != end && !less_than(find, *first);
} break;
case Dqn_BinarySearchType_LowerBound: /*FALLTHRU*/
case Dqn_BinarySearchType_UpperBound: {
result.found = first != end;
} break;
}
result.index = first - array;
return result;
}
// NOTE: [$BITS] Dqn_Bit ===========================================================================
DQN_API void Dqn_Bit_UnsetInplace(uint32_t *flags, uint32_t bitfield);
DQN_API void Dqn_Bit_SetInplace (uint32_t *flags, uint32_t bitfield);
DQN_API bool Dqn_Bit_IsSet (uint32_t bits, uint32_t bits_to_set);
DQN_API bool Dqn_Bit_IsNotSet (uint32_t bits, uint32_t bits_to_check);
// NOTE: [$SAFE] Dqn_Safe ==========================================================================
// @proc Dqn_Safe_AddI64, Dqn_Safe_MulI64
// @desc Add/multiply 2 I64's together, safe asserting that the operation does
// not overflow.
// @return The result of operation, INT64_MAX if it overflowed.
// @proc Dqn_Safe_AddU64, Dqn_Safe_MulU64
// @desc Add/multiply 2 U64's together, safe asserting that the operation does
// not overflow.
// @return The result of operation, UINT64_MAX if it overflowed.
// @proc Dqn_Safe_SubU64, Dqn_Safe_SubU32
// @desc Subtract 2xU64 or 2xU32's together, safe asserting that the operation
// does not overflow.
// @return The result of operation, 0 if it overflowed.
// @proc Dqn_Safe_SaturateCastUSizeToInt,
// Dqn_Safe_SaturateCastUSizeToI8,
// Dqn_Safe_SaturateCastUSizeToI16,
// Dqn_Safe_SaturateCastUSizeToI32,
// Dqn_Safe_SaturateCastUSizeToI64
//
// Dqn_Safe_SaturateCastU64ToUInt,
// Dqn_Safe_SaturateCastU64ToU8,
// Dqn_Safe_SaturateCastU64ToU16,
// Dqn_Safe_SaturateCastU64ToU32,
//
// Dqn_Safe_SaturateCastUSizeToU8,
// Dqn_Safe_SaturateCastUSizeToU16,
// Dqn_Safe_SaturateCastUSizeToU32,
// Dqn_Safe_SaturateCastUSizeToU64
//
// Dqn_Safe_SaturateCastISizeToInt,
// Dqn_Safe_SaturateCastISizeToI8,
// Dqn_Safe_SaturateCastISizeToI16,
// Dqn_Safe_SaturateCastISizeToI32,
// Dqn_Safe_SaturateCastISizeToI64,
//
// int Dqn_Safe_SaturateCastISizeToUInt,
// Dqn_Safe_SaturateCastISizeToU8,
// Dqn_Safe_SaturateCastISizeToU16,
// Dqn_Safe_SaturateCastISizeToU32,
// Dqn_Safe_SaturateCastISizeToU64,
//
// Dqn_Safe_SaturateCastI64ToISize,
// Dqn_Safe_SaturateCastI64ToI8,
// Dqn_Safe_SaturateCastI64ToI16,
// Dqn_Safe_SaturateCastI64ToI32,
//
// Dqn_Safe_SaturateCastIntToI8,
// Dqn_Safe_SaturateCastIntToU8,
// Dqn_Safe_SaturateCastIntToU16,
// Dqn_Safe_SaturateCastIntToU32,
// Dqn_Safe_SaturateCastIntToU64,
//
// @desc Truncate the lhs operand to the right clamping the result to the max
// value of the desired data type. Safe asserts if clamping occurs.
//
// The following sentinel values are returned when saturated,
// USize -> Int: INT_MAX
// USize -> I8: INT8_MAX
// USize -> I16: INT16_MAX
// USize -> I32: INT32_MAX
// USize -> I64: INT64_MAX
//
// U64 -> UInt: UINT_MAX
// U64 -> U8: UINT8_MAX
// U64 -> U16: UINT16_MAX
// U64 -> U32: UINT32_MAX
//
// USize -> U8: UINT8_MAX
// USize -> U16: UINT16_MAX
// USize -> U32: UINT32_MAX
// USize -> U64: UINT64_MAX
//
// ISize -> Int: INT_MIN or INT_MAX
// ISize -> I8: INT8_MIN or INT8_MAX
// ISize -> I16: INT16_MIN or INT16_MAX
// ISize -> I32: INT32_MIN or INT32_MAX
// ISize -> I64: INT64_MIN or INT64_MAX
//
// ISize -> UInt: 0 or UINT_MAX
// ISize -> U8: 0 or UINT8_MAX
// ISize -> U16: 0 or UINT16_MAX
// ISize -> U32: 0 or UINT32_MAX
// ISize -> U64: 0 or UINT64_MAX
//
// I64 -> ISize: DQN_ISIZE_MIN or DQN_ISIZE_MAX
// I64 -> I8: INT8_MIN or INT8_MAX
// I64 -> I16: INT16_MIN or INT16_MAX
// I64 -> I32: INT32_MIN or INT32_MAX
//
// Int -> I8: INT8_MIN or INT8_MAX
// Int -> I16: INT16_MIN or INT16_MAX
// Int -> U8: 0 or UINT8_MAX
// Int -> U16: 0 or UINT16_MAX
// Int -> U32: 0 or UINT32_MAX
// Int -> U64: 0 or UINT64_MAX
DQN_API int64_t Dqn_Safe_AddI64 (int64_t a, int64_t b);
DQN_API int64_t Dqn_Safe_MulI64 (int64_t a, int64_t b);
DQN_API uint64_t Dqn_Safe_AddU64 (uint64_t a, uint64_t b);
DQN_API uint64_t Dqn_Safe_MulU64 (uint64_t a, uint64_t b);
DQN_API uint64_t Dqn_Safe_SubU64 (uint64_t a, uint64_t b);
DQN_API uint32_t Dqn_Safe_SubU32 (uint32_t a, uint32_t b);
DQN_API int Dqn_Safe_SaturateCastUSizeToInt (Dqn_usize val);
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8 (Dqn_usize val);
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16 (Dqn_usize val);
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32 (Dqn_usize val);
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64 (Dqn_usize val);
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt (uint64_t val);
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8 (uint64_t val);
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16 (uint64_t val);
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32 (uint64_t val);
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8 (Dqn_usize val);
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16 (Dqn_usize val);
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32 (Dqn_usize val);
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64 (Dqn_usize val);
DQN_API int Dqn_Safe_SaturateCastISizeToInt (Dqn_isize val);
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8 (Dqn_isize val);
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16 (Dqn_isize val);
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32 (Dqn_isize val);
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64 (Dqn_isize val);
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt(Dqn_isize val);
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8 (Dqn_isize val);
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16 (Dqn_isize val);
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32 (Dqn_isize val);
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64 (Dqn_isize val);
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize (int64_t val);
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8 (int64_t val);
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16 (int64_t val);
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32 (int64_t val);
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8 (int val);
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16 (int val);
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8 (int val);
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16 (int val);
DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32 (int val);
DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64 (int val);
// NOTE: [$MISC] Misc ==============================================================================
// NOTE: API =======================================================================================
// @proc Dqn_SNPrintFDotTruncate
// @desc Write the format string to the buffer truncating with a trailing ".."
// if there is insufficient space in the buffer followed by null-terminating
// the buffer (uses stb_sprintf underneath).
// @return The size of the string written to the buffer *not* including the
// null-terminator.
//
// @proc Dqn_U64ToStr8
// @desc Convert a 64 bit unsigned value to its string representation.
// @param[in] val Value to convert into a string
// @param[in] separator The separator to insert every 3 digits. Set this to
// 0 if no separator is desired.
// NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
struct Dqn_U64Str8
{
char data[27+1]; // NOTE(dqn): 27 is the maximum size of uint64_t including a separtor
uint8_t size;
};
DQN_API int Dqn_SNPrintFDotTruncate(char *buffer, int size, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API Dqn_U64Str8 Dqn_U64ToStr8 (uint64_t val, char separator);
enum Dqn_U64ByteSizeType
{
Dqn_U64ByteSizeType_B,
Dqn_U64ByteSizeType_KiB,
Dqn_U64ByteSizeType_MiB,
Dqn_U64ByteSizeType_GiB,
Dqn_U64ByteSizeType_TiB,
Dqn_U64ByteSizeType_Count,
Dqn_U64ByteSizeType_Auto,
};
struct Dqn_U64ByteSize
{
Dqn_U64ByteSizeType type;
Dqn_Str8 suffix; // "KiB", "MiB", "GiB" .. e.t.c
Dqn_f64 bytes;
};
enum Dqn_U64AgeUnit
{
Dqn_U64AgeUnit_Sec = 1 << 0,
Dqn_U64AgeUnit_Min = 1 << 1,
Dqn_U64AgeUnit_Hr = 1 << 2,
Dqn_U64AgeUnit_Day = 1 << 3,
Dqn_U64AgeUnit_Week = 1 << 4,
Dqn_U64AgeUnit_Year = 1 << 5,
Dqn_U64AgeUnit_HMS = Dqn_U64AgeUnit_Sec | Dqn_U64AgeUnit_Min | Dqn_U64AgeUnit_Hr,
Dqn_U64AgeUnit_All = Dqn_U64AgeUnit_HMS | Dqn_U64AgeUnit_Day | Dqn_U64AgeUnit_Week | Dqn_U64AgeUnit_Year,
};
#if !defined(DQN_NO_PROFILER)
// NOTE: [$PROF] Dqn_Profiler ======================================================================
// A profiler based off Casey Muratori's Computer Enhance course, Performance
// Aware Programming. This profiler measures function elapsed time using the
// CPU's time stamp counter (e.g. rdtsc) providing a rough cycle count
// that can be converted into a duration.
//
// This profiler uses a double buffer scheme for storing profiling markers.
// After an application's typical update/frame cycle you can swap the profiler's
// buffer whereby the front buffer contains the previous frames profiling
// metrics and the back buffer will be populated with the new frame's profiling
// metrics.
#if 0
uint64_t tsc_per_seconds = Dqn_EstimateTSCPerSecond(/*duration_ms_to_gauge_tsc_frequency*/ 100);
for (;;) { // Main update loop
enum Zone { Zone_MainLoop, Zone_Count };
Dqn_ProfilerZone profiler_zone_main_update = Dqn_Profiler_BeginZone(Zone_MainLoop);
Dqn_ProfilerAnchor *anchors = Dqn_ProfilerAnchorBuffer(Dqn_ProfilerAnchorBuffer_Front);
for (size_t index = 0; index < Zone_Count; index++) {
Dqn_ProfilerAnchor *anchor = anchors + index;
printf("%.*s[%u] %zu cycles (%.1fms)\n",
DQN_STRING_FMT(anchor->anchor_index),
anchor->hit_count,
anchor->tsc_inclusive,
anchor->tsc_inclusive * tsc_per_seconds * 1000.f);
}
Dqn_Profiler_EndZone(&profiler_zone_main_update);
Dqn_Profiler_SwapAnchorBuffer(Zone_Count); // Should occur after all profiling zones are ended!
}
#endif
// @proc Dqn_Profiler_AnchorBuffer
// Retrieve the requested buffer from the profiler for writing/reading
// profiling metrics.
//
// @param buffer Enum that lets you specify which buffer to grab from the
// profiler. The front buffer contains the previous frame's profiling metrics
// and the back buffer is where the profiler is current writing to.
//
// For end user intents and purposes, you likely only need to read the front
// buffer which contain the metrics that you can visualise regarding the most
// profiling metrics recorded.
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
#if !defined(DQN_PROFILER_ANCHOR_BUFFER_SIZE)
#define DQN_PROFILER_ANCHOR_BUFFER_SIZE 128
#endif
@ -601,81 +196,68 @@ struct Dqn_Profiler
uint8_t active_anchor_buffer;
uint16_t parent_zone;
};
// NOTE: Macros ====================================================================================
#define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneWithIndex(DQN_STRING8(name), __COUNTER__ + 1)
// NOTE: API =======================================================================================
Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex(Dqn_Str8 name, uint16_t anchor_index);
void Dqn_Profiler_EndZone (Dqn_ProfilerZone zone);
Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer (Dqn_ProfilerAnchorBuffer buffer);
void Dqn_Profiler_SwapAnchorBuffer ();
void Dqn_Profiler_Dump (uint64_t tsc_per_second);
#endif // !defined(DQN_NO_PROFILER)
// NOTE: [$DLIB] Dqn_Library =======================================================================
// NOTE: [$JOBQ] Dqn_JobQueue ///////////////////////////////////////////////////////////////////////
typedef void (Dqn_JobQueueFunc)(void *user_context);
struct Dqn_Job
{
Dqn_Arena *arena;
Dqn_JobQueueFunc *func;
void *user_context;
};
struct Dqn_JobQueueSPMC
{
Dqn_OSMutex mutex;
Dqn_OSSemaphore thread_wait_for_job_semaphore;
Dqn_OSSemaphore wait_for_completion_semaphore;
uint32_t threads_waiting_for_completion;
Dqn_Job jobs[32];
Dqn_b32 quit;
uint32_t quit_exit_code;
uint32_t volatile read_index;
uint32_t volatile complete_index;
uint32_t volatile write_index;
};
// NOTE: [$DLIB] Dqn_Library ///////////////////////////////////////////////////////////////////////
// Book-keeping data for the library and allow customisation of certain features
// provided.
//
// NOTE: API =======================================================================================
// @proc Dqn_Library_SetLogCallback
// @desc Update the default logging function, all logging functions will run through
// this callback
// @param[in] proc The new logging function, set to nullptr to revert back to
// the default logger.
// @param[in] user_data A user defined parameter to pass to the callback
// @proc Dqn_Library_SetLogFile
// @param[in] file Pass in nullptr to turn off writing logs to disk, otherwise
// point it to the FILE that you wish to write to.
// @proc Dqn_Library_DumpThreadContextArenaStat
// @desc Dump the per-thread arena statistics to the specified file
struct Dqn_Library
{
bool lib_init; // True if the library has been initialised via `Dqn_Library_Init`
Dqn_TicketMutex lib_mutex;
Dqn_Str8 exe_dir; // The directory of the current executable
Dqn_Arena arena;
Dqn_ArenaCatalog arena_catalog;
Dqn_LogProc *log_callback; // Set this pointer to override the logging routine
void * log_user_data;
bool log_to_file; // Output logs to file as well as standard out
Dqn_FsFile log_file; // TODO(dqn): Hmmm, how should we do this... ?
Dqn_TicketMutex log_file_mutex; // Is locked when instantiating the log_file for the first time
bool log_no_colour; // Disable colours in the logging output
// NOTE: Leak Tracing ==========================================================================
bool allocs_are_allowed_to_leak;
bool alloc_tracking_disabled;
#if defined(DQN_LEAK_TRACING)
#if defined(DQN_NO_DSMAP)
#error "DSMap is required for allocation tracing"
#endif
// TODO(doyle): @robust Setting some of these flags is not multi-thread safe
uint16_t stack_trace_offset_to_our_call_stack;
bool lib_init; // True if the library has been initialised via `Dqn_Library_Init`
Dqn_TicketMutex lib_mutex;
Dqn_Str8 exe_dir; // The directory of the current executable
Dqn_Arena arena;
Dqn_ChunkPool pool; // Uses 'arena' for malloc-like allocations
Dqn_ArenaCatalog arena_catalog;
bool slow_verification_checks; // Enable expensive library verification checks
// NOTE: Logging ///////////////////////////////////////////////////////////////////////////////
Dqn_LogProc * log_callback; // Set this pointer to override the logging routine
void * log_user_data;
bool log_to_file; // Output logs to file as well as standard out
Dqn_OSFile log_file; // TODO(dqn): Hmmm, how should we do this... ?
Dqn_TicketMutex log_file_mutex; // Is locked when instantiating the log_file for the first time
bool log_no_colour; // Disable colours in the logging output
// NOTE: Leak Tracing //////////////////////////////////////////////////////////////////////////
#if defined(DQN_LEAK_TRACKING)
Dqn_DSMap<Dqn_DebugAlloc> alloc_table;
Dqn_TicketMutex alloc_table_mutex;
Dqn_DSMap<Dqn_AllocRecord> alloc_table;
Dqn_Arena alloc_table_arena;
#endif
// NOTE: OS ====================================================================================
// NOTE: Win32 /////////////////////////////////////////////////////////////////////////////////
#if defined(DQN_OS_WIN32)
LARGE_INTEGER win32_qpc_frequency;
Dqn_TicketMutex win32_bcrypt_rng_mutex;
void *win32_bcrypt_rng_handle;
LARGE_INTEGER win32_qpc_frequency;
Dqn_TicketMutex win32_bcrypt_rng_mutex;
void * win32_bcrypt_rng_handle;
#endif
uint32_t os_page_size;
uint32_t os_alloc_granularity;
// NOTE: Profiler ==============================================================================
bool win32_sym_initialised;
// NOTE: OS ////////////////////////////////////////////////////////////////////////////////////
uint32_t os_page_size;
uint32_t os_alloc_granularity;
// NOTE: Profiler //////////////////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_PROFILER)
Dqn_Profiler *profiler;
Dqn_Profiler profiler_default_instance;
@ -688,12 +270,233 @@ enum Dqn_LibraryOnInit
Dqn_LibraryOnInit_LogFeatures,
};
// NOTE: API =======================================================================================
DQN_API Dqn_Library *Dqn_Library_Init (Dqn_LibraryOnInit on_init);
DQN_API void Dqn_Library_SetPointer (Dqn_Library *library);
#if !defined(DQN_NO_PROFILER)
DQN_API void Dqn_Library_SetProfiler (Dqn_Profiler *profiler);
#endif
DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data);
DQN_API void Dqn_Library_DumpThreadContextArenaStat(Dqn_Str8 file_path);
// NOTE: [$PCGX] Dqn_PCG32 /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_PCG32 Dqn_PCG32_Init (uint64_t seed);
DQN_API uint32_t Dqn_PCG32_Next (Dqn_PCG32 *rng);
DQN_API uint64_t Dqn_PCG32_Next64 (Dqn_PCG32 *rng);
DQN_API uint32_t Dqn_PCG32_Range (Dqn_PCG32 *rng, uint32_t low, uint32_t high);
DQN_API Dqn_f32 Dqn_PCG32_NextF32 (Dqn_PCG32 *rng);
DQN_API Dqn_f64 Dqn_PCG32_NextF64 (Dqn_PCG32 *rng);
DQN_API void Dqn_PCG32_Advance (Dqn_PCG32 *rng, uint64_t delta);
#if !defined(DQN_NO_JSON_BUILDER)
// NOTE: [$JSON] Dqn_JSONBuilder ///////////////////////////////////////////////////////////////////
#define Dqn_JSONBuilder_Object(builder) \
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBegin(builder), \
Dqn_JSONBuilder_ObjectEnd(builder))
#define Dqn_JSONBuilder_ObjectNamed(builder, name) \
DQN_DEFER_LOOP(Dqn_JSONBuilder_ObjectBeginNamed(builder, name), \
Dqn_JSONBuilder_ObjectEnd(builder))
#define Dqn_JSONBuilder_Array(builder) \
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBegin(builder), \
Dqn_JSONBuilder_ArrayEnd(builder))
#define Dqn_JSONBuilder_ArrayNamed(builder, name) \
DQN_DEFER_LOOP(Dqn_JSONBuilder_ArrayBeginNamed(builder, name), \
Dqn_JSONBuilder_ArrayEnd(builder))
DQN_API Dqn_JSONBuilder Dqn_JSONBuilder_Init (Dqn_Arena *arena, int spaces_per_indent);
DQN_API Dqn_Str8 Dqn_JSONBuilder_Build (Dqn_JSONBuilder const *builder, Dqn_Arena *arena);
DQN_API void Dqn_JSONBuilder_KeyValue (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
DQN_API void Dqn_JSONBuilder_KeyValueF (Dqn_JSONBuilder *builder, Dqn_Str8 key, char const *value_fmt, ...);
DQN_API void Dqn_JSONBuilder_ObjectBeginNamed (Dqn_JSONBuilder *builder, Dqn_Str8 name);
DQN_API void Dqn_JSONBuilder_ObjectEnd (Dqn_JSONBuilder *builder);
DQN_API void Dqn_JSONBuilder_ArrayBeginNamed (Dqn_JSONBuilder *builder, Dqn_Str8 name);
DQN_API void Dqn_JSONBuilder_ArrayEnd (Dqn_JSONBuilder *builder);
DQN_API void Dqn_JSONBuilder_Str8Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
DQN_API void Dqn_JSONBuilder_LiteralNamed (Dqn_JSONBuilder *builder, Dqn_Str8 key, Dqn_Str8 value);
DQN_API void Dqn_JSONBuilder_U64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, uint64_t value);
DQN_API void Dqn_JSONBuilder_I64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, int64_t value);
DQN_API void Dqn_JSONBuilder_F64Named (Dqn_JSONBuilder *builder, Dqn_Str8 key, double value, int decimal_places);
DQN_API void Dqn_JSONBuilder_BoolNamed (Dqn_JSONBuilder *builder, Dqn_Str8 key, bool value);
#define Dqn_JSONBuilder_ObjectBegin(builder) Dqn_JSONBuilder_ObjectBeginNamed(builder, DQN_STR8(""))
#define Dqn_JSONBuilder_ArrayBegin(builder) Dqn_JSONBuilder_ArrayBeginNamed(builder, DQN_STR8(""))
#define Dqn_JSONBuilder_Str8(builder, value) Dqn_JSONBuilder_Str8Named(builder, DQN_STR8(""), value)
#define Dqn_JSONBuilder_Literal(builder, value) Dqn_JSONBuilder_LiteralNamed(builder, DQN_STR8(""), value)
#define Dqn_JSONBuilder_U64(builder, value) Dqn_JSONBuilder_U64Named(builder, DQN_STR8(""), value)
#define Dqn_JSONBuilder_I64(builder, value) Dqn_JSONBuilder_I64Named(builder, DQN_STR8(""), value)
#define Dqn_JSONBuilder_F64(builder, value) Dqn_JSONBuilder_F64Named(builder, DQN_STR8(""), value)
#define Dqn_JSONBuilder_Bool(builder, value) Dqn_JSONBuilder_BoolNamed(builder, DQN_STR8(""), value)
#endif // !defined(DQN_NO_JSON_BUILDER)
#if !defined(DQN_NO_BIN)
// NOTE: [$BHEX] Dqn_Bin ///////////////////////////////////////////////////////////////////////////
// TODO(doyle): I'm not happy with this API. Its ugly and feels complicated
// because I designed it as a C-API first (e.g. ptr + length) vs just accepting
// that Dqn_Str8/Dqn_Slice is a superior API to design for first.
DQN_API char const * Dqn_Bin_HexBufferTrim0x (char const *hex, Dqn_usize size, Dqn_usize *real_size);
DQN_API Dqn_Str8 Dqn_Bin_HexTrim0x (Dqn_Str8 string);
DQN_API Dqn_BinHexU64Str8 Dqn_Bin_U64ToHexU64Str8 (uint64_t number, uint32_t flags);
DQN_API Dqn_Str8 Dqn_Bin_U64ToHex (Dqn_Arena *arena, uint64_t number, uint32_t flags);
DQN_API uint64_t Dqn_Bin_HexToU64 (Dqn_Str8 hex);
DQN_API uint64_t Dqn_Bin_HexPtrToU64 (char const *hex, Dqn_usize size);
DQN_API Dqn_Str8 Dqn_Bin_BytesToHexArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
DQN_API char * Dqn_Bin_BytesToHexBufferArena (Dqn_Arena *arena, void const *src, Dqn_usize size);
DQN_API bool Dqn_Bin_BytesToHexBuffer (void const *src, Dqn_usize src_size, char *dest, Dqn_usize dest_size);
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytesUnchecked (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
DQN_API Dqn_usize Dqn_Bin_HexBufferToBytes (char const *hex, Dqn_usize hex_size, void *dest, Dqn_usize dest_size);
DQN_API char * Dqn_Bin_HexBufferToBytesArena (Dqn_Arena *arena, char const *hex, Dqn_usize hex_size, Dqn_usize *real_size);
DQN_API Dqn_usize Dqn_Bin_HexToBytesUnchecked (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
DQN_API Dqn_usize Dqn_Bin_HexToBytes (Dqn_Str8 hex, void *dest, Dqn_usize dest_size);
DQN_API Dqn_Str8 Dqn_Bin_HexToBytesArena (Dqn_Arena *arena, Dqn_Str8 hex);
#endif // !defined(DQN_NO_BIN)
// NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
template <typename T> bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs);
template <typename T> Dqn_BinarySearchResult Dqn_BinarySearch (T const *array,
Dqn_usize array_size,
T const &find,
Dqn_BinarySearchType type = Dqn_BinarySearchType_Match,
Dqn_BinarySearchLessThanProc<T> less_than = Dqn_BinarySearch_DefaultLessThan);
// NOTE: [$BITS] Dqn_Bit ///////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_Bit_UnsetInplace (Dqn_usize *flags, Dqn_usize bitfield);
DQN_API void Dqn_Bit_SetInplace (Dqn_usize *flags, Dqn_usize bitfield);
DQN_API bool Dqn_Bit_IsSet (Dqn_usize bits, Dqn_usize bits_to_set);
DQN_API bool Dqn_Bit_IsNotSet (Dqn_usize bits, Dqn_usize bits_to_check);
// NOTE: [$SAFE] Dqn_Safe //////////////////////////////////////////////////////////////////////////
DQN_API int64_t Dqn_Safe_AddI64 (int64_t a, int64_t b);
DQN_API int64_t Dqn_Safe_MulI64 (int64_t a, int64_t b);
DQN_API uint64_t Dqn_Safe_AddU64 (uint64_t a, uint64_t b);
DQN_API uint64_t Dqn_Safe_MulU64 (uint64_t a, uint64_t b);
DQN_API uint64_t Dqn_Safe_SubU64 (uint64_t a, uint64_t b);
DQN_API uint32_t Dqn_Safe_SubU32 (uint32_t a, uint32_t b);
DQN_API int Dqn_Safe_SaturateCastUSizeToInt (Dqn_usize val);
DQN_API int8_t Dqn_Safe_SaturateCastUSizeToI8 (Dqn_usize val);
DQN_API int16_t Dqn_Safe_SaturateCastUSizeToI16 (Dqn_usize val);
DQN_API int32_t Dqn_Safe_SaturateCastUSizeToI32 (Dqn_usize val);
DQN_API int64_t Dqn_Safe_SaturateCastUSizeToI64 (Dqn_usize val);
DQN_API int Dqn_Safe_SaturateCastU64ToInt (uint64_t val);
DQN_API unsigned int Dqn_Safe_SaturateCastU64ToUInt (uint64_t val);
DQN_API uint8_t Dqn_Safe_SaturateCastU64ToU8 (uint64_t val);
DQN_API uint16_t Dqn_Safe_SaturateCastU64ToU16 (uint64_t val);
DQN_API uint32_t Dqn_Safe_SaturateCastU64ToU32 (uint64_t val);
DQN_API uint8_t Dqn_Safe_SaturateCastUSizeToU8 (Dqn_usize val);
DQN_API uint16_t Dqn_Safe_SaturateCastUSizeToU16 (Dqn_usize val);
DQN_API uint32_t Dqn_Safe_SaturateCastUSizeToU32 (Dqn_usize val);
DQN_API uint64_t Dqn_Safe_SaturateCastUSizeToU64 (Dqn_usize val);
DQN_API int Dqn_Safe_SaturateCastISizeToInt (Dqn_isize val);
DQN_API int8_t Dqn_Safe_SaturateCastISizeToI8 (Dqn_isize val);
DQN_API int16_t Dqn_Safe_SaturateCastISizeToI16 (Dqn_isize val);
DQN_API int32_t Dqn_Safe_SaturateCastISizeToI32 (Dqn_isize val);
DQN_API int64_t Dqn_Safe_SaturateCastISizeToI64 (Dqn_isize val);
DQN_API unsigned int Dqn_Safe_SaturateCastISizeToUInt (Dqn_isize val);
DQN_API uint8_t Dqn_Safe_SaturateCastISizeToU8 (Dqn_isize val);
DQN_API uint16_t Dqn_Safe_SaturateCastISizeToU16 (Dqn_isize val);
DQN_API uint32_t Dqn_Safe_SaturateCastISizeToU32 (Dqn_isize val);
DQN_API uint64_t Dqn_Safe_SaturateCastISizeToU64 (Dqn_isize val);
DQN_API Dqn_isize Dqn_Safe_SaturateCastI64ToISize (int64_t val);
DQN_API int8_t Dqn_Safe_SaturateCastI64ToI8 (int64_t val);
DQN_API int16_t Dqn_Safe_SaturateCastI64ToI16 (int64_t val);
DQN_API int32_t Dqn_Safe_SaturateCastI64ToI32 (int64_t val);
DQN_API int8_t Dqn_Safe_SaturateCastIntToI8 (int val);
DQN_API int16_t Dqn_Safe_SaturateCastIntToI16 (int val);
DQN_API uint8_t Dqn_Safe_SaturateCastIntToU8 (int val);
DQN_API uint16_t Dqn_Safe_SaturateCastIntToU16 (int val);
DQN_API uint32_t Dqn_Safe_SaturateCastIntToU32 (int val);
DQN_API uint64_t Dqn_Safe_SaturateCastIntToU64 (int val);
// NOTE: [$MISC] Misc //////////////////////////////////////////////////////////////////////////////
DQN_API int Dqn_FmtBuffer3DotTruncate (char *buffer, int size, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API Dqn_U64Str8 Dqn_U64ToStr8 (uint64_t val, char separator);
DQN_API Dqn_U64ByteSize Dqn_U64ToByteSize (uint64_t bytes, Dqn_U64ByteSizeType type);
DQN_API Dqn_Str8 Dqn_U64ToByteSizeStr8 (Dqn_Arena *arena, uint64_t bytes, Dqn_U64ByteSizeType desired_type);
DQN_API Dqn_Str8 Dqn_U64ByteSizeTypeString (Dqn_U64ByteSizeType type);
DQN_API Dqn_Str8 Dqn_U64ToAge (Dqn_Arena *arena, uint64_t age_s, Dqn_usize type);
// NOTE: [$PROF] Dqn_Profiler //////////////////////////////////////////////////////////////////////
#define Dqn_Profiler_BeginZone(name) Dqn_Profiler_BeginZoneWithIndex(DQN_STR8(name), __COUNTER__ + 1)
DQN_API Dqn_ProfilerZone Dqn_Profiler_BeginZoneWithIndex (Dqn_Str8 name, uint16_t anchor_index);
DQN_API void Dqn_Profiler_EndZone (Dqn_ProfilerZone zone);
DQN_API Dqn_ProfilerAnchor *Dqn_Profiler_AnchorBuffer (Dqn_ProfilerAnchorBuffer buffer);
DQN_API void Dqn_Profiler_SwapAnchorBuffer ();
DQN_API void Dqn_Profiler_Dump (uint64_t tsc_per_second);
// NOTE: [$JOBQ] Dqn_JobQueue ///////////////////////////////////////////////////////////////////////
DQN_API Dqn_JobQueueSPMC Dqn_OS_JobQueueSPMCInit ();
DQN_API bool Dqn_OS_JobQueueSPMCAddArray (Dqn_JobQueueSPMC *job_queue, Dqn_Job *jobs, uint32_t count);
DQN_API bool Dqn_OS_JobQueueSPMCAdd (Dqn_JobQueueSPMC *job_queue, Dqn_Job job);
DQN_API void Dqn_OS_JobQueueSPMCWaitForCompletion (Dqn_JobQueueSPMC *job_queue);
DQN_API int32_t Dqn_OS_JobQueueSPMCThread (Dqn_OSThread *thread);
// NOTE: Dqn_Library ///////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_Library * Dqn_Library_Init (Dqn_LibraryOnInit on_init);
DQN_API void Dqn_Library_SetPointer (Dqn_Library *library);
#if !defined(DQN_NO_PROFILER)
DQN_API void Dqn_Library_SetProfiler (Dqn_Profiler *profiler);
#endif
DQN_API void Dqn_Library_SetLogCallback (Dqn_LogProc *proc, void *user_data);
DQN_API void Dqn_Library_DumpThreadContextArenaStat (Dqn_Str8 file_path);
DQN_API Dqn_Arena * Dqn_Library_AllocArenaF (Dqn_usize reserve, Dqn_usize commit, uint8_t arena_flags, char const *fmt, ...);
// NOTE: [$BSEA] Dqn_BinarySearch //////////////////////////////////////////////////////////////////
template <typename T>
bool Dqn_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs)
{
bool result = lhs < rhs;
return result;
}
template <typename T>
Dqn_BinarySearchResult Dqn_BinarySearch(T const *array,
Dqn_usize array_size,
T const &find,
Dqn_BinarySearchType type,
Dqn_BinarySearchLessThanProc<T> less_than)
{
Dqn_BinarySearchResult result = {};
if (!array || array_size <= 0)
return result;
T const *end = array + array_size;
T const *first = array;
T const *last = end;
while (first != last) {
Dqn_usize count = last - first;
T const *it = first + (count / 2);
bool advance_first = false;
if (type == Dqn_BinarySearchType_UpperBound)
advance_first = !less_than(find, it[0]);
else
advance_first = less_than(it[0], find);
if (advance_first)
first = it + 1;
else
last = it;
}
switch (type) {
case Dqn_BinarySearchType_Match: {
result.found = first != end && !less_than(find, *first);
} break;
case Dqn_BinarySearchType_LowerBound: /*FALLTHRU*/
case Dqn_BinarySearchType_UpperBound: {
result.found = first != end;
} break;
}
result.index = first - array;
return result;
}

View File

@ -1,5 +1,20 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$\ $$\ $$$$$$\ $$$$$$$$\ $$\ $$\
// $$$\ $$$ |$$ __$$\\__$$ __|$$ | $$ |
// $$$$\ $$$$ |$$ / $$ | $$ | $$ | $$ |
// $$\$$\$$ $$ |$$$$$$$$ | $$ | $$$$$$$$ |
// $$ \$$$ $$ |$$ __$$ | $$ | $$ __$$ |
// $$ |\$ /$$ |$$ | $$ | $$ | $$ | $$ |
// $$ | \_/ $$ |$$ | $$ | $$ | $$ | $$ |
// \__| \__|\__| \__| \__| \__| \__|
//
// dqn_math.cpp
//
////////////////////////////////////////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_V2)
// NOTE: [$VEC2] Vector2 ===========================================================================
// NOTE: [$VEC2] Vector2 ///////////////////////////////////////////////////////////////////////////
// NOTE: Dqn_V2I
DQN_API bool operator!=(Dqn_V2I lhs, Dqn_V2I rhs)
{
@ -139,6 +154,24 @@ DQN_API Dqn_V2I &operator+=(Dqn_V2I &lhs, Dqn_V2I rhs)
return lhs;
}
DQN_API Dqn_V2I Dqn_V2I_Min(Dqn_V2I a, Dqn_V2I b)
{
Dqn_V2I result = Dqn_V2I_InitNx2(DQN_MIN(a.x, b.x), DQN_MIN(a.y, b.y));
return result;
}
DQN_API Dqn_V2I Dqn_V2I_Max(Dqn_V2I a, Dqn_V2I b)
{
Dqn_V2I result = Dqn_V2I_InitNx2(DQN_MAX(a.x, b.x), DQN_MAX(a.y, b.y));
return result;
}
DQN_API Dqn_V2I Dqn_V2I_Abs(Dqn_V2I a)
{
Dqn_V2I result = Dqn_V2I_InitNx2(DQN_ABS(a.x), DQN_ABS(a.y));
return result;
}
// NOTE: Dqn_V2U16
DQN_API bool operator!=(Dqn_V2U16 lhs, Dqn_V2U16 rhs)
{
@ -309,42 +342,75 @@ DQN_API bool operator>(Dqn_V2 lhs, Dqn_V2 rhs)
return result;
}
// NOTE: Dqn_V2 operator- //////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 operator-(Dqn_V2 lhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(-lhs.x, -lhs.y);
return result;
}
DQN_API Dqn_V2 operator-(Dqn_V2 lhs, Dqn_V2 rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs.x, lhs.y - rhs.y);
return result;
}
DQN_API Dqn_V2 operator-(Dqn_V2 lhs, Dqn_V2I rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs.x, lhs.y - rhs.y);
return result;
}
DQN_API Dqn_V2 operator-(Dqn_V2 lhs, Dqn_f32 rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs, lhs.y - rhs);
return result;
}
DQN_API Dqn_V2 operator-(Dqn_V2 lhs)
DQN_API Dqn_V2 operator-(Dqn_V2 lhs, int32_t rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(-lhs.x, -lhs.y);
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x - rhs, lhs.y - rhs);
return result;
}
// NOTE: Dqn_V2 operator+ //////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 operator+(Dqn_V2 lhs, Dqn_V2 rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs.x, lhs.y + rhs.y);
return result;
}
DQN_API Dqn_V2 operator+(Dqn_V2 lhs, Dqn_V2I rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs.x, lhs.y + rhs.y);
return result;
}
DQN_API Dqn_V2 operator+(Dqn_V2 lhs, Dqn_f32 rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs, lhs.y + rhs);
return result;
}
DQN_API Dqn_V2 operator+(Dqn_V2 lhs, int32_t rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x + rhs, lhs.y + rhs);
return result;
}
// NOTE: Dqn_V2 operator* //////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 operator*(Dqn_V2 lhs, Dqn_V2 rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x * rhs.x, lhs.y * rhs.y);
return result;
}
DQN_API Dqn_V2 operator*(Dqn_V2 lhs, Dqn_V2I rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x * rhs.x, lhs.y * rhs.y);
return result;
}
DQN_API Dqn_V2 operator*(Dqn_V2 lhs, Dqn_f32 rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x * rhs, lhs.y * rhs);
@ -357,7 +423,14 @@ DQN_API Dqn_V2 operator*(Dqn_V2 lhs, int32_t rhs)
return result;
}
DQN_API Dqn_V2 operator/(Dqn_V2 lhs, Dqn_V2 rhs)
// NOTE: Dqn_V2 operator/ //////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 operator/(Dqn_V2 lhs, Dqn_V2 rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x / rhs.x, lhs.y / rhs.y);
return result;
}
DQN_API Dqn_V2 operator/(Dqn_V2 lhs, Dqn_V2I rhs)
{
Dqn_V2 result = Dqn_V2_InitNx2(lhs.x / rhs.x, lhs.y / rhs.y);
return result;
@ -375,12 +448,19 @@ DQN_API Dqn_V2 operator/(Dqn_V2 lhs, int32_t rhs)
return result;
}
// NOTE: Dqn_V2 operator*/ /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, Dqn_V2 rhs)
{
lhs = lhs * rhs;
return lhs;
}
DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, Dqn_V2I rhs)
{
lhs = lhs * rhs;
return lhs;
}
DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, Dqn_f32 rhs)
{
lhs = lhs * rhs;
@ -393,12 +473,19 @@ DQN_API Dqn_V2 &operator*=(Dqn_V2 &lhs, int32_t rhs)
return lhs;
}
// NOTE: Dqn_V2 operator// /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, Dqn_V2 rhs)
{
lhs = lhs / rhs;
return lhs;
}
DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, Dqn_V2I rhs)
{
lhs = lhs / rhs;
return lhs;
}
DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, Dqn_f32 rhs)
{
lhs = lhs / rhs;
@ -411,30 +498,56 @@ DQN_API Dqn_V2 &operator/=(Dqn_V2 &lhs, int32_t rhs)
return lhs;
}
// NOTE: Dqn_V2 operator-/ /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, Dqn_V2 rhs)
{
lhs = lhs - rhs;
return lhs;
}
DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, Dqn_V2I rhs)
{
lhs = lhs - rhs;
return lhs;
}
DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, Dqn_f32 rhs)
{
lhs = lhs - rhs;
return lhs;
}
DQN_API Dqn_V2 &operator-=(Dqn_V2 &lhs, int32_t rhs)
{
lhs = lhs - rhs;
return lhs;
}
// NOTE: Dqn_V2 operator+/ /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, Dqn_V2 rhs)
{
lhs = lhs + rhs;
return lhs;
}
DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, Dqn_V2I rhs)
{
lhs = lhs + rhs;
return lhs;
}
DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, Dqn_f32 rhs)
{
lhs = lhs + rhs;
return lhs;
}
DQN_API Dqn_V2 &operator+=(Dqn_V2 &lhs, int32_t rhs)
{
lhs = lhs + rhs;
return lhs;
}
DQN_API Dqn_V2 Dqn_V2_Min(Dqn_V2 a, Dqn_V2 b)
{
Dqn_V2 result = Dqn_V2_InitNx2(DQN_MIN(a.x, b.x), DQN_MIN(a.y, b.y));
@ -455,7 +568,7 @@ DQN_API Dqn_V2 Dqn_V2_Abs(Dqn_V2 a)
DQN_API Dqn_f32 Dqn_V2_Dot(Dqn_V2 a, Dqn_V2 b)
{
// NOTE: Scalar projection of B onto A =========================================================
// NOTE: Scalar projection of B onto A /////////////////////////////////////////////////////////
//
// Scalar projection calculates the signed distance between `b` and `a`
// where `a` is a unit vector then, the dot product calculates the projection
@ -488,7 +601,7 @@ DQN_API Dqn_f32 Dqn_V2_Dot(Dqn_V2 a, Dqn_V2 b)
// 2 objects. One of the vectors must be normalised (e.g. turned into a unit
// vector).
//
// NOTE: Vector projection =====================================================================
// NOTE: Vector projection /////////////////////////////////////////////////////////////////////
//
// Vector projection calculates the exact X,Y coordinates of where `b` meets
// `a` when it was projected. This is calculated by multipying the
@ -578,7 +691,7 @@ DQN_API Dqn_f32 Dqn_V2_Area(Dqn_V2 a)
#endif // !defined(DQN_NO_V2)
#if !defined(DQN_NO_V3)
// NOTE: [$VEC3] Vector3 ===========================================================================
// NOTE: [$VEC3] Vector3 ///////////////////////////////////////////////////////////////////////////
DQN_API bool operator!=(Dqn_V3 lhs, Dqn_V3 rhs)
{
bool result = !(lhs == rhs);
@ -739,7 +852,7 @@ DQN_API Dqn_V3 Dqn_V3_Normalise(Dqn_V3 a)
#endif // !defined(DQN_NO_V3)
#if !defined(DQN_NO_V4)
// NOTE: [$VEC4] Vector4 ===========================================================================
// NOTE: [$VEC4] Vector4 ///////////////////////////////////////////////////////////////////////////
DQN_API bool operator!=(Dqn_V4 lhs, Dqn_V4 rhs)
{
bool result = !(lhs == rhs);
@ -856,7 +969,7 @@ DQN_API Dqn_f32 Dqn_V4Dot(Dqn_V4 a, Dqn_V4 b)
#endif // !defined(DQN_NO_V4)
#if !defined(DQN_NO_M4)
// NOTE: [$MAT4] Dqn_M4 ============================================================================
// NOTE: [$MAT4] Dqn_M4 ////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_M4 Dqn_M4_Identity()
{
Dqn_M4 result =
@ -924,7 +1037,7 @@ DQN_API Dqn_M4 Dqn_M4_Translate(Dqn_V3 xyz)
DQN_API Dqn_M4 Dqn_M4_Transpose(Dqn_M4 mat)
{
Dqn_M4 result;
Dqn_M4 result = {};
for (int col = 0; col < 4; col++)
for (int row = 0; row < 4; row++)
result.columns[col][row] = mat.columns[row][col];
@ -1106,7 +1219,7 @@ DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat)
#endif
#endif // !defined(DQN_M4)
// NOTE: [$M2x3] Dqn_M2x3 ==========================================================================
// NOTE: [$M2x3] Dqn_M2x3 //////////////////////////////////////////////////////////////////////////
DQN_API bool operator==(Dqn_M2x3 const &lhs, Dqn_M2x3 const &rhs)
{
bool result = DQN_MEMCMP(lhs.e, rhs.e, sizeof(lhs.e[0]) * DQN_ARRAY_UCOUNT(lhs.e)) == 0;
@ -1178,7 +1291,7 @@ DQN_API Dqn_M2x3 Dqn_M2x3_Mul(Dqn_M2x3 m1, Dqn_M2x3 m2)
return result;
}
DQN_API Dqn_V2 Dqn_M2x3_MulV2(Dqn_M2x3 m1, Dqn_V2 v2)
DQN_API Dqn_V2 Dqn_M2x3_Mul2F32(Dqn_M2x3 m1, Dqn_f32 x, Dqn_f32 y)
{
// NOTE: Ordinarily you can't multiply M2x3 with V2 because column count (3)
// != row count (2). We pretend we have a V3 with `z` set to `1`.
@ -1188,14 +1301,20 @@ DQN_API Dqn_V2 Dqn_M2x3_MulV2(Dqn_M2x3 m1, Dqn_V2 v2)
// | 1 |
Dqn_V2 result = {{
m1.e[0]*v2.x + m1.e[1]*v2.y + m1.e[2], // a*x + b*y + c*1
m1.e[3]*v2.x + m1.e[4]*v2.y + m1.e[5], // d*x + e*y + f*1
m1.e[0]*x + m1.e[1]*y + m1.e[2], // a*x + b*y + c*1
m1.e[3]*x + m1.e[4]*y + m1.e[5], // d*x + e*y + f*1
}};
return result;
}
DQN_API Dqn_V2 Dqn_M2x3_MulV2(Dqn_M2x3 m1, Dqn_V2 v2)
{
Dqn_V2 result = Dqn_M2x3_Mul2F32(m1, v2.x, v2.y);
return result;
}
#if !defined(DQN_NO_RECT)
// NOTE: [$RECT] Dqn_Rect ==========================================================================
// NOTE: [$RECT] Dqn_Rect //////////////////////////////////////////////////////////////////////////
DQN_API bool operator==(const Dqn_Rect& lhs, const Dqn_Rect& rhs)
{
bool result = (lhs.pos == rhs.pos) && (lhs.size == rhs.size);
@ -1399,7 +1518,7 @@ DQN_API Dqn_V2 Dqn_Rect_BottomRight(Dqn_Rect rect)
}
#endif // !defined(DQN_NO_RECT)
// NOTE: [$MATH] Raycast ===========================================================================
// NOTE: [$MATH] Raycast ///////////////////////////////////////////////////////////////////////////
DQN_API Dqn_RaycastLineIntersectV2Result Dqn_Raycast_LineIntersectV2(Dqn_V2 origin_a, Dqn_V2 dir_a, Dqn_V2 origin_b, Dqn_V2 dir_b)
{
@ -1432,7 +1551,7 @@ DQN_API Dqn_RaycastLineIntersectV2Result Dqn_Raycast_LineIntersectV2(Dqn_V2 orig
return result;
}
// NOTE: [$MATH] Other =============================================================================
// NOTE: [$MATH] Other /////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_V2 Dqn_Lerp_V2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b)
{
Dqn_V2 result = {};

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_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
#if !defined(DQN_NO_V2)
// NOTE: [$VEC2] Vector2 ===========================================================================
// NOTE: [$VEC2] Vector2 ///////////////////////////////////////////////////////////////////////////
union Dqn_V2I
{
struct { int32_t x, y; };
@ -10,34 +34,6 @@ union Dqn_V2I
int32_t data[2];
};
#define Dqn_V2I_InitNx1(x) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(x)}}
#define Dqn_V2I_InitNx2(x, y) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(y)}}
#define Dqn_V2I_InitV2(xy) DQN_LITERAL(Dqn_V2I){{(int32_t)(xy).x, (int32_t)(xy).y}}
DQN_API bool operator!=(Dqn_V2I lhs, Dqn_V2I rhs);
DQN_API bool operator==(Dqn_V2I lhs, Dqn_V2I rhs);
DQN_API bool operator>=(Dqn_V2I lhs, Dqn_V2I rhs);
DQN_API bool operator<=(Dqn_V2I lhs, Dqn_V2I rhs);
DQN_API bool operator< (Dqn_V2I lhs, Dqn_V2I rhs);
DQN_API bool 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_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_f32 rhs);
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_f32 rhs);
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_f32 rhs);
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_f32 rhs);
DQN_API Dqn_V2I &operator/=(Dqn_V2I& lhs, int32_t rhs);
DQN_API Dqn_V2I &operator-=(Dqn_V2I& lhs, Dqn_V2I rhs);
DQN_API Dqn_V2I &operator+=(Dqn_V2I& lhs, Dqn_V2I rhs);
union Dqn_V2U16
{
struct { uint16_t x, y; };
@ -45,90 +41,16 @@ union Dqn_V2U16
uint16_t data[2];
};
#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)}}
DQN_API bool operator!=(Dqn_V2U16 lhs, Dqn_V2U16 rhs);
DQN_API bool operator==(Dqn_V2U16 lhs, Dqn_V2U16 rhs);
DQN_API bool operator>=(Dqn_V2U16 lhs, Dqn_V2U16 rhs);
DQN_API bool operator<=(Dqn_V2U16 lhs, Dqn_V2U16 rhs);
DQN_API bool operator< (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
DQN_API bool 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);
DQN_API Dqn_V2U16 operator* (Dqn_V2U16 lhs, Dqn_f32 rhs);
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_f32 rhs);
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_f32 rhs);
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_f32 rhs);
DQN_API Dqn_V2U16 &operator/=(Dqn_V2U16& lhs, int32_t rhs);
DQN_API Dqn_V2U16 &operator-=(Dqn_V2U16& lhs, Dqn_V2U16 rhs);
DQN_API Dqn_V2U16 &operator+=(Dqn_V2U16& lhs, Dqn_V2U16 rhs);
union Dqn_V2
{
struct { Dqn_f32 x, y; };
struct { Dqn_f32 w, h; };
Dqn_f32 data[2];
};
#define Dqn_V2_Zero DQN_LITERAL(Dqn_V2){{(Dqn_f32)(0), (Dqn_f32)(0)}}
#define Dqn_V2_One DQN_LITERAL(Dqn_V2){{(Dqn_f32)(1), (Dqn_f32)(1)}}
#define Dqn_V2_InitNx1(x) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(x), (Dqn_f32)(x)}}
#define Dqn_V2_InitNx2(x, y) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(x), (Dqn_f32)(y)}}
#define Dqn_V2_InitV2I(xy) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(xy).x, (Dqn_f32)(xy).y}}
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_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_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, 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, 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, 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 Dqn_V2_Min (Dqn_V2 a, Dqn_V2 b);
DQN_API Dqn_V2 Dqn_V2_Max (Dqn_V2 a, Dqn_V2 b);
DQN_API Dqn_V2 Dqn_V2_Abs (Dqn_V2 a);
DQN_API Dqn_f32 Dqn_V2_Dot (Dqn_V2 a, Dqn_V2 b);
DQN_API Dqn_f32 Dqn_V2_LengthSq_V2x2(Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_f32 Dqn_V2_Length_V2x2 (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_f32 Dqn_V2_LengthSq (Dqn_V2 lhs);
DQN_API Dqn_f32 Dqn_V2_Length (Dqn_V2 lhs);
DQN_API Dqn_V2 Dqn_V2_Normalise (Dqn_V2 a);
DQN_API Dqn_V2 Dqn_V2_Perpendicular(Dqn_V2 a);
DQN_API Dqn_V2 Dqn_V2_Reflect (Dqn_V2 in, Dqn_V2 surface);
DQN_API Dqn_f32 Dqn_V2_Area (Dqn_V2 a);
#endif // !defined(DQN_NO_V2)
#if !defined(DQN_NO_V3)
// NOTE: [$VEC3] Vector3 ===========================================================================
// NOTE: [$VEC3] Vector3 ///////////////////////////////////////////////////////////////////////////
union Dqn_V3
{
struct { Dqn_f32 x, y, z; };
@ -136,41 +58,10 @@ union Dqn_V3
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_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)}}
DQN_API bool operator!=(Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API bool operator==(Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API bool operator>=(Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API bool operator<=(Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API bool operator< (Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API bool 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_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_f32 rhs);
DQN_API Dqn_V3 operator* (Dqn_V3 lhs, int32_t rhs);
DQN_API Dqn_V3 operator/ (Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API Dqn_V3 operator/ (Dqn_V3 lhs, Dqn_f32 rhs);
DQN_API Dqn_V3 operator/ (Dqn_V3 lhs, int32_t rhs);
DQN_API Dqn_V3 &operator*=(Dqn_V3 &lhs, Dqn_V3 rhs);
DQN_API Dqn_V3 &operator*=(Dqn_V3 &lhs, Dqn_f32 rhs);
DQN_API Dqn_V3 &operator*=(Dqn_V3 &lhs, int32_t rhs);
DQN_API Dqn_V3 &operator/=(Dqn_V3 &lhs, Dqn_V3 rhs);
DQN_API Dqn_V3 &operator/=(Dqn_V3 &lhs, Dqn_f32 rhs);
DQN_API Dqn_V3 &operator/=(Dqn_V3 &lhs, int32_t rhs);
DQN_API Dqn_V3 &operator-=(Dqn_V3 &lhs, Dqn_V3 rhs);
DQN_API Dqn_V3 &operator+=(Dqn_V3 &lhs, Dqn_V3 rhs);
DQN_API Dqn_f32 Dqn_V3_LengthSq(Dqn_V3 a);
DQN_API Dqn_f32 Dqn_V3_Length(Dqn_V3 a);
DQN_API Dqn_V3 Dqn_V3_Normalise(Dqn_V3 a);
#endif // !defined(DQN_NO_V3)
#if !defined(DQN_NO_V4)
// NOTE: [$VEC4] Vector4 ===========================================================================
// NOTE: [$VEC4] Vector4 ///////////////////////////////////////////////////////////////////////////
union Dqn_V4
{
struct { Dqn_f32 x, y, z, w; };
@ -181,85 +72,29 @@ union Dqn_V4
#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_InitNx4(x, y, z, w) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(y), (Dqn_f32)(z), (Dqn_f32)(w)}}
#define Dqn_V4_Init_V3x1_Nx1(xyz, w) DQN_LITERAL(Dqn_V4){{xyz.x, xyz.y, xyz.z, w}}
DQN_API bool operator!=(Dqn_V4 lhs, Dqn_V4 rhs);
DQN_API bool operator==(Dqn_V4 lhs, Dqn_V4 rhs);
DQN_API bool operator>=(Dqn_V4 lhs, Dqn_V4 rhs);
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 Dqn_V4 operator- (Dqn_V4 lhs, Dqn_V4 rhs);
DQN_API Dqn_V4 operator- (Dqn_V4 lhs);
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_f32 rhs);
DQN_API Dqn_V4 operator* (Dqn_V4 lhs, int32_t rhs);
DQN_API Dqn_V4 operator/ (Dqn_V4 lhs, Dqn_f32 rhs);
DQN_API Dqn_V4 &operator*=(Dqn_V4 &lhs, Dqn_V4 rhs);
DQN_API Dqn_V4 &operator*=(Dqn_V4 &lhs, Dqn_f32 rhs);
DQN_API Dqn_V4 &operator*=(Dqn_V4 &lhs, int32_t rhs);
DQN_API Dqn_V4 &operator-=(Dqn_V4 &lhs, Dqn_V4 rhs);
DQN_API Dqn_V4 &operator+=(Dqn_V4 &lhs, Dqn_V4 rhs);
#endif // !defined(DQN_NO_V4)
DQN_MSVC_WARNING_POP
#if !defined(DQN_NO_M4)
// NOTE: [$MAT4] Dqn_M4 ============================================================================
// NOTE: Column major matrix
// NOTE: [$MAT4] Dqn_M4 ////////////////////////////////////////////////////////////////////////////
struct Dqn_M4
{
Dqn_f32 columns[4][4];
Dqn_f32 columns[4][4]; // Column major matrix
};
DQN_API Dqn_f32 Dqn_V4Dot(Dqn_V4 a, Dqn_V4 b);
DQN_API Dqn_M4 Dqn_M4_Identity();
DQN_API Dqn_M4 Dqn_M4_ScaleF(Dqn_f32 x, Dqn_f32 y, Dqn_f32 z);
DQN_API Dqn_M4 Dqn_M4_Scale(Dqn_V3 xyz);
DQN_API Dqn_M4 Dqn_M4_TranslateF(Dqn_f32 x, Dqn_f32 y, Dqn_f32 z);
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_Rotate(Dqn_V3 axis, Dqn_f32 radians);
DQN_API Dqn_M4 Dqn_M4_Orthographic(Dqn_f32 left, Dqn_f32 right, Dqn_f32 bottom, Dqn_f32 top, Dqn_f32 z_near, Dqn_f32 z_far);
DQN_API Dqn_M4 Dqn_M4_Perspective(Dqn_f32 fov /*radians*/, Dqn_f32 aspect, Dqn_f32 z_near, Dqn_f32 z_far);
DQN_API Dqn_M4 Dqn_M4_Add(Dqn_M4 lhs, Dqn_M4 rhs);
DQN_API Dqn_M4 Dqn_M4_Sub(Dqn_M4 lhs, Dqn_M4 rhs);
DQN_API Dqn_M4 Dqn_M4_Mul(Dqn_M4 lhs, Dqn_M4 rhs);
DQN_API Dqn_M4 Dqn_M4_Div(Dqn_M4 lhs, Dqn_M4 rhs);
DQN_API Dqn_M4 Dqn_M4_AddF(Dqn_M4 lhs, Dqn_f32 rhs);
DQN_API Dqn_M4 Dqn_M4_SubF(Dqn_M4 lhs, Dqn_f32 rhs);
DQN_API Dqn_M4 Dqn_M4_MulF(Dqn_M4 lhs, Dqn_f32 rhs);
DQN_API Dqn_M4 Dqn_M4_DivF(Dqn_M4 lhs, Dqn_f32 rhs);
#if !defined(DQN_NO_FSTR8)
DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString(Dqn_M4 mat);
#endif
#endif // !defined(DQN_M4)
// 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 Dqn_M2x3 Dqn_M2x3_Identity ();
DQN_API Dqn_M2x3 Dqn_M2x3_Translate(Dqn_V2 offset);
DQN_API Dqn_M2x3 Dqn_M2x3_Scale (Dqn_V2 scale);
DQN_API Dqn_M2x3 Dqn_M2x3_Rotate (Dqn_f32 radians);
DQN_API Dqn_M2x3 Dqn_M2x3_Mul (Dqn_M2x3 m1, Dqn_M2x3 m2);
DQN_API Dqn_V2 Dqn_M2x3_MulV2 (Dqn_M2x3 m1, Dqn_V2 v2);
// NOTE: [$RECT] Dqn_Rect ==========================================================================
// 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;
@ -270,47 +105,12 @@ struct Dqn_RectMinMax
Dqn_V2 min, max;
};
#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}}}
DQN_API bool operator== (const Dqn_Rect& lhs, const Dqn_Rect& rhs);
DQN_API Dqn_V2 Dqn_Rect_Center (Dqn_Rect rect);
DQN_API bool Dqn_Rect_ContainsPoint (Dqn_Rect rect, Dqn_V2 p);
DQN_API bool Dqn_Rect_ContainsRect (Dqn_Rect a, Dqn_Rect b);
DQN_API Dqn_Rect Dqn_Rect_Expand (Dqn_Rect a, Dqn_f32 amount);
DQN_API Dqn_Rect Dqn_Rect_ExpandV2 (Dqn_Rect a, Dqn_V2 amount);
DQN_API bool Dqn_Rect_Intersects (Dqn_Rect a, Dqn_Rect b);
DQN_API Dqn_Rect Dqn_Rect_Intersection (Dqn_Rect a, Dqn_Rect b);
DQN_API Dqn_Rect Dqn_Rect_Union (Dqn_Rect a, Dqn_Rect b);
DQN_API Dqn_RectMinMax Dqn_Rect_MinMax (Dqn_Rect a);
DQN_API Dqn_f32 Dqn_Rect_Area (Dqn_Rect a);
DQN_API Dqn_V2 Dqn_Rect_InterpolatedPoint(Dqn_Rect rect, Dqn_V2 t01);
DQN_API Dqn_V2 Dqn_Rect_TopLeft (Dqn_Rect rect);
DQN_API Dqn_V2 Dqn_Rect_TopRight (Dqn_Rect rect);
DQN_API Dqn_V2 Dqn_Rect_BottomLeft (Dqn_Rect rect);
DQN_API Dqn_V2 Dqn_Rect_BottomRight (Dqn_Rect rect);
enum Dqn_RectCutClip
{
Dqn_RectCutClip_No,
Dqn_RectCutClip_Yes,
};
DQN_API Dqn_Rect Dqn_Rect_CutLeftClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
DQN_API Dqn_Rect Dqn_Rect_CutRightClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
DQN_API Dqn_Rect Dqn_Rect_CutTopClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
DQN_API Dqn_Rect Dqn_Rect_CutBottomClip(Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
#define Dqn_Rect_CutLeft(rect, amount) Dqn_Rect_CutLeftClip(rect, amount, Dqn_RectCutClip_Yes)
#define Dqn_Rect_CutRight(rect, amount) Dqn_Rect_CutRightClip(rect, amount, Dqn_RectCutClip_Yes)
#define Dqn_Rect_CutTop(rect, amount) Dqn_Rect_CutTopClip(rect, amount, Dqn_RectCutClip_Yes)
#define Dqn_Rect_CutBottom(rect, amount) Dqn_Rect_CutBottomClip(rect, amount, Dqn_RectCutClip_Yes)
#define Dqn_Rect_CutLeftNoClip(rect, amount) Dqn_Rect_CutLeftClip(rect, amount, Dqn_RectCutClip_No)
#define Dqn_Rect_CutRightNoClip(rect, amount) Dqn_Rect_CutRightClip(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)
enum Dqn_RectCutSide
{
Dqn_RectCutSide_Left,
@ -324,27 +124,10 @@ struct Dqn_RectCut
Dqn_Rect* rect;
Dqn_RectCutSide 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_Right(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Right}
#define Dqn_RectCut_Top(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Top}
#define Dqn_RectCut_Bottom(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Bottom}
DQN_API Dqn_Rect Dqn_RectCut_Cut(Dqn_RectCut rect_cut, Dqn_V2 size, Dqn_RectCutClip clip);
#endif // !defined(DQN_NO_RECT)
// NOTE: [$MATH] Raycast ===========================================================================
//
// NOTE: [$MATH] Other /////////////////////////////////////////////////////////////////////////////
// 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
@ -352,9 +135,274 @@ struct Dqn_RaycastLineIntersectV2Result
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);
#if !defined(DQN_NO_V2)
// NOTE: [$VEC2] Vector2 ///////////////////////////////////////////////////////////////////////////
#define Dqn_V2I_Zero DQN_LITERAL(Dqn_V2I){{(int32_t)(0), (int32_t)(0)}}
#define Dqn_V2I_One DQN_LITERAL(Dqn_V2I){{(int32_t)(1), (int32_t)(1)}}
#define Dqn_V2I_InitNx1(x) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(x)}}
#define Dqn_V2I_InitNx2(x, y) DQN_LITERAL(Dqn_V2I){{(int32_t)(x), (int32_t)(y)}}
#define Dqn_V2I_InitV2(xy) DQN_LITERAL(Dqn_V2I){{(int32_t)(xy).x, (int32_t)(xy).y}}
// NOTE: [$MATH] Other =============================================================================
DQN_API Dqn_V2 Dqn_Lerp_V2(Dqn_V2 a, Dqn_f32 t, Dqn_V2 b);
DQN_API Dqn_f32 Dqn_Lerp_F32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b);
DQN_MSVC_WARNING_POP
DQN_API bool operator!= (Dqn_V2I lhs, Dqn_V2I rhs);
DQN_API bool operator== (Dqn_V2I lhs, Dqn_V2I rhs);
DQN_API bool operator>= (Dqn_V2I lhs, Dqn_V2I rhs);
DQN_API bool operator<= (Dqn_V2I lhs, Dqn_V2I rhs);
DQN_API bool operator< (Dqn_V2I lhs, Dqn_V2I rhs);
DQN_API bool 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_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_f32 rhs);
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_f32 rhs);
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_f32 rhs);
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_f32 rhs);
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 Dqn_V2I_Min (Dqn_V2I a, Dqn_V2I b);
DQN_API Dqn_V2I Dqn_V2I_Max (Dqn_V2I a, Dqn_V2I b);
DQN_API Dqn_V2I Dqn_V2I_Abs (Dqn_V2I a);
#define Dqn_V2U16_Zero DQN_LITERAL(Dqn_V2U16){{(uint16_t)(0), (uint16_t)(0)}}
#define Dqn_V2U16_One DQN_LITERAL(Dqn_V2U16){{(uint16_t)(1), (uint16_t)(1)}}
#define Dqn_V2U16_InitNx1(x) DQN_LITERAL(Dqn_V2U16){{(uint16_t)(x), (uint16_t)(x)}}
#define Dqn_V2U16_InitNx2(x, y) DQN_LITERAL(Dqn_V2U16){{(uint16_t)(x), (uint16_t)(y)}}
DQN_API bool operator!= (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
DQN_API bool operator== (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
DQN_API bool operator>= (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
DQN_API bool operator<= (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
DQN_API bool operator< (Dqn_V2U16 lhs, Dqn_V2U16 rhs);
DQN_API bool 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);
DQN_API Dqn_V2U16 operator* (Dqn_V2U16 lhs, Dqn_f32 rhs);
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_f32 rhs);
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_f32 rhs);
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_f32 rhs);
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);
#define Dqn_V2_Zero DQN_LITERAL(Dqn_V2){{(Dqn_f32)(0), (Dqn_f32)(0)}}
#define Dqn_V2_One DQN_LITERAL(Dqn_V2){{(Dqn_f32)(1), (Dqn_f32)(1)}}
#define Dqn_V2_InitNx1(x) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(x), (Dqn_f32)(x)}}
#define Dqn_V2_InitNx2(x, y) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(x), (Dqn_f32)(y)}}
#define Dqn_V2_InitV2I(xy) DQN_LITERAL(Dqn_V2){{(Dqn_f32)(xy).x, (Dqn_f32)(xy).y}}
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_API Dqn_V2 operator- (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 operator- (Dqn_V2 lhs, Dqn_V2I rhs);
DQN_API Dqn_V2 operator- (Dqn_V2 lhs, Dqn_f32 rhs);
DQN_API Dqn_V2 operator- (Dqn_V2 lhs, int32_t rhs);
DQN_API Dqn_V2 operator+ (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 operator+ (Dqn_V2 lhs, Dqn_V2I rhs);
DQN_API Dqn_V2 operator+ (Dqn_V2 lhs, Dqn_f32 rhs);
DQN_API Dqn_V2 operator+ (Dqn_V2 lhs, int32_t rhs);
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, Dqn_V2I rhs);
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, Dqn_f32 rhs);
DQN_API Dqn_V2 operator* (Dqn_V2 lhs, int32_t rhs);
DQN_API Dqn_V2 operator/ (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 operator/ (Dqn_V2 lhs, Dqn_V2I rhs);
DQN_API Dqn_V2 operator/ (Dqn_V2 lhs, Dqn_f32 rhs);
DQN_API Dqn_V2 operator/ (Dqn_V2 lhs, int32_t rhs);
DQN_API Dqn_V2 & operator*= (Dqn_V2& lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 & operator*= (Dqn_V2& lhs, Dqn_V2I rhs);
DQN_API Dqn_V2 & operator*= (Dqn_V2& lhs, Dqn_f32 rhs);
DQN_API Dqn_V2 & operator*= (Dqn_V2& lhs, int32_t rhs);
DQN_API Dqn_V2 & operator/= (Dqn_V2& lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 & operator/= (Dqn_V2& lhs, Dqn_V2I rhs);
DQN_API Dqn_V2 & operator/= (Dqn_V2& lhs, Dqn_f32 rhs);
DQN_API Dqn_V2 & operator/= (Dqn_V2& lhs, int32_t rhs);
DQN_API Dqn_V2 & operator-= (Dqn_V2& lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 & operator-= (Dqn_V2& lhs, Dqn_V2I rhs);
DQN_API Dqn_V2 & operator-= (Dqn_V2& lhs, Dqn_f32 rhs);
DQN_API Dqn_V2 & operator-= (Dqn_V2& lhs, int32_t rhs);
DQN_API Dqn_V2 & operator+= (Dqn_V2& lhs, Dqn_V2 rhs);
DQN_API Dqn_V2 & operator+= (Dqn_V2& lhs, Dqn_V2I rhs);
DQN_API Dqn_V2 & operator+= (Dqn_V2& lhs, Dqn_f32 rhs);
DQN_API Dqn_V2 & operator+= (Dqn_V2& lhs, int32_t rhs);
DQN_API Dqn_V2 Dqn_V2_Min (Dqn_V2 a, Dqn_V2 b);
DQN_API Dqn_V2 Dqn_V2_Max (Dqn_V2 a, Dqn_V2 b);
DQN_API Dqn_V2 Dqn_V2_Abs (Dqn_V2 a);
DQN_API Dqn_f32 Dqn_V2_Dot (Dqn_V2 a, Dqn_V2 b);
DQN_API Dqn_f32 Dqn_V2_LengthSq_V2x2 (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_f32 Dqn_V2_Length_V2x2 (Dqn_V2 lhs, Dqn_V2 rhs);
DQN_API Dqn_f32 Dqn_V2_LengthSq (Dqn_V2 lhs);
DQN_API Dqn_f32 Dqn_V2_Length (Dqn_V2 lhs);
DQN_API Dqn_V2 Dqn_V2_Normalise (Dqn_V2 a);
DQN_API Dqn_V2 Dqn_V2_Perpendicular (Dqn_V2 a);
DQN_API Dqn_V2 Dqn_V2_Reflect (Dqn_V2 in, Dqn_V2 surface);
DQN_API Dqn_f32 Dqn_V2_Area (Dqn_V2 a);
#endif // !defined(DQN_NO_V2)
#if !defined(DQN_NO_V3)
// NOTE: [$VEC3] Vector3 ///////////////////////////////////////////////////////////////////////////
#define Dqn_V3_InitNx1(x) DQN_LITERAL(Dqn_V3){{(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}}
#define Dqn_V3_InitNx3(x, y, z) DQN_LITERAL(Dqn_V3){{(Dqn_f32)(x), (Dqn_f32)(y), (Dqn_f32)(z)}}
#define Dqn_V3_InitV2x1_Nx1(xy, z) DQN_LITERAL(Dqn_V3){{(Dqn_f32)(xy.x), (Dqn_f32)(xy.y), (Dqn_f32)(z)}}
DQN_API bool operator!= (Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API bool operator== (Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API bool operator>= (Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API bool operator<= (Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API bool operator< (Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API bool 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_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_f32 rhs);
DQN_API Dqn_V3 operator* (Dqn_V3 lhs, int32_t rhs);
DQN_API Dqn_V3 operator/ (Dqn_V3 lhs, Dqn_V3 rhs);
DQN_API Dqn_V3 operator/ (Dqn_V3 lhs, Dqn_f32 rhs);
DQN_API Dqn_V3 operator/ (Dqn_V3 lhs, int32_t rhs);
DQN_API Dqn_V3 & operator*= (Dqn_V3 &lhs, Dqn_V3 rhs);
DQN_API Dqn_V3 & operator*= (Dqn_V3 &lhs, Dqn_f32 rhs);
DQN_API Dqn_V3 & operator*= (Dqn_V3 &lhs, int32_t rhs);
DQN_API Dqn_V3 & operator/= (Dqn_V3 &lhs, Dqn_V3 rhs);
DQN_API Dqn_V3 & operator/= (Dqn_V3 &lhs, Dqn_f32 rhs);
DQN_API Dqn_V3 & operator/= (Dqn_V3 &lhs, int32_t rhs);
DQN_API Dqn_V3 & operator-= (Dqn_V3 &lhs, Dqn_V3 rhs);
DQN_API Dqn_V3 & operator+= (Dqn_V3 &lhs, Dqn_V3 rhs);
DQN_API Dqn_f32 Dqn_V3_LengthSq (Dqn_V3 a);
DQN_API Dqn_f32 Dqn_V3_Length (Dqn_V3 a);
DQN_API Dqn_V3 Dqn_V3_Normalise (Dqn_V3 a);
#endif // !defined(DQN_NO_V3)
#if !defined(DQN_NO_V4)
// NOTE: [$VEC4] Vector4 ///////////////////////////////////////////////////////////////////////////
#define Dqn_V4_InitNx1(x) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x), (Dqn_f32)(x)}}
#define Dqn_V4_InitNx4(x, y, z, w) DQN_LITERAL(Dqn_V4){{(Dqn_f32)(x), (Dqn_f32)(y), (Dqn_f32)(z), (Dqn_f32)(w)}}
#define Dqn_V4_Init_V3x1_Nx1(xyz, w) DQN_LITERAL(Dqn_V4){{xyz.x, xyz.y, xyz.z, w}}
DQN_API bool operator!= (Dqn_V4 lhs, Dqn_V4 rhs);
DQN_API bool operator== (Dqn_V4 lhs, Dqn_V4 rhs);
DQN_API bool operator>= (Dqn_V4 lhs, Dqn_V4 rhs);
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 Dqn_V4 operator- (Dqn_V4 lhs, Dqn_V4 rhs);
DQN_API Dqn_V4 operator- (Dqn_V4 lhs);
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_f32 rhs);
DQN_API Dqn_V4 operator* (Dqn_V4 lhs, int32_t rhs);
DQN_API Dqn_V4 operator/ (Dqn_V4 lhs, Dqn_f32 rhs);
DQN_API Dqn_V4 & operator*= (Dqn_V4 &lhs, Dqn_V4 rhs);
DQN_API Dqn_V4 & operator*= (Dqn_V4 &lhs, Dqn_f32 rhs);
DQN_API Dqn_V4 & operator*= (Dqn_V4 &lhs, int32_t rhs);
DQN_API Dqn_V4 & operator-= (Dqn_V4 &lhs, Dqn_V4 rhs);
DQN_API Dqn_V4 & operator+= (Dqn_V4 &lhs, Dqn_V4 rhs);
#endif // !defined(DQN_NO_V4)
#if !defined(DQN_NO_M4)
// NOTE: [$MAT4] Dqn_M4 ////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_f32 Dqn_V4Dot (Dqn_V4 a, Dqn_V4 b);
DQN_API Dqn_M4 Dqn_M4_Identity ();
DQN_API Dqn_M4 Dqn_M4_ScaleF (Dqn_f32 x, Dqn_f32 y, Dqn_f32 z);
DQN_API Dqn_M4 Dqn_M4_Scale (Dqn_V3 xyz);
DQN_API Dqn_M4 Dqn_M4_TranslateF (Dqn_f32 x, Dqn_f32 y, Dqn_f32 z);
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_Rotate (Dqn_V3 axis, Dqn_f32 radians);
DQN_API Dqn_M4 Dqn_M4_Orthographic (Dqn_f32 left, Dqn_f32 right, Dqn_f32 bottom, Dqn_f32 top, Dqn_f32 z_near, Dqn_f32 z_far);
DQN_API Dqn_M4 Dqn_M4_Perspective (Dqn_f32 fov /*radians*/, Dqn_f32 aspect, Dqn_f32 z_near, Dqn_f32 z_far);
DQN_API Dqn_M4 Dqn_M4_Add (Dqn_M4 lhs, Dqn_M4 rhs);
DQN_API Dqn_M4 Dqn_M4_Sub (Dqn_M4 lhs, Dqn_M4 rhs);
DQN_API Dqn_M4 Dqn_M4_Mul (Dqn_M4 lhs, Dqn_M4 rhs);
DQN_API Dqn_M4 Dqn_M4_Div (Dqn_M4 lhs, Dqn_M4 rhs);
DQN_API Dqn_M4 Dqn_M4_AddF (Dqn_M4 lhs, Dqn_f32 rhs);
DQN_API Dqn_M4 Dqn_M4_SubF (Dqn_M4 lhs, Dqn_f32 rhs);
DQN_API Dqn_M4 Dqn_M4_MulF (Dqn_M4 lhs, Dqn_f32 rhs);
DQN_API Dqn_M4 Dqn_M4_DivF (Dqn_M4 lhs, Dqn_f32 rhs);
#if !defined(DQN_NO_FSTR8)
DQN_API Dqn_FStr8<256> Dqn_M4_ColumnMajorString (Dqn_M4 mat);
#endif
#endif // !defined(DQN_NO_M4)
// NOTE: [$M2x3] Dqn_M2x3 //////////////////////////////////////////////////////////////////////////
DQN_API bool operator== (Dqn_M2x3 const &lhs, Dqn_M2x3 const &rhs);
DQN_API bool operator!= (Dqn_M2x3 const &lhs, Dqn_M2x3 const &rhs);
DQN_API Dqn_M2x3 Dqn_M2x3_Identity ();
DQN_API Dqn_M2x3 Dqn_M2x3_Translate (Dqn_V2 offset);
DQN_API Dqn_M2x3 Dqn_M2x3_Scale (Dqn_V2 scale);
DQN_API Dqn_M2x3 Dqn_M2x3_Rotate (Dqn_f32 radians);
DQN_API Dqn_M2x3 Dqn_M2x3_Mul (Dqn_M2x3 m1, Dqn_M2x3 m2);
DQN_API Dqn_V2 Dqn_M2x3_Mul2F32 (Dqn_M2x3 m1, Dqn_f32 x, Dqn_f32 y);
DQN_API Dqn_V2 Dqn_M2x3_MulV2 (Dqn_M2x3 m1, Dqn_V2 v2);
#if !defined(DQN_NO_RECT)
// NOTE: [$RECT] Dqn_Rect //////////////////////////////////////////////////////////////////////////
#define Dqn_Rect_InitV2x2(pos, size) DQN_LITERAL(Dqn_Rect){(pos), (size)}
#define Dqn_Rect_InitNx4(x, y, w, h) DQN_LITERAL(Dqn_Rect){DQN_LITERAL(Dqn_V2){{x, y}}, DQN_LITERAL(Dqn_V2){{w, h}}}
DQN_API bool operator== (const Dqn_Rect& lhs, const Dqn_Rect& rhs);
DQN_API Dqn_V2 Dqn_Rect_Center (Dqn_Rect rect);
DQN_API bool Dqn_Rect_ContainsPoint (Dqn_Rect rect, Dqn_V2 p);
DQN_API bool Dqn_Rect_ContainsRect (Dqn_Rect a, Dqn_Rect b);
DQN_API Dqn_Rect Dqn_Rect_Expand (Dqn_Rect a, Dqn_f32 amount);
DQN_API Dqn_Rect Dqn_Rect_ExpandV2 (Dqn_Rect a, Dqn_V2 amount);
DQN_API bool Dqn_Rect_Intersects (Dqn_Rect a, Dqn_Rect b);
DQN_API Dqn_Rect Dqn_Rect_Intersection (Dqn_Rect a, Dqn_Rect b);
DQN_API Dqn_Rect Dqn_Rect_Union (Dqn_Rect a, Dqn_Rect b);
DQN_API Dqn_RectMinMax Dqn_Rect_MinMax (Dqn_Rect a);
DQN_API Dqn_f32 Dqn_Rect_Area (Dqn_Rect a);
DQN_API Dqn_V2 Dqn_Rect_InterpolatedPoint (Dqn_Rect rect, Dqn_V2 t01);
DQN_API Dqn_V2 Dqn_Rect_TopLeft (Dqn_Rect rect);
DQN_API Dqn_V2 Dqn_Rect_TopRight (Dqn_Rect rect);
DQN_API Dqn_V2 Dqn_Rect_BottomLeft (Dqn_Rect rect);
DQN_API Dqn_V2 Dqn_Rect_BottomRight (Dqn_Rect rect);
DQN_API Dqn_Rect Dqn_Rect_CutLeftClip (Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
DQN_API Dqn_Rect Dqn_Rect_CutRightClip (Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
DQN_API Dqn_Rect Dqn_Rect_CutTopClip (Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
DQN_API Dqn_Rect Dqn_Rect_CutBottomClip (Dqn_Rect *rect, Dqn_f32 amount, Dqn_RectCutClip clip);
#define Dqn_Rect_CutLeft(rect, amount) Dqn_Rect_CutLeftClip(rect, amount, Dqn_RectCutClip_Yes)
#define Dqn_Rect_CutRight(rect, amount) Dqn_Rect_CutRightClip(rect, amount, Dqn_RectCutClip_Yes)
#define Dqn_Rect_CutTop(rect, amount) Dqn_Rect_CutTopClip(rect, amount, Dqn_RectCutClip_Yes)
#define Dqn_Rect_CutBottom(rect, amount) Dqn_Rect_CutBottomClip(rect, amount, Dqn_RectCutClip_Yes)
#define Dqn_Rect_CutLeftNoClip(rect, amount) Dqn_Rect_CutLeftClip(rect, amount, Dqn_RectCutClip_No)
#define Dqn_Rect_CutRightNoClip(rect, amount) Dqn_Rect_CutRightClip(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)
DQN_API Dqn_Rect Dqn_RectCut_Cut (Dqn_RectCut rect_cut, Dqn_V2 size, Dqn_RectCutClip clip);
#define Dqn_RectCut_Init(rect, side) DQN_LITERAL(Dqn_RectCut){rect, side}
#define Dqn_RectCut_Left(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Left}
#define Dqn_RectCut_Right(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Right}
#define Dqn_RectCut_Top(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Top}
#define Dqn_RectCut_Bottom(rect) DQN_LITERAL(Dqn_RectCut){rect, Dqn_RectCutSide_Bottom}
#endif // !defined(DQN_NO_RECT)
// NOTE: [$MATH] Other /////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_RaycastLineIntersectV2Result Dqn_Raycast_LineIntersectV2(Dqn_V2 origin_a, Dqn_V2 dir_a, Dqn_V2 origin_b, Dqn_V2 dir_b);
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);

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)
ExitProcess(exit_code);
#else
exit(exit_code);
#endif
}
Dqn_OSDateTimeStr8 result = {};
result.hms_size = DQN_CAST(uint8_t) DQN_SNPRINTF(result.hms,
DQN_ARRAY_ICOUNT(result.hms),
"%02hhu%c%02hhu%c%02hhu",
time.hour,
hms_separator,
time.minutes,
hms_separator,
time.seconds);
// NOTE: [$EXEC] Dqn_OSExec ========================================================================
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;
}
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);
#if defined(DQN_OS_WIN32)
DWORD exec_result = WaitForSingleObject(handle.process, INFINITE);
if (exec_result == WAIT_FAILED) {
result.os_error_code = GetLastError();
return result;
}
DWORD exit_status;
if (!GetExitCodeProcess(handle.process, &exit_status)) {
result.os_error_code = GetLastError();
return result;
}
result.exit_code = exit_status;
CloseHandle(handle.process);
#elif defined(DQN_PLATFORM_EMSCRIPTEN)
DQN_ASSERTF(false, "Unsupported operation");
#else
for (;;) {
int status = 0;
if (waitpid(DQN_CAST(pid_t)handle.process, &status, 0) < 0) {
result.os_error_code = errno;
break;
}
if (WIFEXITED(status)) {
result.exit_code = WEXITSTATUS(status);
break;
}
if (WIFSIGNALLED(status)) {
result.os_error_code = WTERMSIG(status);
break;
}
}
#endif
DQN_ASSERT(result.hms_size < DQN_ARRAY_UCOUNT(result.hms));
DQN_ASSERT(result.date_size < DQN_ARRAY_UCOUNT(result.date));
return result;
}
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Str8 cmd, Dqn_Str8 working_dir)
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8Now(char date_separator, char hms_separator)
{
Dqn_OSExecAsyncHandle result = {};
if (cmd.size == 0)
return result;
Dqn_OSDateTime time = Dqn_OS_DateLocalTimeNow();
Dqn_OSDateTimeStr8 result = Dqn_OS_DateLocalTimeStr8(time, date_separator, hms_separator);
return result;
}
#if defined(DQN_OS_WIN32)
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str16 cmd16 = Dqn_Win_Str8ToStr16(scratch.arena, cmd);
Dqn_Str16 working_dir16 = Dqn_Win_Str8ToStr16(scratch.arena, working_dir);
PROCESS_INFORMATION proc_info = {};
STARTUPINFOW startup_info = {};
startup_info.cb = sizeof(STARTUPINFOW);
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startup_info.dwFlags |= STARTF_USESTDHANDLES;
BOOL create_result = CreateProcessW(nullptr, cmd16.data, nullptr, nullptr, true, 0, nullptr, working_dir16.data, &startup_info, &proc_info);
if (!create_result) {
result.os_error_code = GetLastError();
DQN_API Dqn_Str8 Dqn_OS_EXEDir(Dqn_Arena *arena)
{
Dqn_Str8 result = {};
if (!arena)
return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
Dqn_Str8 exe_path = Dqn_OS_EXEPath(scratch.arena);
Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")};
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverseArray(exe_path, separators, DQN_ARRAY_UCOUNT(separators));
result = Dqn_Str8_Copy(arena, split.lhs);
return result;
}
DQN_API Dqn_f64 Dqn_OS_PerfCounterS(uint64_t begin, uint64_t end)
{
uint64_t frequency = Dqn_OS_PerfCounterFrequency();
uint64_t ticks = end - begin;
Dqn_f64 result = ticks / DQN_CAST(Dqn_f64)frequency;
return result;
}
DQN_API Dqn_f64 Dqn_OS_PerfCounterMs(uint64_t begin, uint64_t end)
{
uint64_t frequency = Dqn_OS_PerfCounterFrequency();
uint64_t ticks = end - begin;
Dqn_f64 result = (ticks * 1'000) / DQN_CAST(Dqn_f64)frequency;
return result;
}
DQN_API Dqn_f64 Dqn_OS_PerfCounterUs(uint64_t begin, uint64_t end)
{
uint64_t frequency = Dqn_OS_PerfCounterFrequency();
uint64_t ticks = end - begin;
Dqn_f64 result = (ticks * 1'000'000) / DQN_CAST(Dqn_f64)frequency;
return result;
}
DQN_API Dqn_f64 Dqn_OS_PerfCounterNs(uint64_t begin, uint64_t end)
{
uint64_t frequency = Dqn_OS_PerfCounterFrequency();
uint64_t ticks = end - begin;
Dqn_f64 result = (ticks * 1'000'000'000) / DQN_CAST(Dqn_f64)frequency;
return result;
}
DQN_API Dqn_OSTimer Dqn_OS_TimerBegin()
{
Dqn_OSTimer result = {};
result.start = Dqn_OS_PerfCounterNow();
return result;
}
DQN_API void Dqn_OS_TimerEnd(Dqn_OSTimer *timer)
{
timer->end = Dqn_OS_PerfCounterNow();
}
DQN_API Dqn_f64 Dqn_OS_TimerS(Dqn_OSTimer timer)
{
Dqn_f64 result = Dqn_OS_PerfCounterS(timer.start, timer.end);
return result;
}
DQN_API Dqn_f64 Dqn_OS_TimerMs(Dqn_OSTimer timer)
{
Dqn_f64 result = Dqn_OS_PerfCounterMs(timer.start, timer.end);
return result;
}
DQN_API Dqn_f64 Dqn_OS_TimerUs(Dqn_OSTimer timer)
{
Dqn_f64 result = Dqn_OS_PerfCounterUs(timer.start, timer.end);
return result;
}
DQN_API Dqn_f64 Dqn_OS_TimerNs(Dqn_OSTimer timer)
{
Dqn_f64 result = Dqn_OS_PerfCounterNs(timer.start, timer.end);
return result;
}
DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency)
{
uint64_t os_frequency = Dqn_OS_PerfCounterFrequency();
uint64_t os_target_elapsed = duration_ms_to_gauge_tsc_frequency * os_frequency / 1000ULL;
uint64_t tsc_begin = Dqn_CPU_TSC();
uint64_t result = 0;
if (tsc_begin) {
uint64_t os_elapsed = 0;
for (uint64_t os_begin = Dqn_OS_PerfCounterNow(); os_elapsed < os_target_elapsed; )
os_elapsed = Dqn_OS_PerfCounterNow() - os_begin;
uint64_t tsc_end = Dqn_CPU_TSC();
uint64_t tsc_elapsed = tsc_end - tsc_begin;
result = tsc_elapsed / os_elapsed * os_frequency;
}
return result;
}
#if !defined(DQN_NO_OS_FILE_API)
// NOTE: [$FILE] Dqn_OSPathInfo/File ///////////////////////////////////////////////////////////////
DQN_API bool Dqn_OS_WriteFile(Dqn_OSFile *file, Dqn_Str8 buffer)
{
bool result = Dqn_OS_WriteFileBuffer(file, buffer.data, buffer.size);
return result;
}
DQN_API bool Dqn_OS_WriteFileFV(Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, va_list args)
{
bool result = false;
if (!file || !fmt)
return result;
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
result = Dqn_OS_WriteFileBuffer(file, buffer.data, buffer.size);
return result;
}
DQN_API bool Dqn_OS_WriteFileF(Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
bool result = Dqn_OS_WriteFileFV(file, fmt, args);
va_end(args);
return result;
}
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
DQN_API bool Dqn_OS_WriteAll(Dqn_Str8 path, Dqn_Str8 buffer)
{
Dqn_OSFile file = Dqn_OS_OpenFile(path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_Write);
bool result = Dqn_OS_WriteFile(&file, buffer);
Dqn_OS_CloseFile(&file);
return result;
}
DQN_API bool Dqn_OS_WriteAllFV(Dqn_Str8 file_path, DQN_FMT_ATTRIB char const *fmt, va_list args)
{
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
bool result = Dqn_OS_WriteAll(file_path, buffer);
return result;
}
DQN_API bool Dqn_OS_WriteAllF(Dqn_Str8 file_path, DQN_FMT_ATTRIB char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
bool result = Dqn_OS_WriteAllFV(file_path, fmt, args);
va_end(args);
return result;
}
DQN_API bool Dqn_OS_WriteAllSafe(Dqn_Str8 path, Dqn_Str8 buffer)
{
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 tmp_path = Dqn_Str8_InitF(scratch.arena, "%.*s.tmp", DQN_STR_FMT(path));
if (!Dqn_OS_WriteAll(tmp_path, buffer)) {
Dqn_Log_ErrorF("Failed to write to temporary file [path=%.*s]", DQN_STR_FMT(tmp_path));
return false;
}
CloseHandle(proc_info.hThread);
result.process = proc_info.hProcess;
#else
DQN_ASSERTF(false, "Unsupported operation");
// TODO: This API will need to switch to an array of strings for unix
#if 0
pid_t child_pid = fork();
if (child_pid < 0) {
result.os_error_code = errno;
return result;
if (!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 (child_pid == 0) {
if (working_dir.size) {
if (chdir(working_dir.data) == -1) {
result.os_error_code = errno;
return result;
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 (;;) {
Dqn_Str8BinarySplitResult delimiter = Dqn_Str8_BinarySplitArray(path, delimiter_array, DQN_ARRAY_UCOUNT(delimiter_array));
for (; delimiter.lhs.data; delimiter = Dqn_Str8_BinarySplitArray(delimiter.rhs, delimiter_array, DQN_ARRAY_UCOUNT(delimiter_array))) {
if (delimiter.lhs.size <= 0)
continue;
Dqn_OSPathLink *link = Dqn_Arena_New(arena, Dqn_OSPathLink, Dqn_ZeroMem_Yes);
if (!link)
return false;
link->string = delimiter.lhs;
link->prev = fs_path->tail;
if (fs_path->tail) {
fs_path->tail->next = link;
} else {
fs_path->head = link;
}
fs_path->tail = link;
fs_path->links_size += 1;
fs_path->string_size += delimiter.lhs.size;
}
if (!delimiter.lhs.data)
break;
}
return true;
}
DQN_API bool Dqn_OS_PathAdd(Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path)
{
Dqn_Str8 copy = Dqn_Str8_Copy(arena, path);
bool result = Dqn_Str8_HasData(copy) ? true : Dqn_OS_PathAddRef(arena, fs_path, copy);
return result;
}
DQN_API bool Dqn_OS_PathAddF(Dqn_Arena *arena, Dqn_OSPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...)
{
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;
}
}
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
result.data[string_size] = 0;
return result;
}
DQN_API Dqn_OSExecResult Dqn_OS_Exec(Dqn_Str8 cmd, Dqn_Str8 working_dir)
// 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, working_dir);
Dqn_OSExecAsyncHandle async_handle = Dqn_OS_ExecAsync(cmd_line, working_dir);
Dqn_OSExecResult result = Dqn_OS_ExecWait(async_handle);
return result;
}
DQN_API void Dqn_OS_ExecOrAbort(Dqn_Str8 cmd, Dqn_Str8 working_dir)
DQN_API void Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir)
{
Dqn_OSExecResult result = Dqn_OS_Exec(cmd, working_dir);
Dqn_OSExecResult result = Dqn_OS_Exec(cmd_line, working_dir);
if (result.os_error_code || result.exit_code) {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 cmd_combined = Dqn_Slice_Str8Render(scratch.arena, cmd_line, DQN_STR8(" ") /*separator*/);
if (result.os_error_code) {
Dqn_Log_ErrorF("OS failed to execute the requested command returning the error code %u. The command was\n\n%.*s", result.os_error_code, DQN_STR_FMT(cmd));
Dqn_Log_ErrorF("OS failed to execute the requested command returning the error code %u. The command was\n\n%.*s", result.os_error_code, DQN_STR_FMT(cmd_combined));
Dqn_OS_Exit(result.os_error_code);
}
if (result.exit_code) {
Dqn_Log_ErrorF("OS executed command and returned a non-zero status: %u. The command was\n\n%.*s", result.exit_code, DQN_STR_FMT(cmd));
Dqn_Log_ErrorF("OS executed command and returned a non-zero status: %u. The command was\n\n%.*s", result.exit_code, DQN_STR_FMT(cmd_combined));
Dqn_OS_Exit(result.exit_code);
}
}
}
// NOTE: [$HTTP] Dqn_OSHttp ////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_OS_HttpRequestWait(Dqn_OSHttpResponse *response)
{
if (response && Dqn_OS_SemaphoreHasData(&response->on_complete_semaphore))
Dqn_OS_SemaphoreWait(&response->on_complete_semaphore, DQN_OS_SEMAPHORE_INFINITE_TIMEOUT);
}
DQN_API Dqn_OSHttpResponse Dqn_OS_HttpRequest(Dqn_Arena *arena, Dqn_Str8 host, Dqn_Str8 path, Dqn_OSHttpRequestSecure secure, Dqn_Str8 method, Dqn_Str8 body, Dqn_Str8 headers)
{
// TODO(doyle): Revise the memory allocation and its lifetime
Dqn_OSHttpResponse result = {};
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
result.scratch_arena = scratch.arena;
Dqn_OS_HttpRequestAsync(&result, arena, host, path, secure, method, body, headers);
Dqn_OS_HttpRequestWait(&result);
return result;
}

376
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
{
uint32_t os_error_code;
uint32_t exit_code;
void *process;
};
@ -11,8 +183,200 @@ struct Dqn_OSExecResult
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_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Str8 cmd, Dqn_Str8 working_dir);
DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Str8 cmd, Dqn_Str8 working_dir);
DQN_API void Dqn_OS_ExecOrAbort(Dqn_Str8 cmd, Dqn_Str8 working_dir);
#if !defined(DQN_NO_SEMAPHORE)
// NOTE: [$SEMA] Dqn_OSSemaphore ///////////////////////////////////////////////////////////////////
uint32_t const DQN_OS_SEMAPHORE_INFINITE_TIMEOUT = UINT32_MAX;
struct Dqn_OSSemaphore
{
#if defined(DQN_OS_WIN32) && !defined(DQN_OS_WIN32_USE_PTHREADS)
void *win32_handle;
#else
sem_t posix_handle;
bool posix_init;
#endif
};
enum Dqn_OSSemaphoreWaitResult
{
Dqn_OSSemaphoreWaitResult_Failed,
Dqn_OSSemaphoreWaitResult_Success,
Dqn_OSSemaphoreWaitResult_Timeout,
};
#endif // !defined(DQN_NO_SEMAPHORE)
// NOTE: [$MUTX] Dqn_OSMutex ///////////////////////////////////////////////////////////////////////
struct Dqn_OSMutex
{
#if defined(DQN_OS_WIN32) && !defined(DQN_OS_WIN32_USE_PTHREADS)
char win32_handle[48];
#else
pthread_mutex_t posix_handle;
pthread_mutexattr_t posix_attribs;
#endif
};
// NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE)
typedef int32_t (Dqn_OSThreadFunc)(struct Dqn_OSThread*);
struct Dqn_OSThread
{
void *handle;
uint64_t thread_id;
void *user_context;
Dqn_OSThreadFunc *func;
Dqn_OSSemaphore init_semaphore;
};
#endif // !defined(DQN_NO_THREAD)
// NOTE: [$HTTP] Dqn_OSHttp ////////////////////////////////////////////////////////////////////////
enum Dqn_OSHttpRequestSecure
{
Dqn_OSHttpRequestSecure_No,
Dqn_OSHttpRequestSecure_Yes,
};
struct Dqn_OSHttpResponse
{
// NOTE: Response data
uint32_t error_code;
Dqn_Str8 error_msg;
uint16_t http_status;
Dqn_Str8 body;
Dqn_b32 done;
// NOTE: Book-keeping
Dqn_Arena *arena; // Allocates memory for the response
// NOTE: Async book-keeping
// Synchronous HTTP response uses the TLS scratch arena whereas async
// calls use their own dedicated arena.
Dqn_Arena tmp_arena;
Dqn_Arena *scratch_arena;
Dqn_Str8Builder builder;
Dqn_OSSemaphore on_complete_semaphore;
#if defined(DQN_PLATFORM_EMSCRIPTEN)
emscripten_fetch_t *em_handle;
#elif defined(DQN_OS_WIN32)
HINTERNET win32_request_session;
HINTERNET win32_request_connection;
HINTERNET win32_request_handle;
#endif
};
// NOTE: [$OMEM] Memory //////////////////////////////////////////////////////////////////////////
DQN_API void * Dqn_OS_MemReserve (Dqn_usize size, Dqn_OSMemCommit commit, uint32_t page_flags);
DQN_API bool Dqn_OS_MemCommit (void *ptr, Dqn_usize size, uint32_t page_flags);
DQN_API void Dqn_OS_MemDecommit(void *ptr, Dqn_usize size);
DQN_API void Dqn_OS_MemRelease (void *ptr, Dqn_usize size);
DQN_API int Dqn_OS_MemProtect (void *ptr, Dqn_usize size, uint32_t page_flags);
// NOTE: [$DATE] Date //////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_OSDateTime Dqn_OS_DateLocalTimeNow ();
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8Now(char date_separator = '-', char hms_separator = ':');
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8 (Dqn_OSDateTime time, char date_separator = '-', char hms_separator = ':');
DQN_API uint64_t Dqn_OS_DateUnixTime ();
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
DQN_API bool Dqn_OS_SecureRNGBytes (void *buffer, uint32_t size);
DQN_API Dqn_Str8 Dqn_OS_EXEPath (Dqn_Arena *arena);
DQN_API Dqn_Str8 Dqn_OS_EXEDir (Dqn_Arena *arena);
DQN_API void Dqn_OS_SleepMs (Dqn_uint milliseconds);
// NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
DQN_API uint64_t Dqn_OS_PerfCounterNow ();
DQN_API uint64_t Dqn_OS_PerfCounterFrequency();
DQN_API Dqn_f64 Dqn_OS_PerfCounterS (uint64_t begin, uint64_t end);
DQN_API Dqn_f64 Dqn_OS_PerfCounterMs (uint64_t begin, uint64_t end);
DQN_API Dqn_f64 Dqn_OS_PerfCounterUs (uint64_t begin, uint64_t end);
DQN_API Dqn_f64 Dqn_OS_PerfCounterNs (uint64_t begin, uint64_t end);
DQN_API Dqn_OSTimer Dqn_OS_TimerBegin ();
DQN_API void Dqn_OS_TimerEnd (Dqn_OSTimer *timer);
DQN_API Dqn_f64 Dqn_OS_TimerS (Dqn_OSTimer timer);
DQN_API Dqn_f64 Dqn_OS_TimerMs (Dqn_OSTimer timer);
DQN_API Dqn_f64 Dqn_OS_TimerUs (Dqn_OSTimer timer);
DQN_API Dqn_f64 Dqn_OS_TimerNs (Dqn_OSTimer timer);
DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_frequency);
#if !defined(DQN_NO_OS_FILE_API)
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo (Dqn_Str8 path);
DQN_API bool Dqn_OS_PathDelete(Dqn_Str8 path);
DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path);
DQN_API bool Dqn_OS_FileCopy (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite);
DQN_API bool Dqn_OS_FileMove (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite);
DQN_API bool Dqn_OS_DirExists (Dqn_Str8 path);
DQN_API bool Dqn_OS_DirMake (Dqn_Str8 path);
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_OSFile Dqn_OS_OpenFile (Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint32_t access);
DQN_API bool Dqn_OS_WriteFileBuffer(Dqn_OSFile *file, void const *data, Dqn_usize size);
DQN_API bool Dqn_OS_WriteFile (Dqn_OSFile *file, Dqn_Str8 buffer);
DQN_API bool Dqn_OS_WriteFileFV (Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API bool Dqn_OS_WriteFileF (Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API void Dqn_OS_CloseFile (Dqn_OSFile *file);
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
DQN_API Dqn_Str8 Dqn_OS_ReadAll (Dqn_Str8 path, Dqn_Arena *arena);
DQN_API bool Dqn_OS_WriteAll (Dqn_Str8 path, Dqn_Str8 buffer);
DQN_API bool Dqn_OS_WriteAllFV (Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API bool Dqn_OS_WriteAllF (Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API bool Dqn_OS_WriteAllSafe (Dqn_Str8 path, Dqn_Str8 buffer);
DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, va_list args);
DQN_API bool Dqn_OS_WriteAllSafeF (Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, ...);
#endif // !defined(DQN_NO_OS_FILE_API)
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
DQN_API bool Dqn_OS_PathAddRef (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path);
DQN_API bool Dqn_OS_PathAdd (Dqn_Arena *arena, Dqn_OSPath *fs_path, Dqn_Str8 path);
DQN_API bool Dqn_OS_PathAddF (Dqn_Arena *arena, Dqn_OSPath *fs_path, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API bool Dqn_OS_PathPop (Dqn_OSPath *fs_path);
DQN_API Dqn_Str8 Dqn_OS_PathBuildWithSeparator(Dqn_Arena *arena, Dqn_OSPath const *fs_path, Dqn_Str8 path_separator);
DQN_API Dqn_Str8 Dqn_OS_PathConvertTo (Dqn_Arena *arena, Dqn_Str8 path, Dqn_Str8 path_separtor);
DQN_API Dqn_Str8 Dqn_OS_PathConvertToF (Dqn_Arena *arena, Dqn_Str8 path_separator, DQN_FMT_ATTRIB char const *fmt, ...);
DQN_API Dqn_Str8 Dqn_OS_PathConvert (Dqn_Arena *arena, Dqn_Str8 path);
DQN_API Dqn_Str8 Dqn_OS_PathConvertF (Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...);
#define Dqn_OS_PathBuildFwdSlash(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, DQN_STR8("/"))
#define Dqn_OS_PathBuildBackSlash(allocator, fs_path) Dqn_OS_PathBuildWithSeparator(allocator, fs_path, DQN_STR8("\\"))
#if defined(DQN_OS_WIN32)
#define Dqn_OS_PathBuild(allocator, fs_path) Dqn_OS_PathBuildBackSlash(allocator, fs_path)
#else
#define Dqn_OS_PathBuild(allocator, fs_path) Dqn_OS_PathBuildFwdSlash(allocator, fs_path)
#endif
// NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_OS_Exit (uint32_t exit_code);
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait (Dqn_OSExecAsyncHandle handle);
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir);
DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir);
DQN_API void Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir);
// NOTE: [$SEMA] Dqn_OSSemaphore ///////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_SEMAPHORE)
DQN_API Dqn_OSSemaphore Dqn_OS_SemaphoreInit (uint32_t initial_count);
DQN_API bool Dqn_OS_SemaphoreIsValid (Dqn_OSSemaphore *semaphore);
DQN_API void Dqn_OS_SemaphoreDeinit (Dqn_OSSemaphore *semaphore);
DQN_API void Dqn_OS_SemaphoreIncrement(Dqn_OSSemaphore *semaphore, uint32_t amount);
DQN_API Dqn_OSSemaphoreWaitResult Dqn_OS_SemaphoreWait (Dqn_OSSemaphore *semaphore, uint32_t timeout_ms);
#endif // !defined(DQN_NO_SEMAPHORE)
// NOTE: [$MUTX] Dqn_OSMutex ///////////////////////////////////////////////////////////////////////
DQN_API Dqn_OSMutex Dqn_OS_MutexInit (uint32_t initial_count, uint32_t max_count);
DQN_API void Dqn_OS_MutexDeinit(Dqn_OSMutex *mutex);
DQN_API void Dqn_OS_MutexLock (Dqn_OSMutex mutex);
DQN_API void Dqn_OS_MutexUnlock(Dqn_OSMutex mutex);
// NOTE: [$THRD] Dqn_OSThread /////////////////////////////////////////////////////////////////////
#if !defined(DQN_NO_THREAD) && !defined(DQN_NO_SEMAPHORE)
DQN_API bool Dqn_OS_ThreadInit (Dqn_OSThread *thread, Dqn_OSThreadFunc *func, void *user_context);
DQN_API void Dqn_OS_ThreadDeinit(Dqn_OSThread thread);
DQN_API uint32_t Dqn_OS_ThreadID ();
#endif // !defined(DQN_NO_THREAD)
// NOTE: [$HTTP] Dqn_OSHttp ////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_OS_HttpRequestAsync(Dqn_OSHttpResponse *response, Dqn_Arena *arena, Dqn_Str8 host, Dqn_Str8 path, Dqn_OSHttpRequestSecure secure, Dqn_Str8 method, Dqn_Str8 body, Dqn_Str8 headers);
DQN_API void Dqn_OS_HttpRequestWait (Dqn_OSHttpResponse *response);
DQN_API void Dqn_OS_HttpRequestFree (Dqn_OSHttpResponse *response);
DQN_API Dqn_OSHttpResponse Dqn_OS_HttpRequest (Dqn_Arena *arena, Dqn_Str8 host, Dqn_Str8 path, Dqn_OSHttpRequestSecure secure, Dqn_Str8 method, Dqn_Str8 body, Dqn_Str8 headers);

926
dqn_os_posix.cpp Normal file
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, ...)
{
va_list args;
va_start(args, fmt);
Dqn_usize result = STB_SPRINTF_DECORATE(vsnprintf)(nullptr, 0, fmt, args);
Dqn_usize result = DQN_VSNPRINTF(nullptr, 0, fmt, args);
va_end(args);
return result;
}
@ -12,7 +27,7 @@ DQN_API Dqn_usize Dqn_CStr8_FVSize(DQN_FMT_ATTRIB char const *fmt, va_list args)
{
va_list args_copy;
va_copy(args_copy, args);
Dqn_usize result = STB_SPRINTF_DECORATE(vsnprintf)(nullptr, 0, fmt, args_copy);
Dqn_usize result = DQN_VSNPRINTF(nullptr, 0, fmt, args_copy);
va_end(args_copy);
return result;
}
@ -38,17 +53,32 @@ DQN_API Dqn_usize Dqn_CStr16_Size(wchar_t const *src)
return result;
}
// NOTE: [$STR8] Dqn_Str8 =======================================================================
// NOTE: [$STR6] Dqn_Str16 /////////////////////////////////////////////////////////////////////////
DQN_API bool operator==(Dqn_Str16 const &lhs, Dqn_Str16 const &rhs)
{
bool result = false;
if (lhs.size == rhs.size)
result = DQN_MEMCMP(lhs.data, rhs.data, lhs.size * sizeof(*lhs.data)) == 0;
return result;
}
DQN_API bool operator!=(Dqn_Str16 const &lhs, Dqn_Str16 const &rhs)
{
bool result = !(lhs == rhs);
return result;
}
// NOTE: [$STR8] Dqn_Str8 //////////////////////////////////////////////////////////////////////////
DQN_API Dqn_Str8 Dqn_Str8_InitCStr8(char const *src)
{
Dqn_usize size = Dqn_CStr8_Size(src);
Dqn_Str8 result = Dqn_Str8_Init(src, size);
Dqn_usize size = Dqn_CStr8_Size(src);
Dqn_Str8 result = Dqn_Str8_Init(src, size);
return result;
}
DQN_API bool Dqn_Str8_IsAll(Dqn_Str8 string, Dqn_Str8IsAll is_all)
{
bool result = Dqn_Str8_IsValid(string);
bool result = Dqn_Str8_HasData(string);
if (!result)
return result;
@ -73,7 +103,7 @@ DQN_API bool Dqn_Str8_IsAll(Dqn_Str8 string, Dqn_Str8IsAll is_all)
DQN_API Dqn_Str8 Dqn_Str8_Slice(Dqn_Str8 string, Dqn_usize offset, Dqn_usize size)
{
Dqn_Str8 result = Dqn_Str8_Init(string.data, 0);
if (!Dqn_Str8_IsValid(result))
if (!Dqn_Str8_HasData(string))
return result;
Dqn_usize capped_offset = DQN_MIN(offset, string.size);
@ -92,7 +122,7 @@ DQN_API Dqn_Str8 Dqn_Str8_Advance(Dqn_Str8 string, Dqn_usize amount)
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
{
Dqn_Str8BinarySplitResult result = {};
if (!Dqn_Str8_IsValid(string) || !find || find_size == 0)
if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
return result;
result.lhs = string;
@ -121,7 +151,7 @@ DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplit(Dqn_Str8 string, Dqn_Str8
DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverseArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
{
Dqn_Str8BinarySplitResult result = {};
if (!Dqn_Str8_IsValid(string) || !find || find_size == 0)
if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
return result;
result.lhs = string;
@ -147,17 +177,17 @@ DQN_API Dqn_Str8BinarySplitResult Dqn_Str8_BinarySplitReverse(Dqn_Str8 string, D
return result;
}
DQN_API Dqn_usize Dqn_Str8_Split(Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count)
DQN_API Dqn_usize Dqn_Str8_Split(Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *splits, Dqn_usize splits_count, Dqn_Str8SplitIncludeEmptyStrings mode)
{
Dqn_usize result = 0; // The number of splits in the actual string.
if (!Dqn_Str8_IsValid(string) || !Dqn_Str8_IsValid(delimiter) || delimiter.size <= 0)
if (!Dqn_Str8_HasData(string) || !Dqn_Str8_HasData(delimiter) || delimiter.size <= 0)
return result;
Dqn_Str8BinarySplitResult split = {};
Dqn_Str8 first = string;
do {
split = Dqn_Str8_BinarySplit(first, delimiter);
if (split.lhs.size) {
if (split.lhs.size || mode == Dqn_Str8SplitIncludeEmptyStrings_Yes) {
if (splits && result < splits_count)
splits[result] = split.lhs;
result++;
@ -168,15 +198,13 @@ DQN_API Dqn_usize Dqn_Str8_Split(Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8 *
return result;
}
DQN_API Dqn_Str8SplitAllocResult Dqn_Str8_SplitAlloc(Dqn_Allocator allocator,
Dqn_Str8 string,
Dqn_Str8 delimiter)
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8_SplitAlloc(Dqn_Arena *arena, Dqn_Str8 string, Dqn_Str8 delimiter, Dqn_Str8SplitIncludeEmptyStrings mode)
{
Dqn_Str8SplitAllocResult result = {};
Dqn_usize splits_required = Dqn_Str8_Split(string, delimiter, /*splits*/ nullptr, /*count*/ 0);
result.data = Dqn_Allocator_NewArray(allocator, Dqn_Str8, splits_required, Dqn_ZeroMem_No);
Dqn_Slice<Dqn_Str8> result = {};
Dqn_usize splits_required = Dqn_Str8_Split(string, delimiter, /*splits*/ nullptr, /*count*/ 0, mode);
result.data = Dqn_Arena_NewArray(arena, Dqn_Str8, splits_required, Dqn_ZeroMem_No);
if (result.data) {
result.size = Dqn_Str8_Split(string, delimiter, result.data, splits_required);
result.size = Dqn_Str8_Split(string, delimiter, result.data, splits_required, mode);
DQN_ASSERT(splits_required == result.size);
}
return result;
@ -185,7 +213,7 @@ DQN_API Dqn_Str8SplitAllocResult Dqn_Str8_SplitAlloc(Dqn_Allocator allocator,
DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirstStringArray(Dqn_Str8 string, Dqn_Str8 const *find, Dqn_usize find_size)
{
Dqn_Str8FindResult result = {};
if (!Dqn_Str8_IsValid(string) || !find || find_size == 0)
if (!Dqn_Str8_HasData(string) || !find || find_size == 0)
return result;
for (Dqn_usize index = 0; !result.found && index < string.size; index++) {
@ -229,18 +257,26 @@ DQN_API Dqn_Str8FindResult Dqn_Str8_FindFirst(Dqn_Str8 string, uint32_t flags)
return result;
}
DQN_API Dqn_Str8 Dqn_Str8_Segment(Dqn_Allocator allocator, Dqn_Str8 src, Dqn_usize segment_size, char segment_char)
DQN_API Dqn_Str8 Dqn_Str8_Segment(Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char)
{
Dqn_usize result_size = src.size;
if (result_size > segment_size)
result_size += (src.size / segment_size) - 1; // NOTE: No segment on the first chunk.
if (!segment_size || !Dqn_Str8_HasData(src)) {
Dqn_Str8 result = Dqn_Str8_Copy(arena, src);
return result;
}
Dqn_Str8 result = Dqn_Str8_Allocate(allocator, result_size, Dqn_ZeroMem_Yes);
Dqn_usize write_index = 0;
Dqn_usize segments = src.size / segment_size;
if (src.size % segment_size == 0)
segments--;
Dqn_usize segment_counter = 0;
Dqn_Str8 result = Dqn_Str8_Alloc(arena, src.size + segments, Dqn_ZeroMem_Yes);
Dqn_usize write_index = 0;
DQN_FOR_UINDEX(src_index, src.size) {
result.data[write_index++] = src.data[src_index];
if ((src_index + 1) % segment_size == 0 && (src_index + 1) < src.size)
if ((src_index + 1) % segment_size == 0 && segment_counter < segments) {
result.data[write_index++] = segment_char;
segment_counter++;
}
DQN_ASSERTF(write_index <= result.size, "result.size=%zu, write_index=%zu", result.size, write_index);
}
@ -248,6 +284,38 @@ DQN_API Dqn_Str8 Dqn_Str8_Segment(Dqn_Allocator allocator, Dqn_Str8 src, Dqn_usi
return result;
}
DQN_API Dqn_Str8 Dqn_Str8_ReverseSegment(Dqn_Arena *arena, Dqn_Str8 src, Dqn_usize segment_size, char segment_char)
{
if (!segment_size || !Dqn_Str8_HasData(src)) {
Dqn_Str8 result = Dqn_Str8_Copy(arena, src);
return result;
}
Dqn_usize segments = src.size / segment_size;
if (src.size % segment_size == 0)
segments--;
Dqn_usize write_counter = 0;
Dqn_usize segment_counter = 0;
Dqn_Str8 result = Dqn_Str8_Alloc(arena, src.size + segments, Dqn_ZeroMem_Yes);
Dqn_usize write_index = result.size - 1;
DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE(6293) // NOTE: Ill-defined loop
for (size_t src_index = src.size - 1; src_index < src.size; src_index--) {
DQN_MSVC_WARNING_POP
result.data[write_index--] = src.data[src_index];
if (++write_counter % segment_size == 0 && segment_counter < segments) {
result.data[write_index--] = segment_char;
segment_counter++;
}
}
DQN_ASSERT(write_index == SIZE_MAX);
return result;
}
DQN_API bool Dqn_Str8_Eq(Dqn_Str8 lhs, Dqn_Str8 rhs, Dqn_Str8EqCase eq_case)
{
@ -342,7 +410,7 @@ DQN_API Dqn_Str8 Dqn_Str8_TrimAround(Dqn_Str8 string, Dqn_Str8 trim_string)
DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround(Dqn_Str8 string)
{
Dqn_Str8 result = string;
if (!Dqn_Str8_IsValid(string))
if (!Dqn_Str8_HasData(string))
return result;
char const *start = string.data;
@ -360,7 +428,7 @@ DQN_API Dqn_Str8 Dqn_Str8_TrimWhitespaceAround(Dqn_Str8 string)
DQN_API Dqn_Str8 Dqn_Str8_TrimByteOrderMark(Dqn_Str8 string)
{
Dqn_Str8 result = string;
if (!Dqn_Str8_IsValid(result))
if (!Dqn_Str8_HasData(result))
return result;
// TODO(dqn): This is little endian
@ -382,7 +450,7 @@ DQN_API Dqn_Str8 Dqn_Str8_FileNameFromPath(Dqn_Str8 path)
{
Dqn_Str8 separators[] = {DQN_STR8("/"), DQN_STR8("\\")};
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverseArray(path, separators, DQN_ARRAY_UCOUNT(separators));
Dqn_Str8 result = split.rhs;
Dqn_Str8 result = Dqn_Str8_HasData(split.rhs) ? split.rhs : split.lhs;
return result;
}
@ -400,17 +468,26 @@ DQN_API Dqn_Str8 Dqn_Str8_FilePathNoExtension(Dqn_Str8 path)
return result;
}
DQN_API Dqn_Str8 Dqn_Str8_FileExtension(Dqn_Str8 path)
{
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverse(path, DQN_STR8("."));
Dqn_Str8 result = split.rhs;
return result;
}
DQN_API Dqn_Str8ToU64Result Dqn_Str8_ToU64(Dqn_Str8 string, char separator)
{
// NOTE: Argument check
Dqn_Str8ToU64Result result = {};
if (!Dqn_Str8_IsValid(string))
if (!Dqn_Str8_HasData(string)) {
result.success = true;
return result;
}
// NOTE: Sanitize input/output
Dqn_Str8 trim_string = Dqn_Str8_TrimWhitespaceAround(string);
if (trim_string.size == 0) {
result.success = false;
result.success = true;
return result;
}
@ -446,13 +523,15 @@ DQN_API Dqn_Str8ToI64Result Dqn_Str8_ToI64(Dqn_Str8 string, char separator)
{
// NOTE: Argument check
Dqn_Str8ToI64Result result = {};
if (!Dqn_Str8_IsValid(string))
if (!Dqn_Str8_HasData(string)) {
result.success = true;
return result;
}
// NOTE: Sanitize input/output
Dqn_Str8 trim_string = Dqn_Str8_TrimWhitespaceAround(string);
if (trim_string.size == 0) {
result.success = false;
result.success = true;
return result;
}
@ -492,20 +571,20 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string,
Dqn_Str8 find,
Dqn_Str8 replace,
Dqn_usize start_index,
Dqn_Allocator allocator,
Dqn_Arena *arena,
Dqn_Str8EqCase eq_case)
{
Dqn_Str8 result = {};
if (!Dqn_Str8_IsValid(string) || !Dqn_Str8_IsValid(find) || find.size > string.size || find.size == 0 || string.size == 0) {
result = Dqn_Str8_Copy(allocator, string);
if (!Dqn_Str8_HasData(string) || !Dqn_Str8_HasData(find) || find.size > string.size || find.size == 0 || string.size == 0) {
result = Dqn_Str8_Copy(arena, string);
return result;
}
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(allocator.user_context);
Dqn_Str8Builder string_builder = {};
string_builder.allocator = scratch.allocator;
Dqn_usize max = string.size - find.size;
Dqn_usize head = start_index;
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
Dqn_Str8Builder string_builder = {};
string_builder.arena = scratch.arena;
Dqn_usize max = string.size - find.size;
Dqn_usize head = start_index;
for (Dqn_usize tail = head; tail <= max; tail++) {
Dqn_Str8 check = Dqn_Str8_Slice(string, tail, find.size);
@ -530,25 +609,25 @@ DQN_API Dqn_Str8 Dqn_Str8_Replace(Dqn_Str8 string,
if (string_builder.string_size == 0) {
// NOTE: No replacement possible, so we just do a full-copy
result = Dqn_Str8_Copy(allocator, string);
result = Dqn_Str8_Copy(arena, string);
} else {
Dqn_Str8 remainder = Dqn_Str8_Init(string.data + head, string.size - head);
Dqn_Str8Builder_AppendRef(&string_builder, remainder);
result = Dqn_Str8Builder_Build(&string_builder, allocator);
result = Dqn_Str8Builder_Build(&string_builder, arena);
}
return result;
}
DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive(Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Allocator allocator)
DQN_API Dqn_Str8 Dqn_Str8_ReplaceInsensitive(Dqn_Str8 string, Dqn_Str8 find, Dqn_Str8 replace, Dqn_usize start_index, Dqn_Arena *arena)
{
Dqn_Str8 result = Dqn_Str8_Replace(string, find, replace, start_index, allocator, Dqn_Str8EqCase_Insensitive);
Dqn_Str8 result = Dqn_Str8_Replace(string, find, replace, start_index, arena, Dqn_Str8EqCase_Insensitive);
return result;
}
DQN_API void Dqn_Str8_Remove(Dqn_Str8 *string, Dqn_usize offset, Dqn_usize size)
{
if (!string || !Dqn_Str8_IsValid(*string))
if (!string || !Dqn_Str8_HasData(*string))
return;
char *end = string->data + string->size;
@ -573,16 +652,16 @@ DQN_API bool operator!=(Dqn_Str8 const &lhs, Dqn_Str8 const &rhs)
}
#endif
DQN_API Dqn_Str8 Dqn_Str8_InitF(Dqn_Allocator allocator, DQN_FMT_ATTRIB char const *fmt, ...)
DQN_API Dqn_Str8 Dqn_Str8_InitF(Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, ...)
{
va_list va;
va_start(va, fmt);
Dqn_Str8 result = Dqn_Str8_InitFV(allocator, fmt, va);
Dqn_Str8 result = Dqn_Str8_InitFV(arena, fmt, va);
va_end(va);
return result;
}
DQN_API Dqn_Str8 Dqn_Str8_InitFV(Dqn_Allocator allocator, DQN_FMT_ATTRIB char const *fmt, va_list args)
DQN_API Dqn_Str8 Dqn_Str8_InitFV(Dqn_Arena *arena, DQN_FMT_ATTRIB char const *fmt, va_list args)
{
Dqn_Str8 result = {};
if (!fmt)
@ -590,82 +669,92 @@ DQN_API Dqn_Str8 Dqn_Str8_InitFV(Dqn_Allocator allocator, DQN_FMT_ATTRIB char co
Dqn_usize size = Dqn_CStr8_FVSize(fmt, args);
if (size) {
result = Dqn_Str8_Allocate(allocator, size, Dqn_ZeroMem_No);
if (Dqn_Str8_IsValid(result))
STB_SPRINTF_DECORATE(vsnprintf)(result.data, Dqn_Safe_SaturateCastISizeToInt(size + 1 /*null-terminator*/), fmt, args);
result = Dqn_Str8_Alloc(arena, size, Dqn_ZeroMem_No);
if (Dqn_Str8_HasData(result))
DQN_VSNPRINTF(result.data, Dqn_Safe_SaturateCastISizeToInt(size + 1 /*null-terminator*/), fmt, args);
}
return result;
}
DQN_API Dqn_Str8 Dqn_Str8_Allocate(Dqn_Allocator allocator, Dqn_usize size, Dqn_ZeroMem zero_mem)
DQN_API Dqn_Str8 Dqn_Str8_Alloc(Dqn_Arena *arena, Dqn_usize size, Dqn_ZeroMem zero_mem)
{
Dqn_Str8 result = {};
result.data = (char *)Dqn_Allocator_Alloc(allocator, size + 1, alignof(char), zero_mem);
result.data = Dqn_Arena_NewArray(arena, char, size + 1, zero_mem);
if (result.data)
result.size = size;
return result;
}
DQN_API Dqn_Str8 Dqn_Str8_CopyCString(Dqn_Allocator allocator, char const *string, Dqn_usize size)
DQN_API Dqn_Str8 Dqn_Str8_CopyCString(Dqn_Arena *arena, char const *string, Dqn_usize size)
{
Dqn_Str8 result = {};
if (!string)
return result;
result = Dqn_Str8_Allocate(allocator, size, Dqn_ZeroMem_No);
if (Dqn_Str8_IsValid(result)) {
result = Dqn_Str8_Alloc(arena, size, Dqn_ZeroMem_No);
if (Dqn_Str8_HasData(result)) {
DQN_MEMCPY(result.data, string, size);
result.data[size] = 0;
}
return result;
}
DQN_API Dqn_Str8 Dqn_Str8_Copy(Dqn_Allocator allocator, Dqn_Str8 string)
DQN_API Dqn_Str8 Dqn_Str8_Copy(Dqn_Arena *arena, Dqn_Str8 string)
{
Dqn_Str8 result = Dqn_Str8_CopyCString(allocator, string.data, string.size);
Dqn_Str8 result = Dqn_Str8_CopyCString(arena, string.data, string.size);
return result;
}
// NOTE: [$STRB] Dqn_Str8Builder ================================================================
DQN_API bool Dqn_Str8Builder_AppendRef(Dqn_Str8Builder *builder, Dqn_Str8 string)
// NOTE: [$STRB] Dqn_Str8Builder ////////////////////////////////////////////////////////////////
DQN_API bool Dqn_Str8Builder_AppendRefArray(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array)
{
if (!builder || !string.data || string.size <= 0)
if (!builder)
return false;
Dqn_Str8Link *link = Dqn_Allocator_New(builder->allocator, Dqn_Str8Link, Dqn_ZeroMem_No);
if (!link)
return false;
for (Dqn_Str8 string : array) {
if (!builder || !string.data || string.size <= 0)
return false;
link->string = string;
link->next = NULL;
Dqn_Str8Link *link = Dqn_Arena_New(builder->arena, Dqn_Str8Link, Dqn_ZeroMem_No);
if (!link)
return false;
if (builder->head)
builder->tail->next = link;
else
builder->head = link;
link->string = string;
link->next = NULL;
if (builder->head)
builder->tail->next = link;
else
builder->head = link;
builder->tail = link;
builder->count++;
builder->string_size += string.size;
}
builder->tail = link;
builder->count++;
builder->string_size += string.size;
return true;
}
DQN_API bool Dqn_Str8Builder_AppendCopy(Dqn_Str8Builder *builder, Dqn_Str8 string)
DQN_API bool Dqn_Str8Builder_AppendCopyArray(Dqn_Str8Builder *builder, Dqn_Slice<Dqn_Str8> array)
{
Dqn_Str8 copy = Dqn_Str8_Copy(builder->allocator, string);
bool result = Dqn_Str8Builder_AppendRef(builder, copy);
return result;
for (Dqn_Str8 string : array) {
Dqn_Str8 copy = Dqn_Str8_Copy(builder->arena, string);
if (!Dqn_Str8Builder_AppendRef(builder, copy))
return false;
}
return true;
}
DQN_API bool Dqn_Str8Builder_AppendFV(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB char const *fmt, va_list args)
{
Dqn_Str8 string = Dqn_Str8_InitFV(builder->allocator, fmt, args);
Dqn_Str8 string = Dqn_Str8_InitFV(builder->arena, fmt, args);
if (string.size == 0)
return true;
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(builder->arena);
bool result = Dqn_Str8Builder_AppendRef(builder, string);
if (!result)
Dqn_Allocator_Dealloc(builder->allocator, string.data, string.size + 1);
Dqn_Arena_TempMemEnd(temp_mem);
return result;
}
@ -678,13 +767,27 @@ DQN_API bool Dqn_Str8Builder_AppendF(Dqn_Str8Builder *builder, DQN_FMT_ATTRIB ch
return result;
}
DQN_API Dqn_Str8 Dqn_Str8Builder_Build(Dqn_Str8Builder const *builder, Dqn_Allocator allocator)
DQN_API bool Dqn_Str8Builder_AppendRef(Dqn_Str8Builder *builder, Dqn_Str8 string)
{
Dqn_Slice<Dqn_Str8> array = Dqn_Slice_Init(&string, 1);
bool result = Dqn_Str8Builder_AppendRefArray(builder, array);
return result;
}
DQN_API bool Dqn_Str8Builder_AppendCopy(Dqn_Str8Builder *builder, Dqn_Str8 string)
{
Dqn_Slice<Dqn_Str8> array = Dqn_Slice_Init(&string, 1);
bool result = Dqn_Str8Builder_AppendCopyArray(builder, array);
return result;
}
DQN_API Dqn_Str8 Dqn_Str8Builder_Build(Dqn_Str8Builder const *builder, Dqn_Arena *arena)
{
Dqn_Str8 result = DQN_ZERO_INIT;
if (!builder || builder->string_size <= 0 || builder->count <= 0)
return result;
result.data = Dqn_Allocator_NewArray(allocator, char, builder->string_size + 1, Dqn_ZeroMem_No);
result.data = Dqn_Arena_NewArray(arena, char, builder->string_size + 1, Dqn_ZeroMem_No);
if (!result.data)
return result;
@ -698,7 +801,46 @@ DQN_API Dqn_Str8 Dqn_Str8Builder_Build(Dqn_Str8Builder const *builder, Dqn_Alloc
return result;
}
// NOTE: [$CHAR] Dqn_Char ==========================================================================
DQN_API Dqn_Str8 Dqn_Str8Builder_BuildCRT(Dqn_Str8Builder const *builder)
{
Dqn_Str8 result = DQN_ZERO_INIT;
if (!builder || builder->string_size <= 0 || builder->count <= 0)
return result;
result.data = DQN_CAST(char *)malloc(builder->string_size + 1);
if (!result.data)
return result;
for (Dqn_Str8Link *link = builder->head; link; link = link->next) {
DQN_MEMCPY(result.data + result.size, link->string.data, link->string.size);
result.size += link->string.size;
}
result.data[result.size] = 0;
DQN_ASSERT(result.size == builder->string_size);
return result;
}
DQN_API Dqn_Slice<Dqn_Str8> Dqn_Str8Builder_BuildSlice(Dqn_Str8Builder const *builder, Dqn_Arena *arena)
{
Dqn_Slice<Dqn_Str8> result = DQN_ZERO_INIT;
if (!builder || builder->string_size <= 0 || builder->count <= 0)
return result;
result = Dqn_Slice_Alloc<Dqn_Str8>(arena, builder->count, Dqn_ZeroMem_No);
if (!result.data)
return result;
Dqn_usize slice_index = 0;
for (Dqn_Str8Link *link = builder->head; link; link = link->next)
result.data[slice_index++] = Dqn_Str8_Copy(arena, link->string);
DQN_ASSERT(slice_index == builder->count);
return result;
}
// NOTE: [$CHAR] Dqn_Char //////////////////////////////////////////////////////////////////////////
DQN_API bool Dqn_Char_IsAlphabet(char ch)
{
bool result = (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
@ -766,7 +908,7 @@ DQN_API char Dqn_Char_ToLower(char ch)
return result;
}
// NOTE: [$UTFX] Dqn_UTF ===========================================================================
// NOTE: [$UTFX] Dqn_UTF ///////////////////////////////////////////////////////////////////////////
DQN_API int Dqn_UTF8_EncodeCodepoint(uint8_t utf8[4], uint32_t codepoint)
{
// NOTE: Table from https://www.reedbeta.com/blog/programmers-intro-to-unicode/
@ -779,33 +921,29 @@ DQN_API int Dqn_UTF8_EncodeCodepoint(uint8_t utf8[4], uint32_t codepoint)
// 1111'0xxx 10yy'yyyy 10zz'zzzz 10ww'wwww | x'xxyy'yyyy'zzzz'zzww'wwww | U+10000 - U+10FFFF |
// ----------------------------------------+----------------------------+--------------------+
if (codepoint <= 0b0111'1111)
{
utf8[0] = DQN_CAST(uint8_t)codepoint;
if (codepoint <= 0b0111'1111) {
utf8[0] = DQN_CAST(uint8_t) codepoint;
return 1;
}
if (codepoint <= 0b0111'1111'1111)
{
if (codepoint <= 0b0111'1111'1111) {
utf8[0] = (0b1100'0000 | ((codepoint >> 6) & 0b01'1111)); // x
utf8[1] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // y
return 2;
}
if (codepoint <= 0b1111'1111'1111'1111)
{
if (codepoint <= 0b1111'1111'1111'1111) {
utf8[0] = (0b1110'0000 | ((codepoint >> 12) & 0b00'1111)); // x
utf8[1] = (0b1000'0000 | ((codepoint >> 6) & 0b11'1111)); // y
utf8[2] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // z
utf8[1] = (0b1000'0000 | ((codepoint >> 6) & 0b11'1111)); // y
utf8[2] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // z
return 3;
}
if (codepoint <= 0b1'1111'1111'1111'1111'1111)
{
if (codepoint <= 0b1'1111'1111'1111'1111'1111) {
utf8[0] = (0b1111'0000 | ((codepoint >> 18) & 0b00'0111)); // x
utf8[1] = (0b1000'0000 | ((codepoint >> 12) & 0b11'1111)); // y
utf8[2] = (0b1000'0000 | ((codepoint >> 6) & 0b11'1111)); // z
utf8[3] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // w
utf8[2] = (0b1000'0000 | ((codepoint >> 6) & 0b11'1111)); // z
utf8[3] = (0b1000'0000 | ((codepoint >> 0) & 0b11'1111)); // w
return 4;
}
@ -822,17 +960,15 @@ 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 |
// ----------------------------------------+------------------------------------+------------------+
if (codepoint <= 0b1111'1111'1111'1111)
{
utf16[0] = DQN_CAST(uint16_t)codepoint;
if (codepoint <= 0b1111'1111'1111'1111) {
utf16[0] = DQN_CAST(uint16_t) codepoint;
return 1;
}
if (codepoint <= 0b1111'1111'1111'1111'1111)
{
if (codepoint <= 0b1111'1111'1111'1111'1111) {
uint32_t surrogate_codepoint = codepoint + 0x10000;
utf16[0] = 0b1101'1000'0000'0000 | ((surrogate_codepoint >> 10) & 0b11'1111'1111); // x
utf16[1] = 0b1101'1100'0000'0000 | ((surrogate_codepoint >> 0) & 0b11'1111'1111); // y
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
return 2;
}

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
#include "dqn_utest.h"
@ -25,28 +5,22 @@ static Dqn_UTest Dqn_Test_Arena()
{
Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_Arena") {
DQN_UTEST_TEST("Grow arena, reserve 4k, commit 1k (e.g. different sizes)") {
Dqn_Arena arena = {};
Dqn_Arena_Grow(&arena, DQN_KILOBYTES(4), DQN_KILOBYTES(1), /*flags*/ 0);
Dqn_Arena_Free(&arena);
}
DQN_UTEST_TEST("Reused memory is zeroed out") {
uint8_t alignment = 1;
Dqn_usize alloc_size = DQN_KILOBYTES(128);
Dqn_MemBlockSizeRequiredResult size_required = Dqn_MemBlock_SizeRequired(nullptr, alloc_size, alignment, Dqn_MemBlockFlag_Nil);
Dqn_Arena arena = {};
Dqn_Arena_Grow(&arena, size_required.block_size, /*commit*/ size_required.block_size, /*flags*/ 0);
uint8_t alignment = 1;
Dqn_usize alloc_size = DQN_KILOBYTES(128);
Dqn_Arena arena = {};
DQN_DEFER {
Dqn_Arena_Deinit(&arena);
};
// NOTE: Allocate 128 kilobytes, fill it with garbage, then reset the arena
uintptr_t first_ptr_address = 0;
{
Dqn_ArenaTempMemory temp_mem = Dqn_Arena_BeginTempMemory(&arena);
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(&arena);
void *ptr = Dqn_Arena_Alloc(&arena, alloc_size, alignment, Dqn_ZeroMem_Yes);
first_ptr_address = DQN_CAST(uintptr_t)ptr;
DQN_MEMSET(ptr, 'z', alloc_size);
Dqn_Arena_EndTempMemory(temp_mem, false /*cancel*/);
Dqn_Arena_TempMemEnd(temp_mem);
}
// NOTE: Reallocate 128 kilobytes
@ -58,99 +32,60 @@ static Dqn_UTest Dqn_Test_Arena()
// NOTE: Check that the bytes are set to 0
for (Dqn_usize i = 0; i < alloc_size; i++)
DQN_UTEST_ASSERT(&test, ptr[i] == 0);
Dqn_Arena_Free(&arena);
}
DQN_UTEST_TEST("Test arena grows naturally, 1mb + 4mb") {
Dqn_Arena arena = {};
// NOTE: Allocate 1mb, then 4mb, this should force the arena to grow
char *ptr_1mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
char *ptr_4mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(4), 1 /*align*/, Dqn_ZeroMem_Yes);
Dqn_Arena arena = Dqn_Arena_InitSize(DQN_MEGABYTES(2), DQN_MEGABYTES(2), Dqn_ArenaFlag_Nil);
DQN_DEFER {
Dqn_Arena_Deinit(&arena);
};
char *ptr_1mb = Dqn_Arena_NewArray(&arena, char, DQN_MEGABYTES(1), Dqn_ZeroMem_Yes);
char *ptr_4mb = Dqn_Arena_NewArray(&arena, char, DQN_MEGABYTES(4), Dqn_ZeroMem_Yes);
DQN_UTEST_ASSERT(&test, ptr_1mb);
DQN_UTEST_ASSERT(&test, ptr_4mb);
Dqn_MemBlock const *block_1mb = arena.head;
char const *block_1mb_begin = DQN_CAST(char *)block_1mb->data;
char const *block_1mb_end = DQN_CAST(char *)block_1mb->data + block_1mb->size;
Dqn_ArenaBlock const *block_4mb_begin = arena.curr;
char const *block_4mb_end = DQN_CAST(char *)block_4mb_begin + block_4mb_begin->reserve;
Dqn_MemBlock const *block_4mb = arena.curr;
char const *block_4mb_begin = DQN_CAST(char *)block_4mb->data;
char const *block_4mb_end = DQN_CAST(char *)block_4mb->data + block_4mb->size;
Dqn_ArenaBlock const *block_1mb_begin = block_4mb_begin->prev;
DQN_UTEST_ASSERTF(&test, block_1mb_begin, "New block should have been allocated");
char const *block_1mb_end = DQN_CAST(char *)block_1mb_begin + block_1mb_begin->reserve;
DQN_UTEST_ASSERTF(&test, block_1mb != block_4mb, "New block should have been allocated and linked");
DQN_UTEST_ASSERTF(&test, ptr_1mb >= block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
DQN_UTEST_ASSERTF(&test, ptr_4mb >= block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
DQN_UTEST_ASSERT (&test, arena.curr == arena.tail);
DQN_UTEST_ASSERT (&test, arena.curr != arena.head);
Dqn_Arena_Free(&arena);
DQN_UTEST_ASSERTF(&test, block_1mb_begin != block_4mb_begin, "New block should have been allocated and linked");
DQN_UTEST_ASSERTF(&test, ptr_1mb >= DQN_CAST(char *)block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
DQN_UTEST_ASSERTF(&test, ptr_4mb >= DQN_CAST(char *)block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
}
DQN_UTEST_TEST("Test arena grows naturally, 1mb, temp memory 4mb") {
Dqn_Arena arena = {};
Dqn_Arena arena = Dqn_Arena_InitSize(DQN_MEGABYTES(2), DQN_MEGABYTES(2), Dqn_ArenaFlag_Nil);
DQN_DEFER {
Dqn_Arena_Deinit(&arena);
};
// NOTE: Allocate 1mb, then 4mb, this should force the arena to grow
char *ptr_1mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
DQN_UTEST_ASSERT(&test, ptr_1mb);
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
Dqn_ArenaTempMem temp_memory = Dqn_Arena_TempMemBegin(&arena);
{
char *ptr_4mb = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(4), 1 /*align*/, Dqn_ZeroMem_Yes);
char *ptr_4mb = Dqn_Arena_NewArray(&arena, char, DQN_MEGABYTES(4), Dqn_ZeroMem_Yes);
DQN_UTEST_ASSERT(&test, ptr_4mb);
Dqn_MemBlock const *block_1mb = arena.head;
char const *block_1mb_begin = DQN_CAST(char *)block_1mb->data;
char const *block_1mb_end = DQN_CAST(char *)block_1mb->data + block_1mb->size;
Dqn_ArenaBlock const *block_4mb_begin = arena.curr;
char const *block_4mb_end = DQN_CAST(char *) block_4mb_begin + block_4mb_begin->reserve;
Dqn_MemBlock const *block_4mb = arena.curr;
char const *block_4mb_begin = DQN_CAST(char *)block_4mb->data;
char const *block_4mb_end = DQN_CAST(char *)block_4mb->data + block_4mb->size;
Dqn_ArenaBlock const *block_1mb_begin = block_4mb_begin->prev;
char const *block_1mb_end = DQN_CAST(char *) block_1mb_begin + block_1mb_begin->reserve;
DQN_UTEST_ASSERTF(&test, block_1mb != block_4mb, "New block should have been allocated and linked");
DQN_UTEST_ASSERTF(&test, ptr_1mb >= block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
DQN_UTEST_ASSERTF(&test, ptr_4mb >= block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
DQN_UTEST_ASSERT (&test, arena.curr == arena.tail);
DQN_UTEST_ASSERT (&test, arena.curr != arena.head);
DQN_UTEST_ASSERTF(&test, block_1mb_begin != block_4mb_begin, "New block should have been allocated and linked");
DQN_UTEST_ASSERTF(&test, ptr_1mb >= DQN_CAST(char *)block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
DQN_UTEST_ASSERTF(&test, ptr_4mb >= DQN_CAST(char *)block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
}
Dqn_Arena_EndTempMemory(temp_memory, false /*cancel*/);
DQN_UTEST_ASSERT (&test, arena.curr == arena.head);
DQN_UTEST_ASSERT (&test, arena.curr == arena.tail);
DQN_UTEST_ASSERT (&test, arena.curr->next == nullptr);
DQN_UTEST_ASSERTF(&test, arena.curr->size >= DQN_MEGABYTES(1),
"size=%zuMiB (%zuB), expect=%zuB", (arena.curr->size / 1024 / 1024), arena.curr->size, DQN_MEGABYTES(1));
Dqn_Arena_Free(&arena);
}
DQN_UTEST_TEST("Init arena, temp region then free inside regions") {
Dqn_Arena arena = {};
Dqn_Arena_Grow(&arena, DQN_KILOBYTES(1), 0, Dqn_ZeroMem_No);
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
{
char *ptr = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
DQN_UTEST_ASSERT(&test, ptr);
Dqn_Arena_Free(&arena);
}
Dqn_Arena_EndTempMemory(temp_memory, false /*cancel*/);
Dqn_Arena_Free(&arena);
}
DQN_UTEST_TEST("Init arena, allocate, temp region then free inside region") {
Dqn_Arena arena = {};
char *outside = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
DQN_UTEST_ASSERT(&test, outside);
Dqn_ArenaTempMemory temp_memory = Dqn_Arena_BeginTempMemory(&arena);
{
char *inside = DQN_CAST(char *)Dqn_Arena_Alloc(&arena, DQN_MEGABYTES(2), 1 /*align*/, Dqn_ZeroMem_Yes);
DQN_UTEST_ASSERT(&test, inside);
Dqn_Arena_Free(&arena);
}
Dqn_Arena_EndTempMemory(temp_memory, false /*cancel*/);
Dqn_Arena_Free(&arena);
Dqn_Arena_TempMemEnd(temp_memory);
DQN_UTEST_ASSERT (&test, arena.curr->prev == nullptr);
DQN_UTEST_ASSERTF(&test, arena.curr->reserve >= DQN_MEGABYTES(1), "size=%zuMiB (%zuB), expect=%zuB", (arena.curr->reserve / 1024 / 1024), arena.curr->reserve, DQN_MEGABYTES(1));
}
}
return test;
@ -158,7 +93,7 @@ static Dqn_UTest Dqn_Test_Arena()
static Dqn_UTest Dqn_Test_Bin()
{
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_Bin") {
DQN_UTEST_TEST("Convert 0x123") {
@ -553,11 +488,12 @@ static Dqn_UTest Dqn_Test_DSMap()
{
Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_DSMap") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
{
uint32_t const MAP_SIZE = 64;
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(MAP_SIZE);
DQN_DEFER { Dqn_DSMap_Deinit(&map); };
Dqn_Arena arena = {};
uint32_t const MAP_SIZE = 64;
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE);
DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); };
DQN_UTEST_TEST("Find non-existent value") {
uint64_t *value = Dqn_DSMap_FindKeyStr8(&map, DQN_STR8("Foo")).value;
@ -597,10 +533,11 @@ static Dqn_UTest Dqn_Test_DSMap()
case DSMapTestType_MakeSlot: prefix = DQN_STR8("Make slot"); break;
}
Dqn_Arena_TempMemoryScope(scratch.arena);
uint32_t const MAP_SIZE = 64;
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(MAP_SIZE);
DQN_DEFER { Dqn_DSMap_Deinit(&map); };
Dqn_ArenaTempMemScope temp_mem_scope = Dqn_ArenaTempMemScope(scratch.arena);
Dqn_Arena arena = {};
uint32_t const MAP_SIZE = 64;
Dqn_DSMap<uint64_t> map = Dqn_DSMap_Init<uint64_t>(&arena, MAP_SIZE);
DQN_DEFER { Dqn_DSMap_Deinit(&map, Dqn_ZeroMem_Yes); };
DQN_UTEST_TEST("%.*s: Test growing", DQN_STR_FMT(prefix)) {
uint64_t map_start_size = map.size;
@ -761,51 +698,51 @@ static Dqn_UTest Dqn_Test_Fs()
Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_Fs") {
DQN_UTEST_TEST("Make directory recursive \"abcd/efgh\"") {
DQN_UTEST_ASSERTF(&test, Dqn_Fs_MakeDir(DQN_STR8("abcd/efgh")), "Failed to make directory");
DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(DQN_STR8("abcd")), "Directory was not made");
DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(DQN_STR8("abcd/efgh")), "Subdirectory was not made");
DQN_UTEST_ASSERTF(&test, Dqn_Fs_Exists(DQN_STR8("abcd")) == false, "This function should only return true for files");
DQN_UTEST_ASSERTF(&test, Dqn_Fs_Exists(DQN_STR8("abcd/efgh")) == false, "This function should only return true for files");
DQN_UTEST_ASSERTF(&test, Dqn_Fs_Delete(DQN_STR8("abcd/efgh")), "Failed to delete directory");
DQN_UTEST_ASSERTF(&test, Dqn_Fs_Delete(DQN_STR8("abcd")), "Failed to cleanup directory");
DQN_UTEST_ASSERTF(&test, Dqn_OS_DirMake(DQN_STR8("abcd/efgh")), "Failed to make directory");
DQN_UTEST_ASSERTF(&test, Dqn_OS_DirExists(DQN_STR8("abcd")), "Directory was not made");
DQN_UTEST_ASSERTF(&test, Dqn_OS_DirExists(DQN_STR8("abcd/efgh")), "Subdirectory was not made");
DQN_UTEST_ASSERTF(&test, Dqn_OS_FileExists(DQN_STR8("abcd")) == false, "This function should only return true for files");
DQN_UTEST_ASSERTF(&test, Dqn_OS_FileExists(DQN_STR8("abcd/efgh")) == false, "This function should only return true for files");
DQN_UTEST_ASSERTF(&test, Dqn_OS_PathDelete(DQN_STR8("abcd/efgh")), "Failed to delete directory");
DQN_UTEST_ASSERTF(&test, Dqn_OS_PathDelete(DQN_STR8("abcd")), "Failed to cleanup directory");
}
DQN_UTEST_TEST("Write file, read it, copy it, move it and delete it") {
DQN_UTEST_TEST("File write, read, copy, move and delete") {
// NOTE: Write step
Dqn_Str8 const SRC_FILE = DQN_STR8("dqn_test_file");
Dqn_b32 write_result = Dqn_Fs_WriteCStr8(SRC_FILE.data, SRC_FILE.size, "test", 4);
Dqn_Str8 const SRC_FILE = DQN_STR8("dqn_test_file");
Dqn_b32 write_result = Dqn_OS_WriteAll(SRC_FILE, DQN_STR8("test"));
DQN_UTEST_ASSERT(&test, write_result);
DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(SRC_FILE));
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(SRC_FILE));
// NOTE: Read step
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str8 read_file = Dqn_Fs_Read(SRC_FILE, scratch.allocator);
DQN_UTEST_ASSERTF(&test, Dqn_Str8_IsValid(read_file), "Failed to load file");
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 read_file = Dqn_OS_ReadAll(SRC_FILE, scratch.arena);
DQN_UTEST_ASSERTF(&test, Dqn_Str8_HasData(read_file), "Failed to load file");
DQN_UTEST_ASSERTF(&test, read_file.size == 4, "File read wrong amount of bytes");
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(read_file, DQN_STR8("test")), "read(%zu): %.*s", read_file.size, DQN_STR_FMT(read_file));
// NOTE: Copy step
Dqn_Str8 const COPY_FILE = DQN_STR8("dqn_test_file_copy");
Dqn_b32 copy_result = Dqn_Fs_Copy(SRC_FILE, COPY_FILE, true /*overwrite*/);
Dqn_Str8 const COPY_FILE = DQN_STR8("dqn_test_file_copy");
Dqn_b32 copy_result = Dqn_OS_FileCopy(SRC_FILE, COPY_FILE, true /*overwrite*/);
DQN_UTEST_ASSERT(&test, copy_result);
DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(COPY_FILE));
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(COPY_FILE));
// NOTE: Move step
Dqn_Str8 const MOVE_FILE = DQN_STR8("dqn_test_file_move");
Dqn_b32 move_result = Dqn_Fs_Move(COPY_FILE, MOVE_FILE, true /*overwrite*/);
Dqn_Str8 const MOVE_FILE = DQN_STR8("dqn_test_file_move");
Dqn_b32 move_result = Dqn_OS_FileMove(COPY_FILE, MOVE_FILE, true /*overwrite*/);
DQN_UTEST_ASSERT(&test, move_result);
DQN_UTEST_ASSERT(&test, Dqn_Fs_Exists(MOVE_FILE));
DQN_UTEST_ASSERTF(&test, Dqn_Fs_Exists(COPY_FILE) == false, "Moving a file should remove the original");
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(MOVE_FILE));
DQN_UTEST_ASSERTF(&test, Dqn_OS_FileExists(COPY_FILE) == false, "Moving a file should remove the original");
// NOTE: Delete step
Dqn_b32 delete_src_file = Dqn_Fs_Delete(SRC_FILE);
Dqn_b32 delete_moved_file = Dqn_Fs_Delete(MOVE_FILE);
Dqn_b32 delete_src_file = Dqn_OS_PathDelete(SRC_FILE);
Dqn_b32 delete_moved_file = Dqn_OS_PathDelete(MOVE_FILE);
DQN_UTEST_ASSERT(&test, delete_src_file);
DQN_UTEST_ASSERT(&test, delete_moved_file);
// NOTE: Deleting non-existent file fails
Dqn_b32 delete_non_existent_src_file = Dqn_Fs_Delete(SRC_FILE);
Dqn_b32 delete_non_existent_moved_file = Dqn_Fs_Delete(MOVE_FILE);
Dqn_b32 delete_non_existent_src_file = Dqn_OS_PathDelete(SRC_FILE);
Dqn_b32 delete_non_existent_moved_file = Dqn_OS_PathDelete(MOVE_FILE);
DQN_UTEST_ASSERT(&test, delete_non_existent_moved_file == false);
DQN_UTEST_ASSERT(&test, delete_non_existent_src_file == false);
}
@ -966,7 +903,7 @@ Dqn_Str8 const DQN_UTEST_HASH_STRING_[] =
void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input)
{
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 input_hex = Dqn_Hex_BytesToStr8Arena(scratch.arena, input.data, input.size);
switch(hash_type)
@ -1203,10 +1140,10 @@ static Dqn_UTest Dqn_Test_OS()
}
DQN_UTEST_TEST("Query executable directory") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 result = Dqn_OS_EXEDir(scratch.arena);
DQN_UTEST_ASSERT(&test, Dqn_Str8_IsValid(result));
DQN_UTEST_ASSERTF(&test, Dqn_Fs_DirExists(result), "result(%zu): %.*s", result.size, DQN_STR_FMT(result));
DQN_UTEST_ASSERT(&test, Dqn_Str8_HasData(result));
DQN_UTEST_ASSERTF(&test, Dqn_OS_DirExists(result), "result(%zu): %.*s", result.size, DQN_STR_FMT(result));
}
DQN_UTEST_TEST("Dqn_OS_PerfCounterNow") {
@ -1221,15 +1158,15 @@ static Dqn_UTest Dqn_Test_OS()
}
DQN_UTEST_TEST("Ticks to time are a correct order of magnitude") {
uint64_t a = Dqn_OS_PerfCounterNow();
uint64_t b = Dqn_OS_PerfCounterNow();
Dqn_f64 s = Dqn_OS_PerfCounterS(a, b);
Dqn_f64 ms = Dqn_OS_PerfCounterMs(a, b);
Dqn_f64 micro_s = Dqn_OS_PerfCounterMicroS(a, b);
Dqn_f64 ns = Dqn_OS_PerfCounterNs(a, b);
DQN_UTEST_ASSERTF(&test, s <= ms, "s: %f, ms: %f", s, ms);
DQN_UTEST_ASSERTF(&test, ms <= micro_s, "ms: %f, micro_s: %f", ms, micro_s);
DQN_UTEST_ASSERTF(&test, micro_s <= ns, "micro_s: %f, ns: %f", micro_s, ns);
uint64_t a = Dqn_OS_PerfCounterNow();
uint64_t b = Dqn_OS_PerfCounterNow();
Dqn_f64 s = Dqn_OS_PerfCounterS(a, b);
Dqn_f64 ms = Dqn_OS_PerfCounterMs(a, b);
Dqn_f64 us = Dqn_OS_PerfCounterUs(a, b);
Dqn_f64 ns = Dqn_OS_PerfCounterNs(a, b);
DQN_UTEST_ASSERTF(&test, s <= ms, "s: %f, ms: %f", s, ms);
DQN_UTEST_ASSERTF(&test, ms <= us, "ms: %f, us: %f", ms, us);
DQN_UTEST_ASSERTF(&test, us <= ns, "us: %f, ns: %f", us, ns);
}
}
@ -1376,8 +1313,8 @@ static Dqn_UTest Dqn_Test_Str8()
}
DQN_UTEST_TEST("Initialise with format string") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str8 string = Dqn_Str8_InitF(scratch.allocator, "%s", "AB");
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 string = Dqn_Str8_InitF(scratch.arena, "%s", "AB");
DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64u", string.size);
DQN_UTEST_ASSERTF(&test, string.data[0] == 'A', "string[0]: %c", string.data[0]);
DQN_UTEST_ASSERTF(&test, string.data[1] == 'B', "string[1]: %c", string.data[1]);
@ -1385,9 +1322,9 @@ static Dqn_UTest Dqn_Test_Str8()
}
DQN_UTEST_TEST("Copy string") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 string = DQN_STR8("AB");
Dqn_Str8 copy = Dqn_Str8_Copy(scratch.allocator, string);
Dqn_Str8 copy = Dqn_Str8_Copy(scratch.arena, string);
DQN_UTEST_ASSERTF(&test, copy.size == 2, "size: %I64u", copy.size);
DQN_UTEST_ASSERTF(&test, copy.data[0] == 'A', "copy[0]: %c", copy.data[0]);
DQN_UTEST_ASSERTF(&test, copy.data[1] == 'B', "copy[1]: %c", copy.data[1]);
@ -1400,8 +1337,8 @@ static Dqn_UTest Dqn_Test_Str8()
}
DQN_UTEST_TEST("Allocate string from arena") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str8 string = Dqn_Str8_Allocate(scratch.allocator, 2, Dqn_ZeroMem_No);
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 string = Dqn_Str8_Alloc(scratch.arena, 2, Dqn_ZeroMem_No);
DQN_UTEST_ASSERTF(&test, string.size == 2, "size: %I64u", string.size);
}
@ -1431,8 +1368,7 @@ static Dqn_UTest Dqn_Test_Str8()
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(result, input), "%.*s", DQN_STR_FMT(result));
}
// NOTE: Dqn_Str8_IsAllDigits
// ---------------------------------------------------------------------------------------------
// NOTE: Dqn_Str8_IsAllDigits //////////////////////////////////////////////////////////////
DQN_UTEST_TEST("Is all digits fails on non-digit string") {
Dqn_b32 result = Dqn_Str8_IsAll(DQN_STR8("@123string"), Dqn_Str8IsAll_Digits);
DQN_UTEST_ASSERT(&test, result == false);
@ -1448,10 +1384,10 @@ static Dqn_UTest Dqn_Test_Str8()
DQN_UTEST_ASSERT(&test, result == false);
}
DQN_UTEST_TEST("Is all digits succeeds on string w/ 0 size") {
DQN_UTEST_TEST("Is all digits fails on string w/ 0 size") {
char const buf[] = "@123string";
Dqn_b32 result = Dqn_Str8_IsAll(Dqn_Str8_Init(buf, 0), Dqn_Str8IsAll_Digits);
DQN_UTEST_ASSERT(&test, result);
DQN_UTEST_ASSERT(&test, !result);
}
DQN_UTEST_TEST("Is all digits success") {
@ -1503,17 +1439,16 @@ static Dqn_UTest Dqn_Test_Str8()
}
}
// NOTE: Dqn_Str8_ToI64
// =========================================================================================
// NOTE: Dqn_Str8_ToI64 ////////////////////////////////////////////////////////////////////
DQN_UTEST_TEST("To I64: Convert null string") {
Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(Dqn_Str8_Init(nullptr, 5), 0);
DQN_UTEST_ASSERT(&test, !result.success);
DQN_UTEST_ASSERT(&test, result.success);
DQN_UTEST_ASSERT(&test, result.value == 0);
}
DQN_UTEST_TEST("To I64: Convert empty string") {
Dqn_Str8ToI64Result result = Dqn_Str8_ToI64(DQN_STR8(""), 0);
DQN_UTEST_ASSERT(&test, !result.success);
DQN_UTEST_ASSERT(&test, result.success);
DQN_UTEST_ASSERT(&test, result.value == 0);
}
@ -1563,13 +1498,13 @@ static Dqn_UTest Dqn_Test_Str8()
// ---------------------------------------------------------------------------------------------
DQN_UTEST_TEST("To U64: Convert nullptr") {
Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(Dqn_Str8_Init(nullptr, 5), 0);
DQN_UTEST_ASSERT(&test, !result.success);
DQN_UTEST_ASSERT(&test, result.success);
DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value);
}
DQN_UTEST_TEST("To U64: Convert empty string") {
Dqn_Str8ToU64Result result = Dqn_Str8_ToU64(DQN_STR8(""), 0);
DQN_UTEST_ASSERT(&test, !result.success);
DQN_UTEST_ASSERT(&test, result.success);
DQN_UTEST_ASSERTF(&test, result.value == 0, "result: %I64u", result.value);
}
@ -1700,8 +1635,10 @@ static Dqn_UTest Dqn_Test_VArray()
Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_VArray") {
{
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_VArray<uint32_t> array = Dqn_VArray_InitByteSize<uint32_t>(scratch.arena, DQN_KILOBYTES(64));
Dqn_VArray<uint32_t> array = Dqn_VArray_InitByteSize<uint32_t>(DQN_KILOBYTES(64), 0);
DQN_DEFER {
Dqn_VArray_Deinit(&array);
};
DQN_UTEST_TEST("Test adding an array of items to the array") {
uint32_t array_literal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
@ -1805,8 +1742,10 @@ static Dqn_UTest Dqn_Test_VArray()
};
DQN_MSVC_WARNING_POP
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_VArray<UnalignedObject> array = Dqn_VArray_InitByteSize<UnalignedObject>(scratch.arena, DQN_KILOBYTES(64));
Dqn_VArray<UnalignedObject> array = Dqn_VArray_InitByteSize<UnalignedObject>(DQN_KILOBYTES(64), 0);
DQN_DEFER {
Dqn_VArray_Deinit(&array);
};
// NOTE: Verify that the items returned from the data array are
// contiguous in memory.
@ -1840,54 +1779,29 @@ static Dqn_UTest Dqn_Test_VArray()
static Dqn_UTest Dqn_Test_Win()
{
Dqn_UTest test = {};
DQN_UTEST_GROUP(test, "Dqn_Win") {
DQN_UTEST_TEST("Str8 to Str16 size required") {
int result = Dqn_Win_Str8ToStr16Buffer(DQN_STR8("a"), nullptr, 0);
DQN_UTEST_ASSERTF(&test, result == 1, "Size returned: %d. This size should not include the null-terminator", result);
}
DQN_UTEST_TEST("Str16 to Str8 size required") {
int result = Dqn_Win_Str16ToStr8Buffer(DQN_STR16(L"a"), nullptr, 0);
DQN_UTEST_ASSERTF(&test, result == 1, "Size returned: %d. This size should not include the null-terminator", result);
}
DQN_UTEST_TEST("Str8 to Str16 size required") {
int result = Dqn_Win_Str8ToStr16Buffer(DQN_STR8("String"), nullptr, 0);
DQN_UTEST_ASSERTF(&test, result == 6, "Size returned: %d. This size should not include the null-terminator", result);
}
DQN_UTEST_TEST("Str16 to Str8 size required") {
int result = Dqn_Win_Str16ToStr8Buffer(DQN_STR16(L"String"), nullptr, 0);
DQN_UTEST_ASSERTF(&test, result == 6, "Size returned: %d. This size should not include the null-terminator", result);
}
DQN_UTEST_GROUP(test, "OS Win32") {
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
Dqn_Str8 input8 = DQN_STR8("String");
Dqn_Str16 input16 = Dqn_Str16{(wchar_t *)(L"String"), sizeof(L"String") / sizeof(L"String"[0]) - 1};
DQN_UTEST_TEST("Str8 to Str16") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str8 const INPUT = DQN_STR8("String");
int size_required = Dqn_Win_Str8ToStr16Buffer(INPUT, nullptr, 0);
wchar_t *string = Dqn_Arena_NewArray(scratch.arena, wchar_t, size_required + 1, Dqn_ZeroMem_No);
// Fill the string with error sentinels, which ensures the string is zero terminated
DQN_MEMSET(string, 'Z', size_required + 1);
int size_returned = Dqn_Win_Str8ToStr16Buffer(INPUT, string, size_required + 1);
wchar_t const EXPECTED[] = {L'S', L't', L'r', L'i', L'n', L'g', 0};
DQN_UTEST_ASSERTF(&test, size_required == size_returned, "string_size: %d, result: %d", size_required, size_returned);
DQN_UTEST_ASSERTF(&test, size_returned == DQN_ARRAY_UCOUNT(EXPECTED) - 1, "string_size: %d, expected: %zu", size_returned, DQN_ARRAY_UCOUNT(EXPECTED) - 1);
DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string, sizeof(EXPECTED)) == 0);
Dqn_Str16 result = Dqn_Win_Str8ToStr16(scratch.arena, input8);
DQN_UTEST_ASSERT(&test, result == input16);
}
DQN_UTEST_TEST("Str16 to Str8: No null-terminate") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str16 INPUT = DQN_STR16(L"String");
int size_required = Dqn_Win_Str16ToStr8Buffer(INPUT, nullptr, 0);
char *string = Dqn_Arena_NewArray(scratch.arena, char, size_required + 1, Dqn_ZeroMem_No);
DQN_UTEST_TEST("Str16 to Str8") {
Dqn_Str8 result = Dqn_Win_Str16ToStr8(scratch.arena, input16);
DQN_UTEST_ASSERT(&test, result == input8);
}
// Fill the string with error sentinels, which ensures the string is zero terminated
DQN_UTEST_TEST("Str16 to Str8: Null terminates string") {
int size_required = Dqn_Win_Str16ToStr8Buffer(input16, nullptr, 0);
char *string = Dqn_Arena_NewArray(scratch.arena, char, size_required + 1, Dqn_ZeroMem_No);
// Fill the string with error sentinels
DQN_MEMSET(string, 'Z', size_required + 1);
int size_returned = Dqn_Win_Str16ToStr8Buffer(INPUT, string, size_required + 1);
int size_returned = Dqn_Win_Str16ToStr8Buffer(input16, string, size_required + 1);
char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0};
DQN_UTEST_ASSERTF(&test, size_required == size_returned, "string_size: %d, result: %d", size_required, size_returned);
@ -1895,49 +1809,21 @@ static Dqn_UTest Dqn_Test_Win()
DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string, sizeof(EXPECTED)) == 0);
}
DQN_UTEST_TEST("Str8 to Str16 arena") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str8 const INPUT = DQN_STR8("String");
Dqn_Str16 string16 = Dqn_Win_Str8ToStr16(scratch.arena, INPUT);
int size_returned = Dqn_Win_Str8ToStr16Buffer(INPUT, nullptr, 0);
wchar_t const EXPECTED[] = {L'S', L't', L'r', L'i', L'n', L'g', 0};
DQN_UTEST_ASSERTF(&test, DQN_CAST(int)string16.size == size_returned, "string_size: %d, result: %d", DQN_CAST(int)string16.size, size_returned);
DQN_UTEST_ASSERTF(&test, DQN_CAST(int)string16.size == DQN_ARRAY_UCOUNT(EXPECTED) - 1, "string_size: %d, expected: %zu", DQN_CAST(int)string16.size, DQN_ARRAY_UCOUNT(EXPECTED) - 1);
DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string16.data, sizeof(EXPECTED)) == 0);
}
DQN_UTEST_TEST("Str16 to Str8: No null-terminate arena") {
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str16 INPUT = DQN_STR16(L"String");
Dqn_Str8 string8 = Dqn_Win_Str16ToStr8(scratch.arena, INPUT);
int size_returned = Dqn_Win_Str16ToStr8Buffer(INPUT, nullptr, 0);
char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0};
DQN_UTEST_TEST("Str16 to Str8: Arena null terminates string") {
Dqn_Str8 string8 = Dqn_Win_Str16ToStr8(scratch.arena, input16);
int size_returned = Dqn_Win_Str16ToStr8Buffer(input16, nullptr, 0);
char const EXPECTED[] = {'S', 't', 'r', 'i', 'n', 'g', 0};
DQN_UTEST_ASSERTF(&test, DQN_CAST(int)string8.size == size_returned, "string_size: %d, result: %d", DQN_CAST(int)string8.size, size_returned);
DQN_UTEST_ASSERTF(&test, DQN_CAST(int)string8.size == DQN_ARRAY_UCOUNT(EXPECTED) - 1, "string_size: %d, expected: %zu", DQN_CAST(int)string8.size, DQN_ARRAY_UCOUNT(EXPECTED) - 1);
DQN_UTEST_ASSERT(&test, DQN_MEMCMP(EXPECTED, string8.data, sizeof(EXPECTED)) == 0);
DQN_UTEST_ASSERT (&test, DQN_MEMCMP(EXPECTED, string8.data, sizeof(EXPECTED)) == 0);
}
}
return test;
}
static void Dqn_Test_CustomLogProc(Dqn_Str8 type, int log_type, void *user_data, Dqn_CallSite call_site, char const *fmt, va_list args)
void Dqn_Test_RunSuite()
{
(void)user_data;
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_Str8 log = Dqn_Log_MakeStr8(scratch.allocator, true /*colour*/, type, log_type, call_site, fmt, args);
DQN_UTEST_LOG("%.*s", DQN_STR_FMT(log));
}
static void Dqn_Test_RunSuite()
{
Dqn_Library *dqn_library = Dqn_Library_Init(Dqn_LibraryOnInit_LogFeatures);
auto *prev_log_callback = dqn_library->log_callback;
dqn_library->log_callback = Dqn_Test_CustomLogProc;
Dqn_UTest tests[] =
{
Dqn_Test_Arena(),
@ -1968,7 +1854,6 @@ static void Dqn_Test_RunSuite()
}
fprintf(stdout, "Summary: %d/%d tests succeeded\n", total_good_tests, total_tests);
dqn_library->log_callback = prev_log_callback;
}
#if defined(DQN_TEST_WITH_MAIN)

View File

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

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 <shellapi.h> // Dqn_Win_MakeProcessDPIAware -> SetProcessDpiAwareProc
#include <winhttp.h> // WinHttp*
#include <DbgHelp.h>
#if !defined(DQN_NO_WINNET)
DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE(6553) // wininet.h|940 warning| The annotation for function 'InternetConnectA' on _Param_(8) does not apply to a value type.
#include <wininet.h> // Dqn_Win_Net -> InternetConnect ... etc
DQN_MSVC_WARNING_POP
#endif // DQN_NO_WINNET
#elif !defined(_INC_WINDOWS)
#else
DQN_MSVC_WARNING_PUSH
DQN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
// NOTE: basetsd.h =============================================================================
// NOTE: basetsd.h /////////////////////////////////////////////////////////////////////////////
typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
typedef ULONG_PTR SIZE_T, *PSIZE_T;
typedef __int64 LONG_PTR, *PLONG_PTR;
@ -25,7 +38,7 @@
typedef unsigned __int64 ULONG64, *PULONG64;
typedef unsigned __int64 DWORD64, *PDWORD64;
// NOTE: shared/minwindef.h ====================================================================
// NOTE: shared/minwindef.h ////////////////////////////////////////////////////////////////////
struct HINSTANCE__ {
int unused;
};
@ -40,6 +53,8 @@
typedef unsigned char BYTE;
typedef unsigned char UCHAR;
typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */
typedef void * HANDLE;
typedef HANDLE HLOCAL;
#define MAX_PATH 260
@ -48,15 +63,15 @@
DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME;
// NOTE: shared/winerror.h =====================================================================
// NOTE: shared/winerror.h /////////////////////////////////////////////////////////////////////
// NOTE: GetModuleFileNameW
#define ERROR_INSUFFICIENT_BUFFER 122L // dderror
// NOTE: um/winnls.h ===========================================================================
// NOTE: um/winnls.h ///////////////////////////////////////////////////////////////////////////
// NOTE: MultiByteToWideChar
#define CP_UTF8 65001 // UTF-8 translation
// NOTE: um/winnt.h ============================================================================
// NOTE: um/winnt.h ////////////////////////////////////////////////////////////////////////////
typedef void VOID;
typedef __int64 LONGLONG;
typedef unsigned __int64 ULONGLONG;
@ -66,6 +81,7 @@
typedef long LONG;
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
typedef CHAR * NPSTR, *LPSTR, *PSTR;
typedef WCHAR * NWPSTR, *LPWSTR, *PWSTR;
// NOTE: VirtualAlloc: Allocation Type
#define MEM_RESERVE 0x00002000
@ -93,6 +109,10 @@
#define FILE_APPEND_DATA (0x0004) // file
// NOTE: CreateFile/FindFirstFile
#define FILE_SHARE_READ 0x00000001
#define FILE_SHARE_WRITE 0x00000002
#define FILE_SHARE_DELETE 0x00000004
#define FILE_ATTRIBUTE_READONLY 0x00000001
#define FILE_ATTRIBUTE_HIDDEN 0x00000002
#define FILE_ATTRIBUTE_SYSTEM 0x00000004
@ -102,6 +122,11 @@
// NOTE: STACKFRAME64
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
// NOTE: WaitForSingleObject
#define WAIT_TIMEOUT 258L // dderror
#define STATUS_WAIT_0 ((DWORD )0x00000000L)
#define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L)
typedef union _ULARGE_INTEGER {
struct {
DWORD LowPart;
@ -230,16 +255,71 @@
DWORD64 LastExceptionFromRip;
} CONTEXT;
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, PRLIST_ENTRY;
typedef struct _RTL_CRITICAL_SECTION_DEBUG {
WORD Type;
WORD CreatorBackTraceIndex;
struct _RTL_CRITICAL_SECTION *CriticalSection;
LIST_ENTRY ProcessLocksList;
DWORD EntryCount;
DWORD ContentionCount;
DWORD Flags;
WORD CreatorBackTraceIndexHigh;
WORD Identifier;
} RTL_CRITICAL_SECTION_DEBUG, *PRTL_CRITICAL_SECTION_DEBUG, RTL_RESOURCE_DEBUG, *PRTL_RESOURCE_DEBUG;
#pragma pack(push, 8)
typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
//
// The following three fields control entering and exiting the critical
// section for the resource
//
LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread; // from the thread's ClientId->UniqueThread
HANDLE LockSemaphore;
ULONG_PTR SpinCount; // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
#pragma pack(pop)
typedef struct _MODLOAD_DATA {
DWORD ssize; // size of this struct
DWORD ssig; // signature identifying the passed data
VOID *data; // pointer to passed data
DWORD size; // size of passed data
DWORD flags; // options
} MODLOAD_DATA, *PMODLOAD_DATA;
#define SLMFLAG_VIRTUAL 0x1
#define SLMFLAG_ALT_INDEX 0x2
#define SLMFLAG_NO_SYMBOLS 0x4
extern "C"
{
__declspec(dllimport) VOID __stdcall RtlCaptureContext(CONTEXT *ContextRecord);
__declspec(dllimport) HANDLE __stdcall GetCurrentProcess(void);
__declspec(dllimport) HANDLE __stdcall GetCurrentThread(void);
__declspec(dllimport) DWORD __stdcall SymSetOptions(DWORD SymOptions);
__declspec(dllimport) BOOL __stdcall SymInitialize(HANDLE hProcess, const CHAR* UserSearchPath, BOOL fInvadeProcess);
__declspec(dllimport) VOID __stdcall RtlCaptureContext(CONTEXT *ContextRecord);
__declspec(dllimport) HANDLE __stdcall GetCurrentProcess(void);
__declspec(dllimport) HANDLE __stdcall GetCurrentThread(void);
__declspec(dllimport) DWORD __stdcall SymSetOptions(DWORD SymOptions);
__declspec(dllimport) BOOL __stdcall SymInitialize(HANDLE hProcess, const CHAR* UserSearchPath, BOOL fInvadeProcess);
__declspec(dllimport) DWORD64 __stdcall SymLoadModuleEx(HANDLE hProcess, HANDLE hFile, CHAR const *ImageName, CHAR const *ModuleName, DWORD64 BaseOfDll, DWORD DllSize, MODLOAD_DATA *Data, DWORD Flags);
__declspec(dllimport) BOOL __stdcall SymUnloadModule64(HANDLE hProcess, DWORD64 BaseOfDll);
}
// NOTE: handleapi.h ===========================================================================
// NOTE: shared/windef.h ////////////////////////////////////////////////////////////////////
typedef struct tagPOINT
{
LONG x;
LONG y;
} POINT, *PPOINT, *NPPOINT, *LPPOINT;
// NOTE: handleapi.h ///////////////////////////////////////////////////////////////////////////
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
extern "C"
@ -247,7 +327,7 @@
__declspec(dllimport) BOOL __stdcall CloseHandle(HANDLE hObject);
}
// NOTE: consoleapi.h ===========================================================================
// NOTE: consoleapi.h ///////////////////////////////////////////////////////////////////////////
extern "C"
{
__declspec(dllimport) BOOL __stdcall WriteConsoleA(HANDLE hConsoleOutput, const VOID* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD *lpNumberOfCharsWritten, VOID *lpReserved);
@ -257,11 +337,17 @@
__declspec(dllimport) BOOL __stdcall GetConsoleMode(HANDLE hConsoleHandle, DWORD *lpMode);
}
// NOTE: um/minwinbase.h =======================================================================
// NOTE: um/minwinbase.h ///////////////////////////////////////////////////////////////////////
// NOTE: FindFirstFile
#define FIND_FIRST_EX_CASE_SENSITIVE 0x00000001
#define FIND_FIRST_EX_LARGE_FETCH 0x00000002
// NOTE: WaitFor..
#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
#define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 )
#define WAIT_ABANDONED ((STATUS_ABANDONED_WAIT_0 ) + 0 )
#define WAIT_ABANDONED_0 ((STATUS_ABANDONED_WAIT_0 ) + 0 )
typedef enum _GET_FILEEX_INFO_LEVELS {
GetFileExInfoStandard,
GetFileExMaxInfoLevel
@ -329,7 +415,9 @@
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;
// NOTE: um/winbase.h ==========================================================================
typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;
// NOTE: um/winbase.h //////////////////////////////////////////////////////////////////////////
#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
#define WAIT_OBJECT_0 ((STATUS_WAIT_0 ) + 0 )
@ -344,9 +432,10 @@
#define MOVEFILE_COPY_ALLOWED 0x00000002
// NOTE: FormatMessageA
#define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200
#define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
#define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100
#define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200
#define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
#define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
// NOTE: CreateProcessW
#define STARTF_USESTDHANDLES 0x00000100
@ -356,17 +445,18 @@
__declspec(dllimport) BOOL __stdcall MoveFileExW (const WCHAR *lpExistingFileName, const WCHAR *lpNewFileName, DWORD dwFlags);
__declspec(dllimport) BOOL __stdcall CopyFileW (const WCHAR *lpExistingFileName, const WCHAR *lpNewFileName, BOOL bFailIfExists);
__declspec(dllimport) HANDLE __stdcall CreateSemaphoreA(SECURITY_ATTRIBUTES *lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, const CHAR *lpName);
__declspec(dllimport) DWORD __stdcall FormatMessageA (DWORD dwFlags, const VOID *lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize, va_list *Arguments);
__declspec(dllimport) DWORD __stdcall FormatMessageW (DWORD dwFlags, VOID const *lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPWSTR lpBuffer, DWORD nSize, va_list *Arguments);
__declspec(dllimport) HLOCAL __stdcall LocalFree (HLOCAL hMem);
}
// NOTE: um/stringapiset.h =====================================================================
// NOTE: um/stringapiset.h /////////////////////////////////////////////////////////////////////
extern "C"
{
__declspec(dllimport) int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, const CHAR *lpMultiByteStr, int cbMultiByte, WCHAR *lpWideCharStr, int cchWideChar);
__declspec(dllimport) int __stdcall WideCharToMultiByte(UINT CodePage, DWORD dwFlags, const WCHAR *lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, const CHAR *lpDefaultChar, BOOL *lpUsedDefaultChar);
}
// NOTE: um/fileapi.h ==========================================================================
// NOTE: um/fileapi.h //////////////////////////////////////////////////////////////////////////
#define INVALID_FILE_SIZE ((DWORD)0xFFFFFFFF)
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
@ -401,14 +491,14 @@
}
// NOTE: um/processenv.h =======================================================================
// NOTE: um/processenv.h ///////////////////////////////////////////////////////////////////////
extern "C"
{
__declspec(dllimport) DWORD __stdcall GetCurrentDirectoryW(DWORD nBufferLength, WCHAR *lpBuffer);
__declspec(dllimport) HANDLE __stdcall GetStdHandle(DWORD nStdHandle);
}
// NOTE: um/sysinfoapi.h =======================================================================
// NOTE: um/sysinfoapi.h ///////////////////////////////////////////////////////////////////////
typedef struct _SYSTEM_INFO {
union {
DWORD dwOemId; // Obsolete field...do not use
@ -436,7 +526,7 @@
__declspec(dllimport) VOID __stdcall GetLocalTime(SYSTEMTIME *lpSystemTime);
}
// NOTE: shared/windef.h =======================================================================
// NOTE: shared/windef.h ///////////////////////////////////////////////////////////////////////
typedef struct tagRECT {
LONG left;
LONG top;
@ -463,7 +553,20 @@
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)
// NOTE: um/winuser.h ==========================================================================
// NOTE: um/winuser.h //////////////////////////////////////////////////////////////////////////
typedef struct tagWINDOWPLACEMENT {
UINT length;
UINT flags;
UINT showCmd;
POINT ptMinPosition;
POINT ptMaxPosition;
RECT rcNormalPosition;
#ifdef _MAC
RECT rcDevice;
#endif
} WINDOWPLACEMENT;
typedef WINDOWPLACEMENT *PWINDOWPLACEMENT, *LPWINDOWPLACEMENT;
#define SW_HIDE 0
#define SW_NORMAL 1
#define SW_MAXIMIZE 3
@ -477,88 +580,262 @@
__declspec(dllimport) BOOL __stdcall SetWindowPos (HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags);
__declspec(dllimport) UINT __stdcall GetWindowModuleFileNameA(HWND hwnd, LPSTR pszFileName, UINT cchFileNameMax);
__declspec(dllimport) BOOL __stdcall ShowWindow (HWND hWnd, int nCmdShow);
__declspec(dllimport) BOOL __stdcall GetWindowPlacement (HWND hWnd, WINDOWPLACEMENT *lpwndpl);
}
// NOTE: um/wininet.h ==========================================================================
// NOTE: um/wininet.h //////////////////////////////////////////////////////////////////////////
typedef WORD INTERNET_PORT;
typedef VOID *HINTERNET;
#define INTERNET_OPEN_TYPE_PRECONFIG 0 // use registry configuration
#define INTERNET_INVALID_PORT_NUMBER 0 // use the protocol-specific default
#define INTERNET_DEFAULT_FTP_PORT 21 // default for FTP servers
#define INTERNET_DEFAULT_HTTP_PORT 80 // " " HTTP "
#define INTERNET_DEFAULT_HTTPS_PORT 443 // " " HTTPS "
#define INTERNET_SERVICE_HTTP 3
// NOTE: um/winhttp.h //////////////////////////////////////////////////////////////////////////
#define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0
#define WINHTTP_ACCESS_TYPE_NO_PROXY 1
#define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3
#define WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY 4
#define INTERNET_OPTION_USERNAME 28
#define INTERNET_OPTION_PASSWORD 29
#define INTERNET_OPTION_USER_AGENT 41
#define INTERNET_DEFAULT_PORT 0 // use the protocol-specific default
#define INTERNET_DEFAULT_HTTP_PORT 80 // " " HTTP "
#define INTERNET_DEFAULT_HTTPS_PORT 443 // " " HTTPS "
#define INTERNET_FLAG_NO_AUTH 0x00040000 // no automatic authentication handling
#define INTERNET_FLAG_SECURE 0x00800000 // use PCT/SSL if applicable (HTTP)
// NOTE: WinHttpOpen
#define WINHTTP_FLAG_ASYNC 0x10000000 // this session is asynchronous (where supported)
#define WINHTTP_FLAG_SECURE_DEFAULTS 0x30000000 // note that this flag also forces async
#define HTTP_QUERY_RAW_HEADERS 21 // special: all headers as ASCIIZ
#define HTTP_QUERY_RAW_HEADERS_CRLF 22 // special: all headers
// NOTE: WinHttpOpenRequest
#define WINHTTP_FLAG_SECURE 0x00800000 // use SSL if applicable (HTTPS)
#define WINHTTP_FLAG_ESCAPE_PERCENT 0x00000004 // if escaping enabled, escape percent as well
#define WINHTTP_FLAG_NULL_CODEPAGE 0x00000008 // assume all symbols are ASCII, use fast convertion
#define WINHTTP_FLAG_ESCAPE_DISABLE 0x00000040 // disable escaping
#define WINHTTP_FLAG_ESCAPE_DISABLE_QUERY 0x00000080 // if escaping enabled escape path part, but do not escape query
#define WINHTTP_FLAG_BYPASS_PROXY_CACHE 0x00000100 // add "pragma: no-cache" request header
#define WINHTTP_FLAG_REFRESH WINHTTP_FLAG_BYPASS_PROXY_CACHE
#define WINHTTP_FLAG_AUTOMATIC_CHUNKING 0x00000200 // Send request without content-length header or chunked TE
#define HTTP_ADDREQ_FLAG_ADD_IF_NEW 0x10000000
#define HTTP_ADDREQ_FLAG_ADD 0x20000000
#define HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA 0x40000000
#define HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON 0x01000000
#define HTTP_ADDREQ_FLAG_COALESCE HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA
#define HTTP_ADDREQ_FLAG_REPLACE 0x80000000
#define WINHTTP_NO_PROXY_NAME NULL
#define WINHTTP_NO_PROXY_BYPASS NULL
typedef enum {
INTERNET_SCHEME_PARTIAL = -2,
INTERNET_SCHEME_UNKNOWN = -1,
INTERNET_SCHEME_DEFAULT = 0,
INTERNET_SCHEME_FTP,
INTERNET_SCHEME_GOPHER,
INTERNET_SCHEME_HTTP,
INTERNET_SCHEME_HTTPS,
INTERNET_SCHEME_FILE,
INTERNET_SCHEME_NEWS,
INTERNET_SCHEME_MAILTO,
INTERNET_SCHEME_SOCKS,
INTERNET_SCHEME_JAVASCRIPT,
INTERNET_SCHEME_VBSCRIPT,
INTERNET_SCHEME_RES,
INTERNET_SCHEME_FIRST = INTERNET_SCHEME_FTP,
INTERNET_SCHEME_LAST = INTERNET_SCHEME_RES
} INTERNET_SCHEME, * LPINTERNET_SCHEME;
//
// WINHTTP_QUERY_FLAG_NUMBER - if this bit is set in the dwInfoLevel parameter of
// HttpQueryHeader(), then the value of the header will be converted to a number
// before being returned to the caller, if applicable
//
#define WINHTTP_QUERY_FLAG_NUMBER 0x20000000
typedef struct {
DWORD dwStructSize; // size of this structure. Used in version check
LPSTR lpszScheme; // pointer to scheme name
DWORD dwSchemeLength; // length of scheme name
INTERNET_SCHEME nScheme; // enumerated scheme type (if known)
LPSTR lpszHostName; // pointer to host name
DWORD dwHostNameLength; // length of host name
INTERNET_PORT nPort; // converted port number
LPSTR lpszUserName; // pointer to user name
DWORD dwUserNameLength; // length of user name
LPSTR lpszPassword; // pointer to password
DWORD dwPasswordLength; // length of password
LPSTR lpszUrlPath; // pointer to URL-path
DWORD dwUrlPathLength; // length of URL-path
LPSTR lpszExtraInfo; // pointer to extra information (e.g. ?foo or #foo)
DWORD dwExtraInfoLength; // length of extra information
} URL_COMPONENTSA, * LPURL_COMPONENTSA;
#define WINHTTP_QUERY_MIME_VERSION 0
#define WINHTTP_QUERY_CONTENT_TYPE 1
#define WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING 2
#define WINHTTP_QUERY_CONTENT_ID 3
#define WINHTTP_QUERY_CONTENT_DESCRIPTION 4
#define WINHTTP_QUERY_CONTENT_LENGTH 5
#define WINHTTP_QUERY_CONTENT_LANGUAGE 6
#define WINHTTP_QUERY_ALLOW 7
#define WINHTTP_QUERY_PUBLIC 8
#define WINHTTP_QUERY_DATE 9
#define WINHTTP_QUERY_EXPIRES 10
#define WINHTTP_QUERY_LAST_MODIFIED 11
#define WINHTTP_QUERY_MESSAGE_ID 12
#define WINHTTP_QUERY_URI 13
#define WINHTTP_QUERY_DERIVED_FROM 14
#define WINHTTP_QUERY_COST 15
#define WINHTTP_QUERY_LINK 16
#define WINHTTP_QUERY_PRAGMA 17
#define WINHTTP_QUERY_VERSION 18 // special: part of status line
#define WINHTTP_QUERY_STATUS_CODE 19 // special: part of status line
#define WINHTTP_QUERY_STATUS_TEXT 20 // special: part of status line
#define WINHTTP_QUERY_RAW_HEADERS 21 // special: all headers as ASCIIZ
#define WINHTTP_QUERY_RAW_HEADERS_CRLF 22 // special: all headers
#define WINHTTP_QUERY_CONNECTION 23
#define WINHTTP_QUERY_ACCEPT 24
#define WINHTTP_QUERY_ACCEPT_CHARSET 25
#define WINHTTP_QUERY_ACCEPT_ENCODING 26
#define WINHTTP_QUERY_ACCEPT_LANGUAGE 27
#define WINHTTP_QUERY_AUTHORIZATION 28
#define WINHTTP_QUERY_CONTENT_ENCODING 29
#define WINHTTP_QUERY_FORWARDED 30
#define WINHTTP_QUERY_FROM 31
#define WINHTTP_QUERY_IF_MODIFIED_SINCE 32
#define WINHTTP_QUERY_LOCATION 33
#define WINHTTP_QUERY_ORIG_URI 34
#define WINHTTP_QUERY_REFERER 35
#define WINHTTP_QUERY_RETRY_AFTER 36
#define WINHTTP_QUERY_SERVER 37
#define WINHTTP_QUERY_TITLE 38
#define WINHTTP_QUERY_USER_AGENT 39
#define WINHTTP_QUERY_WWW_AUTHENTICATE 40
#define WINHTTP_QUERY_PROXY_AUTHENTICATE 41
#define WINHTTP_QUERY_ACCEPT_RANGES 42
#define WINHTTP_QUERY_SET_COOKIE 43
#define WINHTTP_QUERY_COOKIE 44
#define WINHTTP_QUERY_REQUEST_METHOD 45 // special: GET/POST etc.
#define WINHTTP_QUERY_REFRESH 46
#define WINHTTP_QUERY_CONTENT_DISPOSITION 47
// NOTE: WinHttpQueryHeaders prettifiers for optional parameters.
#define WINHTTP_HEADER_NAME_BY_INDEX NULL
#define WINHTTP_NO_OUTPUT_BUFFER NULL
#define WINHTTP_NO_HEADER_INDEX NULL
// NOTE: Http Response Status Codes
#define HTTP_STATUS_CONTINUE 100 // OK to continue with request
#define HTTP_STATUS_SWITCH_PROTOCOLS 101 // server has switched protocols in upgrade header
#define HTTP_STATUS_OK 200 // request completed
#define HTTP_STATUS_CREATED 201 // object created, reason = new URI
#define HTTP_STATUS_ACCEPTED 202 // async completion (TBS)
#define HTTP_STATUS_PARTIAL 203 // partial completion
#define HTTP_STATUS_NO_CONTENT 204 // no info to return
#define HTTP_STATUS_RESET_CONTENT 205 // request completed, but clear form
#define HTTP_STATUS_PARTIAL_CONTENT 206 // partial GET fulfilled
#define HTTP_STATUS_WEBDAV_MULTI_STATUS 207 // WebDAV Multi-Status
#define HTTP_STATUS_AMBIGUOUS 300 // server couldn't decide what to return
#define HTTP_STATUS_MOVED 301 // object permanently moved
#define HTTP_STATUS_REDIRECT 302 // object temporarily moved
#define HTTP_STATUS_REDIRECT_METHOD 303 // redirection w/ new access method
#define HTTP_STATUS_NOT_MODIFIED 304 // if-modified-since was not modified
#define HTTP_STATUS_USE_PROXY 305 // redirection to proxy, location header specifies proxy to use
#define HTTP_STATUS_REDIRECT_KEEP_VERB 307 // HTTP/1.1: keep same verb
#define HTTP_STATUS_PERMANENT_REDIRECT 308 // Object permanently moved keep verb
#define HTTP_STATUS_BAD_REQUEST 400 // invalid syntax
#define HTTP_STATUS_DENIED 401 // access denied
#define HTTP_STATUS_PAYMENT_REQ 402 // payment required
#define HTTP_STATUS_FORBIDDEN 403 // request forbidden
#define HTTP_STATUS_NOT_FOUND 404 // object not found
#define HTTP_STATUS_BAD_METHOD 405 // method is not allowed
#define HTTP_STATUS_NONE_ACCEPTABLE 406 // no response acceptable to client found
#define HTTP_STATUS_PROXY_AUTH_REQ 407 // proxy authentication required
#define HTTP_STATUS_REQUEST_TIMEOUT 408 // server timed out waiting for request
#define HTTP_STATUS_CONFLICT 409 // user should resubmit with more info
#define HTTP_STATUS_GONE 410 // the resource is no longer available
#define HTTP_STATUS_LENGTH_REQUIRED 411 // the server refused to accept request w/o a length
#define HTTP_STATUS_PRECOND_FAILED 412 // precondition given in request failed
#define HTTP_STATUS_REQUEST_TOO_LARGE 413 // request entity was too large
#define HTTP_STATUS_URI_TOO_LONG 414 // request URI too long
#define HTTP_STATUS_UNSUPPORTED_MEDIA 415 // unsupported media type
#define HTTP_STATUS_RETRY_WITH 449 // retry after doing the appropriate action.
#define HTTP_STATUS_SERVER_ERROR 500 // internal server error
#define HTTP_STATUS_NOT_SUPPORTED 501 // required not supported
#define HTTP_STATUS_BAD_GATEWAY 502 // error response received from gateway
#define HTTP_STATUS_SERVICE_UNAVAIL 503 // temporarily overloaded
#define HTTP_STATUS_GATEWAY_TIMEOUT 504 // timed out waiting for gateway
#define HTTP_STATUS_VERSION_NOT_SUP 505 // HTTP version not supported
#define HTTP_STATUS_FIRST HTTP_STATUS_CONTINUE
#define HTTP_STATUS_LAST HTTP_STATUS_VERSION_NOT_SUP
#define WINHTTP_CALLBACK_STATUS_RESOLVING_NAME 0x00000001
#define WINHTTP_CALLBACK_STATUS_NAME_RESOLVED 0x00000002
#define WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER 0x00000004
#define WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER 0x00000008
#define WINHTTP_CALLBACK_STATUS_SENDING_REQUEST 0x00000010
#define WINHTTP_CALLBACK_STATUS_REQUEST_SENT 0x00000020
#define WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE 0x00000040
#define WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED 0x00000080
#define WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION 0x00000100
#define WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED 0x00000200
#define WINHTTP_CALLBACK_STATUS_HANDLE_CREATED 0x00000400
#define WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING 0x00000800
#define WINHTTP_CALLBACK_STATUS_DETECTING_PROXY 0x00001000
#define WINHTTP_CALLBACK_STATUS_REDIRECT 0x00004000
#define WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE 0x00008000
#define WINHTTP_CALLBACK_STATUS_SECURE_FAILURE 0x00010000
#define WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE 0x00020000
#define WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE 0x00040000
#define WINHTTP_CALLBACK_STATUS_READ_COMPLETE 0x00080000
#define WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE 0x00100000
#define WINHTTP_CALLBACK_STATUS_REQUEST_ERROR 0x00200000
#define WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE 0x00400000
#define WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE 0x01000000
#define WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE 0x02000000
#define WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE 0x04000000
#define WINHTTP_CALLBACK_STATUS_SETTINGS_WRITE_COMPLETE 0x10000000
#define WINHTTP_CALLBACK_STATUS_SETTINGS_READ_COMPLETE 0x20000000
#define WINHTTP_CALLBACK_FLAG_RESOLVE_NAME (WINHTTP_CALLBACK_STATUS_RESOLVING_NAME | WINHTTP_CALLBACK_STATUS_NAME_RESOLVED)
#define WINHTTP_CALLBACK_FLAG_CONNECT_TO_SERVER (WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER | WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER)
#define WINHTTP_CALLBACK_FLAG_SEND_REQUEST (WINHTTP_CALLBACK_STATUS_SENDING_REQUEST | WINHTTP_CALLBACK_STATUS_REQUEST_SENT)
#define WINHTTP_CALLBACK_FLAG_RECEIVE_RESPONSE (WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE | WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED)
#define WINHTTP_CALLBACK_FLAG_CLOSE_CONNECTION (WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION | WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED)
#define WINHTTP_CALLBACK_FLAG_HANDLES (WINHTTP_CALLBACK_STATUS_HANDLE_CREATED | WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING)
#define WINHTTP_CALLBACK_FLAG_DETECTING_PROXY WINHTTP_CALLBACK_STATUS_DETECTING_PROXY
#define WINHTTP_CALLBACK_FLAG_REDIRECT WINHTTP_CALLBACK_STATUS_REDIRECT
#define WINHTTP_CALLBACK_FLAG_INTERMEDIATE_RESPONSE WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE
#define WINHTTP_CALLBACK_FLAG_SECURE_FAILURE WINHTTP_CALLBACK_STATUS_SECURE_FAILURE
#define WINHTTP_CALLBACK_FLAG_SENDREQUEST_COMPLETE WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE
#define WINHTTP_CALLBACK_FLAG_HEADERS_AVAILABLE WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE
#define WINHTTP_CALLBACK_FLAG_DATA_AVAILABLE WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE
#define WINHTTP_CALLBACK_FLAG_READ_COMPLETE WINHTTP_CALLBACK_STATUS_READ_COMPLETE
#define WINHTTP_CALLBACK_FLAG_WRITE_COMPLETE WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE
#define WINHTTP_CALLBACK_FLAG_REQUEST_ERROR WINHTTP_CALLBACK_STATUS_REQUEST_ERROR
#define WINHTTP_CALLBACK_FLAG_GETPROXYFORURL_COMPLETE WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE
#define WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS (WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE \
| WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE \
| WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE \
| WINHTTP_CALLBACK_STATUS_READ_COMPLETE \
| WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE \
| WINHTTP_CALLBACK_STATUS_REQUEST_ERROR \
| WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE)
#define WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS 0xffffffff
#define WINHTTP_INVALID_STATUS_CALLBACK ((WINHTTP_STATUS_CALLBACK)(-1L))
typedef struct _WINHTTP_EXTENDED_HEADER
{
union
{
CHAR const *pwszName;
WCHAR const *pszName;
};
union
{
WCHAR const *pwszValue;
CHAR const *pszValue;
};
} WINHTTP_EXTENDED_HEADER, *PWINHTTP_EXTENDED_HEADER;
typedef struct _WINHTTP_ASYNC_RESULT
{
DWORD *dwResult; // indicates which async API has encountered an error
DWORD dwError; // the error code if the API failed
} WINHTTP_ASYNC_RESULT, *LPWINHTTP_ASYNC_RESULT, *PWINHTTP_ASYNC_RESULT;
typedef
VOID
(*WINHTTP_STATUS_CALLBACK)(
HINTERNET hInternet,
DWORD *dwContext,
DWORD dwInternetStatus,
VOID *lpvStatusInformation,
DWORD dwStatusInformationLength
);
extern "C"
{
__declspec(dllimport) BOOL __stdcall InternetCrackUrlA (CHAR const *lpszUrl, DWORD dwUrlLength, DWORD dwFlags, URL_COMPONENTSA *lpUrlComponents);
__declspec(dllimport) HINTERNET __stdcall InternetOpenA (CHAR const *lpszAgent, DWORD dwAccessType, CHAR const *lpszProxy, CHAR const *lpszProxyBypass, DWORD dwFlags);
__declspec(dllimport) HINTERNET __stdcall InternetConnectA (HINTERNET hInternet, CHAR const *lpszServerName, INTERNET_PORT nServerPort, CHAR const *lpszUserName, CHAR const *lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext);
__declspec(dllimport) BOOL __stdcall InternetSetOptionA (HINTERNET hInternet, DWORD dwOption, VOID *lpBuffer, DWORD dwBufferLength);
__declspec(dllimport) BOOL __stdcall InternetReadFile (HINTERNET hFile, VOID *lpBuffer, DWORD dwNumberOfBytesToRead, DWORD *lpdwNumberOfBytesRead);
__declspec(dllimport) BOOL __stdcall InternetCloseHandle (HINTERNET hInternet);
__declspec(dllimport) HINTERNET __stdcall HttpOpenRequestA (HINTERNET hConnect, CHAR const *lpszVerb, CHAR const *lpszObjectName, CHAR const *lpszVersion, CHAR const *lpszReferrer, CHAR const *lplpszAcceptTypes, DWORD dwFlags, DWORD_PTR dwContext);
__declspec(dllimport) BOOL __stdcall HttpSendRequestA (HINTERNET hRequest, CHAR const *lpszHeaders, DWORD dwHeadersLength, VOID *lpOptional, DWORD dwOptionalLength);
__declspec(dllimport) BOOL __stdcall HttpAddRequestHeadersA(HINTERNET hRequest, CHAR const *lpszHeaders, DWORD dwHeadersLength, DWORD dwModifiers);
__declspec(dllimport) BOOL __stdcall HttpQueryInfoA (HINTERNET hRequest, DWORD dwInfoLevel, VOID *lpBuffer, DWORD *lpdwBufferLength, DWORD *lpdwIndex);
__declspec(dllimport) HINTERNET __stdcall WinHttpOpen(WCHAR const *pszAgentW, DWORD dwAccessType, WCHAR const *pszProxyW, WCHAR const *pszProxyBypassW, DWORD dwFlags);
__declspec(dllimport) BOOL __stdcall WinHttpCloseHandle(HINTERNET hInternet);
__declspec(dllimport) HINTERNET __stdcall WinHttpConnect(HINTERNET hSession, WCHAR const *pswzServerName, INTERNET_PORT nServerPort, DWORD dwReserved);
__declspec(dllimport) BOOL __stdcall WinHttpReadData(HINTERNET hRequest, VOID *lpBuffer, DWORD dwNumberOfBytesToRead, DWORD *lpdwNumberOfBytesRead);
__declspec(dllimport) HINTERNET __stdcall WinHttpOpenRequest(HINTERNET hConnect, WCHAR const *pwszVerb, WCHAR const *pwszObjectName, WCHAR const *pwszVersion, WCHAR const *pwszReferrer, WCHAR const *ppwszAcceptTypes, DWORD dwFlags);
__declspec(dllimport) BOOL __stdcall WinHttpSendRequest(HINTERNET hRequest, WCHAR const *lpszHeaders, DWORD dwHeadersLength, VOID *lpOptional, DWORD dwOptionalLength, DWORD dwTotalLength, DWORD_PTR dwContext);
__declspec(dllimport) DWORD __stdcall WinHttpAddRequestHeadersEx(HINTERNET hRequest, DWORD dwModifiers, ULONGLONG ullFlags, ULONGLONG ullExtra, DWORD cHeaders, WINHTTP_EXTENDED_HEADER *pHeaders);
__declspec(dllimport) BOOL __stdcall WinHttpSetCredentials(HINTERNET hRequest, // HINTERNET handle returned by WinHttpOpenRequest.
DWORD AuthTargets, // Only WINHTTP_AUTH_TARGET_SERVER and WINHTTP_AUTH_TARGET_PROXY are supported in this version and they are mutually exclusive
DWORD AuthScheme, // must be one of the supported Auth Schemes returned from WinHttpQueryAuthSchemes()
WCHAR * pwszUserName, // 1) NULL if default creds is to be used, in which case pszPassword will be ignored
WCHAR * pwszPassword, // 1) "" == Blank Password; 2)Parameter ignored if pszUserName is NULL; 3) Invalid to pass in NULL if pszUserName is not NULL
VOID * pAuthParams);
__declspec(dllimport) BOOL __stdcall WinHttpQueryHeaders(HINTERNET hRequest, DWORD dwInfoLevel, WCHAR const *pwszName, VOID *lpBuffer, DWORD *lpdwBufferLength, DWORD *lpdwIndex);
__declspec(dllimport) BOOL __stdcall WinHttpReceiveResponse(HINTERNET hRequest, VOID *lpReserved);
__declspec(dllimport) WINHTTP_STATUS_CALLBACK __stdcall WinHttpSetStatusCallback(HINTERNET hInternet, WINHTTP_STATUS_CALLBACK lpfnInternetCallback, DWORD dwNotificationFlags, DWORD_PTR dwReserved);
}
// NOTE: um/DbgHelp.h ==========================================================================
// NOTE: um/DbgHelp.h //////////////////////////////////////////////////////////////////////////
#define SYMOPT_CASE_INSENSITIVE 0x00000001
#define SYMOPT_UNDNAME 0x00000002
#define SYMOPT_DEFERRED_LOADS 0x00000004
@ -680,15 +957,16 @@
__declspec(dllimport) VOID * __stdcall SymFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase);
__declspec(dllimport) BOOL __stdcall SymGetLineFromAddrW64 (HANDLE hProcess, DWORD64 dwAddr, DWORD *pdwDisplacement, IMAGEHLP_LINEW64 *Line);
__declspec(dllimport) DWORD64 __stdcall SymGetModuleBase64 (HANDLE hProcess, DWORD64 qwAddr);
__declspec(dllimport) BOOL __stdcall SymRefreshModuleList (HANDLE hProcess);
};
// NOTE: um/errhandlingapi.h ===================================================================
// NOTE: um/errhandlingapi.h ///////////////////////////////////////////////////////////////////
extern "C"
{
__declspec(dllimport) DWORD __stdcall GetLastError(VOID);
}
// NOTE: um/libloaderapi.h =====================================================================
// NOTE: um/libloaderapi.h /////////////////////////////////////////////////////////////////////
extern "C"
{
__declspec(dllimport) HMODULE __stdcall LoadLibraryA (const CHAR *lpLibFileName);
@ -698,22 +976,30 @@
__declspec(dllimport) DWORD __stdcall GetModuleFileNameW(HMODULE hModule, WCHAR *lpFilename, DWORD nSize);
}
// NOTE: um/synchapi.h =========================================================================
// NOTE: um/synchapi.h /////////////////////////////////////////////////////////////////////////
extern "C"
{
__declspec(dllimport) DWORD __stdcall WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
__declspec(dllimport) BOOL __stdcall ReleaseSemaphore (HANDLE hSemaphore, LONG lReleaseCount, LONG *lpPreviousCount);
__declspec(dllimport) VOID __stdcall Sleep (DWORD dwMilliseconds);
__declspec(dllimport) VOID __stdcall InitializeCriticalSection (CRITICAL_SECTION *lpCriticalSection);
__declspec(dllimport) VOID __stdcall EnterCriticalSection (CRITICAL_SECTION *lpCriticalSection);
__declspec(dllimport) VOID __stdcall LeaveCriticalSection (CRITICAL_SECTION *lpCriticalSection);
__declspec(dllimport) BOOL __stdcall InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount);
__declspec(dllimport) BOOL __stdcall InitializeCriticalSectionEx (CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount, DWORD Flags);
__declspec(dllimport) DWORD __stdcall SetCriticalSectionSpinCount (CRITICAL_SECTION *lpCriticalSection, DWORD dwSpinCount);
__declspec(dllimport) BOOL __stdcall TryEnterCriticalSection (CRITICAL_SECTION *lpCriticalSection);
__declspec(dllimport) VOID __stdcall DeleteCriticalSection (CRITICAL_SECTION *lpCriticalSection);
__declspec(dllimport) DWORD __stdcall WaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds);
__declspec(dllimport) BOOL __stdcall ReleaseSemaphore (HANDLE hSemaphore, LONG lReleaseCount, LONG *lpPreviousCount);
__declspec(dllimport) VOID __stdcall Sleep (DWORD dwMilliseconds);
}
// NOTE: um/profileapi.h =======================================================================
// NOTE: um/profileapi.h ///////////////////////////////////////////////////////////////////////
extern "C"
{
__declspec(dllimport) BOOL __stdcall QueryPerformanceCounter (LARGE_INTEGER* lpPerformanceCount);
__declspec(dllimport) BOOL __stdcall QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency);
}
// NOTE: um/processthreadsapi.h ================================================================
// NOTE: um/processthreadsapi.h ////////////////////////////////////////////////////////////////
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
@ -757,7 +1043,7 @@
}
// NOTE: um/memoryapi.h ========================================================================
// NOTE: um/memoryapi.h ////////////////////////////////////////////////////////////////////////
extern "C"
{
__declspec(dllimport) VOID * __stdcall VirtualAlloc (VOID *lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
@ -765,7 +1051,7 @@
__declspec(dllimport) BOOL __stdcall VirtualFree (VOID *lpAddress, SIZE_T dwSize, DWORD dwFreeType);
}
// NOTE: shared/bcrypt.h =======================================================================
// NOTE: shared/bcrypt.h ///////////////////////////////////////////////////////////////////////
typedef VOID *BCRYPT_ALG_HANDLE;
typedef LONG NTSTATUS;
@ -775,12 +1061,17 @@
__declspec(dllimport) NTSTATUS __stdcall BCryptGenRandom (BCRYPT_ALG_HANDLE hAlgorithm, UCHAR *pbBuffer, ULONG cbBuffer, ULONG dwFlags);
}
// NOTE: um/shellapi.h =========================================================================
// NOTE: um/shellapi.h /////////////////////////////////////////////////////////////////////////
extern "C"
{
__declspec(dllimport) HINSTANCE __stdcall ShellExecuteA(HWND hwnd, CHAR const *lpOperation, CHAR const *lpFile, CHAR const *lpParameters, CHAR const *lpDirectory, INT nShowCmd);
}
// NOTE: um/debugapi.h /////////////////////////////////////////////////////////////////////////
extern "C"
{
__declspec(dllimport) BOOL __stdcall IsDebuggerPresent();
}
DQN_MSVC_WARNING_POP
#endif // !defined(_INC_WINDOWS)
#endif /// defined(DQN_OS_WIN32)