Add experimental win32 http, tester lib
This commit is contained in:
		
							parent
							
								
									b9e91b5497
								
							
						
					
					
						commit
						05fe604f5c
					
				
							
								
								
									
										
											BIN
										
									
								
								Project/dqn.rdbg
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Project/dqn.rdbg
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -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
 | ||||||
|  | |||||||
							
								
								
									
										515
									
								
								dqn_jsmn.h
									
									
									
									
									
								
							
							
						
						
									
										515
									
								
								dqn_jsmn.h
									
									
									
									
									
								
							| @ -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; | ||||||
| bool     operator==(Dqn_JsmnString lhs, Dqn_JsmnString rhs); | } 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); | ||||||
|  | #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; | ||||||
| 
 | 
 | ||||||
| #define DQN_JSMN_ERROR_HANDLE_SIZE 128 | #if !defined(DQN_JSMN_ERROR_HANDLE_SIZE) | ||||||
| struct Dqn_JsmnErrorHandle |     #define DQN_JSMN_ERROR_HANDLE_SIZE 128 | ||||||
|  | #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_Object: result = token.type == JSMN_OBJECT; break; |         case Dqn_JsmnTokenIs_Invalid: result = token.type == JSMN_UNDEFINED; break; | ||||||
|         case Dqn_JsmnTokenIs_Array:  result = token.type == JSMN_ARRAY; break; |         case Dqn_JsmnTokenIs_Object: result  = token.type == JSMN_OBJECT; break; | ||||||
|         case Dqn_JsmnTokenIs_String: result = token.type == JSMN_STRING; break; |         case Dqn_JsmnTokenIs_Array:  result  = token.type == JSMN_ARRAY; 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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										764
									
								
								dqn_keccak.h
									
									
									
									
									
								
							
							
						
						
									
										764
									
								
								dqn_keccak.h
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1399
									
								
								dqn_metadesk.h
									
									
									
									
									
								
							
							
						
						
									
										1399
									
								
								dqn_metadesk.h
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										180
									
								
								dqn_tester.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								dqn_tester.h
									
									
									
									
									
										Normal 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
 | ||||||
| @ -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; | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user