Add experimental win32 http, tester lib

This commit is contained in:
doyle 2021-12-17 00:07:33 +11:00
parent b9e91b5497
commit 05fe604f5c
8 changed files with 1573 additions and 2258 deletions

Binary file not shown.

758
dqn.h

File diff suppressed because it is too large Load Diff

View File

@ -17,58 +17,58 @@ struct Dqn_CppFile
bool append_extra_new_line; bool append_extra_new_line;
}; };
int Dqn_CppFile_SpacePerIndent(Dqn_CppFile *cpp); int Dqn_CppFSpacePerIndent(Dqn_CppFile *cpp);
void Dqn_CppFile_LineBeginV (Dqn_CppFile *cpp, char const *fmt, va_list args); void Dqn_CppFLineBeginV (Dqn_CppFile *cpp, char const *fmt, va_list args);
void Dqn_CppFile_LineBegin (Dqn_CppFile *cpp, char const *fmt, ...); void Dqn_CppFLineBegin (Dqn_CppFile *cpp, char const *fmt, ...);
void Dqn_CppFile_LineEnd (Dqn_CppFile *cpp, char const *fmt, ...); void Dqn_CppFLineEnd (Dqn_CppFile *cpp, char const *fmt, ...);
void Dqn_CppFile_LineAdd (Dqn_CppFile *cpp, char const *fmt, ...); void Dqn_CppFLineAdd (Dqn_CppFile *cpp, char const *fmt, ...);
void Dqn_CppFile_LineV (Dqn_CppFile *cpp, char const *fmt, va_list args); void Dqn_CppFLineV (Dqn_CppFile *cpp, char const *fmt, va_list args);
void Dqn_CppFile_Line (Dqn_CppFile *cpp, char const *fmt, ...); void Dqn_CppFLine (Dqn_CppFile *cpp, char const *fmt, ...);
void Dqn_CppFile_NewLine (Dqn_CppFile *cpp); void Dqn_CppFNewLine (Dqn_CppFile *cpp);
void Dqn_CppFile_Indent (Dqn_CppFile *cpp); void Dqn_CppFIndent (Dqn_CppFile *cpp);
void Dqn_CppFile_Unindent (Dqn_CppFile *cpp); void Dqn_CppFUnindent (Dqn_CppFile *cpp);
// fmt: (Optional) The format string to print at the beginning of the block. // fmt: (Optional) The format string to print at the beginning of the block.
// When the fmt string is given, it will place a new-line at the end of the fmt // When the fmt string is given, it will place a new-line at the end of the fmt
// string. When fmt is nullptr, no new line will be appended. // string. When fmt is nullptr, no new line will be appended.
void Dqn_CppFile_BeginBlock (Dqn_CppFile *cpp, char const *fmt, ...); void Dqn_CppFBeginBlock (Dqn_CppFile *cpp, char const *fmt, ...);
void Dqn_CppFile_EndBlock (Dqn_CppFile *cpp, bool trailing_semicolon, bool new_line_on_next_block); void Dqn_CppFEndBlock (Dqn_CppFile *cpp, bool trailing_semicolon, bool new_line_on_next_block);
#define Dqn_CppFile_EndEnumBlock(cpp) Dqn_CppFile_EndBlock(cpp, true /*trailing_semicolon*/, true /*new_line_on_next_block*/) #define Dqn_CppFEndEnumBlock(cpp) Dqn_CppFEndBlock(cpp, true /*trailing_semicolon*/, true /*new_line_on_next_block*/)
#define Dqn_CppFile_EndForBlock(cpp) Dqn_CppFile_EndBlock(cpp, false /*trailing_semicolon*/, false /*new_line_on_next_block*/) #define Dqn_CppFEndForBlock(cpp) Dqn_CppFEndBlock(cpp, false /*trailing_semicolon*/, false /*new_line_on_next_block*/)
#define Dqn_CppFile_EndIfBlock(cpp) Dqn_CppFile_EndBlock(cpp, false /*trailing_semicolon*/, false /*new_line_on_next_block*/) #define Dqn_CppFEndIfBlock(cpp) Dqn_CppFEndBlock(cpp, false /*trailing_semicolon*/, false /*new_line_on_next_block*/)
#define Dqn_CppFile_EndFuncBlock(cpp) Dqn_CppFile_EndBlock(cpp, false /*trailing_semicolon*/, true /*new_line_on_next_block*/) #define Dqn_CppFEndFuncBlock(cpp) Dqn_CppFEndBlock(cpp, false /*trailing_semicolon*/, true /*new_line_on_next_block*/)
#define Dqn_CppFile_EndStructBlock(cpp) Dqn_CppFile_EndBlock(cpp, true /*trailing_semicolon*/, true /*new_line_on_next_block*/) #define Dqn_CppFEndStructBlock(cpp) Dqn_CppFEndBlock(cpp, true /*trailing_semicolon*/, true /*new_line_on_next_block*/)
#endif // DQN_CPP_FILE_H #endif // DQN_CPP_FILE_H
#if defined(DQN_CPP_FILE_IMPLEMENTATION) #if defined(DQN_CPP_FILE_IMPLEMENTATION)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// NOTE: Dqn_CppFile Implementation // NOTE: Dqn_CppFile Implementation
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
int Dqn_CppFile_SpacePerIndent(Dqn_CppFile *cpp) int Dqn_CppFSpacePerIndent(Dqn_CppFile *cpp)
{ {
int result = cpp->space_per_indent == 0 ? 4 : cpp->space_per_indent; int result = cpp->space_per_indent == 0 ? 4 : cpp->space_per_indent;
return result; return result;
} }
void Dqn_CppFile_LineBeginV(Dqn_CppFile *cpp, char const *fmt, va_list args) void Dqn_CppFLineBeginV(Dqn_CppFile *cpp, char const *fmt, va_list args)
{ {
int spaces = cpp->indent * Dqn_CppFile_SpacePerIndent(cpp); int spaces = cpp->indent * Dqn_CppFSpacePerIndent(cpp);
fprintf(cpp->file, "%*s", spaces, ""); fprintf(cpp->file, "%*s", spaces, "");
vfprintf(cpp->file, fmt, args); vfprintf(cpp->file, fmt, args);
} }
void Dqn_CppFile_LineBegin(Dqn_CppFile *cpp, char const *fmt, ...) void Dqn_CppFLineBegin(Dqn_CppFile *cpp, char const *fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
Dqn_CppFile_LineBeginV(cpp, fmt, args); Dqn_CppFLineBeginV(cpp, fmt, args);
va_end(args); va_end(args);
} }
void Dqn_CppFile_LineEnd(Dqn_CppFile *cpp, char const *fmt, ...) void Dqn_CppFLineEnd(Dqn_CppFile *cpp, char const *fmt, ...)
{ {
if (fmt) if (fmt)
{ {
@ -81,7 +81,7 @@ void Dqn_CppFile_LineEnd(Dqn_CppFile *cpp, char const *fmt, ...)
fputc('\n', cpp->file); fputc('\n', cpp->file);
} }
void Dqn_CppFile_LineAdd(Dqn_CppFile *cpp, char const *fmt, ...) void Dqn_CppFLineAdd(Dqn_CppFile *cpp, char const *fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
@ -89,55 +89,55 @@ void Dqn_CppFile_LineAdd(Dqn_CppFile *cpp, char const *fmt, ...)
va_end(args); va_end(args);
} }
void Dqn_CppFile_LineV(Dqn_CppFile *cpp, char const *fmt, va_list args) void Dqn_CppFLineV(Dqn_CppFile *cpp, char const *fmt, va_list args)
{ {
Dqn_CppFile_LineBeginV(cpp, fmt, args); Dqn_CppFLineBeginV(cpp, fmt, args);
Dqn_CppFile_LineEnd(cpp, nullptr); Dqn_CppFLineEnd(cpp, nullptr);
} }
void Dqn_CppFile_Line(Dqn_CppFile *cpp, char const *fmt, ...) void Dqn_CppFLine(Dqn_CppFile *cpp, char const *fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
Dqn_CppFile_LineBeginV(cpp, fmt, args); Dqn_CppFLineBeginV(cpp, fmt, args);
Dqn_CppFile_LineEnd(cpp, nullptr); Dqn_CppFLineEnd(cpp, nullptr);
va_end(args); va_end(args);
} }
void Dqn_CppFile_NewLine(Dqn_CppFile *cpp) void Dqn_CppFNewLine(Dqn_CppFile *cpp)
{ {
fputc('\n', cpp->file); fputc('\n', cpp->file);
} }
void Dqn_CppFile_Indent(Dqn_CppFile *cpp) void Dqn_CppFIndent(Dqn_CppFile *cpp)
{ {
cpp->indent++; cpp->indent++;
} }
void Dqn_CppFile_Unindent(Dqn_CppFile *cpp) void Dqn_CppFUnindent(Dqn_CppFile *cpp)
{ {
cpp->indent--; cpp->indent--;
DQN_CPPF_ASSERT(cpp->indent >= 0); DQN_CPPF_ASSERT(cpp->indent >= 0);
} }
void Dqn_CppFile_BeginBlock(Dqn_CppFile *cpp, char const *fmt, ...) void Dqn_CppFBeginBlock(Dqn_CppFile *cpp, char const *fmt, ...)
{ {
if (fmt) if (fmt)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
Dqn_CppFile_LineV(cpp, fmt, args); Dqn_CppFLineV(cpp, fmt, args);
va_end(args); va_end(args);
} }
Dqn_CppFile_Line(cpp, "{"); Dqn_CppFLine(cpp, "{");
Dqn_CppFile_Indent(cpp); Dqn_CppFIndent(cpp);
} }
void Dqn_CppFile_EndBlock(Dqn_CppFile *cpp, bool trailing_semicolon, bool new_line_on_next_block) void Dqn_CppFEndBlock(Dqn_CppFile *cpp, bool trailing_semicolon, bool new_line_on_next_block)
{ {
Dqn_CppFile_Unindent(cpp); Dqn_CppFUnindent(cpp);
Dqn_CppFile_Line(cpp, trailing_semicolon ? "};" : "}"); Dqn_CppFLine(cpp, trailing_semicolon ? "};" : "}");
if (new_line_on_next_block) fputc('\n', cpp->file); if (new_line_on_next_block) fputc('\n', cpp->file);
} }
#endif // DQN_CPP_FILE_IMPLEMENTATION #endif // DQN_CPP_FILE_IMPLEMENTATION

View File

@ -14,7 +14,48 @@
// code of the header file. This will also automatically enable the JSMN // code of the header file. This will also automatically enable the JSMN
// implementation. // implementation.
// //
#include <assert.h> // #define DQN_JSMN_NO_CRT
// Define this macro to disable functions using the CRT where possible. JSMN
// itself includes <stddef.h> and this is the only dependency.
//
#if !defined(DQN_JSMN_NO_CRT)
#include <stdio.h>
#include <stdlib.h>
#endif
#if defined(__cplusplus)
#define DQN_JSMN_CLITERAL(Type) Type
#define DQN_JSMN_ZERO_INIT {}
#else
#define DQN_JSMN_CLITERAL(Type) (Type)
#define DQN_JSMN_ZERO_INIT {0}
#include <stdbool.h>
#endif
#if !defined(DQN_JSMN_DEBUG_BREAK)
#include <signal.h>
#if defined(NDEBUG)
#define DQN_JSMN_DEBUG_BREAK
#else
#if defined(_WIN32)
#define DQN_JSMN_DEBUG_BREAK __debugbreak()
#else
#define DQN_JSMN_DEBUG_BREAK raise(SIGTRAP)
#endif
#endif
#endif
#if !defined(DQN_JSMN_ASSERT)
#define DQN_JSMN_ASSERT(expr) \
do \
{ \
if (!(expr)) \
{ \
DQN_JSMN_DEBUG_BREAK; \
} \
} while (0)
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// NOTE: JSMN Configuration // NOTE: JSMN Configuration
@ -242,69 +283,78 @@ int jsmn_iterator_next( jsmn_iterator_t *iterator, jsmntok_t **jsmn_identifier,
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Header File // Header File
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#define DQN_JSMN_STRING(string) Dqn_JsmnString{(string), sizeof(string) - 1} #if defined(_WIN32)
struct Dqn_JsmnString typedef unsigned __int64 Dqn_JsmnU64;
{ typedef signed __int64 Dqn_JsmnI64;
union { #else
char * str; typedef unsigned long long Dqn_JsmnU64;
char const *const_str; typedef signed long long Dqn_JsmnI64;
}; #endif
int size;
};
bool Dqn_Jsmn_StringEq(Dqn_JsmnString lhs, Dqn_JsmnString rhs); typedef struct Dqn_JsmnString
bool Dqn_Jsmn_StringIsValid(Dqn_JsmnString string); {
bool Dqn_Jsmn_IsDigit(char ch); union Dqn_JsmnStringBuffer { char const *const_str; char *str; } buf;
uint64_t Dqn_Jsmn_StringToU64(Dqn_JsmnString string); Dqn_JsmnI64 size;
} Dqn_JsmnString;
#define DQN_JSMN_STRING(string) DQN_JSMN_CLITERAL(Dqn_JsmnString){{(string)}, sizeof(string) - 1}
#define DQN_JSMN_STRING_FMT(string) (int)((string).size), (string).buf.str
bool Dqn_JsmnStringEq(Dqn_JsmnString lhs, Dqn_JsmnString rhs);
bool Dqn_JsmnStringIsValid(Dqn_JsmnString string);
bool Dqn_JsmnIsDigit(char ch);
Dqn_JsmnU64 Dqn_JsmnStringToU64(Dqn_JsmnString string);
#if defined(__cplusplus)
bool operator==(Dqn_JsmnString lhs, Dqn_JsmnString rhs); bool operator==(Dqn_JsmnString lhs, Dqn_JsmnString rhs);
#endif // __cplusplus
#define DQN_JSMN_X_MACRO \ #define DQN_JSMN_X_MACRO \
DQN_JSMN_X_ENTRY(Invalid) \
DQN_JSMN_X_ENTRY(Object) \ DQN_JSMN_X_ENTRY(Object) \
DQN_JSMN_X_ENTRY(Array) \ DQN_JSMN_X_ENTRY(Array) \
DQN_JSMN_X_ENTRY(String) \ DQN_JSMN_X_ENTRY(String) \
DQN_JSMN_X_ENTRY(Number) \ DQN_JSMN_X_ENTRY(Number) \
DQN_JSMN_X_ENTRY(Bool) DQN_JSMN_X_ENTRY(Bool)
enum Dqn_JsmnTokenIs typedef enum Dqn_JsmnTokenIs
{ {
#define DQN_JSMN_X_ENTRY(enum_val) Dqn_JsmnTokenIs_##enum_val, #define DQN_JSMN_X_ENTRY(enum_val) Dqn_JsmnTokenIs_##enum_val,
DQN_JSMN_X_MACRO DQN_JSMN_X_MACRO
#undef DQN_JSMN_X_ENTRY #undef DQN_JSMN_X_ENTRY
}; } Dqn_JsmnTokenIs;
Dqn_JsmnString const Dqn_Jsmn_TokenIsToString[] typedef struct Dqn_JsmnError
{
#define DQN_JSMN_X_ENTRY(enum_val) DQN_JSMN_STRING(#enum_val),
DQN_JSMN_X_MACRO
#undef DQN_JSMN_X_ENTRY
};
struct Dqn_JsmnError
{ {
jsmntok_t token; jsmntok_t token;
Dqn_JsmnString json; Dqn_JsmnString json;
Dqn_JsmnTokenIs actual;
Dqn_JsmnTokenIs expected; Dqn_JsmnTokenIs expected;
char const *cpp_file; // The file of the .cpp/h source code that triggered the error char const *cpp_file; // The file of the .cpp/h source code that triggered the error
int cpp_line; // The line of the .cpp/h source code that triggered the error int cpp_line; // The line of the .cpp/h source code that triggered the error
}; } Dqn_JsmnError;
#if !defined(DQN_JSMN_ERROR_HANDLE_SIZE)
#define DQN_JSMN_ERROR_HANDLE_SIZE 128 #define DQN_JSMN_ERROR_HANDLE_SIZE 128
struct Dqn_JsmnErrorHandle #endif
typedef struct Dqn_JsmnErrorHandle
{ {
Dqn_JsmnError errors[DQN_JSMN_ERROR_HANDLE_SIZE]; Dqn_JsmnError errors[DQN_JSMN_ERROR_HANDLE_SIZE];
int errors_size; int errors_size;
}; } Dqn_JsmnErrorHandle;
struct Dqn_Jsmn typedef struct Dqn_Jsmn
{ {
bool valid; bool valid;
jsmn_parser parser; jsmn_parser parser;
Dqn_JsmnString json; Dqn_JsmnString json;
int tokens_size; bool json_needs_crt_free;
unsigned int tokens_size;
jsmntok_t *tokens; jsmntok_t *tokens;
}; } Dqn_Jsmn;
struct Dqn_JsmnIterator typedef struct Dqn_JsmnIterator
{ {
bool init; bool init;
jsmn_iterator_t jsmn_it; jsmn_iterator_t jsmn_it;
@ -316,217 +366,316 @@ struct Dqn_JsmnIterator
// parent iterator so that it knows where to continue off from and to skip // parent iterator so that it knows where to continue off from and to skip
// over the object/array we just iterated. // over the object/array we just iterated.
int token_index_hint; int token_index_hint;
}; } Dqn_JsmnIterator;
// Calculate the number of tokens required to parse the 'json' input. // Calculate the number of tokens required to parse the 'json' input.
int Dqn_Jsmn_TokensRequired(char const *json, int size); unsigned int Dqn_JsmnTokensRequired(char const *json, Dqn_JsmnI64 size);
Dqn_JsmnString Dqn_JsmnTokenIsToString(Dqn_JsmnTokenIs enum_val);
// To initialise successfully, call this function with the 'jsmn' parameter // Initialise a Dqn_Jsmn context by passing the JSON string and pass in a token
// set with the 'jsmn->tokens' and 'jsmn->tokens_size' fields set to a valid // array sufficient to parse the JSON string. The number of tokens that must be
// destination buffer with a sufficient size that will be written on completion // allocated and passed in can be calculated using Dqn_JsmnTokensRequire(...).
// of the function. The required amount of tokens can be calculated using Dqn_Jsmn Dqn_JsmnInitWithJSONCString(char const *json, Dqn_JsmnI64 size, jsmntok_t *tokens, unsigned int tokens_size);
// Dqn_Jsmn_TokensRequired. Dqn_Jsmn Dqn_JsmnInitWithJSONString(Dqn_JsmnString json, jsmntok_t *tokens, unsigned int tokens_size);
//
// The function *does* not validate that the 'jsmn->tokens_size' is sufficient #if !defined(DQN_JSMN_NO_CRT)
// to hold the tokens in release mode. Dqn_Jsmn Dqn_JsmnInitWithMallocJSONCString(char const *json, Dqn_JsmnI64 size);
// Dqn_Jsmn Dqn_JsmnInitWithMallocJSON(Dqn_JsmnString json);
// return: False if any of the parameters are invalid or the 'jsmn' tokens or Dqn_Jsmn Dqn_JsmnInitWithMallocJSONFile(Dqn_JsmnString json_file);
// size are not set, otherwise true. Additionally, 'jsmn->valid' is set void Dqn_JsmnFree(Dqn_Jsmn *jsmn);
// accordingly to match the result of initialisation. void Dqn_JsmnErrorHandleDumpToCRTFile(Dqn_JsmnErrorHandle const *handle, FILE *file);
bool Dqn_Jsmn_InitWithJSONCString(char const *json, int size, Dqn_Jsmn *jsmn); void Dqn_JsmnErrorDumpToCRTFile(Dqn_JsmnError const *error, FILE *file);
#endif // !DQN_JSMN_NO_CRT
#if defined(DQN_H) #if defined(DQN_H)
Dqn_Jsmn Dqn_Jsmn_InitWithJSON(Dqn_JsmnString json, Dqn_ArenaAllocator *arena); Dqn_Jsmn Dqn_JsmnInitWithArenaJSONCString(char const *json, Dqn_JsmnI64 size, Dqn_Arena *arena);
Dqn_Jsmn Dqn_Jsmn_InitWithJSONFile(Dqn_JsmnString file, Dqn_ArenaAllocator *arena); Dqn_Jsmn Dqn_JsmnInitWithArenaJSON(Dqn_JsmnString json, Dqn_Arena *arena);
Dqn_Jsmn Dqn_JsmnInitWithArenaJSONFile(Dqn_JsmnString file, Dqn_Arena *arena);
#endif // DQN_H #endif // DQN_H
// return: If the token is an array, return the size of the array otherwise -1. // return: If the token is an array, return the size of the array otherwise -1.
int Dqn_Jsmn_TokenArraySize(jsmntok_t token); int Dqn_JsmnTokenArraySize(jsmntok_t token);
Dqn_JsmnString Dqn_Jsmn_TokenString(jsmntok_t token, Dqn_JsmnString json); Dqn_JsmnString Dqn_JsmnTokenString(jsmntok_t token, Dqn_JsmnString json);
bool Dqn_Jsmn_TokenBool(jsmntok_t token, Dqn_JsmnString json); bool Dqn_JsmnTokenBool(jsmntok_t token, Dqn_JsmnString json);
uint64_t Dqn_Jsmn_TokenU64(jsmntok_t token, Dqn_JsmnString json); Dqn_JsmnU64 Dqn_JsmnTokenU64(jsmntok_t token, Dqn_JsmnString json);
// Iterator abstraction over jsmn_iterator_t, example on how to use this is // Iterator abstraction over jsmn_iterator_t, example on how to use this is
// shown below. The goal here is to minimise the amount of state the user has to // shown below. The goal here is to minimise the amount of state the user has to
// manage. // manage.
#if 0 #if 0
Dqn_ArenaAllocator arena = {}; Dqn_JsmnString json = DQN_JSMN_STRING("{ \"test\": { \"test2\": 0 } }");
Dqn_JsmnString json = DQN_STRING(R"({ Dqn_Jsmn jsmn_state = Dqn_JsmnInitWithMallocJSON(json);
"test": { for (Dqn_JsmnIterator it = DQN_JSMN_ZERO_INIT; Dqn_JsmnIteratorNext(&it, &jsmn_state, NULL /*prev_it*/); )
"test2": 0
}
})");
Dqn_Jsmn jsmn_state = Dqn_Jsmn_InitWithJSON(json, &arena);
for (Dqn_JsmnIterator it = {}; Dqn_Jsmn_IteratorNext(&it, &jsmn_state, nullptr /*prev_it*/); )
{ {
Dqn_JsmnString key = Dqn_JsmnITerator_Key(&it); Dqn_JsmnString key = Dqn_JsmnIteratorKey(&it);
if (key == DQN_STRING("test")) if (Dqn_JsmnStringEq(key, DQN_JSMN_STRING("test")))
{ {
if (!Dqn_Jsmn_IteratorExpectValue(&it, Dqn_JsmnTokenIs_Object, nullptr)) if (!Dqn_JsmnIteratorExpectValue(&it, Dqn_JsmnTokenIs_Object, NULL))
continue; continue;
for (Dqn_JsmnIterator obj_it = {}; Dqn_Jsmn_IteratorNext(&obj_it, &jsmn_state, &it); ) for (Dqn_JsmnIterator obj_it = DQN_JSMN_ZERO_INIT; Dqn_JsmnIteratorNext(&obj_it, &jsmn_state, &it); )
{ {
Dqn_JsmnString obj_key = Dqn_JsmnITerator_Key(&obj_it); Dqn_JsmnString obj_key = Dqn_JsmnIteratorKey(&obj_it);
if (obj_key == DQN_STRING("test2")) if (Dqn_JsmnStringEq(obj_key, DQN_JSMN_STRING("test2")))
{ {
if (!Dqn_Jsmn_IteratorExpectValue(&obj_it, Dqn_JsmnTokenIs_Number, nullptr)) if (!Dqn_JsmnIteratorExpectValue(&obj_it, Dqn_JsmnTokenIs_Number, NULL))
continue; continue;
uint64_t test_2_value = Dqn_Jsmn_IteratorU64(&obj_it); Dqn_JsmnU64 test_2_value = Dqn_JsmnIteratorU64(&obj_it);
(void)test_2_value;
} }
} }
} }
} }
#endif #endif
bool Dqn_Jsmn_IteratorNext(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnIterator *prev_it); bool Dqn_JsmnIteratorNext(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnIterator *prev_it);
Dqn_JsmnString Dqn_Jsmn_IteratorKey(Dqn_JsmnIterator *it); Dqn_JsmnString Dqn_JsmnIteratorKey(Dqn_JsmnIterator *it);
Dqn_JsmnIterator Dqn_Jsmn_IteratorFindKey(Dqn_Jsmn *jsmn_state, Dqn_JsmnString key, Dqn_JsmnIterator *parent_it); Dqn_JsmnIterator Dqn_JsmnIteratorFindKey(Dqn_Jsmn *jsmn_state, Dqn_JsmnString key, Dqn_JsmnIterator *parent_it);
#define Dqn_Jsmn_IteratorExpectValue(it, expected, err_handle) Dqn_Jsmn_Iterator_ExpectValue(it, expected, err_handle, __FILE__, __LINE__) #define Dqn_JsmnIteratorExpectValue(it, expected, err_handle) Dqn_Jsmn_IteratorExpectValue(it, expected, err_handle, __FILE__, __LINE__)
#define Dqn_Jsmn_IteratorExpectKey(it, expected, err_handle) Dqn_Jsmn_Iterator_ExpectKey(it, expected, err_handle, __FILE__, __LINE__) #define Dqn_JsmnIteratorExpectKey(it, expected, err_handle) Dqn_Jsmn_IteratorExpectKey(it, expected, err_handle, __FILE__, __LINE__)
// Convert the value part of the key-value JSON pair the iterator is currently // Convert the value part of the key-value JSON pair the iterator is currently
// pointing to, to a string/bool/u64. If the iterator's value does not point to // pointing to, to a string/bool/u64. If the iterator's value does not point to
// the type requested, a zero initialised value is returned. // the type requested, a zero initialised value is returned.
Dqn_JsmnString Dqn_Jsmn_IteratorString(Dqn_JsmnIterator const *it); Dqn_JsmnString Dqn_JsmnIteratorString(Dqn_JsmnIterator const *it);
bool Dqn_Jsmn_IteratorBool(Dqn_JsmnIterator const *it); bool Dqn_JsmnIteratorBool(Dqn_JsmnIterator const *it);
uint64_t Dqn_Jsmn_IteratorU64(Dqn_JsmnIterator const *it); Dqn_JsmnU64 Dqn_JsmnIteratorU64(Dqn_JsmnIterator const *it);
#define DQN_JSMN_ERROR_HANDLE_DUMP(handle) \
for (Dqn_ListChunk<Dqn_JsmnError> *chunk = handle.list.head; chunk; chunk = chunk->next) \
{ \
for (auto *error = chunk->data; error != (chunk->data + chunk->count); error++) \
{ \
DQN_LOG_E("Json parsing error in %s:%d, expected token type: %.*s, token was: %.*s", \
Dqn_Str_FileNameFromPath(error->cpp_file), \
error->cpp_line, \
DQN_STRING_FMT(Dqn_Jsmn_TokenIsToString(error->expected)), \
DQN_STRING_FMT(Dqn_Jsmn_TokenString(error->token, error->json))); \
} \
}
#endif // DQN_JSMN_H #endif // DQN_JSMN_H
#if defined(DQN_JSMN_IMPLEMENTATION) #if defined(DQN_JSMN_IMPLEMENTATION)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Implementation // Implementation
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool Dqn_Jsmn_StringEq(Dqn_JsmnString lhs, Dqn_JsmnString rhs) bool Dqn_JsmnStringEq(Dqn_JsmnString lhs, Dqn_JsmnString rhs)
{ {
bool result = lhs.size == rhs.size; bool result = lhs.size == rhs.size;
for (int i = 0; i < lhs.size && result; i++) result &= lhs.str[i] == rhs.str[i]; for (int i = 0; i < lhs.size && result; i++) result &= lhs.buf.str[i] == rhs.buf.str[i];
return result; return result;
} }
bool Dqn_Jsmn_StringIsValid(Dqn_JsmnString string) bool Dqn_JsmnStringIsValid(Dqn_JsmnString string)
{ {
bool result = string.str && string.size >= 0; bool result = string.buf.str && string.size >= 0;
return result; return result;
} }
bool Dqn_Jsmn_IsDigit(char ch) bool Dqn_JsmnIsDigit(char ch)
{ {
bool result = (ch >= '0' && ch <= '9'); bool result = (ch >= '0' && ch <= '9');
return result; return result;
} }
uint64_t Dqn_Jsmn_StringToU64(Dqn_JsmnString string) Dqn_JsmnU64 Dqn_JsmnStringToU64(Dqn_JsmnString string)
{ {
uint64_t result = 0; Dqn_JsmnU64 result = 0;
if (!Dqn_Jsmn_StringIsValid(string)) if (!Dqn_JsmnStringIsValid(string))
return result; return result;
for (int i = 0; i < string.size; i++) for (int i = 0; i < string.size; i++)
{ {
char ch = string.str[i]; char ch = string.buf.str[i];
if (!Dqn_Jsmn_IsDigit(ch)) if (!Dqn_JsmnIsDigit(ch))
return result; return result;
uint64_t digit = ch - '0'; Dqn_JsmnU64 digit = ch - '0';
result = (result * 10) + digit; result = (result * 10) + digit;
} }
return result; return result;
} }
#if defined(__cplusplus)
bool operator==(Dqn_JsmnString lhs, Dqn_JsmnString rhs) bool operator==(Dqn_JsmnString lhs, Dqn_JsmnString rhs)
{ {
bool result = Dqn_Jsmn_StringEq(lhs, rhs); bool result = Dqn_JsmnStringEq(lhs, rhs);
return result; return result;
} }
#endif // __cplusplus
int Dqn_Jsmn_TokensRequired(char const *json, int size) unsigned int Dqn_JsmnTokensRequired(char const *json, Dqn_JsmnI64 size)
{ {
jsmn_parser parser; jsmn_parser parser;
jsmn_init(&parser); jsmn_init(&parser);
int result = jsmn_parse(&parser, json, size, nullptr, 0); unsigned int result = jsmn_parse(&parser, json, size, NULL, 0);
return result; return result;
} }
bool Dqn_Jsmn_InitWithJSONCString(char const *json, int size, Dqn_Jsmn *jsmn) Dqn_JsmnString Dqn_JsmnTokenIsToString(Dqn_JsmnTokenIs enum_val)
{ {
if (!jsmn || !jsmn->tokens || jsmn->tokens_size == 0 || !json) switch (enum_val)
return false; {
#define DQN_JSMN_X_ENTRY(enum_val) case Dqn_JsmnTokenIs_##enum_val: return DQN_JSMN_STRING(#enum_val);
assert(jsmn->tokens_size == Dqn_Jsmn_TokensRequired(json, size)); DQN_JSMN_X_MACRO
#undef DQN_JSMN_X_ENTRY
*jsmn = {}; }
jsmn_init(&jsmn->parser); return DQN_JSMN_STRING("DQN_JSMN_UNHANDLED_ENUM");
jsmn->valid = jsmn_parse(&jsmn->parser, jsmn->json.str, jsmn->json.size, jsmn->tokens, jsmn->tokens_size) > 0;
return jsmn->valid;
} }
#if defined(DQN_H) Dqn_Jsmn Dqn_JsmnInitWithJSONCString(char const *json, Dqn_JsmnI64 size, jsmntok_t *tokens, unsigned int tokens_size)
Dqn_Jsmn Dqn_Jsmn_InitWithJSON(Dqn_JsmnString json, Dqn_ArenaAllocator *arena)
{ {
Dqn_Jsmn result = {}; Dqn_Jsmn result = DQN_JSMN_ZERO_INIT;
result.tokens_size = Dqn_Jsmn_InitWithJSONCString(json.str, json.size, nullptr); if (!tokens || tokens_size <= 0 || !json || size <= 0)
result.tokens = Dqn_ArenaAllocator_NewArray(arena, jsmntok_t, result.tokens_size, Dqn_ZeroMem::No); return result;
Dqn_Jsmn_InitWithJSONCString(json.str, json.size, &result) DQN_JSMN_ASSERT(tokens_size == Dqn_JsmnTokensRequired(json, size));
jsmn_init(&result.parser);
result.json.buf.const_str = json;
result.json.size = size;
result.tokens = tokens;
result.tokens_size = tokens_size;
result.valid = jsmn_parse(&result.parser, result.json.buf.str, result.json.size, result.tokens, result.tokens_size) > 0;
return result; return result;
} }
Dqn_Jsmn Dqn_Jsmn_InitWithJSONFile(Dqn_JsmnString file, Dqn_ArenaAllocator *arena) Dqn_Jsmn Dqn_JsmnInitWithJSONString(Dqn_JsmnString json, jsmntok_t *tokens, unsigned int size)
{ {
Dqn_JsmnString json = Dqn_File_ArenaReadFileToString(file.str, arena); Dqn_Jsmn result = Dqn_JsmnInitWithJSONCString(json.buf.str, json.size, tokens, size);
Dqn_Jsmn result = Dqn_Jsmn_InitWithJSON(json, arena);
return result; return result;
} }
#endif // DQN_H
int Dqn_Jsmn_TokenArraySize(jsmntok_t token) #if !defined(DQN_JSMN_NO_CRT)
Dqn_Jsmn Dqn_JsmnInitWithMallocJSONCString(char const *json, Dqn_JsmnI64 size)
{
unsigned int tokens_size = Dqn_JsmnTokensRequired(json, size);
jsmntok_t *tokens = (jsmntok_t *)malloc(sizeof(jsmntok_t) * tokens_size);
Dqn_Jsmn result = DQN_JSMN_ZERO_INIT;
if (tokens)
result = Dqn_JsmnInitWithJSONCString(json, size, tokens, tokens_size);
if (!result.valid)
free(tokens);
return result;
}
Dqn_Jsmn Dqn_JsmnInitWithMallocJSON(Dqn_JsmnString json)
{
Dqn_Jsmn result = Dqn_JsmnInitWithMallocJSONCString(json.buf.str, json.size);
return result;
}
Dqn_Jsmn Dqn_JsmnInitWithMallocJSONFile(Dqn_JsmnString json_file)
{
Dqn_Jsmn result = DQN_JSMN_ZERO_INIT;
FILE * file_handle = NULL;
fopen_s(&file_handle, json_file.buf.str, "rb");
if (!file_handle)
return result;
fseek(file_handle, 0, SEEK_END);
size_t json_size = ftell(file_handle);
rewind(file_handle);
char *json = (char *)malloc(json_size + 1);
if (json)
{
json[json_size] = 0;
if (fread(json, json_size, 1, file_handle) == 1)
{
unsigned int tokens_size = Dqn_JsmnTokensRequired(json, json_size);
jsmntok_t * tokens = (jsmntok_t *)malloc(sizeof(jsmntok_t) * tokens_size);
if (tokens)
{
result = Dqn_JsmnInitWithJSONCString(json, json_size, tokens, tokens_size);
result.json_needs_crt_free = true;
}
}
}
fclose(file_handle);
if (!result.valid)
Dqn_JsmnFree(&result);
return result;
}
void Dqn_JsmnFree(Dqn_Jsmn *jsmn)
{
if (jsmn->json.buf.str && jsmn->json_needs_crt_free)
free(jsmn->json.buf.str);
if (jsmn->tokens)
free(jsmn->tokens);
jsmn->valid = false;
}
void Dqn_JsmnErrorHandleDumpToCRTFile(Dqn_JsmnErrorHandle const *handle, FILE *file)
{
for (int err_index = 0; err_index < handle->errors_size; err_index++)
{
Dqn_JsmnError const *error = handle->errors + err_index;
Dqn_JsmnErrorDumpToCRTFile(error, file);
}
}
void Dqn_JsmnErrorDumpToCRTFile(Dqn_JsmnError const *error, FILE *file)
{
fprintf(file,
"Json parsing error in %s:%d, expected token type: %.*s, token was: %.*s (%.*s)\n",
error->cpp_file,
error->cpp_line,
DQN_JSMN_STRING_FMT(Dqn_JsmnTokenIsToString(error->expected)),
DQN_JSMN_STRING_FMT(Dqn_JsmnTokenIsToString(error->actual)),
DQN_JSMN_STRING_FMT(Dqn_JsmnTokenString(error->token, error->json)));
}
#endif // !DQN_JSMN_NO_CRT
#if defined(DQN_IMPLEMENTATION)
Dqn_Jsmn Dqn_JsmnInitWithArenaJSONCString(char const *json, Dqn_JsmnI64 size, Dqn_Arena *arena)
{
int tokens_size = Dqn_JsmnTokensRequired(json, size);
jsmntok_t *tokens = Dqn_ArenaNewArray(arena, jsmntok_t, tokens_size, Dqn_ZeroMem::No);
Dqn_Jsmn result = Dqn_JsmnInitWithJSONCString(json, size, tokens, tokens_size);
return result;
}
Dqn_Jsmn Dqn_JsmnInitWithArenaJSON(Dqn_JsmnString json, Dqn_Arena *arena)
{
Dqn_Jsmn result = Dqn_JsmnInitWithArenaJSONCString(json.buf.str, json.size, arena);
return result;
}
Dqn_Jsmn Dqn_JsmnInitWithArenaJSONFile(Dqn_JsmnString file, Dqn_Arena *arena)
{
Dqn_String json = Dqn_FileArenaReadToString(file.buf.str, file.size, arena);
Dqn_Jsmn result = Dqn_JsmnInitWithArenaJSON({{json.str}, (int)json.size}, arena);
return result;
}
#endif // DQN_IMPLEMENTATION
int Dqn_JsmnTokenArraySize(jsmntok_t token)
{ {
int result = token.type == JSMN_ARRAY ? token.size : -1; int result = token.type == JSMN_ARRAY ? token.size : -1;
return result; return result;
} }
Dqn_JsmnString Dqn_Jsmn_TokenString(jsmntok_t token, Dqn_JsmnString json) Dqn_JsmnString Dqn_JsmnTokenString(jsmntok_t token, Dqn_JsmnString json)
{ {
Dqn_JsmnString result = {json.str + token.start, token.end - token.start}; Dqn_JsmnString result = {{json.buf.str + token.start}, token.end - token.start};
return result; return result;
} }
bool Dqn_Jsmn_TokenBool(jsmntok_t token, Dqn_JsmnString json) bool Dqn_JsmnTokenBool(jsmntok_t token, Dqn_JsmnString json)
{ {
assert(token.start < json.size); DQN_JSMN_ASSERT(token.start < json.size);
char ch = json.str[token.start]; char ch = json.buf.str[token.start];
bool result = ch == 't'; bool result = ch == 't';
if (!result) { assert(ch == 'f'); } if (!result) { DQN_JSMN_ASSERT(ch == 'f'); }
return result; return result;
} }
uint64_t Dqn_Jsmn_TokenU64(jsmntok_t token, Dqn_JsmnString json) Dqn_JsmnU64 Dqn_JsmnTokenU64(jsmntok_t token, Dqn_JsmnString json)
{ {
assert(token.start < json.size); DQN_JSMN_ASSERT(token.start < json.size);
Dqn_JsmnString string = {json.str + token.start, token.end - token.start}; Dqn_JsmnString string = {{json.buf.str + token.start}, token.end - token.start};
uint64_t result = Dqn_Jsmn_StringToU64(string); Dqn_JsmnU64 result = Dqn_JsmnStringToU64(string);
return result; return result;
} }
void Dqn_JsmnErrorHandle__AddError(Dqn_JsmnErrorHandle *handle, jsmntok_t token, Dqn_JsmnString json, Dqn_JsmnTokenIs expected, char const *file, int line) void Dqn_JsmnErrorHandleAddError(Dqn_JsmnErrorHandle *handle, jsmntok_t token, Dqn_JsmnString json, Dqn_JsmnTokenIs actual, Dqn_JsmnTokenIs expected, char const *file, int line)
{ {
if (!handle) if (!handle)
return; return;
@ -535,13 +684,15 @@ void Dqn_JsmnErrorHandle__AddError(Dqn_JsmnErrorHandle *handle, jsmntok_t token,
return; return;
Dqn_JsmnError *error = handle->errors + handle->errors_size++; Dqn_JsmnError *error = handle->errors + handle->errors_size++;
error->token = token;
error->actual = actual;
error->expected = expected; error->expected = expected;
error->json = json; error->json = json;
error->cpp_file = file; error->cpp_file = file;
error->cpp_line = line; error->cpp_line = line;
} }
bool Dqn_Jsmn_IteratorNext(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnIterator *prev_it) bool Dqn_JsmnIteratorNext(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnIterator *prev_it)
{ {
if (!it->init) if (!it->init)
{ {
@ -554,7 +705,7 @@ bool Dqn_Jsmn_IteratorNext(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnI
} }
bool result = false; bool result = false;
if (!Dqn_Jsmn_StringIsValid(it->json) || it->json.size <= 0) { if (!Dqn_JsmnStringIsValid(it->json) || it->json.size <= 0) {
return result; return result;
} }
@ -570,21 +721,21 @@ bool Dqn_Jsmn_IteratorNext(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnI
return result; return result;
} }
Dqn_JsmnString Dqn_Jsmn_IteratorKey(Dqn_JsmnIterator *it) Dqn_JsmnString Dqn_JsmnIteratorKey(Dqn_JsmnIterator *it)
{ {
Dqn_JsmnString result = {}; Dqn_JsmnString result = DQN_JSMN_ZERO_INIT;
if (it && it->key) if (it && it->key)
result = {it->json.str + it->key->start, it->key->end - it->key->start}; result = DQN_JSMN_CLITERAL(Dqn_JsmnString){{it->json.buf.str + it->key->start}, it->key->end - it->key->start};
return result; return result;
} }
Dqn_JsmnIterator Dqn_Jsmn_IteratorFindKey(Dqn_Jsmn *jsmn_state, Dqn_JsmnString key, Dqn_JsmnIterator *parent_it) Dqn_JsmnIterator Dqn_JsmnIteratorFindKey(Dqn_Jsmn *jsmn_state, Dqn_JsmnString key, Dqn_JsmnIterator *parent_it)
{ {
Dqn_JsmnIterator result = {}; Dqn_JsmnIterator result = DQN_JSMN_ZERO_INIT;
for (Dqn_JsmnIterator it = {}; Dqn_Jsmn_IteratorNext(&it, jsmn_state, parent_it); ) for (Dqn_JsmnIterator it = DQN_JSMN_ZERO_INIT; Dqn_JsmnIteratorNext(&it, jsmn_state, parent_it); )
{ {
Dqn_JsmnString it_key = Dqn_Jsmn_TokenString(*it.key, jsmn_state->json); Dqn_JsmnString it_key = Dqn_JsmnTokenString(*it.key, jsmn_state->json);
if (it_key == key) if (Dqn_JsmnStringEq(it_key, key))
{ {
result = it; result = it;
break; break;
@ -594,67 +745,87 @@ Dqn_JsmnIterator Dqn_Jsmn_IteratorFindKey(Dqn_Jsmn *jsmn_state, Dqn_JsmnString k
return result; return result;
} }
static bool Dqn_Jsmn_Iterator_Expect(Dqn_JsmnIterator *it, Dqn_JsmnTokenIs expected, jsmntok_t token, Dqn_JsmnErrorHandle *err_handle, char const *file, unsigned int line) static bool Dqn_Jsmn_IteratorExpect(Dqn_JsmnIterator *it, Dqn_JsmnTokenIs expected, jsmntok_t token, Dqn_JsmnErrorHandle *err_handle, char const *file, unsigned int line)
{ {
bool result = false; bool result = false;
switch (expected) switch (expected)
{ {
case Dqn_JsmnTokenIs_Invalid: result = token.type == JSMN_UNDEFINED; break;
case Dqn_JsmnTokenIs_Object: result = token.type == JSMN_OBJECT; break; case Dqn_JsmnTokenIs_Object: result = token.type == JSMN_OBJECT; break;
case Dqn_JsmnTokenIs_Array: result = token.type == JSMN_ARRAY; break; case Dqn_JsmnTokenIs_Array: result = token.type == JSMN_ARRAY; break;
case Dqn_JsmnTokenIs_String: result = token.type == JSMN_STRING; break; case Dqn_JsmnTokenIs_String: result = token.type == JSMN_STRING; break;
case Dqn_JsmnTokenIs_Number: case Dqn_JsmnTokenIs_Number:
{ {
assert(token.start < it->json.size); DQN_JSMN_ASSERT(token.start < it->json.size);
char ch = it->json.str[token.start]; char ch = it->json.buf.str[token.start];
result = token.type == JSMN_PRIMITIVE && (ch == '-' || Dqn_Jsmn_IsDigit(ch)); result = token.type == JSMN_PRIMITIVE && (ch == '-' || Dqn_JsmnIsDigit(ch));
} }
break; break;
case Dqn_JsmnTokenIs_Bool: case Dqn_JsmnTokenIs_Bool:
{ {
assert(token.start < it->json.size); DQN_JSMN_ASSERT(token.start < it->json.size);
char ch = it->json.str[token.start]; char ch = it->json.buf.str[token.start];
result = token.type == JSMN_PRIMITIVE && (ch == 't' || ch == 'f'); result = token.type == JSMN_PRIMITIVE && (ch == 't' || ch == 'f');
} }
break; break;
} }
Dqn_JsmnTokenIs actual = DQN_JSMN_ZERO_INIT;
switch (token.type)
{
case JSMN_UNDEFINED: actual = Dqn_JsmnTokenIs_Invalid; break;
case JSMN_OBJECT: actual = Dqn_JsmnTokenIs_Object; break;
case JSMN_ARRAY: actual = Dqn_JsmnTokenIs_Array; break;
case JSMN_STRING: actual = Dqn_JsmnTokenIs_String; break;
case JSMN_PRIMITIVE:
{
char first = it->json.buf.str[token.start];
if (first == 't' || first == 'f')
actual = Dqn_JsmnTokenIs_Bool;
else
actual = Dqn_JsmnTokenIs_Number;
}
break;
}
if (!result) if (!result)
Dqn_JsmnErrorHandle__AddError(err_handle, token, it->json, expected, file, line); Dqn_JsmnErrorHandleAddError(err_handle, token, it->json, actual, expected, file, line);
return result; return result;
} }
bool Dqn_Jsmn_Iterator_ExpectValue(Dqn_JsmnIterator *it, Dqn_JsmnTokenIs expected, Dqn_JsmnErrorHandle *err_handle, char const *file, unsigned int line) bool Dqn_Jsmn_IteratorExpectValue(Dqn_JsmnIterator *it, Dqn_JsmnTokenIs expected, Dqn_JsmnErrorHandle *err_handle, char const *file, unsigned int line)
{ {
bool result = it->value && Dqn_Jsmn_Iterator_Expect(it, expected, *it->value, err_handle, file, line); bool result = it->value && Dqn_Jsmn_IteratorExpect(it, expected, *it->value, err_handle, file, line);
return result; return result;
} }
bool Dqn_Jsmn_Iterator_ExpectKey(Dqn_JsmnIterator *it, Dqn_JsmnTokenIs expected, Dqn_JsmnErrorHandle *err_handle, char const *file, unsigned int line) bool Dqn_Jsmn_IteratorExpectKey(Dqn_JsmnIterator *it, Dqn_JsmnTokenIs expected, Dqn_JsmnErrorHandle *err_handle, char const *file, unsigned int line)
{ {
bool result = it->key && Dqn_Jsmn_Iterator_Expect(it, expected, *it->key, err_handle, file, line); bool result = it->key && Dqn_Jsmn_IteratorExpect(it, expected, *it->key, err_handle, file, line);
return result; return result;
} }
Dqn_JsmnString Dqn_Jsmn_IteratorString(Dqn_JsmnIterator const *it) Dqn_JsmnString Dqn_JsmnIteratorString(Dqn_JsmnIterator const *it)
{ {
Dqn_JsmnString result = {}; Dqn_JsmnString result = DQN_JSMN_ZERO_INIT;
if (it->value && it->json.str) if (it->value && it->json.buf.str)
result = {it->json.str + it->value->start, it->value->end - it->value->start}; result = DQN_JSMN_CLITERAL(Dqn_JsmnString){{it->json.buf.str + it->value->start}, it->value->end - it->value->start};
return result; return result;
} }
bool Dqn_Jsmn_IteratorBool(Dqn_JsmnIterator const *it) bool Dqn_JsmnIteratorBool(Dqn_JsmnIterator const *it)
{ {
bool result = it->value && Dqn_Jsmn_TokenBool(*it->value, it->json); bool result = it->value && Dqn_JsmnTokenBool(*it->value, it->json);
return result; return result;
} }
uint64_t Dqn_Jsmn_IteratorU64(Dqn_JsmnIterator const *it) Dqn_JsmnU64 Dqn_JsmnIteratorU64(Dqn_JsmnIterator const *it)
{ {
uint64_t result = it->value && Dqn_Jsmn_TokenU64(*it->value, it->json); Dqn_JsmnU64 result = it->value && Dqn_JsmnTokenU64(*it->value, it->json);
return result; return result;
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

180
dqn_tester.h Normal file
View File

@ -0,0 +1,180 @@
#if !defined(DQN_TESTER_H)
#define DQN_TESTER_H
// -----------------------------------------------------------------------------
// NOTE: Overview
// -----------------------------------------------------------------------------
// A super minimal testing framework, most of the logic here is the pretty
// printing of test results.
// -----------------------------------------------------------------------------
// NOTE: Configuration
// -----------------------------------------------------------------------------
// #define DQN_TESTER_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_TESTER_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_TESTER_RESULT_PAD_CHAR
// Define this to a character to specify the default character to use for
// padding. By default this is '.'
//
// #define DQN_TESTER_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_TESTER_BAD_COLOR
// Define this to a terminal color code to specify what color errors will be
// presented as.
//
// #define DQN_TESTER_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>
#if !defined(DQN_TESTER_RESULT_LPAD)
#define DQN_TESTER_RESULT_LPAD 90
#endif
#if !defined(DQN_TESTER_RESULT_PAD_CHAR)
#define DQN_TESTER_RESULT_PAD_CHAR '.'
#endif
#if !defined(DQN_TESTER_SPACING)
#define DQN_TESTER_SPACING 2
#endif
#if !defined(DQN_TESTER_BAD_COLOR)
#define DQN_TESTER_BAD_COLOR "\x1b[31m"
#endif
#if !defined(DQN_TESTER_GOOD_COLOR)
#define DQN_TESTER_GOOD_COLOR "\x1b[32m"
#endif
#define DQN_TESTER_COLOR_RESET "\x1b[0m"
#define DQN_TESTER_BEGIN_GROUP(fmt, ...) fprintf(stdout, fmt "\n", ##__VA_ARGS__)
#define DQN_TESTER_END_GROUP(test) \
do \
{ \
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_TESTER_COLOR_RESET, \
all_clear ? DQN_TESTER_GOOD_COLOR : DQN_TESTER_BAD_COLOR, \
(test)->num_tests_ok_in_group, \
(test)->num_tests_in_group, \
all_clear ? "OK" : "FAILED"); \
} while (0)
#define DQN_TESTER_ASSERT(test, expr, fmt, ...) DQN_TESTER_ASSERT_AT((test), __FILE__, __LINE__, expr, fmt, ## __VA_ARGS__)
#define DQN_TESTER_LOG(test, fmt, ...) \
do \
{ \
if ((test)->log_count++ == 0) fprintf(stdout, "\n"); \
fprintf(stdout, "%*s" fmt "\n", DQN_TESTER_SPACING * 2, "", ##__VA_ARGS__); \
} while (0)
#define DQN_TESTER_ASSERT_AT(test, file, line, expr, fmt, ...) \
do \
{ \
if (!(expr)) \
{ \
if ((test)->log_count++ == 0) fprintf(stdout, "\n"); \
(test)->failed = true; \
fprintf(stderr, \
"%*sFile: %s:%d\n" \
"%*sExpression: [" #expr "]\n" \
"%*sReason: " fmt "\n", \
DQN_TESTER_SPACING * 2, \
"", \
file, \
line, \
DQN_TESTER_SPACING * 2, \
"", \
DQN_TESTER_SPACING * 2, \
"", \
##__VA_ARGS__); \
} \
} while (0)
// -----------------------------------------------------------------------------
// NOTE: Header
// -----------------------------------------------------------------------------
typedef struct Dqn_Tester
{
int num_tests_in_group;
int num_tests_ok_in_group;
int log_count;
bool failed;
} Dqn_Tester;
void Dqn_TesterBegin(Dqn_Tester *test, char const *fmt, ...);
void Dqn_TesterEnd(Dqn_Tester *test);
#endif // DQN_TESTER_H
// -----------------------------------------------------------------------------
// NOTE: Implementation
// -----------------------------------------------------------------------------
#if defined(DQN_TESTER_IMPLEMENTATION)
void Dqn_TesterBegin(Dqn_Tester *test, char const *fmt, ...)
{
test->num_tests_in_group++;
test->failed = false;
test->log_count = 0;
va_list args;
va_start(args, fmt);
int size_required = 0;
{
va_list args_copy;
va_copy(args_copy, args);
size_required = vsnprintf(NULL, 0, fmt, args_copy);
va_end(args_copy);
}
printf("%*s", DQN_TESTER_SPACING, "");
vprintf(fmt, args);
for (int pad = DQN_TESTER_SPACING + size_required; pad < DQN_TESTER_RESULT_LPAD; pad++)
putc(DQN_TESTER_RESULT_PAD_CHAR, stdout);
va_end(args);
}
void Dqn_TesterEnd(Dqn_Tester *test)
{
if (test->log_count != 0)
{
// NOTE: We try and print the result on the same line as the test name,
// but if there were logs printed throughout the test then we must print
// the result on a new line.
printf("%*s", DQN_TESTER_SPACING, "");
for (int pad = DQN_TESTER_SPACING; pad < DQN_TESTER_RESULT_LPAD; pad++)
putc(DQN_TESTER_RESULT_PAD_CHAR, stdout);
}
if (test->failed)
fprintf(stdout, DQN_TESTER_BAD_COLOR " FAILED");
else
{
fprintf(stdout, DQN_TESTER_GOOD_COLOR " OK");
test->num_tests_ok_in_group++;
}
fprintf(stdout, DQN_TESTER_COLOR_RESET "\n");
if (test->log_count != 0)
putc('\n', stdout);
}
#endif // DQN_TESTER_IMPLEMENTATION

View File

@ -266,13 +266,13 @@ Dqn_Test Dqn_Test_File()
// NOTE: Write step // NOTE: Write step
Dqn_String const SRC_FILE = DQN_STRING("dqn_test_file"); Dqn_String const SRC_FILE = DQN_STRING("dqn_test_file");
DQN_TEST(test, "Write file, read it, copy it, move it and delete it"); DQN_TEST(test, "Write file, read it, copy it, move it and delete it");
Dqn_b32 write_result = Dqn_FileWriteFile(SRC_FILE.str, "test", 4); Dqn_b32 write_result = Dqn_FileWriteFile(SRC_FILE.str, SRC_FILE.size, "test", 4);
DQN_TEST_ASSERT(test, write_result); DQN_TEST_ASSERT(test, write_result);
DQN_TEST_ASSERT(test, Dqn_FileExists(SRC_FILE)); DQN_TEST_ASSERT(test, Dqn_FileExists(SRC_FILE));
// NOTE: Read step // NOTE: Read step
Dqn_Arena arena = {}; Dqn_Arena arena = {};
Dqn_String read_file = Dqn_FileArenaReadFileToString(SRC_FILE.str, &arena); Dqn_String read_file = Dqn_FileArenaReadToString(SRC_FILE.str, SRC_FILE.size, &arena);
DQN_TEST_ASSERT(test, Dqn_StringIsValid(read_file)); DQN_TEST_ASSERT(test, Dqn_StringIsValid(read_file));
DQN_TEST_ASSERT(test, read_file.size == 4); DQN_TEST_ASSERT(test, read_file.size == 4);
DQN_TEST_ASSERT_MSG(test, Dqn_StringEq(read_file, DQN_STRING("test")), "read(%zu): %.*s", read_file.size, DQN_STRING_FMT(read_file)); DQN_TEST_ASSERT_MSG(test, Dqn_StringEq(read_file, DQN_STRING("test")), "read(%zu): %.*s", read_file.size, DQN_STRING_FMT(read_file));
@ -987,13 +987,6 @@ Dqn_Test Dqn_Test_OS()
DQN_TEST_ASSERT(test, result == false); DQN_TEST_ASSERT(test, result == false);
} }
{
DQN_TEST(test, "Generate secure RNG bytes with -1 size");
char buf[1];
Dqn_b32 result = Dqn_OSSecureRNGBytes(buf, -1);
DQN_TEST_ASSERT(test, result == false);
}
{ {
DQN_TEST(test, "Generate secure RNG 32 bytes"); DQN_TEST(test, "Generate secure RNG 32 bytes");
char const ZERO[32] = {}; char const ZERO[32] = {};
@ -1520,137 +1513,137 @@ void Dqn_Test__KeccakDispatch(Dqn_Test *test, int hash_type, Dqn_String input)
{ {
case Hash_SHA3_224: case Hash_SHA3_224:
{ {
Dqn_KeccakBytes28 hash = Dqn_Keccak_SHA3_224_StringToBytes28(input); Dqn_KeccakBytes28 hash = Dqn_SHA3_224StringToBytes28(input);
Dqn_KeccakBytes28 expect; Dqn_KeccakBytes28 expect;
FIPS202_SHA3_224(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data); FIPS202_SHA3_224(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data);
DQN_TEST_ASSERT_MSG((*test), DQN_TEST_ASSERT_MSG((*test),
Dqn_Keccak_Bytes28Equals(&hash, &expect), Dqn_KeccakBytes28Equals(&hash, &expect),
"\ninput: %.*s" "\ninput: %.*s"
"\nhash: %.*s" "\nhash: %.*s"
"\nexpect: %.*s" "\nexpect: %.*s"
, ,
DQN_STRING_FMT(input_hex), DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&hash).str), DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&hash).str),
DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&expect).str)); DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&expect).str));
} }
break; break;
case Hash_SHA3_256: case Hash_SHA3_256:
{ {
Dqn_KeccakBytes32 hash = Dqn_Keccak_SHA3_256_StringToBytes32(input); Dqn_KeccakBytes32 hash = Dqn_SHA3_256StringToBytes32(input);
Dqn_KeccakBytes32 expect; Dqn_KeccakBytes32 expect;
FIPS202_SHA3_256(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data); FIPS202_SHA3_256(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data);
DQN_TEST_ASSERT_MSG((*test), DQN_TEST_ASSERT_MSG((*test),
Dqn_Keccak_Bytes32Equals(&hash, &expect), Dqn_KeccakBytes32Equals(&hash, &expect),
"\ninput: %.*s" "\ninput: %.*s"
"\nhash: %.*s" "\nhash: %.*s"
"\nexpect: %.*s" "\nexpect: %.*s"
, ,
DQN_STRING_FMT(input_hex), DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&hash).str), DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&hash).str),
DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&expect).str)); DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&expect).str));
} }
break; break;
case Hash_SHA3_384: case Hash_SHA3_384:
{ {
Dqn_KeccakBytes48 hash = Dqn_Keccak_SHA3_384_StringToBytes48(input); Dqn_KeccakBytes48 hash = Dqn_SHA3_384StringToBytes48(input);
Dqn_KeccakBytes48 expect; Dqn_KeccakBytes48 expect;
FIPS202_SHA3_384(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data); FIPS202_SHA3_384(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data);
DQN_TEST_ASSERT_MSG((*test), DQN_TEST_ASSERT_MSG((*test),
Dqn_Keccak_Bytes48Equals(&hash, &expect), Dqn_KeccakBytes48Equals(&hash, &expect),
"\ninput: %.*s" "\ninput: %.*s"
"\nhash: %.*s" "\nhash: %.*s"
"\nexpect: %.*s" "\nexpect: %.*s"
, ,
DQN_STRING_FMT(input_hex), DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&hash).str), DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&hash).str),
DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&expect).str)); DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&expect).str));
} }
break; break;
case Hash_SHA3_512: case Hash_SHA3_512:
{ {
Dqn_KeccakBytes64 hash = Dqn_Keccak_SHA3_512_StringToBytes64(input); Dqn_KeccakBytes64 hash = Dqn_SHA3_512StringToBytes64(input);
Dqn_KeccakBytes64 expect; Dqn_KeccakBytes64 expect;
FIPS202_SHA3_512(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data); FIPS202_SHA3_512(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data);
DQN_TEST_ASSERT_MSG((*test), DQN_TEST_ASSERT_MSG((*test),
Dqn_Keccak_Bytes64Equals(&hash, &expect), Dqn_KeccakBytes64Equals(&hash, &expect),
"\ninput: %.*s" "\ninput: %.*s"
"\nhash: %.*s" "\nhash: %.*s"
"\nexpect: %.*s" "\nexpect: %.*s"
, ,
DQN_STRING_FMT(input_hex), DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&hash).str), DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&hash).str),
DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&expect).str)); DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&expect).str));
} }
break; break;
case Hash_Keccak_224: case Hash_Keccak_224:
{ {
Dqn_KeccakBytes28 hash = Dqn_Keccak_224_StringToBytes28(input); Dqn_KeccakBytes28 hash = Dqn_Keccak224StringToBytes28(input);
Dqn_KeccakBytes28 expect; Dqn_KeccakBytes28 expect;
Keccak(1152, 448, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); Keccak(1152, 448, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect));
DQN_TEST_ASSERT_MSG((*test), DQN_TEST_ASSERT_MSG((*test),
Dqn_Keccak_Bytes28Equals(&hash, &expect), Dqn_KeccakBytes28Equals(&hash, &expect),
"\ninput: %.*s" "\ninput: %.*s"
"\nhash: %.*s" "\nhash: %.*s"
"\nexpect: %.*s" "\nexpect: %.*s"
, ,
DQN_STRING_FMT(input_hex), DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&hash).str), DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&hash).str),
DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&expect).str)); DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&expect).str));
} }
break; break;
case Hash_Keccak_256: case Hash_Keccak_256:
{ {
Dqn_KeccakBytes32 hash = Dqn_Keccak_256_StringToBytes32(input); Dqn_KeccakBytes32 hash = Dqn_Keccak256StringToBytes32(input);
Dqn_KeccakBytes32 expect; Dqn_KeccakBytes32 expect;
Keccak(1088, 512, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); Keccak(1088, 512, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect));
DQN_TEST_ASSERT_MSG((*test), DQN_TEST_ASSERT_MSG((*test),
Dqn_Keccak_Bytes32Equals(&hash, &expect), Dqn_KeccakBytes32Equals(&hash, &expect),
"\ninput: %.*s" "\ninput: %.*s"
"\nhash: %.*s" "\nhash: %.*s"
"\nexpect: %.*s" "\nexpect: %.*s"
, ,
DQN_STRING_FMT(input_hex), DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&hash).str), DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&hash).str),
DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&expect).str)); DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&expect).str));
} }
break; break;
case Hash_Keccak_384: case Hash_Keccak_384:
{ {
Dqn_KeccakBytes48 hash = Dqn_Keccak_384_StringToBytes48(input); Dqn_KeccakBytes48 hash = Dqn_Keccak384StringToBytes48(input);
Dqn_KeccakBytes48 expect; Dqn_KeccakBytes48 expect;
Keccak(832, 768, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); Keccak(832, 768, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect));
DQN_TEST_ASSERT_MSG((*test), DQN_TEST_ASSERT_MSG((*test),
Dqn_Keccak_Bytes48Equals(&hash, &expect), Dqn_KeccakBytes48Equals(&hash, &expect),
"\ninput: %.*s" "\ninput: %.*s"
"\nhash: %.*s" "\nhash: %.*s"
"\nexpect: %.*s" "\nexpect: %.*s"
, ,
DQN_STRING_FMT(input_hex), DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&hash).str), DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&hash).str),
DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&expect).str)); DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&expect).str));
} }
break; break;
case Hash_Keccak_512: case Hash_Keccak_512:
{ {
Dqn_KeccakBytes64 hash = Dqn_Keccak_512_StringToBytes64(input); Dqn_KeccakBytes64 hash = Dqn_Keccak512StringToBytes64(input);
Dqn_KeccakBytes64 expect; Dqn_KeccakBytes64 expect;
Keccak(576, 1024, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); Keccak(576, 1024, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect));
DQN_TEST_ASSERT_MSG((*test), DQN_TEST_ASSERT_MSG((*test),
Dqn_Keccak_Bytes64Equals(&hash, &expect), Dqn_KeccakBytes64Equals(&hash, &expect),
"\ninput: %.*s" "\ninput: %.*s"
"\nhash: %.*s" "\nhash: %.*s"
"\nexpect: %.*s" "\nexpect: %.*s"
, ,
DQN_STRING_FMT(input_hex), DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&hash).str), DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&hash).str),
DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&expect).str)); DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&expect).str));
} }
break; break;