dqn: Organise into files and use an amalgamated build

This commit is contained in:
2023-07-05 00:04:53 +10:00
parent 807c65a253
commit 8ae369db0d
29 changed files with 12027 additions and 12206 deletions
+227
View File
@@ -0,0 +1,227 @@
#if !defined(DQN_CPP_FILE_H)
#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
typedef struct Dqn_CppFile { ///< Maintains state for printing C++ style formatted files
FILE *file; ///< (Write) File to print to
int indent; ///< Current indent level of the printer
int space_per_indent; ///< (Write) Number of spaces per indent
unsigned char if_chain[256]; ///
unsigned char if_chain_size; ///
} Dqn_CppFile;
/// Print the format string indented and terminate the string with a new-line.
void Dqn_CppLineV(Dqn_CppFile *cpp, char const *fmt, va_list args);
void Dqn_CppLine(Dqn_CppFile *cpp, char const *fmt, ...);
/// Print the format string indented
void Dqn_CppPrintV(Dqn_CppFile *cpp, char const *fmt, va_list args);
void Dqn_CppPrint(Dqn_CppFile *cpp, char const *fmt, ...);
/// Print the format string
#define Dqn_CppAppend(cpp, fmt, ...) fprintf((cpp)->file, fmt, ##__VA_ARGS__)
#define Dqn_CppAppendV(cpp, fmt, args) vfprintf((cpp)->file, fmt, args)
/// End the current line, useful after CppPrint and CppAppend
#define Dqn_CppNewLine(cpp) fputc('\n', (cpp)->file)
/// Manually modify the indent level
#define Dqn_CppIndent(cpp) (cpp)->indent++
#define Dqn_CppUnindent(cpp) (cpp)->indent--; assert((cpp)->indent >= 0)
/// Block scope functions that execute a function on entry and exit of the
/// scope by exploiting the comma operator and a for loop.
///
/// @code
/// Dqn_CppEnumBlock(cpp, "abc") {
/// printf("Hello world!");
/// }
///
/// // Is equivalent to
///
/// Dqn_CppBeginBlock(cpp, "abc");
/// printf("Hello world!");
/// Dqn_CppEndEnumBlock(cpp);
/// @endcode
#define Dqn_CppEnumBlock(cpp, 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_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_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_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndForBlock(cpp), false))
#define Dqn_CppIfOrElseIfBlock(cpp, fmt, ...) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
(Dqn_CppBeginIfOrElseIfBlock(cpp, fmt, ##__VA_ARGS__), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndIfOrElseIfBlock(cpp), false))
#define Dqn_CppElseBlock(cpp) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
(Dqn_CppBeginElseBlock(cpp), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndElseBlock(cpp), false))
#define Dqn_CppFuncBlock(cpp, 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_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_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_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndSwitchBlock(cpp), false))
#define Dqn_CppIfChain(cpp) \
for (bool DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppBeginIfChain(cpp), true); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__); \
DQN_CPP_TOKEN_PASTE_(once_, __LINE__) = (Dqn_CppEndIfChain(cpp), false))
/// Print the format string followed by a "{" and enter a new line whilst
/// 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);
/// 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_CppBeginForBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
#define Dqn_CppEndForBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(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_CppBeginStructBlock(cpp, fmt, ...) Dqn_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
#define Dqn_CppEndStructBlock(cpp) Dqn_CppEndBlock(cpp), Dqn_CppAppend(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")
void Dqn_CppBeginIfOrElseIfBlock (Dqn_CppFile *cpp, char const *fmt, ...);
#define Dqn_CppEndIfOrElseIfBlock(cpp) Dqn_CppEndBlock(cpp)
void Dqn_CppBeginElseBlock (Dqn_CppFile *cpp);
#define Dqn_CppEndElseBlock(cpp) Dqn_CppEndBlock(cpp)
#define DQN_CPP_TOKEN_PASTE2_(x, y) x ## y
#define DQN_CPP_TOKEN_PASTE_(x, y) DQN_CPP_TOKEN_PASTE2_(x, y)
#endif // DQN_CPP_FILE_H
#if defined(DQN_CPP_FILE_IMPLEMENTATION)
void Dqn_CppLineV(Dqn_CppFile *cpp, char const *fmt, va_list args)
{
Dqn_CppPrintV(cpp, fmt, args);
Dqn_CppNewLine(cpp);
}
void Dqn_CppLine(Dqn_CppFile *cpp, char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_CppLineV(cpp, fmt, args);
va_end(args);
}
void Dqn_CppPrintV(Dqn_CppFile *cpp, char const *fmt, va_list args)
{
int space_per_indent = cpp->space_per_indent == 0 ? 4 : cpp->space_per_indent;
int spaces = fmt ? (cpp->indent * space_per_indent) : 0;
fprintf(cpp->file, "%*s", spaces, "");
vfprintf(cpp->file, fmt, args);
}
void Dqn_CppPrint(Dqn_CppFile *cpp, char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_CppPrintV(cpp, fmt, args);
va_end(args);
}
void Dqn_CppBeginBlock(Dqn_CppFile *cpp, bool append, char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_CppBeginBlockV(cpp, append, fmt, args);
va_end(args);
}
void Dqn_CppBeginBlockV(Dqn_CppFile *cpp, bool append, char const *fmt, va_list args)
{
if (append)
Dqn_CppAppendV(cpp, fmt, args);
else
Dqn_CppPrintV(cpp, fmt, args);
Dqn_CppAppend(cpp, " {\n");
Dqn_CppIndent(cpp);
}
void Dqn_CppEndBlock(Dqn_CppFile *cpp)
{
Dqn_CppUnindent(cpp);
Dqn_CppPrint(cpp, "}");
}
void Dqn_CppBeginIfOrElseIfBlock(Dqn_CppFile *cpp, char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (cpp->if_chain[cpp->if_chain_size - 1] == 0)
Dqn_CppPrint(cpp, "if");
else
Dqn_CppAppend(cpp, " else if");
Dqn_CppAppend(cpp, " (");
Dqn_CppAppendV(cpp, fmt, args);
Dqn_CppAppend(cpp, ") {\n");
Dqn_CppIndent(cpp);
va_end(args);
cpp->if_chain[cpp->if_chain_size - 1]++;
}
void Dqn_CppBeginElseBlock(Dqn_CppFile *cpp)
{
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1)
Dqn_CppBeginBlock(cpp, true /*append*/, " else");
}
void Dqn_CppBeginIfChain(Dqn_CppFile *cpp)
{
assert(cpp->if_chain_size < sizeof(cpp->if_chain)/sizeof(cpp->if_chain[0]));
cpp->if_chain_size++;
}
void Dqn_CppEndIfChain(Dqn_CppFile *cpp)
{
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1)
Dqn_CppNewLine(cpp);
}
#endif // DQN_CPP_FILE_IMPLEMENTATION
+502
View File
@@ -0,0 +1,502 @@
#if !defined(SHEREDOM_JSON_H_INCLUDED)
#error Sheredom's json.h (github.com/sheredom/json.h) must be included before this file
#endif
#if !defined(DQN_JSON_H)
#define DQN_JSON_H
// NOTE: Dqn_JSON
// -----------------------------------------------------------------------------
void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count);
char const *Dqn_JSON_TypeEnumCString(json_type_e type, size_t *size);
bool Dqn_JSON_String8Cmp(json_string_s const *lhs, Dqn_String8 rhs);
// NOTE: Dqn_JSON_Iterator
// -----------------------------------------------------------------------------
enum Dqn_JSONIteratorEntryType
{
Dqn_JSON_IteratorEntryTypeObjElement,
Dqn_JSON_IteratorEntryTypeObj,
Dqn_JSON_IteratorEntryTypeArrayElement,
Dqn_JSON_IteratorEntryTypeArray,
Dqn_JSON_IteratorEntryTypeString,
Dqn_JSON_IteratorEntryTypeNumber,
};
struct Dqn_JSONIteratorEntry
{
Dqn_JSONIteratorEntryType type;
void *value;
};
struct Dqn_JSONIterator
{
Dqn_JSONIteratorEntry 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);
// NOTE: Dqn_JSON_Iterator tree navigation
// -----------------------------------------------------------------------------
json_value_s *Dqn_JSON_IteratorPushCurrValue(Dqn_JSONIterator *it);
bool Dqn_JSON_IteratorNext(Dqn_JSONIterator *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);
#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))
// 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_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);
size_t Dqn_JSON_IteratorValueArraySize(Dqn_JSONIterator *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_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);
#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);
#endif // DQN_JSON_H
#if defined(DQN_JSON_IMPLEMENTATION)
// NOTE: Dqn_JSON
// -----------------------------------------------------------------------------
void *Dqn_JSON_ArenaAllocFunc(void *user_data, size_t count)
{
void *result = NULL;
if (!user_data)
return result;
Dqn_Arena *arena = DQN_CAST(Dqn_Arena*)user_data;
result = Dqn_Arena_Allocate(arena, count, alignof(json_value_s), Dqn_ZeroMem_No);
return result;
}
char const *Dqn_JSON_TypeEnumCString(json_type_e type, size_t *size)
{
switch (type) {
case json_type_string: { if (size) { *size = sizeof("string") - 1; } return "string"; }
case json_type_number: { if (size) { *size = sizeof("number") - 1; } return "number"; }
case json_type_object: { if (size) { *size = sizeof("object") - 1; } return "object"; }
case json_type_array: { if (size) { *size = sizeof("array") - 1; } return "array"; }
case json_type_true: { if (size) { *size = sizeof("true (boolean)") - 1; } return "true (boolean)"; }
case json_type_false: { if (size) { *size = sizeof("false (boolean)") - 1; } return "false (boolean)"; }
default: /*FALLTHRU*/
case json_type_null: { if (size) { *size = sizeof("(null)") - 1; } return "(null)"; }
}
}
bool Dqn_JSON_String8Cmp(json_string_s const *lhs, Dqn_String8 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);
}
return result;
}
// NOTE: Dqn_JSON_Iterator_push/pop
// -----------------------------------------------------------------------------
bool Dqn_JSON_IteratorPushObjElement(Dqn_JSONIterator *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};
return true;
}
bool Dqn_JSON_IteratorPushObj(Dqn_JSONIterator *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};
return true;
}
bool Dqn_JSON_IteratorPushArrayElement(Dqn_JSONIterator *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};
return true;
}
bool Dqn_JSON_IteratorPushArray(Dqn_JSONIterator *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};
return true;
}
bool Dqn_JSON_IteratorPushValue(Dqn_JSONIterator *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));
} else if (value->type == json_type_array) {
result = Dqn_JSON_IteratorPushArray(it, value);
}
return result;
}
void Dqn_JSON_IteratorPop(Dqn_JSONIterator *it)
{
if (!it)
return;
DQN_ASSERT(it->stack_count > 0);
if (it->stack_count > 0)
it->stack_count--;
}
// NOTE: Dqn_JSON_Iterator json tree navigation
// -----------------------------------------------------------------------------
json_value_s *Dqn_JSON_IteratorPushCurrValue(Dqn_JSONIterator *it)
{
json_value_s *result = nullptr;
Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it);
if (!curr)
return result;
if (curr->type == Dqn_JSON_IteratorEntryTypeObjElement) {
json_object_element_s *element = DQN_CAST(json_object_element_s *) curr->value;
result = element->value;
} else if (curr->type == Dqn_JSON_IteratorEntryTypeArrayElement) {
json_array_element_s *element = DQN_CAST(json_array_element_s *) curr->value;
result = element->value;
} else {
result = DQN_CAST(json_value_s *) curr->value;
}
if (result->type == json_type_array) {
json_array_s *array = json_value_as_array(result);
DQN_ASSERT(array);
Dqn_JSON_IteratorPushArray(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);
}
return result;
}
bool Dqn_JSON_IteratorNext(Dqn_JSONIterator *it)
{
Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(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) {
auto *obj = DQN_CAST(json_object_s *) curr->value;
obj_element = obj->start;
} else if (curr->type == Dqn_JSON_IteratorEntryTypeObjElement) {
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) {
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) {
auto *element = DQN_CAST(json_array_element_s *) curr->value;
array_element = element->next;
Dqn_JSON_IteratorPop(it);
} else {
Dqn_JSON_IteratorPop(it);
}
if (obj_element)
Dqn_JSON_IteratorPushObjElement(it, obj_element);
else if (array_element)
Dqn_JSON_IteratorPushArrayElement(it, array_element);
bool result = obj_element || array_element;
return result;
}
// NOTE: Dqn_JSON_IteratorCurr
// -----------------------------------------------------------------------------
Dqn_JSONIteratorEntry *Dqn_JSON_IteratorCurr(Dqn_JSONIterator *it)
{
Dqn_JSONIteratorEntry *result = nullptr;
if (!it || it->stack_count <= 0)
return result;
result = &it->stack[it->stack_count - 1];
return result;
}
json_value_s *Dqn_JSON_IteratorCurrValue(Dqn_JSONIterator *it)
{
json_value_s *result = nullptr;
Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it);
if (!curr)
return result;
if (curr->type == Dqn_JSON_IteratorEntryTypeObjElement) {
auto *element = DQN_CAST(json_object_element_s *)curr->value;
result = element->value;
} else if (curr->type == Dqn_JSON_IteratorEntryTypeArrayElement) {
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)
{
result = DQN_CAST(json_value_s *)curr->value;
}
return result;
}
json_object_element_s *Dqn_JSON_IteratorCurrObjElement(Dqn_JSONIterator *it)
{
Dqn_JSONIteratorEntry *curr = Dqn_JSON_IteratorCurr(it);
auto *result = (curr && curr->type == Dqn_JSON_IteratorEntryTypeObjElement)
? 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)
{
json_value_s *curr = Dqn_JSON_IteratorCurrValue(it);
json_value_s *result = (curr && type == curr->type) ? curr : nullptr;
return result;
}
json_object_s *Dqn_JSON_IteratorValueIsObj(Dqn_JSONIterator *it)
{
json_value_s *curr = Dqn_JSON_IteratorCurrValue(it);
json_object_s *result = curr ? json_value_as_object(curr) : nullptr;
return result;
}
json_array_s *Dqn_JSON_IteratorValueIsArray(Dqn_JSONIterator *it)
{
json_value_s *curr = Dqn_JSON_IteratorCurrValue(it);
json_array_s *result = curr ? json_value_as_array(curr) : nullptr;
return result;
}
json_string_s *Dqn_JSON_IteratorValueIsString(Dqn_JSONIterator *it)
{
json_value_s *curr = Dqn_JSON_IteratorCurrValue(it);
json_string_s *result = curr ? json_value_as_string(curr) : nullptr;
return result;
}
json_number_s *Dqn_JSON_IteratorValueIsNumber(Dqn_JSONIterator *it)
{
json_value_s *curr = Dqn_JSON_IteratorCurrValue(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 *curr = Dqn_JSON_IteratorCurrValue(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)
{
size_t result = 0;
if (json_array_s *curr = Dqn_JSON_IteratorValueIsArray(it))
result = curr->length;
return result;
}
// NOTE: Dqn_JSON_IteratorKeyValueIs
// -----------------------------------------------------------------------------
Dqn_String8 Dqn_JSON_IteratorKey(Dqn_JSONIterator *it)
{
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(it);
Dqn_String8 result = {};
if (curr) {
result.data = DQN_CAST(char *)curr->name->string;
result.size = curr->name->string_size;
}
return result;
}
bool Dqn_JSON_IteratorKeyIs(Dqn_JSONIterator *it, Dqn_String8 key)
{
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(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 *result = nullptr;
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(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 *result = nullptr;
json_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(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_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(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_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(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_object_element_s *curr = Dqn_JSON_IteratorCurrObjElement(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)
{
Dqn_String8 result = {};
if (json_string_s *curr = Dqn_JSON_IteratorValueIsString(it))
result = Dqn_String8_Init(curr->string, curr->string_size);
return result;
}
int64_t Dqn_JSON_IteratorValueToI64(Dqn_JSONIterator *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*/);
return result;
}
uint64_t Dqn_JSON_IteratorValueToU64(Dqn_JSONIterator *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*/);
return result;
}
bool Dqn_JSON_IteratorValueToBool(Dqn_JSONIterator *it)
{
bool result = {};
if (json_value_s *curr = Dqn_JSON_IteratorValueIsBool(it))
result = curr->type == json_type_true;
return result;
}
void Dqn_JSON_IteratorErrorUnknownKeyValue_(Dqn_JSONIterator *it, Dqn_CallSite call_site)
{
if (!it)
return;
json_object_element_s const *curr = Dqn_JSON_IteratorCurrObjElement(it);
if (!curr)
return;
size_t value_type_size = 0;
char const *value_type = Dqn_JSON_TypeEnumCString(DQN_CAST(json_type_e)curr->value->type, &value_type_size);
json_string_s const *key = curr->name;
if (it->flags & json_parse_flags_allow_location_information) {
json_string_ex_s const *info = DQN_CAST(json_string_ex_s const *)key;
Dqn_Log_TypeFCallSite(Dqn_LogType_Warning,
call_site,
"Unknown key-value pair in object [loc=%zu:%zu, key=%.*s, value=%.*s]",
info->line_no,
info->row_no,
key->string_size,
key->string,
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,
key->string,
value_type_size,
value_type);
}
}
#endif // defined(DQN_JSON_IMPLEMENTATION)
+656
View File
@@ -0,0 +1,656 @@
#if !defined(DQN_KECCAK_H)
#define DQN_KECCAK_H
// -----------------------------------------------------------------------------
// NOTE: Overview
// -----------------------------------------------------------------------------
// Implementation of the Keccak hashing algorithms from the Keccak and SHA3
// families (including the FIPS202 published algorithms and the non-finalized
// ones, i.e. the ones used in Ethereum and Monero which adopted SHA3 before it
// was finalized. The only difference between the 2 is a different delimited
// suffix).
//
// -----------------------------------------------------------------------------
// NOTE: MIT License
// -----------------------------------------------------------------------------
// Copyright (c) 2021 github.com/doy-lee
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN 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.
//
// -----------------------------------------------------------------------------
// NOTE: Macros
// -----------------------------------------------------------------------------
// #define DQN_KECCAK_IMPLEMENTATION
// Define this in one and only one C++ file to enable the implementation
// code of the header file.
#if !defined(DQN_KECCAK_MEMCPY)
#include <string.h>
#define DQN_KECCAK_MEMCPY(dest, src, count) memcpy(dest, src, count)
#endif
#if !defined(DQN_KECCAK_MEMCMP)
#include <string.h>
#define DQN_KECCAK_MEMCMP(dest, src, count) memcmp(dest, src, count)
#endif
#if !defined(DQN_KECCAK_MEMSET)
#include <string.h>
#define DQN_KECCAK_MEMSET(dest, byte, count) memset(dest, byte, count)
#endif
#if !defined(DQN_KECCAK_ASSERT)
#if defined(NDEBUG)
#define DQN_KECCAK_ASSERT(expr)
#else
#define DQN_KECCAK_ASSERT(expr) \
do \
{ \
if (!(expr)) \
{ \
(*(volatile int *)0) = 0; \
} \
} while (0)
#endif
#endif
// Use this macro in a printf-like function,
/*
Dqn_KeccakString64 string = {};
printf("%.*s\n", DQN_KECCAK_STRING64_FMT(string));
*/
#define DQN_KECCAK_STRING56_FMT(string) 56, string
#define DQN_KECCAK_STRING64_FMT(string) 64, string
#define DQN_KECCAK_STRING96_FMT(string) 96, string
#define DQN_KECCAK_STRING128_FMT(string) 128, string
// -----------------------------------------------------------------------------
// NOTE: API
// -----------------------------------------------------------------------------
typedef unsigned char Dqn_KeccakU8;
typedef unsigned short Dqn_KeccakU16;
typedef unsigned int Dqn_KeccakU32;
typedef unsigned int Dqn_KeccakUint;
#ifdef _MSC_VER
typedef unsigned __int64 Dqn_KeccakU64;
#else
typedef unsigned long long Dqn_KeccakU64;
#endif
// -----------------------------------------------------------------------------
// NOTE: Data structures
// -----------------------------------------------------------------------------
typedef struct Dqn_KeccakBytes28 { char data[28]; } Dqn_KeccakBytes28; // 224 bit
typedef struct Dqn_KeccakBytes32 { char data[32]; } Dqn_KeccakBytes32; // 256 bit
typedef struct Dqn_KeccakBytes48 { char data[48]; } Dqn_KeccakBytes48; // 384 bit
typedef struct Dqn_KeccakBytes64 { char data[64]; } Dqn_KeccakBytes64; // 512 bit
typedef struct Dqn_KeccakString56 { char data[(sizeof(Dqn_KeccakBytes28) * 2) + 1]; } Dqn_KeccakString56;
typedef struct Dqn_KeccakString64 { char data[(sizeof(Dqn_KeccakBytes32) * 2) + 1]; } Dqn_KeccakString64;
typedef struct Dqn_KeccakString96 { char data[(sizeof(Dqn_KeccakBytes48) * 2) + 1]; } Dqn_KeccakString96;
typedef struct Dqn_KeccakString128 { char data[(sizeof(Dqn_KeccakBytes64) * 2) + 1]; } Dqn_KeccakString128;
#define DQN_KECCAK_LANE_SIZE_U64 5
typedef struct Dqn_KeccakState
{
Dqn_KeccakU8 state[DQN_KECCAK_LANE_SIZE_U64 * DQN_KECCAK_LANE_SIZE_U64 * sizeof(Dqn_KeccakU64)];
int state_size; // The number of bytes written to the state
int absorb_size; // The amount of bytes to absorb/sponge in/from the state
int hash_size_bits; // The size of the hash the context was initialised for in bits
char delimited_suffix; // The delimited suffix of the current hash
} Dqn_KeccakState;
// -----------------------------------------------------------------------------
// NOTE: SHA3/Keccak Streaming API
// -----------------------------------------------------------------------------
// Setup a hashing state for either
// - FIPS 202 SHA3
// - Non-finalized SHA3 (only difference is delimited suffix of 0x1 instead of 0x6 in SHA3)
// The non-finalized SHA3 version is the one adopted by many cryptocurrencies
// such as Ethereum and Monero as they adopted SHA3 before it was finalized.
//
// The state produced from this function is to be used alongside the
// 'KeccakUpdate' and 'KeccakFinish' functions.
//
// sha3: If true, setup the state for FIPS 202 SHA3, otherwise the non-finalized version
// hash_size_bits: The number of bits to setup the context for, available sizes are 224, 256, 384 and 512.
Dqn_KeccakState Dqn_KeccakSHA3Init(bool sha3, int hash_size_bits);
// After initialising a 'Dqn_KeccakState' via 'Dqn_KeccakSHA3Init', iteratively
// update the hash with new data by calling 'Dqn_KeccakUpdate'. On completion,
// call 'Dqn_KeccakFinish' to generate the hash from the state. The 'dest_size'
// must be at-least the (bit-size / 8), i.e. for 'Dqn_Keccak512Init' it must be
// atleast (512 / 8) bytes, 64 bytes.
void Dqn_KeccakUpdate(Dqn_KeccakState *keccak, void const *data, Dqn_KeccakU64 data_size);
void Dqn_KeccakFinish(Dqn_KeccakState *keccak, void *dest, int dest_size);
// -----------------------------------------------------------------------------
// NOTE: Simple API
// -----------------------------------------------------------------------------
// Helper function that combines the Init, Update and Finish step in one shot,
// i.e. hashing a singlular contiguous buffer. Use the streaming API if data
// is split across different buffers.
void Dqn_KeccakSHA3Hash(bool sha3, int hash_size_bits, void const *src, Dqn_KeccakU64 src_size, void *dest, int dest_size);
#define Dqn_SHA3Hash(hash_size_bits, src, src_size, dest, dest_size) Dqn_KeccakSHA3Hash(true /*sha3*/, hash_size_bits, src, src_size, dest, dest_size)
#define Dqn_SHA3_224(src, src_size, dest, dest_size) Dqn_SHA3Hash(224, src, src_size, dest, dest_size)
#define Dqn_SHA3_256(src, src_size, dest, dest_size) Dqn_SHA3Hash(256, src, src_size, dest, dest_size)
#define Dqn_SHA3_384(src, src_size, dest, dest_size) Dqn_SHA3Hash(384, src, src_size, dest, dest_size)
#define Dqn_SHA3_512(src, src_size, dest, dest_size) Dqn_SHA3Hash(512, src, src_size, dest, dest_size)
#define Dqn_KeccakHash(hash_size_bits, src, src_size, dest, dest_size) Dqn_KeccakSHA3Hash(false /*sha3*/, hash_size_bits, src, src_size, dest, dest_size)
#define Dqn_Keccak224(src, src_size, dest, dest_size) Dqn_KeccakHash(224, src, src_size, dest, dest_size)
#define Dqn_Keccak256(src, src_size, dest, dest_size) Dqn_KeccakHash(256, src, src_size, dest, dest_size)
#define Dqn_Keccak384(src, src_size, dest, dest_size) Dqn_KeccakHash(384, src, src_size, dest, dest_size)
#define Dqn_Keccak512(src, src_size, dest, dest_size) Dqn_KeccakHash(512, src, src_size, dest, dest_size)
// -----------------------------------------------------------------------------
// NOTE: SHA3 Helpers
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_SHA3_224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size);
Dqn_KeccakBytes32 Dqn_SHA3_256ToBytes32(void *bytes, Dqn_KeccakU64 bytes_size);
Dqn_KeccakBytes48 Dqn_SHA3_384ToBytes48(void *bytes, Dqn_KeccakU64 bytes_size);
Dqn_KeccakBytes64 Dqn_SHA3_512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size);
// -----------------------------------------------------------------------------
// NOTE: Keccak Helpers
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_Keccak224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size);
Dqn_KeccakBytes32 Dqn_Keccak256ToBytes32(void *bytes, Dqn_KeccakU64 bytes_size);
Dqn_KeccakBytes48 Dqn_Keccak384ToBytes48(void *bytes, Dqn_KeccakU64 bytes_size);
Dqn_KeccakBytes64 Dqn_Keccak512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size);
#if defined(DQN_H)
// -----------------------------------------------------------------------------
// NOTE: SHA3 - Helpers for Dqn data structures
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_SHA3_224StringToBytes28(Dqn_String8 string);
Dqn_KeccakBytes32 Dqn_SHA3_256StringToBytes32(Dqn_String8 string);
Dqn_KeccakBytes48 Dqn_SHA3_384StringToBytes48(Dqn_String8 string);
Dqn_KeccakBytes64 Dqn_SHA3_512StringToBytes64(Dqn_String8 string);
// -----------------------------------------------------------------------------
// NOTE: Keccak - Helpers for Dqn data structures
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_Keccak224StringToBytes28(Dqn_String8 string);
Dqn_KeccakBytes32 Dqn_Keccak256StringToBytes32(Dqn_String8 string);
Dqn_KeccakBytes48 Dqn_Keccak384StringToBytes48(Dqn_String8 string);
Dqn_KeccakBytes64 Dqn_Keccak512StringToBytes64(Dqn_String8 string);
#endif // DQN_H
// -----------------------------------------------------------------------------
// NOTE: Helper functions
// -----------------------------------------------------------------------------
// 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). This function does *not* null-terminate the buffer.
void Dqn_KeccakBytesToHex(void const *src, Dqn_KeccakU64 src_size, char *dest, Dqn_KeccakU64 dest_size);
// Converts a fixed amount of bytes into a hexadecimal string. Helper functions
// that call into Dqn_KeccakBytesToHex.
// return: The hexadecimal string of the bytes, null-terminated.
Dqn_KeccakString56 Dqn_KeccakBytes28ToHex(Dqn_KeccakBytes28 const *bytes);
Dqn_KeccakString64 Dqn_KeccakBytes32ToHex(Dqn_KeccakBytes32 const *bytes);
Dqn_KeccakString96 Dqn_KeccakBytes48ToHex(Dqn_KeccakBytes48 const *bytes);
Dqn_KeccakString128 Dqn_KeccakBytes64ToHex(Dqn_KeccakBytes64 const *bytes);
// Compares byte data structures for byte equality (via memcmp).
// return: 1 if the contents are equal otherwise 0.
int Dqn_KeccakBytes28Equals(Dqn_KeccakBytes28 const *a, Dqn_KeccakBytes28 const *b);
int Dqn_KeccakBytes32Equals(Dqn_KeccakBytes32 const *a, Dqn_KeccakBytes32 const *b);
int Dqn_KeccakBytes48Equals(Dqn_KeccakBytes48 const *a, Dqn_KeccakBytes48 const *b);
int Dqn_KeccakBytes64Equals(Dqn_KeccakBytes64 const *a, Dqn_KeccakBytes64 const *b);
#if defined(DQN_H) && defined(DQN_WITH_HEX)
// -----------------------------------------------------------------------------
// NOTE: Other helper functions for Dqn data structures
// -----------------------------------------------------------------------------
// Converts a 64 character hex string into the 32 byte binary representation.
// Invalid hex characters in the string will be represented as 0.
// hex: Must be exactly a 64 character hex string.
Dqn_KeccakBytes32 Dqn_KeccakHex64StringToBytes(Dqn_String8 hex);
#endif // DQN_H && DQN_WITH_HEX
#endif // DQN_KECCAK_H
#if defined(DQN_KECCAK_IMPLEMENTATION)
Dqn_KeccakU64 const DQN_KECCAK_ROUNDS[] = {
0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, 0x000000000000808B,
0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, 0x0000000000000088,
0x0000000080008009, 0x000000008000000A, 0x000000008000808B, 0x800000000000008B, 0x8000000000008089,
0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
};
Dqn_KeccakU64 const DQN_KECCAK_ROTATIONS[][5] =
{
{0, 36, 3, 41, 18},
{1, 44, 10, 45, 2},
{62, 6, 43, 15, 61},
{28, 55, 25, 21, 56},
{27, 20, 39, 8, 14},
};
#define DQN_KECCAK_ROL64(val, rotate) (((val) << (rotate)) | (((val) >> (64 - (rotate)))))
static void Dqn_Keccak__Permute(void *state)
{
// TODO(dqn): Do some profiling on unrolling and can we SIMD some part of
// this? Unroll loop, look at data dependencies and investigate.
Dqn_KeccakU64 *lanes_u64 = (Dqn_KeccakU64 *)state;
for (int round_index = 0; round_index < 24; round_index++)
{
#define LANE_INDEX(x, y) ((x) + ((y) * DQN_KECCAK_LANE_SIZE_U64))
// ---------------------------------------------------------------------
// θ step
// ---------------------------------------------------------------------
#if 1
Dqn_KeccakU64 c[DQN_KECCAK_LANE_SIZE_U64];
for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++)
{
c[x] = lanes_u64[LANE_INDEX(x, 0)] ^
lanes_u64[LANE_INDEX(x, 1)] ^
lanes_u64[LANE_INDEX(x, 2)] ^
lanes_u64[LANE_INDEX(x, 3)] ^
lanes_u64[LANE_INDEX(x, 4)];
}
Dqn_KeccakU64 d[DQN_KECCAK_LANE_SIZE_U64];
for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++)
d[x] = c[(x + 4) % DQN_KECCAK_LANE_SIZE_U64] ^ DQN_KECCAK_ROL64(c[(x + 1) % DQN_KECCAK_LANE_SIZE_U64], 1);
for (int y = 0; y < DQN_KECCAK_LANE_SIZE_U64; y++)
for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++)
lanes_u64[LANE_INDEX(x, y)] ^= d[x];
#else
Dqn_KeccakU64 c[5], d[5];
c[0] = lanes_u64[0 * 5 + 0] ^ lanes_u64[1 * 5 + 0] ^ lanes_u64[2 * 5 + 0] ^ lanes_u64[3 * 5 + 0] ^ lanes_u64[4 * 5 + 0];
c[1] = lanes_u64[0 * 5 + 1] ^ lanes_u64[1 * 5 + 1] ^ lanes_u64[2 * 5 + 1] ^ lanes_u64[3 * 5 + 1] ^ lanes_u64[4 * 5 + 1];
c[2] = lanes_u64[0 * 5 + 2] ^ lanes_u64[1 * 5 + 2] ^ lanes_u64[2 * 5 + 2] ^ lanes_u64[3 * 5 + 2] ^ lanes_u64[4 * 5 + 2];
c[3] = lanes_u64[0 * 5 + 3] ^ lanes_u64[1 * 5 + 3] ^ lanes_u64[2 * 5 + 3] ^ lanes_u64[3 * 5 + 3] ^ lanes_u64[4 * 5 + 3];
c[4] = lanes_u64[0 * 5 + 4] ^ lanes_u64[1 * 5 + 4] ^ lanes_u64[2 * 5 + 4] ^ lanes_u64[3 * 5 + 4] ^ lanes_u64[4 * 5 + 4];
d[0] = c[4] ^ DQN_KECCAK_ROL64(c[1], 1);
d[1] = c[0] ^ DQN_KECCAK_ROL64(c[2], 1);
d[2] = c[1] ^ DQN_KECCAK_ROL64(c[3], 1);
d[3] = c[2] ^ DQN_KECCAK_ROL64(c[4], 1);
d[4] = c[3] ^ DQN_KECCAK_ROL64(c[0], 1);
#endif
// ---------------------------------------------------------------------
// ρ and π steps
// ---------------------------------------------------------------------
Dqn_KeccakU64 b[DQN_KECCAK_LANE_SIZE_U64 * DQN_KECCAK_LANE_SIZE_U64];
for (int y = 0; y < DQN_KECCAK_LANE_SIZE_U64; y++)
{
for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++)
{
Dqn_KeccakU64 lane = lanes_u64[LANE_INDEX(x, y)];
Dqn_KeccakU64 rotate_count = DQN_KECCAK_ROTATIONS[x][y];
b[LANE_INDEX(y, (2 * x + 3 * y) % 5)] = DQN_KECCAK_ROL64(lane, rotate_count);
}
}
// ---------------------------------------------------------------------
// χ step
// ---------------------------------------------------------------------
for (int y = 0; y < DQN_KECCAK_LANE_SIZE_U64; y++)
{
for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++)
{
Dqn_KeccakU64 rhs = ~b[LANE_INDEX((x + 1) % 5, y)] &
b[LANE_INDEX((x + 2) % 5, y)];
lanes_u64[LANE_INDEX(x, y)] = b[LANE_INDEX(x, y)] ^ rhs;
}
}
// ---------------------------------------------------------------------
// ι step
// ---------------------------------------------------------------------
lanes_u64[LANE_INDEX(0, 0)] ^= DQN_KECCAK_ROUNDS[round_index];
#undef LANE_INDEX
#undef DQN_KECCAK_ROL64
}
}
// -----------------------------------------------------------------------------
// NOTE: Streaming API
// -----------------------------------------------------------------------------
Dqn_KeccakState Dqn_KeccakSHA3Init(bool sha3, int hash_size_bits)
{
char const SHA3_DELIMITED_SUFFIX = 0x06;
char const KECCAK_DELIMITED_SUFFIX = 0x01;
int const bitrate = 1600 - (hash_size_bits * 2);
#if defined(__cplusplus)
Dqn_KeccakState result = {};
#else
Dqn_KeccakState result = {0};
#endif
result.hash_size_bits = hash_size_bits;
result.absorb_size = bitrate / 8;
result.delimited_suffix = sha3 ? SHA3_DELIMITED_SUFFIX : KECCAK_DELIMITED_SUFFIX;
DQN_KECCAK_ASSERT(bitrate + (hash_size_bits * 2) /*capacity*/ == 1600);
return result;
}
void Dqn_KeccakUpdate(Dqn_KeccakState *keccak, void const *data, Dqn_KeccakU64 data_size)
{
Dqn_KeccakU8 *state = keccak->state;
Dqn_KeccakU8 const *ptr = (Dqn_KeccakU8 *)data;
Dqn_KeccakU64 ptr_size = data_size;
while (ptr_size > 0)
{
Dqn_KeccakU64 space = keccak->absorb_size - keccak->state_size;
int bytes_to_absorb = (int)(space < ptr_size ? space : ptr_size);
for (int index = 0; index < bytes_to_absorb; index++)
state[keccak->state_size + index] ^= ptr[index];
ptr += bytes_to_absorb;
keccak->state_size += bytes_to_absorb;
ptr_size -= bytes_to_absorb;
if (keccak->state_size >= keccak->absorb_size)
{
DQN_KECCAK_ASSERT(keccak->state_size == keccak->absorb_size);
Dqn_Keccak__Permute(state);
keccak->state_size = 0;
}
}
}
void Dqn_KeccakFinish(Dqn_KeccakState *keccak, void *dest, int dest_size)
{
DQN_KECCAK_ASSERT(dest_size >= keccak->hash_size_bits / 8);
// ---------------------------------------------------------------------
// Sponge Finalization Step: Final padding bit
// ---------------------------------------------------------------------
int const INDEX_OF_0X80_BYTE = keccak->absorb_size - 1;
int const delimited_suffix_index = keccak->state_size;
DQN_KECCAK_ASSERT(delimited_suffix_index < keccak->absorb_size);
Dqn_KeccakU8 *state = keccak->state;
state[delimited_suffix_index] ^= keccak->delimited_suffix;
// NOTE: In the reference implementation, it checks that if the
// delimited suffix is set to the padding bit (0x80), then we need to
// permute twice. Once for the delimited suffix, and a second time for
// the "padding" permute.
//
// However all standard algorithms either specify a 0x01, or 0x06, 0x04
// delimited suffix and so forth- so this case is never hit. We can omit
// this from the implementation here.
state[INDEX_OF_0X80_BYTE] ^= 0x80;
Dqn_Keccak__Permute(state);
// ---------------------------------------------------------------------
// Squeeze Step: Squeeze bytes from the state into our hash
// ---------------------------------------------------------------------
Dqn_KeccakU8 *dest_u8 = (Dqn_KeccakU8 *)dest;
int const squeeze_count = dest_size / keccak->absorb_size;
int squeeze_index = 0;
for (; squeeze_index < squeeze_count; squeeze_index++)
{
if (squeeze_index) Dqn_Keccak__Permute(state);
DQN_KECCAK_MEMCPY(dest_u8, state, keccak->absorb_size);
dest_u8 += keccak->absorb_size;
}
// ---------------------------------------------------------------------
// Squeeze Finalisation Step: Remainder bytes in hash
// ---------------------------------------------------------------------
int const remainder = dest_size % keccak->absorb_size;
if (remainder)
{
if (squeeze_index) Dqn_Keccak__Permute(state);
DQN_KECCAK_MEMCPY(dest_u8, state, remainder);
}
}
void Dqn_KeccakSHA3Hash(bool sha3, int hash_size_bits, void const *src, Dqn_KeccakU64 src_size, void *dest, int dest_size)
{
Dqn_KeccakState keccak = Dqn_KeccakSHA3Init(sha3, hash_size_bits);
Dqn_KeccakUpdate(&keccak, src, src_size);
Dqn_KeccakFinish(&keccak, dest, dest_size);
}
// -----------------------------------------------------------------------------
// NOTE: SHA3 Helpers
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_SHA3_224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size)
{
Dqn_KeccakBytes28 result;
Dqn_SHA3_224(bytes, bytes_size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes32 Dqn_SHA3_256ToBytes32(void *bytes, Dqn_KeccakU64 bytes_size)
{
Dqn_KeccakBytes32 result;
Dqn_SHA3_256(bytes, bytes_size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes48 Dqn_SHA3_384ToBytes48(void *bytes, Dqn_KeccakU64 bytes_size)
{
Dqn_KeccakBytes48 result;
Dqn_SHA3_384(bytes, bytes_size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes64 Dqn_SHA3_512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size)
{
Dqn_KeccakBytes64 result;
Dqn_SHA3_512(bytes, bytes_size, result.data, sizeof(result));
return result;
}
// -----------------------------------------------------------------------------
// NOTE: Keccak Helpers
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_Keccak224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size)
{
Dqn_KeccakBytes28 result;
Dqn_Keccak224(bytes, bytes_size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes32 Dqn_Keccak256ToBytes32(void *bytes, Dqn_KeccakU64 bytes_size)
{
Dqn_KeccakBytes32 result;
Dqn_Keccak256(bytes, bytes_size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes48 Dqn_Keccak384ToBytes48(void *bytes, Dqn_KeccakU64 bytes_size)
{
Dqn_KeccakBytes48 result;
Dqn_Keccak384(bytes, bytes_size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes64 Dqn_Keccak512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size)
{
Dqn_KeccakBytes64 result;
Dqn_Keccak512(bytes, bytes_size, result.data, sizeof(result));
return result;
}
#if defined(DQN_H)
// -----------------------------------------------------------------------------
// NOTE: SHA3 - Helpers for Dqn data structures
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_SHA3_224StringToBytes28(Dqn_String8 string)
{
Dqn_KeccakBytes28 result;
Dqn_SHA3_224(string.data, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes32 Dqn_SHA3_256StringToBytes32(Dqn_String8 string)
{
Dqn_KeccakBytes32 result;
Dqn_SHA3_256(string.data, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes48 Dqn_SHA3_384StringToBytes48(Dqn_String8 string)
{
Dqn_KeccakBytes48 result;
Dqn_SHA3_384(string.data, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes64 Dqn_SHA3_512StringToBytes64(Dqn_String8 string)
{
Dqn_KeccakBytes64 result;
Dqn_SHA3_512(string.data, string.size, result.data, sizeof(result));
return result;
}
#endif // DQN_H
#if defined(DQN_H)
// -----------------------------------------------------------------------------
// NOTE: Keccak - Helpers for Dqn data structures
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_Keccak224StringToBytes28(Dqn_String8 string)
{
Dqn_KeccakBytes28 result;
Dqn_Keccak224(string.data, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes32 Dqn_Keccak256StringToBytes32(Dqn_String8 string)
{
Dqn_KeccakBytes32 result;
Dqn_Keccak256(string.data, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes48 Dqn_Keccak384StringToBytes48(Dqn_String8 string)
{
Dqn_KeccakBytes48 result;
Dqn_Keccak384(string.data, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes64 Dqn_Keccak512StringToBytes64(Dqn_String8 string)
{
Dqn_KeccakBytes64 result;
Dqn_Keccak512(string.data, string.size, result.data, sizeof(result));
return result;
}
#endif // DQN_H
// -----------------------------------------------------------------------------
// NOTE: Helper functions
// -----------------------------------------------------------------------------
void Dqn_KeccakBytesToHex(void const *src, Dqn_KeccakU64 src_size, char *dest, Dqn_KeccakU64 dest_size)
{
(void)src_size; (void)dest_size;
DQN_KECCAK_ASSERT(dest_size >= src_size * 2);
unsigned char *src_u8 = (unsigned char *)src;
for (Dqn_KeccakU64 src_index = 0, dest_index = 0;
src_index < src_size;
src_index += 1, dest_index += 2)
{
char byte = src_u8[src_index];
char hex01 = (byte >> 4) & 0b1111;
char hex02 = (byte >> 0) & 0b1111;
dest[dest_index + 0] = hex01 < 10 ? (hex01 + '0') : (hex01 - 10) + 'a';
dest[dest_index + 1] = hex02 < 10 ? (hex02 + '0') : (hex02 - 10) + 'a';
}
}
Dqn_KeccakString56 Dqn_KeccakBytes28ToHex(Dqn_KeccakBytes28 const *bytes)
{
Dqn_KeccakString56 result;
Dqn_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
result.data[sizeof(result.data) - 1] = 0;
return result;
}
Dqn_KeccakString64 Dqn_KeccakBytes32ToHex(Dqn_KeccakBytes32 const *bytes)
{
Dqn_KeccakString64 result;
Dqn_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
result.data[sizeof(result.data) - 1] = 0;
return result;
}
Dqn_KeccakString96 Dqn_KeccakBytes48ToHex(Dqn_KeccakBytes48 const *bytes)
{
Dqn_KeccakString96 result;
Dqn_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
result.data[sizeof(result.data) - 1] = 0;
return result;
}
Dqn_KeccakString128 Dqn_KeccakBytes64ToHex(Dqn_KeccakBytes64 const *bytes)
{
Dqn_KeccakString128 result;
Dqn_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
result.data[sizeof(result.data) - 1] = 0;
return result;
}
int Dqn_KeccakBytes28Equals(Dqn_KeccakBytes28 const *a, Dqn_KeccakBytes28 const *b)
{
int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
return result;
}
int Dqn_KeccakBytes32Equals(Dqn_KeccakBytes32 const *a, Dqn_KeccakBytes32 const *b)
{
int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
return result;
}
int Dqn_KeccakBytes48Equals(Dqn_KeccakBytes48 const *a, Dqn_KeccakBytes48 const *b)
{
int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
return result;
}
int Dqn_KeccakBytes64Equals(Dqn_KeccakBytes64 const *a, Dqn_KeccakBytes64 const *b)
{
int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
return result;
}
#if defined(DQN_H) && defined(DQN_WITH_HEX)
// -----------------------------------------------------------------------------
// NOTE: Other helper functions for Dqn data structures
// -----------------------------------------------------------------------------
Dqn_KeccakBytes32 Dqn_KeccakHex64StringToBytes(Dqn_String8 hex)
{
DQN_KECCAK_ASSERT(hex.size == 64);
Dqn_KeccakBytes32 result;
Dqn_Hex_CString8ToByteBuffer(hex.data, hex.size, result.data, sizeof(result));
return result;
}
#endif // DQN_H && DQN_WITH_HEX
#endif // DQN_KECCAK_IMPLEMENTATION
+100
View File
@@ -0,0 +1,100 @@
#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
File diff suppressed because it is too large Load Diff
+215
View File
@@ -0,0 +1,215 @@
#if !defined(DQN_UTEST_H)
#define DQN_UTEST_H
//
// NOTE: Overview
// -----------------------------------------------------------------------------
// A super minimal testing framework, most of the logic here is the pretty
// printing of test results.
// NOTE: Configuration
// -----------------------------------------------------------------------------
// #define DQN_UTEST_IMPLEMENTATION
// Define this in one and only one C++ file to enable the implementation
// code of the header file. This will also automatically enable the JSMN
// implementation.
//
// #define DQN_UTEST_RESULT_LPAD
// Define this to a number to specify how much to pad the output of the test
// result line before the test result is printed.
//
// #define DQN_UTEST_RESULT_PAD_CHAR
// Define this to a character to specify the default character to use for
// padding. By default this is '.'
//
// #define DQN_UTEST_SPACING
// Define this to a number to specify the number of spaces between the group
// declaration and the test output in the group.
//
// #define DQN_UTEST_BAD_COLOR
// Define this to a terminal color code to specify what color errors will be
// presented as.
//
// #define DQN_UTEST_GOOD_COLOR
// Define this to a terminal color code to specify what color sucess will be
// presented as.
// NOTE: Macros
// -----------------------------------------------------------------------------
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#if !defined(DQN_UTEST_RESULT_LPAD)
#define DQN_UTEST_RESULT_LPAD 90
#endif
#if !defined(DQN_UTEST_RESULT_PAD_CHAR)
#define DQN_UTEST_RESULT_PAD_CHAR '.'
#endif
#if !defined(DQN_UTEST_SPACING)
#define DQN_UTEST_SPACING 2
#endif
#if !defined(DQN_UTEST_BAD_COLOR)
#define DQN_UTEST_BAD_COLOR "\x1b[31m"
#endif
#if !defined(DQN_UTEST_GOOD_COLOR)
#define DQN_UTEST_GOOD_COLOR "\x1b[32m"
#endif
#define DQN_UTEST_COLOR_RESET "\x1b[0m"
#define DQN_UTEST_GROUP(test, fmt, ...) \
for (Dqn_UTest *test_var_ = (printf(fmt "\n", ## __VA_ARGS__), &test); \
test_var_ != nullptr; \
Dqn_UTest_PrintStats(&test), test_var_ = nullptr)
#define DQN_UTEST_TEST(fmt, ...) \
for (int dummy_ = (Dqn_UTest_Begin(test_var_, fmt, ## __VA_ARGS__), 0); \
(void)dummy_, test_var_->state == Dqn_UTestState_TestBegun; \
Dqn_UTest_End(test_var_))
#define DQN_UTEST_ASSERTF(test, expr, fmt, ...) \
DQN_UTEST_ASSERTF_AT((test), __FILE__, __LINE__, (expr), fmt, ##__VA_ARGS__)
#define DQN_UTEST_ASSERT(test, expr) \
DQN_UTEST_ASSERT_AT((test), __FILE__, __LINE__, (expr))
// TODO: Fix the logs. They print before the tests, we should accumulate logs
// per test, then, dump them on test on. But to do this nicely without crappy
// mem management we need to implement an arena.
#define DQN_UTEST_LOG(fmt, ...) \
fprintf(stdout, "%*s" fmt "\n", DQN_UTEST_SPACING * 2, "", ##__VA_ARGS__)
#define DQN_UTEST_ASSERTF_AT(test, file, line, expr, fmt, ...) \
do { \
if (!(expr)) { \
(test)->state = Dqn_UTestState_TestFailed; \
fprintf(stderr, \
"%*sAssertion Triggered\n" \
"%*sFile: %s:%d\n" \
"%*sExpression: [" #expr "]\n" \
"%*sReason: " fmt "\n", \
DQN_UTEST_SPACING * 2, \
"", \
DQN_UTEST_SPACING * 3, \
"", \
file, \
line, \
DQN_UTEST_SPACING * 3, \
"", \
DQN_UTEST_SPACING * 3, \
"", \
##__VA_ARGS__); \
} \
} while (0)
#define DQN_UTEST_ASSERT_AT(test, file, line, expr) \
do { \
if (!(expr)) { \
(test)->state = Dqn_UTestState_TestFailed; \
fprintf(stderr, \
"%*sFile: %s:%d\n" \
"%*sExpression: [" #expr "]\n", \
DQN_UTEST_SPACING * 2, \
"", \
file, \
line, \
DQN_UTEST_SPACING * 2, \
""); \
} \
} while (0)
// NOTE: Header
// -----------------------------------------------------------------------------
typedef enum Dqn_UTestState {
Dqn_UTestState_Nil,
Dqn_UTestState_TestBegun,
Dqn_UTestState_TestFailed,
} Dqn_UTestState;
typedef struct Dqn_UTest {
int num_tests_in_group;
int num_tests_ok_in_group;
Dqn_UTestState state;
bool finished;
char name[1024];
size_t name_size;
} Dqn_UTest;
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);
// NOTE: Implementation
// -----------------------------------------------------------------------------
#if defined(DQN_UTEST_IMPLEMENTATION)
void Dqn_UTest_PrintStats(Dqn_UTest *test)
{
if (test->finished)
return;
test->finished = true;
bool all_clear = test->num_tests_ok_in_group == test->num_tests_in_group;
fprintf(stdout,
"%s\n %02d/%02d tests passed -- %s\n\n" DQN_UTEST_COLOR_RESET,
all_clear ? DQN_UTEST_GOOD_COLOR : DQN_UTEST_BAD_COLOR,
test->num_tests_ok_in_group,
test->num_tests_in_group,
all_clear ? "OK" : "FAILED");
}
void Dqn_UTest_BeginV(Dqn_UTest *test, char const *fmt, va_list args)
{
assert(test->state == Dqn_UTestState_Nil &&
"Nesting a unit test within another unit test is not allowed, ensure"
"the first test has finished by calling Dqn_UTest_End");
test->num_tests_in_group++;
test->state = Dqn_UTestState_TestBegun;
test->name_size = 0;
{
va_list args_copy;
va_copy(args_copy, args);
test->name_size = vsnprintf(NULL, 0, fmt, args_copy);
va_end(args_copy);
}
assert(test->name_size < sizeof(test->name));
vsnprintf(test->name, sizeof(test->name), fmt, args);
}
void Dqn_UTest_Begin(Dqn_UTest *test, char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_UTest_BeginV(test, fmt, args);
va_end(args);
}
void Dqn_UTest_End(Dqn_UTest *test)
{
assert(test->state != Dqn_UTestState_Nil && "Test was marked as ended but a test was never commenced using Dqn_UTest_Begin");
int pad_size = DQN_UTEST_RESULT_LPAD - (DQN_UTEST_SPACING + test->name_size);
if (pad_size < 0)
pad_size = 0;
char pad_buffer[DQN_UTEST_RESULT_LPAD] = {};
memset(pad_buffer, DQN_UTEST_RESULT_PAD_CHAR, pad_size);
printf("%*s%.*s%.*s", DQN_UTEST_SPACING, "", (int)test->name_size, test->name, pad_size, pad_buffer);
if (test->state == Dqn_UTestState_TestFailed) {
printf(DQN_UTEST_BAD_COLOR " FAILED");
} else {
printf(DQN_UTEST_GOOD_COLOR " OK");
test->num_tests_ok_in_group++;
}
printf(DQN_UTEST_COLOR_RESET "\n");
test->state = Dqn_UTestState_Nil;
}
#endif // DQN_UTEST_IMPLEMENTATION
#endif // DQN_UTEST_H