From 05fe604f5cd74a5e3d07e25a26f0075dd63025b4 Mon Sep 17 00:00:00 2001 From: doyle Date: Fri, 17 Dec 2021 00:07:33 +1100 Subject: [PATCH] Add experimental win32 http, tester lib --- Project/dqn.rdbg | Bin 360 -> 853 bytes dqn.h | 818 ++++++++++++++++++++++----- dqn_cpp_file.h | 80 +-- dqn_jsmn.h | 515 +++++++++++------ dqn_keccak.h | 764 ++++++++++--------------- dqn_metadesk.h | 1399 ---------------------------------------------- dqn_tester.h | 180 ++++++ dqn_tests.cpp | 75 ++- 8 files changed, 1573 insertions(+), 2258 deletions(-) delete mode 100644 dqn_metadesk.h create mode 100644 dqn_tester.h diff --git a/Project/dqn.rdbg b/Project/dqn.rdbg index 73b0d79bbe46ae6082d331b91b81e0091859c986..24060281c61a0a7165c1ad8ceb9563974abfba6d 100644 GIT binary patch literal 853 zcmbtS%TB{E5KKV44qTA9bAuu^;({n}fl?l4L@!`4uCtIRj+5FB3ctaXZ{kbfBUn3) zXh5iny3$yiRc5_2-s?gCvg3K)#Syv6QXpqp2qYX!(l25Wl2pHNG8jvG1KQ{SO2B_z zEaAC_XcAu~OJm-ZPo|q^oIq(nNhTWAi-UpT!+Hz4e^5 zBBI`tS;RjYD=v*dcCK5ShcY&m)kyOuJWHEL``0oyz_5Mh zu>TYJ_SIJrl!}Zqq1Q3r;Sp0?yPXl1lFhnMebSLsv#zCeVZ~+tA9f52-}kNJ Gw($)%<>;0G literal 360 zcmWG?adOvWU|?{wit)(LO^tERPf3kQDa?y;D$UGEiODT4OOA0V%!?06EiNh6ORY!+ zs)YkaC<{cZ12KjnVADX_L-R6AAVwq?6fiI|Ft7oYgK$x5acNEoP=FyPHIE%gC6%Vd z7nBq+0h!4eAn~%q9E3uUO|#>KtYJ0-xeN>-wOT-oVLikT$vKI6>9{??05S_?Gd=)P K1I8eykOKfkgFMmz diff --git a/dqn.h b/dqn.h index 8b8c35b..743fad6 100644 --- a/dqn.h +++ b/dqn.h @@ -109,6 +109,7 @@ #endif #include // va_list +#include // fprintf, FILE, stdout, stderr // ------------------------------------------------------------------------------------------------- // NOTE: Overridable Macros @@ -908,7 +909,7 @@ enum struct Dqn_ArenaMemProvider struct Dqn_ArenaStatsString { char str[256]; - int size; + int size; }; struct Dqn_ArenaStats @@ -919,7 +920,7 @@ struct Dqn_ArenaStats Dqn_i32 block_count; }; -DQN_API Dqn_ArenaStatsString Dqn_ArenaStats_String(Dqn_ArenaStats const *stats); +DQN_API Dqn_ArenaStatsString Dqn_ArenaStatsToString(Dqn_ArenaStats const *stats); Dqn_usize const DQN_MEM_ARENA_DEFAULT_MIN_BLOCK_SIZE = DQN_KILOBYTES(4); struct Dqn_Arena @@ -1071,6 +1072,7 @@ struct Dqn_DSMap template Dqn_DSMap Dqn_DSMapInit(Dqn_isize size); template void Dqn_DSMapFree(Dqn_DSMap *map); +template Dqn_DSMapEntry *Dqn_DSMapFind(Dqn_DSMap *map, Dqn_u64 hash); template Dqn_DSMapEntry *Dqn_DSMapFindOrAdd(Dqn_DSMap *map, Dqn_u64 hash, Dqn_b32 find_only); template Dqn_DSMapEntry *Dqn_DSMapAdd(Dqn_DSMap *map, Dqn_u64 hash, T &value); template Dqn_DSMapEntry *Dqn_DSMapAddCopy(Dqn_DSMap *map, Dqn_u64 hash, T const &value); @@ -1113,8 +1115,8 @@ template DQN_API T * Dqn_ArrayPeek(Dqn_Array *a); // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_String // ------------------------------------------------------------------------------------------------- -#define DQN_STRING(string) Dqn_String{(char *)string, (Dqn_isize)DQN_CHAR_COUNT(string), (Dqn_isize)DQN_CHAR_COUNT(string)} -#define DQN_STRINGW(string) Dqn_StringW{(wchar_t *)string, (sizeof(string)/sizeof(string[0])) - 1, (sizeof(string)/sizeof(string[0])) - 1} +#define DQN_STRING(string) Dqn_String{(char *)(string), (Dqn_isize)DQN_CHAR_COUNT(string), (Dqn_isize)DQN_CHAR_COUNT(string)} +#define DQN_STRINGW(string) Dqn_StringW{(wchar_t *)(string), (sizeof(string)/sizeof(string[0])) - 1} #define DQN_STRING_FMT(string) (int)((string).size), (string).str struct Dqn_String { @@ -1132,7 +1134,6 @@ struct Dqn_StringW { wchar_t *str; Dqn_isize size; - Dqn_isize cap; wchar_t const *begin() const { return str; } wchar_t const *end () const { return str + size; } @@ -1151,11 +1152,11 @@ DQN_API Dqn_String Dqn_StringInitMemory(char *buf, Dqn_isize capacity); // otherwise true. DQN_API Dqn_b32 Dqn_StringIsValid(Dqn_String in); -#define Dqn_StringTaggedFmt(arena, tag, fmt, ...) Dqn_String_Fmt(arena DQN_CALL_SITE(tag), fmt, ## __VA_ARGS__) -#define Dqn_StringTaggedFmtV(arena, tag, fmt, ...) Dqn_String_FmtV(arena DQN_CALL_SITE(tag), fmt, ## __VA_ARGS__) +#define Dqn_StringTaggedFmt(arena, tag, fmt, ...) Dqn__StringFmt(arena DQN_CALL_SITE(tag), fmt, ## __VA_ARGS__) +#define Dqn_StringTaggedFmtV(arena, tag, fmt, ...) Dqn__StringFmtV(arena DQN_CALL_SITE(tag), fmt, ## __VA_ARGS__) -#define Dqn_StringFmt(arena, fmt, ...) Dqn_String_Fmt(arena DQN_CALL_SITE(""), fmt, ## __VA_ARGS__) -#define Dqn_StringFmtV(arena, fmt, ...) Dqn_String_FmtV(arena DQN_CALL_SITE(""), fmt, ## __VA_ARGS__) +#define Dqn_StringFmt(arena, fmt, ...) Dqn__StringFmt(arena DQN_CALL_SITE(""), fmt, ## __VA_ARGS__) +#define Dqn_StringFmtV(arena, fmt, ...) Dqn__StringFmtV(arena DQN_CALL_SITE(""), fmt, ## __VA_ARGS__) #define Dqn_StringTaggedAllocate(arena, size, zero_mem, tag) Dqn_String_Allocate(arena, size, zero_mem DQN_CALL_SITE(tag)) #define Dqn_StringTaggedCopy(src, arena, tag) Dqn_String_Copy(src, arena DQN_CALL_SITE(tag)) @@ -1165,8 +1166,8 @@ DQN_API Dqn_b32 Dqn_StringIsValid(Dqn_String in); #define Dqn_StringCopyCString(src, size, arena) Dqn_String_CopyCString(src, size, arena DQN_CALL_SITE("")) #define Dqn_StringCopy(src, arena) Dqn_String_Copy(src, arena DQN_CALL_SITE("")) -DQN_API Dqn_String Dqn_String_Fmt(Dqn_Arena *arena DQN_CALL_SITE_ARGS, char const *fmt, ...); -DQN_API Dqn_String Dqn_String_FmtV(Dqn_Arena *arena, char const *fmt, va_list va DQN_CALL_SITE_ARGS); +DQN_API Dqn_String Dqn__StringFmt(Dqn_Arena *arena DQN_CALL_SITE_ARGS, char const *fmt, ...); +DQN_API Dqn_String Dqn__StringFmtV(Dqn_Arena *arena, char const *fmt, va_list va DQN_CALL_SITE_ARGS); DQN_API Dqn_String Dqn_String_Allocate(Dqn_Arena *arena, Dqn_isize size, Dqn_ZeroMem zero_mem); DQN_API Dqn_String Dqn_String_CopyCString(char const *string, Dqn_isize size, Dqn_Arena *arena DQN_CALL_SITE_ARGS); DQN_API Dqn_String Dqn_String_Copy(Dqn_String const src, Dqn_Arena *arena DQN_CALL_SITE_ARGS); @@ -1208,7 +1209,12 @@ DQN_API void Dqn_StringRemove(Dqn_String *in, Dqn_isize begin, // start_index: Set an index within the src string to start the search from, if not desired, set to 0 // return: The index of the matching find, -1 if it is not found -DQN_API Dqn_isize Dqn_StringFind(Dqn_String src, Dqn_String find, Dqn_isize start_index, Dqn_StringEqCase eq_case = Dqn_StringEqCase::Sensitive); +DQN_API Dqn_isize Dqn_StringFindOffset(Dqn_String src, Dqn_String find, Dqn_isize start_index, Dqn_StringEqCase eq_case = Dqn_StringEqCase::Sensitive); + +// start_index: Set an index within the src string to start the search from, if not desired, set to 0 +// return: A string that points to the matching find, otherwise a 0 length string. +DQN_API Dqn_String Dqn_StringFind(Dqn_String src, Dqn_String find, Dqn_isize start_index, Dqn_StringEqCase eq_case = Dqn_StringEqCase::Sensitive); + DQN_API Dqn_String Dqn_StringReplace(Dqn_String src, Dqn_String find, Dqn_String replace, Dqn_isize start_index, Dqn_Arena *arena, Dqn_Arena *temp_arena, Dqn_StringEqCase eq_case = Dqn_StringEqCase::Sensitive); DQN_API Dqn_String Dqn_StringReplaceInsensitive(Dqn_String src, Dqn_String find, Dqn_String replace, Dqn_isize start_index, Dqn_Arena *arena, Dqn_Arena *temp_arena); @@ -1753,8 +1759,8 @@ DQN_API char const *Dqn_CStringTrimPrefix(char const *src, Dqn_isize size, char DQN_API Dqn_b32 Dqn_CStringIsAllDigits(char const *src, Dqn_isize size); // separator: The separator between the thousand-th digits, i.e. separator = ',' converts '1,234' to '1234'. -DQN_API Dqn_u64 Dqn_CStringToU64(char const *buf, int size = -1, char separator = ','); -DQN_API Dqn_i64 Dqn_CStringToI64(char const *buf, int size = -1, char separator = ','); +DQN_API Dqn_u64 Dqn_CStringToU64(char const *buf, Dqn_isize size = -1, char separator = ','); +DQN_API Dqn_i64 Dqn_CStringToI64(char const *buf, Dqn_isize size = -1, char separator = ','); DQN_API Dqn_isize Dqn_LStringSize(wchar_t const *a); @@ -1787,13 +1793,14 @@ DQN_API Dqn_b32 Dqn_FileDelete(Dqn_String path); // file_size: (Optional) The size of the file in bytes, the allocated buffer is (file_size + 1 [null terminator]) in bytes. // allocator: (Optional) When null, the buffer is allocated with DQN_MALLOC, result should be freed with DQN_FREE. // return: nullptr if allocation failed. -#define Dqn_FileArenaReadFile(file, file_size, arena) Dqn__FileArenaReadFile(file, file_size, arena DQN_CALL_SITE("")) -#define Dqn_FileTaggedArenaReadFileToString(file, arena, tag) Dqn__FileArenaReadFileToString(file, arena DQN_CALL_SITE(tag)) -#define Dqn_FileArenaReadFileToString(file, arena) Dqn__FileArenaReadFileToString(file, arena DQN_CALL_SITE("")) -DQN_API Dqn_b32 Dqn_FileWriteFile(char const *file, char const *buffer, Dqn_isize buffer_size); +#define Dqn_FileArenaRead(file_path, file_path_size, file_size, arena) Dqn__FileArenaRead(file_path, file_path_size, file_size, arena DQN_CALL_SITE("")) +#define Dqn_FileTaggedArenaReadFileToString(file_path, file_path_size, arena, tag) Dqn__FileArenaReadToString(file_path, file_path_size, arena DQN_CALL_SITE(tag)) +#define Dqn_FileArenaReadToString(file_path, file_path_size, arena) Dqn__FileArenaReadToString(file_path, file_path_size, arena DQN_CALL_SITE("")) +DQN_API Dqn_b32 Dqn_FileWriteFile(char const *file_path, Dqn_isize file_path_size, char const *buffer, Dqn_isize buffer_size); -DQN_API char *Dqn__FileArenaReadFile(char const *file, Dqn_isize *file_size, Dqn_Arena *arena DQN_CALL_SITE_ARGS); -DQN_API Dqn_String Dqn__FileArenaReadFileToString(char const *file, Dqn_Arena *arena DQN_CALL_SITE_ARGS); +DQN_API char *Dqn__FileRead(char const *file_path, Dqn_isize file_path_size, Dqn_isize *file_size, Dqn_Arena *arena DQN_CALL_SITE_ARGS); +DQN_API char *Dqn__FileArenaRead(char const *file_path, Dqn_isize file_path_size, Dqn_isize *file_size, Dqn_Arena *arena DQN_CALL_SITE_ARGS); +DQN_API Dqn_String Dqn__FileArenaReadToString(char const *file, Dqn_isize file_path_size, Dqn_Arena *arena DQN_CALL_SITE_ARGS); // ------------------------------------------------------------------------------------------------- // NOTE: Date @@ -1829,7 +1836,7 @@ DQN_API Dqn_u64 Dqn_DateEpochTime(); // NOTE: OS // ------------------------------------------------------------------------------------------------- // Generate cryptographically secure bytes -DQN_API Dqn_b32 Dqn_OSSecureRNGBytes(void *buffer, Dqn_isize size); +DQN_API Dqn_b32 Dqn_OSSecureRNGBytes(void *buffer, Dqn_u32 size); // return: The directory without the trailing '/' or ('\' for windows). Empty // string with a nullptr if it fails. @@ -2015,7 +2022,7 @@ DQN_API int Dqn_WinUTF8ToWChar(Dqn_String src, wchar_t *dest, int dest_s DQN_API Dqn_StringW Dqn_WinArenaUTF8ToWChar(Dqn_String src, Dqn_Arena *arena); // See: Dqn_WinUTF8ToWchar notes -DQN_API int Dqn_WinWCharToUTF8SizeRequired(Dqn_String src); +DQN_API int Dqn_WinWCharToUTF8SizeRequired(Dqn_StringW src); DQN_API int Dqn_WinWCharToUTF8(Dqn_StringW src, char *dest, int dest_size); DQN_API Dqn_String Dqn_WinArenaWCharToUTF8(Dqn_StringW src, Dqn_Arena *arena); @@ -2024,10 +2031,95 @@ DQN_API Dqn_StringW Dqn_WinExecutableDirectoryW(Dqn_Arena *arena); // size: (Optional) The size of the current directory string returned // suffix: (Optional) A suffix to append to the current working directory // suffix_size: (Optional) The size of the suffix to append -DQN_API Dqn_String Dqn_WinCurrentDir(Dqn_Arena *arena, Dqn_Arena *temp_arena, Dqn_String suffix); -DQN_API Dqn_StringW Dqn_WinCurrentDirW(Dqn_Arena *arena, Dqn_StringW suffix); -DQN_API Dqn_Array Dqn_WinFolderFiles(Dqn_String path, Dqn_Arena *arena, Dqn_Arena *temp_arena); -DQN_API Dqn_Array Dqn_WinFolderFilesW(Dqn_StringW path, Dqn_Arena *arena); +DQN_API Dqn_String Dqn_WinCurrentDir(Dqn_Arena *arena, Dqn_Arena *temp_arena, Dqn_String suffix); +DQN_API Dqn_StringW Dqn_WinCurrentDirW(Dqn_Arena *arena, Dqn_StringW suffix); + +struct Dqn_WinFolderIteratorW +{ + void *handle; + Dqn_StringW file_name; + wchar_t file_name_buf[512]; +}; + +struct Dqn_WinFolderIterator +{ + void *handle; + Dqn_String file_name; + char file_name_buf[512]; +}; + +DQN_API bool Dqn_WinFolderIterate(Dqn_String path, Dqn_WinFolderIterator *it); +DQN_API bool Dqn_WinFolderWIterate(Dqn_StringW path, Dqn_WinFolderIteratorW *it); + +enum struct Dqn_WinNetHandleState +{ + Invalid, + Initialised, + RequestFailed, + RequestGood, +}; + +// The number of bytes each pump of the connection downloads at most. If this is +// zero we default to DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE. +#if !defined(DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE) + #define DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE 4096 +#endif + +struct Dqn_WinNetHandle +{ + // NOTE: We copy out the host name because it needs to be null-terminated. + // Luckily, we can assume a DNS domain won't exceed 256 characters so this + // will generally always work. + char host_name[256]; + int host_name_size; + + // NOTE: Everything after the domain/host name part of the string i.e. the + // '/test' part of the full url 'mywebsite.com/test'. + // TODO(dqn): I don't want to make our network API allocate here so we don't + // copy the string since we require that the string is null-terminated so + // then taking a pointer to the input string should work .. maybe this is + // ok? + char *url; + int url_size; + + // NOTE: docs.microsoft.com/en-us/windows/win32/wininet/setting-and-retrieving-internet-options#scope-of-hinternet-handle + // These handles have three levels: + // + // The root HINTERNET handle (created by a call to InternetOpen) would contain all the Internet options that affect this instance of WinINet. + // HINTERNET handles that connect to a server (created by a call to InternetConnect) + // HINTERNET handles associated with a resource or enumeration of resources on a particular server. + // + // More detailed information about the HINTERNET dependency is listed here + // NOTE: https://docs.microsoft.com/en-us/windows/win32/wininet/appendix-a-hinternet-handles + void *internet_open_handle; + void *internet_connect_handle; + void *http_handle; + Dqn_WinNetHandleState state; +}; + +// TODO(dqn): Useful options to expose in the handle +// https://docs.microsoft.com/en-us/windows/win32/wininet/option-flags +// INTERNET_OPTION_CONNECT_RETRIES -- default is 5 retries +// INTERNET_OPTION_CONNECT_TIMEOUT -- milliseconds +// INTERNET_OPTION_RECEIVE_TIMEOUT +// INTERNET_OPTION_SEND_TIMEOUT + +// Initialise a new networking handle that is pointing to the specified URL. +// The URL string must be null-terminated because Windows is a C API and +// requires null-termination. +Dqn_WinNetHandle Dqn_WinNetHandleInitCString(char const *url, int url_size); +Dqn_WinNetHandle Dqn_WinNetHandleInitString(Dqn_String url); + +void Dqn_WinNetHandleFinish(Dqn_WinNetHandle *handle); +bool Dqn_WinNetHandleIsValid(Dqn_WinNetHandle const *handle); +void Dqn_WinNetHandleSetUserAgentCString(Dqn_WinNetHandle *handle, char const *user_agent, int user_agent_size); +bool Dqn_WinNetHandlePump(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size, char *dest, int dest_size, size_t *download_size); +char * Dqn_WinNetHandlePumpToCString(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size, Dqn_Arena *arena, size_t *download_size); +Dqn_String Dqn_WinNetHandlePumpToString(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size, Dqn_Arena *arena); + +void Dqn_WinNetHandlePumpToCRTFile(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size, FILE *file); +char *Dqn_WinNetHandlePumpToMallocCString(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size, size_t *download_size); +Dqn_String Dqn_WinNetHandlePumpToMallocString(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size); #endif // DQN_OS_WIN32 // ------------------------------------------------------------------------------------------------- @@ -2281,21 +2373,30 @@ void Dqn_DSMapFree(Dqn_DSMap *map) *map = {}; } +template +Dqn_DSMapEntry *Dqn_DSMapFind(Dqn_DSMap *map, Dqn_u64 hash) +{ + Dqn_DSMapEntry *result = Dqn_DSMapFindOrAdd(map, hash, true /*find_only*/); + return result; +} + template Dqn_DSMapEntry *Dqn_DSMapFindOrAdd(Dqn_DSMap *map, Dqn_u64 hash, Dqn_b32 find_only) { if (!map->slots) { - if (!find_only) *map = Dqn_DSMapInit(DQN_DS_MAP_MIN_SIZE); - return nullptr; + if (find_only) + return nullptr; + else + *map = Dqn_DSMapInit(DQN_DS_MAP_MIN_SIZE); } Dqn_isize index = hash % map->size; Dqn_DSMapEntry *result = map->slots + index; while (result->occupied && result->hash != hash) { - Dqn_isize next_index = (index + 1) % map->size; - result = map->slots + next_index; + index = (index + 1) % map->size; + result = map->slots + index; } if (result->occupied) @@ -2852,10 +2953,10 @@ Dqn_b32 Dqn_ListIterate(Dqn_List *list, Dqn_ListIterator *iterator) // NOTE: Implementation // ------------------------------------------------------------------------------------------------- #if defined(DQN_IMPLEMENTATION) -#include // fprintf, FILE, stdout, stderr #if defined(DQN_OS_WIN32) #pragma comment(lib, "bcrypt") + #pragma comment(lib, "wininet") #if !defined(DQN_NO_WIN32_MINIMAL_HEADER) // Taken from Windows.h @@ -2916,6 +3017,29 @@ Dqn_b32 Dqn_ListIterate(Dqn_List *list, Dqn_ListIterator *iterator) #define MOVEFILE_REPLACE_EXISTING 0x00000001 #define MOVEFILE_COPY_ALLOWED 0x00000002 + // NOTE: Wininet + typedef WORD INTERNET_PORT; + #define INTERNET_OPEN_TYPE_PRECONFIG 0 // use registry configuration + #define INTERNET_DEFAULT_HTTPS_PORT 443 // HTTPS + #define INTERNET_SERVICE_HTTP 3 + #define INTERNET_OPTION_USER_AGENT 41 + #define INTERNET_FLAG_NO_AUTH 0x00040000 // no automatic authentication handling + #define INTERNET_FLAG_SECURE 0x00800000 // use PCT/SSL if applicable (HTTP) + + // NOTE: CreateFile + #define GENERIC_READ (0x80000000L) + #define GENERIC_WRITE (0x40000000L) + #define GENERIC_EXECUTE (0x20000000L) + #define GENERIC_ALL (0x10000000L) + + #define CREATE_NEW 1 + #define CREATE_ALWAYS 2 + #define OPEN_EXISTING 3 + #define OPEN_ALWAYS 4 + #define TRUNCATE_EXISTING 5 + + #define INVALID_FILE_SIZE ((DWORD)0xFFFFFFFF) + // --------------------------------------------------------------------- // Data Structures // --------------------------------------------------------------------- @@ -3020,6 +3144,45 @@ Dqn_b32 Dqn_ListIterate(Dqn_List *list, Dqn_ListIterator *iterator) FindExSearchMaxSearchOp } FINDEX_SEARCH_OPS; + + typedef enum { + INTERNET_SCHEME_PARTIAL = -2, + INTERNET_SCHEME_UNKNOWN = -1, + INTERNET_SCHEME_DEFAULT = 0, + INTERNET_SCHEME_FTP, + INTERNET_SCHEME_GOPHER, + INTERNET_SCHEME_HTTP, + INTERNET_SCHEME_HTTPS, + INTERNET_SCHEME_FILE, + INTERNET_SCHEME_NEWS, + INTERNET_SCHEME_MAILTO, + INTERNET_SCHEME_SOCKS, + INTERNET_SCHEME_JAVASCRIPT, + INTERNET_SCHEME_VBSCRIPT, + INTERNET_SCHEME_RES, + INTERNET_SCHEME_FIRST = INTERNET_SCHEME_FTP, + INTERNET_SCHEME_LAST = INTERNET_SCHEME_RES + } INTERNET_SCHEME; + + typedef struct { + DWORD dwStructSize; // size of this structure. Used in version check + char *lpszScheme; // pointer to scheme name + DWORD dwSchemeLength; // length of scheme name + INTERNET_SCHEME nScheme; // enumerated scheme type (if known) + char *lpszHostName; // pointer to host name + DWORD dwHostNameLength; // length of host name + INTERNET_PORT nPort; // converted port number + char *lpszUserName; // pointer to user name + DWORD dwUserNameLength; // length of user name + char *lpszPassword; // pointer to password + DWORD dwPasswordLength; // length of password + char *lpszUrlPath; // pointer to URL-path + DWORD dwUrlPathLength; // length of URL-path + char *lpszExtraInfo; // pointer to extra information (e.g. ?foo or #foo) + DWORD dwExtraInfoLength; // length of extra information + } URL_COMPONENTSA; + + // --------------------------------------------------------------------- // Functions // --------------------------------------------------------------------- @@ -3069,6 +3232,19 @@ Dqn_b32 Dqn_ListIterate(Dqn_List *list, Dqn_ListIterator *iterator) NTSTATUS BCryptOpenAlgorithmProvider(BCRYPT_ALG_HANDLE *phAlgorithm, wchar_t const *pszAlgId, wchar_t const *pszImplementation, unsigned long dwFlags); NTSTATUS BCryptGenRandom (BCRYPT_ALG_HANDLE hAlgorithm, unsigned char *pbBuffer, unsigned long cbBuffer, unsigned long dwFlags); + + BOOL InternetCrackUrlA (char const *lpszUrl, DWORD dwUrlLength, DWORD dwFlags, URL_COMPONENTSA *lpUrlComponents); + void *InternetOpenA (char const *lpszAgent, DWORD dwAccessType, char const *lpszProxy, char const *lpszProxyBypass, DWORD dwFlags); + void *InternetConnectA (void *hInternet, char const *lpszServerName, INTERNET_PORT nServerPort, char const *lpszUserName, char const *lpszPassword, DWORD dwService, DWORD dwFlags, DWORD *dwContext); + bool InternetSetOptionA (void *hInternet, DWORD dwOption, void *lpBuffer, DWORD dwBufferLength); + BOOL InternetReadFile (void *hFile, void *lpBuffer, DWORD dwNumberOfBytesToRead, DWORD *lpdwNumberOfBytesRead); + void *HttpOpenRequestA (void *hConnect, char const *lpszVerb, char const *lpszObjectName, char const *lpszVersion, char const *lpszReferrer, char const **lplpszAcceptTypes, DWORD dwFlags, DWORD *dwContext); + BOOL HttpSendRequestA (void *hRequest, char const *lpszHeaders, DWORD dwHeadersLength, void *lpOptional, DWORD dwOptionalLength); + BOOL InternetCloseHandle (void *hInternet); + HANDLE CreateFileW (wchar_t const *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); + BOOL CloseHandle (HANDLE hObject); + BOOL ReadFile (HANDLE hFile, void *lpBuffer, DWORD nNumberOfBytesToRead, DWORD *lpNumberOfBytesRead, struct OVERLAPPED *lpOverlapped); + BOOL GetFileSizeEx (HANDLE hFile, LARGE_INTEGER *lpFileSize); } #endif // !defined(DQN_NO_WIN32_MINIMAL_HEADER) #elif defined(DQN_OS_UNIX) @@ -3469,16 +3645,16 @@ DQN_API Dqn_b32 Dqn_StringIsValid(Dqn_String in) return result; } -DQN_API Dqn_String Dqn_String_Fmt(Dqn_Arena *arena DQN_CALL_SITE_ARGS, char const *fmt, ...) +DQN_API Dqn_String Dqn__StringFmt(Dqn_Arena *arena DQN_CALL_SITE_ARGS, char const *fmt, ...) { va_list va; va_start(va, fmt); - Dqn_String result = Dqn_String_FmtV(arena, fmt, va DQN_CALL_SITE_ARGS_INPUT); + Dqn_String result = Dqn__StringFmtV(arena, fmt, va DQN_CALL_SITE_ARGS_INPUT); va_end(va); return result; } -DQN_API Dqn_String Dqn_String_FmtV(Dqn_Arena *arena, char const *fmt, va_list va DQN_CALL_SITE_ARGS) +DQN_API Dqn_String Dqn__StringFmtV(Dqn_Arena *arena, char const *fmt, va_list va DQN_CALL_SITE_ARGS) { Dqn_String result = {}; va_list va2; @@ -3776,9 +3952,12 @@ DQN_API void Dqn_StringRemove(Dqn_String *in, Dqn_isize begin, Dqn_isize size) in->size -= size; } -DQN_API Dqn_isize Dqn_StringFind(Dqn_String src, Dqn_String find, Dqn_isize start_index, Dqn_StringEqCase eq_case) +DQN_API Dqn_isize Dqn_StringFindOffset(Dqn_String src, Dqn_String find, Dqn_isize start_index, Dqn_StringEqCase eq_case) { Dqn_isize result = -1; + if (!Dqn_StringIsValid(src) || !Dqn_StringIsValid(find)) + return -1; + Dqn_isize src_end = src.size - find.size; for (Dqn_isize index = start_index; index <= src_end; index++) { @@ -3793,6 +3972,23 @@ DQN_API Dqn_isize Dqn_StringFind(Dqn_String src, Dqn_String find, Dqn_isize star return result; } +DQN_API Dqn_String Dqn_StringFind(Dqn_String src, Dqn_String find, Dqn_isize start_index, Dqn_StringEqCase eq_case) +{ + Dqn_isize offset = Dqn_StringFindOffset(src, find, start_index, eq_case); + Dqn_String result = src; + if (offset == -1) + { + result.size = 0; + } + else + { + result.str += offset; + result.size -= offset; + } + + return result; +} + DQN_API Dqn_String Dqn_StringReplace(Dqn_String src, Dqn_String find, Dqn_String replace, @@ -3956,7 +4152,7 @@ DQN_API Dqn_String Dqn_StringListBuild(Dqn_StringList const *list, Dqn_Arena *ar // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_Arena // ------------------------------------------------------------------------------------------------- -DQN_API Dqn_ArenaStatsString Dqn_ArenaStats_String(Dqn_ArenaStats const *stats) +DQN_API Dqn_ArenaStatsString Dqn_ArenaStatsToString(Dqn_ArenaStats const *stats) { // NOTE: We use a non-standard format string that is only usable via // stb sprintf that GCC warns about as an error. This pragma mutes that. @@ -3973,7 +4169,7 @@ DQN_API Dqn_ArenaStatsString Dqn_ArenaStats_String(Dqn_ArenaStats const *stats) Dqn_ArenaStatsString result = {}; result.size = STB_SPRINTF_DECORATE(snprintf)(result.str, - Dqn_ArrayCountI(result.str), + DQN_CAST(int)Dqn_ArrayCountI(result.str), "%_$$I64d/%_$$I64d (wasted %_$$I64d - %d blks)", stats->bytes_used, stats->bytes_allocated, @@ -4248,10 +4444,10 @@ DQN_API void *Dqn__ArenaAllocate(Dqn_Arena *arena, Dqn_isize size, Dqn_u8 alignm return result; } -DQN_API void Dqn_ArenaLogStats(Dqn_Arena const *arena, char const *label) +DQN_API void Dqn_ArenaLogStats(Dqn_Arena const *arena) { - Dqn_ArenaStatsString highest = Dqn_ArenaStats_String(&arena->highest_stats); - Dqn_ArenaStatsString current = Dqn_ArenaStats_String(&arena->current_stats); + Dqn_ArenaStatsString highest = Dqn_ArenaStatsToString(&arena->highest_stats); + Dqn_ArenaStatsString current = Dqn_ArenaStatsToString(&arena->current_stats); DQN_LOG_M("HIGH %.*s\nCURR %.*s\n", highest.size, highest.str, current.size, current.str); } @@ -5009,8 +5205,8 @@ DQN_API int Dqn_UTF16EncodeCodepoint(Dqn_u16 utf16[2], Dqn_u32 codepoint) if (codepoint <= 0b1111'1111'1111'1111'1111) { Dqn_u32 surrogate_codepoint = codepoint + 0x10000; - utf16[0] = 0b1101'1000'0000'0000 | ((codepoint >> 10) & 0b11'1111'1111); // x - utf16[1] = 0b1101'1100'0000'0000 | ((codepoint >> 0) & 0b11'1111'1111); // y + utf16[0] = 0b1101'1000'0000'0000 | ((surrogate_codepoint >> 10) & 0b11'1111'1111); // x + utf16[1] = 0b1101'1100'0000'0000 | ((surrogate_codepoint >> 0) & 0b11'1111'1111); // y return 2; } @@ -5438,7 +5634,7 @@ DQN_API Dqn_b32 Dqn_CStringIsAllDigits(char const *src, Dqn_isize size) return result; } -DQN_API Dqn_u64 Dqn_CStringToU64(char const *buf, int size, char separator) +DQN_API Dqn_u64 Dqn_CStringToU64(char const *buf, Dqn_isize size, char separator) { Dqn_u64 result = 0; if (!buf) @@ -5447,7 +5643,7 @@ DQN_API Dqn_u64 Dqn_CStringToU64(char const *buf, int size, char separator) if (size <= -1) size = Dqn_CStringSize(buf); - for (int index = 0; index < size; ++index) + for (Dqn_isize index = 0; index < size; ++index) { char ch = buf[index]; if (index && ch == separator) @@ -5464,7 +5660,7 @@ DQN_API Dqn_u64 Dqn_CStringToU64(char const *buf, int size, char separator) return result; } -DQN_API Dqn_i64 Dqn_CStringToI64(char const *buf, int size, char separator) +DQN_API Dqn_i64 Dqn_CStringToI64(char const *buf, Dqn_isize size, char separator) { Dqn_i64 result = 0; if (!buf) @@ -5515,16 +5711,66 @@ DQN_API Dqn_isize Dqn_LStringSize(wchar_t const *src) // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_File // ------------------------------------------------------------------------------------------------- -DQN_API char *Dqn__FileReadFile(char const *file, Dqn_isize *file_size, Dqn_Arena *arena DQN_CALL_SITE_ARGS) +DQN_API char *Dqn__FileRead(char const *file_path, Dqn_isize file_path_size, Dqn_isize *file_size, Dqn_Arena *arena DQN_CALL_SITE_ARGS) { +#if defined(DQN_OS_WIN32) + if (file_path_size <= 0) + file_path_size = Dqn_CStringSize(file_path); + + Dqn_ThreadScratch scratch = Dqn_ThreadGetScratch(arena); + Dqn_String file_path_string = Dqn_StringInit(file_path, file_path_size); + Dqn_StringW file_path_string_w = Dqn_WinArenaUTF8ToWChar(file_path_string, scratch.arena); + + HANDLE file_handle = + CreateFileW(file_path_string_w.str, GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, nullptr); + if (file_handle == INVALID_HANDLE_VALUE) + { + Dqn_WinDumpLastError("Failed to open file %.*s", file_path_size, file_path); + return nullptr; + } + + DQN_DEFER { CloseHandle(file_handle); }; + LARGE_INTEGER win_file_size; + bool get_file_size_result = GetFileSizeEx(file_handle, &win_file_size); + if (!get_file_size_result || win_file_size.QuadPart > (DWORD)-1) + { + if (!get_file_size_result) + { + Dqn_WinDumpLastError("GetFileSizeEx"); + } + else + { + DQN_LOG_E( + "We don't support files larger than %$.2d yet, Win32 layer needs to use overlapped IO to chunk reading in " + "the data"); + } + + return nullptr; + } + + auto arena_undo = Dqn_ArenaBeginScope(arena); + auto *result = DQN_CAST(char *) Dqn__ArenaAllocate(arena, win_file_size.QuadPart + 1, alignof(char), Dqn_ZeroMem::No DQN_CALL_SITE_ARGS_INPUT); + + // TODO(dqn): We need to chunk this and ensure that readfile read the bytes we wanted. + DWORD bytes_read = 0; + if (ReadFile(file_handle, result, DQN_CAST(DWORD)win_file_size.QuadPart, &bytes_read, nullptr /*overlapped*/) == 0) + { + Dqn_ArenaEndScope(arena_undo); + Dqn_WinDumpLastError("ReadFile"); + return nullptr; + } + + if (file_size) *file_size = win_file_size.QuadPart; + return result; +#else Dqn_isize file_size_ = 0; if (!file_size) file_size = &file_size_; - FILE *file_handle = fopen(file, "rb"); + FILE *file_handle = fopen(file_path, "rb"); if (!file_handle) { - DQN_LOG_E("Failed to open file '%s' using fopen\n", file); + DQN_LOG_E("Failed to open file '%s' using fopen\n", file_path); return nullptr; } @@ -5556,22 +5802,28 @@ DQN_API char *Dqn__FileReadFile(char const *file, Dqn_isize *file_size, Dqn_Aren } return result; +#endif } -DQN_API Dqn_String Dqn__FileArenaReadFileToString(char const *file, Dqn_Arena *arena DQN_CALL_SITE_ARGS) +DQN_API Dqn_String Dqn__FileArenaReadToString(char const * file_path, + Dqn_isize file_path_size, + Dqn_Arena *arena DQN_CALL_SITE_ARGS) { Dqn_isize file_size = 0; - char * string = Dqn__FileReadFile(file, &file_size, arena DQN_CALL_SITE_ARGS_INPUT); + char * string = Dqn__FileRead(file_path, file_path_size, &file_size, arena DQN_CALL_SITE_ARGS_INPUT); Dqn_String result = Dqn_StringInit(string, file_size); return result; } -DQN_API Dqn_b32 Dqn_FileWriteFile(char const *file, char const *buffer, Dqn_isize buffer_size) +DQN_API Dqn_b32 Dqn_FileWriteFile(char const *file_path, Dqn_isize file_path_size, char const *buffer, Dqn_isize buffer_size) { - FILE *file_handle = fopen(file, "w+b"); + // TODO(dqn): Use OS apis + (void)file_path_size; + + FILE *file_handle = fopen(file_path, "w+b"); if (!file_handle) { - DQN_LOG_E("Failed to open file '%s' using fopen\n", file); + DQN_LOG_E("Failed to open file '%s' using fopen\n", file_path); return false; } @@ -5580,7 +5832,7 @@ DQN_API Dqn_b32 Dqn_FileWriteFile(char const *file, char const *buffer, Dqn_isiz if (write_count != 1) { - DQN_LOG_E("Failed to write to file '%s' using fwrite\n", file); + DQN_LOG_E("Failed to write to file '%s' using fwrite\n", file_path); return false; } @@ -5612,7 +5864,7 @@ DQN_API Dqn_b32 Dqn_FileExists(Dqn_String path) Dqn_b32 result = false; #if defined(DQN_OS_WIN32) wchar_t path_w[DQN_OS_WIN32_MAX_PATH]; - Dqn_WinUTF8ToWChar(path, path_w, Dqn_ArrayCountI(path_w)); + Dqn_WinUTF8ToWChar(path, path_w, DQN_CAST(int)Dqn_ArrayCountI(path_w)); WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; if (GetFileAttributesExW(path_w, GetFileExInfoStandard, &attrib_data)) @@ -5639,7 +5891,7 @@ DQN_API Dqn_b32 Dqn_FileDirExists(Dqn_String path) Dqn_b32 result = false; #if defined(DQN_OS_WIN32) wchar_t path_w[DQN_OS_WIN32_MAX_PATH]; - Dqn_WinUTF8ToWChar(path, path_w, Dqn_ArrayCountInt(path_w)); + Dqn_WinUTF8ToWChar(path, path_w, DQN_CAST(int)Dqn_ArrayCountInt(path_w)); WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; if (GetFileAttributesExW(path_w, GetFileExInfoStandard, &attrib_data)) @@ -5665,7 +5917,7 @@ DQN_API Dqn_FileInfo Dqn_FileGetInfo(Dqn_String path) #if defined(DQN_OS_WIN32) WIN32_FILE_ATTRIBUTE_DATA attrib_data = {}; wchar_t path_w[DQN_OS_WIN32_MAX_PATH]; - Dqn_WinUTF8ToWChar(path, path_w, Dqn_ArrayCountInt(path_w)); + Dqn_WinUTF8ToWChar(path, path_w, DQN_CAST(int)Dqn_ArrayCountInt(path_w)); if (!GetFileAttributesExW(path_w, GetFileExInfoStandard, &attrib_data)) return result; @@ -5707,8 +5959,8 @@ DQN_API Dqn_b32 Dqn_FileCopy(Dqn_String src, Dqn_String dest, Dqn_b32 overwrite) #if defined(DQN_OS_WIN32) wchar_t src_w [DQN_OS_WIN32_MAX_PATH]; wchar_t dest_w[DQN_OS_WIN32_MAX_PATH]; - Dqn_WinUTF8ToWChar(src, src_w, Dqn_ArrayCountInt(src_w)); - Dqn_WinUTF8ToWChar(dest, dest_w, Dqn_ArrayCountInt(dest_w)); + Dqn_WinUTF8ToWChar(src, src_w, DQN_CAST(int)Dqn_ArrayCountInt(src_w)); + Dqn_WinUTF8ToWChar(dest, dest_w, DQN_CAST(int)Dqn_ArrayCountInt(dest_w)); BOOL fail_if_exists = overwrite == false; result = CopyFileW(src_w, dest_w, fail_if_exists) != 0; @@ -5753,6 +6005,8 @@ DQN_API Dqn_b32 Dqn_FileMakeDir(Dqn_String path, Dqn_Arena *temp_arena) Dqn_u16 path_indexes[64] = {}; #if defined(DQN_OS_WIN32) + (void)temp_arena; + wchar_t src_w[DQN_OS_WIN32_MAX_PATH]; int src_w_size = Dqn_WinUTF8ToWChar(path, src_w, DQN_CAST(int)Dqn_ArrayCountI(src_w)); @@ -5917,7 +6171,7 @@ DQN_API Dqn_b32 Dqn_FileDelete(Dqn_String path) Dqn_b32 result = false; #if defined(DQN_OS_WIN32) wchar_t path_w [DQN_OS_WIN32_MAX_PATH]; - Dqn_WinUTF8ToWChar(path, path_w, Dqn_ArrayCountInt(path_w)); + Dqn_WinUTF8ToWChar(path, path_w, DQN_CAST(int)Dqn_ArrayCountInt(path_w)); result = DeleteFileW(path_w); if (!result) result = RemoveDirectoryW(path_w); @@ -5981,23 +6235,23 @@ DQN_API Dqn_DateHMSTimeString Dqn_DateHMSLocalTimeStringNow(char date_separator, Dqn_DateHMSTime const time = Dqn_DateHMSLocalTimeNow(); Dqn_DateHMSTimeString result = {}; - result.hms_size = STB_SPRINTF_DECORATE(snprintf)(result.hms, - Dqn_ArrayCountI(result.hms), - "%02d%c%02d%c%02d", - time.hour, - hms_separator, - time.minutes, - hms_separator, - time.seconds); + result.hms_size = DQN_CAST(Dqn_i8) STB_SPRINTF_DECORATE(snprintf)(result.hms, + DQN_CAST(int) Dqn_ArrayCountI(result.hms), + "%02d%c%02d%c%02d", + time.hour, + hms_separator, + time.minutes, + hms_separator, + time.seconds); - result.date_size = STB_SPRINTF_DECORATE(snprintf)(result.date, - Dqn_ArrayCountI(result.date), - "%d%c%02d%c%02d", - time.year, - date_separator, - time.month, - date_separator, - time.day); + result.date_size = DQN_CAST(Dqn_i8) STB_SPRINTF_DECORATE(snprintf)(result.date, + DQN_CAST(int) Dqn_ArrayCountI(result.date), + "%d%c%02d%c%02d", + time.year, + date_separator, + time.month, + date_separator, + time.day); return result; } @@ -6028,7 +6282,7 @@ DQN_API Dqn_u64 Dqn_DateEpochTime() // ------------------------------------------------------------------------------------------------- // NOTE: OS // ------------------------------------------------------------------------------------------------- -DQN_API Dqn_b32 Dqn_OSSecureRNGBytes(void *buffer, Dqn_isize size) +DQN_API Dqn_b32 Dqn_OSSecureRNGBytes(void *buffer, Dqn_u32 size) { if (!buffer || size < 0) return false; @@ -6084,9 +6338,9 @@ DQN_API Dqn_String Dqn_OSExecutableDirectory(Dqn_Arena *arena) #if defined(DQN_OS_WIN32) char temp_mem[sizeof(wchar_t) * DQN_OS_WIN32_MAX_PATH + sizeof(Dqn_ArenaMemBlock)]; - Dqn_Arena temp_arena = Dqn_ArenaInitWithMemory(temp_mem, Dqn_ArrayCountI(temp_mem)); - Dqn_StringW exe_dir_w = Dqn_WinExecutableDirectoryW(&temp_arena); - result = Dqn_WinArenaWCharToUTF8(exe_dir_w, arena); + Dqn_Arena temp_arena = Dqn_ArenaInitWithMemory(temp_mem, Dqn_ArrayCountI(temp_mem)); + Dqn_StringW exe_dir_w = Dqn_WinExecutableDirectoryW(&temp_arena); + result = Dqn_WinArenaWCharToUTF8(exe_dir_w, arena); #elif defined(DQN_OS_UNIX) @@ -6699,14 +6953,14 @@ DQN_API void Dqn__WinDumpLastError(Dqn_String file, Dqn_String function, Dqn_uin { va_list args; va_start(args, fmt); - Dqn_LogV(Dqn_LogType::Error, dqn__lib.log_user_data, file_name, file_name_size, function.str, function.size, line, fmt, args); + Dqn_LogV(Dqn_LogType::Error, dqn__lib.log_user_data, file_name, DQN_CAST(int)file_name_size, function.str, DQN_CAST(int)function.size, line, fmt, args); va_end(args); } if (msg.size) - Dqn_Log(Dqn_LogType::Error, dqn__lib.log_user_data, file_name, file_name_size, function.str, function.size, line, "Error: %.*s", msg.size, msg.str); + Dqn_Log(Dqn_LogType::Error, dqn__lib.log_user_data, file_name, DQN_CAST(int)file_name_size, function.str, DQN_CAST(int)function.size, line, "Error: %.*s", msg.size, msg.str); else - Dqn_Log(Dqn_LogType::Error, dqn__lib.log_user_data, file_name, file_name_size, function.str, function.size, line, "FormatMessage error: %d. No error message for: %d", GetLastError(), msg.code); + Dqn_Log(Dqn_LogType::Error, dqn__lib.log_user_data, file_name, DQN_CAST(int)file_name_size, function.str, DQN_CAST(int)function.size, line, "FormatMessage error: %d. No error message for: %d", GetLastError(), msg.code); } DQN_API int Dqn_WinUTF8ToWCharSizeRequired(Dqn_String src) @@ -6752,7 +7006,7 @@ DQN_API Dqn_StringW Dqn_WinArenaUTF8ToWChar(Dqn_String src, Dqn_Arena *arena) wchar_t *string = Dqn_ArenaNewArray(arena, wchar_t, required + 1, Dqn_ZeroMem::No); int string_size = Dqn_WinUTF8ToWChar(src, string, required); - result = Dqn_StringW{string, string_size, string_size}; + result = Dqn_StringW{string, string_size}; return result; } @@ -6805,7 +7059,7 @@ DQN_API Dqn_String Dqn_WinArenaWCharToUTF8(Dqn_StringW src, Dqn_Arena *arena) DQN_API Dqn_StringW Dqn_WinExecutableDirectoryW(Dqn_Arena *arena) { wchar_t buffer[DQN_OS_WIN32_MAX_PATH]; - int file_path_size = GetModuleFileNameW(nullptr /*module*/, buffer, Dqn_ArrayCountI(buffer)); + int file_path_size = GetModuleFileNameW(nullptr /*module*/, buffer, DQN_CAST(DWORD)Dqn_ArrayCountI(buffer)); DQN_HARD_ASSERT_MSG(GetLastError() != ERROR_INSUFFICIENT_BUFFER, "How the hell?"); int directory_size = file_path_size; @@ -6826,7 +7080,7 @@ DQN_API Dqn_StringW Dqn_WinExecutableDirectoryW(Dqn_Arena *arena) { DQN_MEMCOPY(str, buffer, sizeof(wchar_t) * directory_size); str[directory_size] = 0; - result = Dqn_StringW{str, directory_size, directory_size}; + result = Dqn_StringW{str, directory_size}; } } @@ -6869,77 +7123,369 @@ DQN_API Dqn_StringW Dqn_WinCurrentDirW(Dqn_Arena *arena, Dqn_StringW suffix) w_path[desired_size] = 0; } - result = Dqn_StringW{w_path, desired_size - 1, desired_size - 1}; + result = Dqn_StringW{w_path, desired_size - 1}; return result; } -DQN_API Dqn_Array Dqn_WinFolderFilesW(Dqn_StringW path, Dqn_Arena *arena) +DQN_API bool Dqn_WinFolderWIterate(Dqn_StringW path, Dqn_WinFolderIteratorW *it) { - enum Step + WIN32_FIND_DATAW find_data = {}; + if (it->handle) { - Step_CountFiles, - Step_Allocate, - Step_Count - }; - - Dqn_Array result = {}; - Dqn_isize num_files = 0; - for (int step = Step_CountFiles; step < Step_Count; step++) + if (FindNextFileW(it->handle, &find_data) == 0) + return false; + } + else { - if (step == Step_Allocate) - result = Dqn_ArrayInitWithArenaNoGrow(arena, Dqn_StringW, num_files, 0, Dqn_ZeroMem::No); + it->handle = FindFirstFileExW(path.str, /*LPCWSTR lpFileName,*/ + FindExInfoStandard, /*FINDEX_INFO_LEVELS fInfoLevelId,*/ + &find_data, /*LPVOID lpFindFileData,*/ + FindExSearchNameMatch, /*FINDEX_SEARCH_OPS fSearchOp,*/ + nullptr, /*LPVOID lpSearchFilter,*/ + FIND_FIRST_EX_LARGE_FETCH /*DWORD dwAdditionalFlags)*/); - WIN32_FIND_DATAW find_data = {}; - HANDLE find_handle = FindFirstFileExW(path.str, /*LPCWSTR lpFileName,*/ - FindExInfoStandard, /*FINDEX_INFO_LEVELS fInfoLevelId,*/ - &find_data, /*LPVOID lpFindFileData,*/ - FindExSearchNameMatch, /*FINDEX_SEARCH_OPS fSearchOp,*/ - nullptr, /*LPVOID lpSearchFilter,*/ - FIND_FIRST_EX_LARGE_FETCH /*DWORD dwAdditionalFlags)*/); + if (it->handle == INVALID_HANDLE_VALUE) + return false; + } - if (find_handle == INVALID_HANDLE_VALUE) - return result; + it->file_name_buf[0] = 0; + it->file_name = Dqn_StringW{it->file_name_buf, 0}; - do + do + { + if (find_data.cFileName[0] == '.' || (find_data.cFileName[0] == '.' && find_data.cFileName[1] == '.')) + continue; + + if (find_data.dwFileAttributes & ~FILE_ATTRIBUTE_DIRECTORY) { - if (find_data.cFileName[0] == '.' || (find_data.cFileName[0] == '.' && find_data.cFileName[1] == '.')) - { - continue; - } + it->file_name.size = Dqn_LStringSize(find_data.cFileName); + DQN_ASSERT(it->file_name.size < (Dqn_ArrayCountI(it->file_name_buf) - 1)); - if (find_data.dwFileAttributes & ~FILE_ATTRIBUTE_DIRECTORY) + DQN_MEMCOPY(it->file_name.str, find_data.cFileName, it->file_name.size * sizeof(wchar_t)); + it->file_name_buf[it->file_name.size] = 0; + + break; + } + } while (FindNextFileW(it->handle, &find_data) != 0); + + return true; +} + +DQN_API bool Dqn_WinFolderIterate(Dqn_String path, Dqn_WinFolderIterator *it) +{ + wchar_t path_w[DQN_OS_WIN32_MAX_PATH]; + path_w[0] = 0; + int path_w_size = 0; + + Dqn_WinFolderIteratorW wide_it = {}; + if (it->handle) + wide_it.handle = it->handle; + else + { + path_w_size = Dqn_WinUTF8ToWChar(path, path_w, DQN_OS_WIN32_MAX_PATH); + if (path_w[path_w_size - 2] != L'\\' && + path_w[path_w_size - 1] != L'*') + { + if ((path_w_size + 2) < (DQN_OS_WIN32_MAX_PATH - 1)) { - LARGE_INTEGER file_size = {}; - file_size.LowPart = find_data.nFileSizeLow; - file_size.HighPart = find_data.nFileSizeHigh; - if (step == Step_CountFiles) - { - num_files++; - } - else - { - DQN_ASSERT(step == Step_Allocate); - Dqn_StringW *string = Dqn_ArrayMake(&result, 1); - string->size = Dqn_LStringSize(find_data.cFileName); - string->str = Dqn_ArenaCopyNullTerminate(arena, wchar_t, find_data.cFileName, string->size); - } + path_w[path_w_size++] = L'\\'; + path_w[path_w_size++] = L'*'; } - } while (FindNextFileW(find_handle, &find_data) != 0); + } + else if (path_w[path_w_size - 1] == L'\\') + { + if ((path_w_size + 1) < (DQN_OS_WIN32_MAX_PATH - 1)) + { + path_w[path_w_size++] = L'*'; + } + } + + path_w[path_w_size++] = 0; + DQN_ASSERT(path_w_size <= DQN_OS_WIN32_MAX_PATH); + } + + bool result = Dqn_WinFolderWIterate(Dqn_StringW{path_w, path_w_size}, &wide_it); + it->handle = wide_it.handle; + if (result) + { + int size = Dqn_WinWCharToUTF8(wide_it.file_name, it->file_name_buf, DQN_CAST(int)Dqn_ArrayCountI(it->file_name_buf)); + it->file_name = Dqn_StringInit(it->file_name_buf, size); } return result; } -DQN_API Dqn_Array Dqn_WinFolderFiles(Dqn_String path, Dqn_Arena *arena, Dqn_Arena *temp_arena) +DQN_API Dqn_WinNetHandle Dqn_WinNetHandleInitCString(char const *url, int url_size) { - auto arena_scope = Dqn_ArenaScope(temp_arena); - Dqn_StringW w_path = Dqn_WinArenaUTF8ToWChar(path, temp_arena); - Dqn_Array files = Dqn_WinFolderFilesW(w_path, temp_arena); + URL_COMPONENTSA components = {}; + components.dwStructSize = sizeof(components); + components.dwHostNameLength = url_size; + components.dwUrlPathLength = url_size; - Dqn_Array result = Dqn_ArrayInitWithArenaNoGrow(arena, Dqn_String, files.size, 0, Dqn_ZeroMem::No); - for (Dqn_StringW file : files) - Dqn_ArrayAdd(&result, Dqn_WinArenaWCharToUTF8(file, arena)); + // Seperate the URL into bits and bobs + Dqn_WinNetHandle result = {}; + if (!InternetCrackUrlA(url, url_size, 0 /*flags*/, &components)) + { + Dqn_WinDumpLastError("InternetCrackUrlA"); + return result; + } + if (url[url_size] != 0) + { + DQN_LOG_E("URL '%.*s' must be null-terminated", url_size, url); + return result; + } + + if (components.dwHostNameLength > (sizeof(result.host_name)/sizeof(result.host_name[0])) - 1) + { + // TODO(dqn): Error message for host being too long + return result; + } + + result.host_name_size = components.dwHostNameLength; + DQN_MEMCOPY(result.host_name, components.lpszHostName, result.host_name_size); + result.host_name[result.host_name_size] = 0; + + result.url_size = components.dwUrlPathLength; + result.url = components.lpszUrlPath; + + // Create the Win32 networking handles we need + result.internet_open_handle = InternetOpenA("Generic/Win32", + INTERNET_OPEN_TYPE_PRECONFIG, + nullptr /*proxy*/, + nullptr /*proxy bypass*/, + 0 /*flags*/); + + result.internet_connect_handle = InternetConnectA(result.internet_open_handle, + result.host_name, + INTERNET_DEFAULT_HTTPS_PORT, + nullptr /*username*/, + nullptr /*password*/, + INTERNET_SERVICE_HTTP, + 0 /*flags*/, + 0 /*context*/); + + result.state = Dqn_WinNetHandleState::Initialised; + return result; +} + +DQN_API Dqn_WinNetHandle Dqn_WinNetHandleInitString(Dqn_String url) +{ + Dqn_WinNetHandle result = Dqn_WinNetHandleInitCString(url.str, DQN_CAST(int)url.size); + return result; +} + +DQN_API void Dqn_WinNetHandleFinish(Dqn_WinNetHandle *handle) +{ + if (!Dqn_WinNetHandleIsValid(handle)) + return; + + InternetCloseHandle(handle->internet_open_handle); + InternetCloseHandle(handle->internet_connect_handle); + InternetCloseHandle(handle->http_handle); + handle->internet_open_handle = nullptr; + handle->internet_connect_handle = nullptr; + handle->http_handle = nullptr; +} + +DQN_API bool Dqn_WinNetHandleIsValid(Dqn_WinNetHandle const *handle) +{ + bool result = handle->state >= Dqn_WinNetHandleState::Initialised; + return result; +} + +DQN_API void Dqn_WinNetHandleSetUserAgentCString(Dqn_WinNetHandle *handle, char const *user_agent, int user_agent_size) +{ + if (!Dqn_WinNetHandleIsValid(handle)) + return; + + InternetSetOptionA(handle->internet_open_handle, INTERNET_OPTION_USER_AGENT, (void *)user_agent, user_agent_size); +} + +DQN_API bool Dqn_WinNetHandlePump(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size, char *dest, int dest_size, size_t *download_size) +{ + if (!Dqn_WinNetHandleIsValid(handle)) + return false; + + if (handle->state == Dqn_WinNetHandleState::Initialised) + { + DQN_ASSERT(handle->http_handle == nullptr); + + const char *ACCEPT_TYPES[] = {"text/html", nullptr}; + handle->http_handle = HttpOpenRequestA(handle->internet_connect_handle, + http_verb, + handle->url, + nullptr /*http version*/, + nullptr /*referrer*/, + nullptr, + INTERNET_FLAG_NO_AUTH | INTERNET_FLAG_SECURE, + 0 /*context*/); + + if (HttpSendRequestA(handle->http_handle, + nullptr /*headers*/, + 0 /*headers length*/, + post_data, + post_data_size)) + { + handle->state = Dqn_WinNetHandleState::RequestGood; + return true; + } + else + { + handle->state = Dqn_WinNetHandleState::RequestFailed; + Dqn_WinDumpLastError("Failed to send request for: %.*s", handle->host_name_size, handle->host_name); + } + } + + if (handle->state == Dqn_WinNetHandleState::RequestFailed) + return false; + + bool result = true; + DWORD bytes_read; + if (InternetReadFile(handle->http_handle, dest, dest_size, &bytes_read)) + { + if (bytes_read == 0) + result = false; + *download_size = bytes_read; + } + else + { + *download_size = 0; + result = false; + } + + if (!result) + { + // NOTE: If it's false here, we've finished downloading/the pumping the + // handled finished. We can reset the handle state to allow the user to + // re-use this handle by calling the function again with new post data. + // IF they need to set a new URL/resource location then they need to + // make a new handle for that otherwise they can re-use this handle to + // hit that same end point. + handle->state = Dqn_WinNetHandleState::Initialised; + InternetCloseHandle(handle->http_handle); + handle->http_handle = nullptr; + } + + return result; +} + +struct Dqn_WinNetChunk +{ + char data[DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE]; + size_t size; + Dqn_WinNetChunk *next; +}; + +DQN_API char *Dqn_WinNetHandlePumpToCString(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size, Dqn_Arena *arena, size_t *download_size) +{ + Dqn_ThreadScratch scratch = Dqn_ThreadGetScratch(arena); + size_t total_size = 0; + Dqn_WinNetChunk * first_chunk = nullptr; + for (Dqn_WinNetChunk *last_chunk = nullptr;;) + { + Dqn_WinNetChunk *chunk = Dqn_ArenaNew(scratch.arena, Dqn_WinNetChunk, Dqn_ZeroMem::Yes); + bool pump_result = Dqn_WinNetHandlePump(handle, http_verb, post_data, post_data_size, chunk->data, DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE, &chunk->size); + if (chunk->size) + { + total_size += chunk->size; + if (first_chunk) + { + last_chunk->next = chunk; + last_chunk = chunk; + } + else + { + first_chunk = chunk; + last_chunk = chunk; + } + } + + if (!pump_result) + break; + } + + char *result = Dqn_ArenaNewArray(arena, char, total_size + 1 /*null-terminator*/, Dqn_ZeroMem::No); + char *result_ptr = result; + for (Dqn_WinNetChunk *chunk = first_chunk; chunk; chunk = chunk->next) + { + DQN_MEMCOPY(result_ptr, chunk->data, chunk->size); + result_ptr += chunk->size; + } + + *download_size = total_size; + result[total_size] = 0; + return result; +} + +DQN_API Dqn_String Dqn_WinNetHandlePumpToString(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size, Dqn_Arena *arena) +{ + size_t size = 0; + char * download = Dqn_WinNetHandlePumpToCString(handle, http_verb, post_data, post_data_size, arena, &size); + Dqn_String result = Dqn_StringInit(download, size); + return result; +} + +DQN_API void Dqn_WinNetHandlePumpToCRTFile(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size, FILE *file) +{ + for (bool keep_pumping = true; keep_pumping;) + { + char buffer[DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE]; + size_t buffer_size = 0; + keep_pumping = Dqn_WinNetHandlePump(handle, http_verb, post_data, post_data_size, buffer, sizeof(buffer), &buffer_size); + fprintf(file, "%.*s", (int)buffer_size, buffer); + } +} + +DQN_API char *Dqn_WinNetHandlePumpToMallocCString(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size, size_t *download_size) +{ + size_t total_size = 0; + Dqn_WinNetChunk * first_chunk = nullptr; + for (Dqn_WinNetChunk *last_chunk = nullptr;;) + { + Dqn_WinNetChunk *chunk = (Dqn_WinNetChunk *)calloc(1, sizeof(Dqn_WinNetChunk)); + bool pump_result = Dqn_WinNetHandlePump(handle, http_verb, post_data, post_data_size, chunk->data, DQN_WIN_NET_HANDLE_DOWNLOAD_SIZE, &chunk->size); + if (chunk->size) + { + total_size += chunk->size; + if (first_chunk) + { + last_chunk->next = chunk; + last_chunk = chunk; + } + else + { + first_chunk = chunk; + last_chunk = chunk; + } + } + + if (!pump_result) + break; + } + + char *result = (char *)malloc((total_size + 1) * sizeof(char)); + char *result_ptr = result; + for (Dqn_WinNetChunk *chunk = first_chunk; chunk;) + { + DQN_MEMCOPY(result_ptr, chunk->data, chunk->size); + result_ptr += chunk->size; + + Dqn_WinNetChunk *prev_chunk = chunk; + chunk = chunk->next; + free(prev_chunk); + } + + *download_size = total_size; + result[total_size] = 0; + return result; +} + +DQN_API Dqn_String Dqn_WinNetHandlePumpToMallocString(Dqn_WinNetHandle *handle, char const *http_verb, char *post_data, int post_data_size) +{ + size_t download_size = 0; + char * download = Dqn_WinNetHandlePumpToMallocCString(handle, http_verb, post_data, post_data_size, &download_size); + Dqn_String result = Dqn_StringInit(download, download_size); return result; } #endif diff --git a/dqn_cpp_file.h b/dqn_cpp_file.h index 0f9fb7f..a7f2de2 100644 --- a/dqn_cpp_file.h +++ b/dqn_cpp_file.h @@ -17,58 +17,58 @@ struct Dqn_CppFile 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_CppFile_LineBegin (Dqn_CppFile *cpp, char const *fmt, ...); -void Dqn_CppFile_LineEnd (Dqn_CppFile *cpp, char const *fmt, ...); -void Dqn_CppFile_LineAdd (Dqn_CppFile *cpp, char const *fmt, ...); -void Dqn_CppFile_LineV (Dqn_CppFile *cpp, char const *fmt, va_list args); -void Dqn_CppFile_Line (Dqn_CppFile *cpp, char const *fmt, ...); +void Dqn_CppFLineBeginV (Dqn_CppFile *cpp, char const *fmt, va_list args); +void Dqn_CppFLineBegin (Dqn_CppFile *cpp, char const *fmt, ...); +void Dqn_CppFLineEnd (Dqn_CppFile *cpp, char const *fmt, ...); +void Dqn_CppFLineAdd (Dqn_CppFile *cpp, char const *fmt, ...); +void Dqn_CppFLineV (Dqn_CppFile *cpp, char const *fmt, va_list args); +void Dqn_CppFLine (Dqn_CppFile *cpp, char const *fmt, ...); -void Dqn_CppFile_NewLine (Dqn_CppFile *cpp); -void Dqn_CppFile_Indent (Dqn_CppFile *cpp); -void Dqn_CppFile_Unindent (Dqn_CppFile *cpp); +void Dqn_CppFNewLine (Dqn_CppFile *cpp); +void Dqn_CppFIndent (Dqn_CppFile *cpp); +void Dqn_CppFUnindent (Dqn_CppFile *cpp); // 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 // string. When fmt is nullptr, no new line will be appended. -void Dqn_CppFile_BeginBlock (Dqn_CppFile *cpp, char const *fmt, ...); -void Dqn_CppFile_EndBlock (Dqn_CppFile *cpp, bool trailing_semicolon, bool new_line_on_next_block); +void Dqn_CppFBeginBlock (Dqn_CppFile *cpp, char const *fmt, ...); +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_CppFile_EndForBlock(cpp) Dqn_CppFile_EndBlock(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_CppFile_EndFuncBlock(cpp) Dqn_CppFile_EndBlock(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_CppFEndEnumBlock(cpp) Dqn_CppFEndBlock(cpp, true /*trailing_semicolon*/, true /*new_line_on_next_block*/) +#define Dqn_CppFEndForBlock(cpp) Dqn_CppFEndBlock(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_CppFEndFuncBlock(cpp) Dqn_CppFEndBlock(cpp, false /*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 #if defined(DQN_CPP_FILE_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; 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, ""); 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_start(args, fmt); - Dqn_CppFile_LineBeginV(cpp, fmt, args); + Dqn_CppFLineBeginV(cpp, fmt, 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) { @@ -81,7 +81,7 @@ void Dqn_CppFile_LineEnd(Dqn_CppFile *cpp, char const *fmt, ...) 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_start(args, fmt); @@ -89,55 +89,55 @@ void Dqn_CppFile_LineAdd(Dqn_CppFile *cpp, char const *fmt, ...) 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_CppFile_LineEnd(cpp, nullptr); + Dqn_CppFLineBeginV(cpp, fmt, args); + 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_start(args, fmt); - Dqn_CppFile_LineBeginV(cpp, fmt, args); - Dqn_CppFile_LineEnd(cpp, nullptr); + Dqn_CppFLineBeginV(cpp, fmt, args); + Dqn_CppFLineEnd(cpp, nullptr); va_end(args); } -void Dqn_CppFile_NewLine(Dqn_CppFile *cpp) +void Dqn_CppFNewLine(Dqn_CppFile *cpp) { fputc('\n', cpp->file); } -void Dqn_CppFile_Indent(Dqn_CppFile *cpp) +void Dqn_CppFIndent(Dqn_CppFile *cpp) { cpp->indent++; } -void Dqn_CppFile_Unindent(Dqn_CppFile *cpp) +void Dqn_CppFUnindent(Dqn_CppFile *cpp) { cpp->indent--; 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) { va_list args; va_start(args, fmt); - Dqn_CppFile_LineV(cpp, fmt, args); + Dqn_CppFLineV(cpp, fmt, args); va_end(args); } - Dqn_CppFile_Line(cpp, "{"); - Dqn_CppFile_Indent(cpp); + Dqn_CppFLine(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_CppFile_Line(cpp, trailing_semicolon ? "};" : "}"); + Dqn_CppFUnindent(cpp); + Dqn_CppFLine(cpp, trailing_semicolon ? "};" : "}"); if (new_line_on_next_block) fputc('\n', cpp->file); } #endif // DQN_CPP_FILE_IMPLEMENTATION diff --git a/dqn_jsmn.h b/dqn_jsmn.h index 23fa674..16dc793 100644 --- a/dqn_jsmn.h +++ b/dqn_jsmn.h @@ -14,7 +14,48 @@ // code of the header file. This will also automatically enable the JSMN // implementation. // -#include +// #define DQN_JSMN_NO_CRT +// Define this macro to disable functions using the CRT where possible. JSMN +// itself includes and this is the only dependency. +// + +#if !defined(DQN_JSMN_NO_CRT) + #include + #include +#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 +#endif + +#if !defined(DQN_JSMN_DEBUG_BREAK) + #include + #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 @@ -242,69 +283,78 @@ int jsmn_iterator_next( jsmn_iterator_t *iterator, jsmntok_t **jsmn_identifier, // ----------------------------------------------------------------------------- // Header File // ----------------------------------------------------------------------------- -#define DQN_JSMN_STRING(string) Dqn_JsmnString{(string), sizeof(string) - 1} -struct Dqn_JsmnString -{ - union { - char * str; - char const *const_str; - }; - int size; -}; +#if defined(_WIN32) + typedef unsigned __int64 Dqn_JsmnU64; + typedef signed __int64 Dqn_JsmnI64; +#else + typedef unsigned long long Dqn_JsmnU64; + typedef signed long long Dqn_JsmnI64; +#endif -bool Dqn_Jsmn_StringEq(Dqn_JsmnString lhs, Dqn_JsmnString rhs); -bool Dqn_Jsmn_StringIsValid(Dqn_JsmnString string); -bool Dqn_Jsmn_IsDigit(char ch); -uint64_t Dqn_Jsmn_StringToU64(Dqn_JsmnString string); -bool operator==(Dqn_JsmnString lhs, Dqn_JsmnString rhs); +typedef struct Dqn_JsmnString +{ + union Dqn_JsmnStringBuffer { char const *const_str; char *str; } buf; + 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); +#endif // __cplusplus #define DQN_JSMN_X_MACRO \ + DQN_JSMN_X_ENTRY(Invalid) \ DQN_JSMN_X_ENTRY(Object) \ DQN_JSMN_X_ENTRY(Array) \ DQN_JSMN_X_ENTRY(String) \ DQN_JSMN_X_ENTRY(Number) \ DQN_JSMN_X_ENTRY(Bool) -enum Dqn_JsmnTokenIs +typedef enum Dqn_JsmnTokenIs { #define DQN_JSMN_X_ENTRY(enum_val) Dqn_JsmnTokenIs_##enum_val, DQN_JSMN_X_MACRO #undef DQN_JSMN_X_ENTRY -}; +} Dqn_JsmnTokenIs; -Dqn_JsmnString const Dqn_Jsmn_TokenIsToString[] -{ -#define DQN_JSMN_X_ENTRY(enum_val) DQN_JSMN_STRING(#enum_val), - DQN_JSMN_X_MACRO -#undef DQN_JSMN_X_ENTRY -}; - -struct Dqn_JsmnError +typedef struct Dqn_JsmnError { jsmntok_t token; Dqn_JsmnString json; + Dqn_JsmnTokenIs actual; Dqn_JsmnTokenIs expected; 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 -}; +} Dqn_JsmnError; -#define DQN_JSMN_ERROR_HANDLE_SIZE 128 -struct Dqn_JsmnErrorHandle +#if !defined(DQN_JSMN_ERROR_HANDLE_SIZE) + #define DQN_JSMN_ERROR_HANDLE_SIZE 128 +#endif + +typedef struct Dqn_JsmnErrorHandle { Dqn_JsmnError errors[DQN_JSMN_ERROR_HANDLE_SIZE]; int errors_size; -}; +} Dqn_JsmnErrorHandle; -struct Dqn_Jsmn +typedef struct Dqn_Jsmn { bool valid; jsmn_parser parser; Dqn_JsmnString json; - int tokens_size; + bool json_needs_crt_free; + unsigned int tokens_size; jsmntok_t *tokens; -}; +} Dqn_Jsmn; -struct Dqn_JsmnIterator +typedef struct Dqn_JsmnIterator { bool init; 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 // over the object/array we just iterated. int token_index_hint; -}; +} Dqn_JsmnIterator; // 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 -// set with the 'jsmn->tokens' and 'jsmn->tokens_size' fields set to a valid -// destination buffer with a sufficient size that will be written on completion -// of the function. The required amount of tokens can be calculated using -// Dqn_Jsmn_TokensRequired. -// -// The function *does* not validate that the 'jsmn->tokens_size' is sufficient -// to hold the tokens in release mode. -// -// return: False if any of the parameters are invalid or the 'jsmn' tokens or -// size are not set, otherwise true. Additionally, 'jsmn->valid' is set -// accordingly to match the result of initialisation. -bool Dqn_Jsmn_InitWithJSONCString(char const *json, int size, Dqn_Jsmn *jsmn); +// Initialise a Dqn_Jsmn context by passing the JSON string and pass in a token +// array sufficient to parse the JSON string. The number of tokens that must be +// allocated and passed in can be calculated using Dqn_JsmnTokensRequire(...). +Dqn_Jsmn Dqn_JsmnInitWithJSONCString(char const *json, Dqn_JsmnI64 size, jsmntok_t *tokens, unsigned int tokens_size); +Dqn_Jsmn Dqn_JsmnInitWithJSONString(Dqn_JsmnString json, jsmntok_t *tokens, unsigned int tokens_size); + +#if !defined(DQN_JSMN_NO_CRT) +Dqn_Jsmn Dqn_JsmnInitWithMallocJSONCString(char const *json, Dqn_JsmnI64 size); +Dqn_Jsmn Dqn_JsmnInitWithMallocJSON(Dqn_JsmnString json); +Dqn_Jsmn Dqn_JsmnInitWithMallocJSONFile(Dqn_JsmnString json_file); +void Dqn_JsmnFree(Dqn_Jsmn *jsmn); +void Dqn_JsmnErrorHandleDumpToCRTFile(Dqn_JsmnErrorHandle const *handle, FILE *file); +void Dqn_JsmnErrorDumpToCRTFile(Dqn_JsmnError const *error, FILE *file); +#endif // !DQN_JSMN_NO_CRT #if defined(DQN_H) -Dqn_Jsmn Dqn_Jsmn_InitWithJSON(Dqn_JsmnString json, Dqn_ArenaAllocator *arena); -Dqn_Jsmn Dqn_Jsmn_InitWithJSONFile(Dqn_JsmnString file, Dqn_ArenaAllocator *arena); +Dqn_Jsmn Dqn_JsmnInitWithArenaJSONCString(char const *json, Dqn_JsmnI64 size, Dqn_Arena *arena); +Dqn_Jsmn Dqn_JsmnInitWithArenaJSON(Dqn_JsmnString json, Dqn_Arena *arena); +Dqn_Jsmn Dqn_JsmnInitWithArenaJSONFile(Dqn_JsmnString file, Dqn_Arena *arena); #endif // DQN_H // return: If the token is an array, return the size of the array otherwise -1. -int Dqn_Jsmn_TokenArraySize(jsmntok_t token); -Dqn_JsmnString Dqn_Jsmn_TokenString(jsmntok_t token, Dqn_JsmnString json); -bool Dqn_Jsmn_TokenBool(jsmntok_t token, Dqn_JsmnString json); -uint64_t Dqn_Jsmn_TokenU64(jsmntok_t token, Dqn_JsmnString json); +int Dqn_JsmnTokenArraySize(jsmntok_t token); +Dqn_JsmnString Dqn_JsmnTokenString(jsmntok_t token, Dqn_JsmnString json); +bool Dqn_JsmnTokenBool(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 // shown below. The goal here is to minimise the amount of state the user has to // manage. #if 0 - Dqn_ArenaAllocator arena = {}; - Dqn_JsmnString json = DQN_STRING(R"({ - "test": { - "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 json = DQN_JSMN_STRING("{ \"test\": { \"test2\": 0 } }"); + Dqn_Jsmn jsmn_state = Dqn_JsmnInitWithMallocJSON(json); + for (Dqn_JsmnIterator it = DQN_JSMN_ZERO_INIT; Dqn_JsmnIteratorNext(&it, &jsmn_state, NULL /*prev_it*/); ) { - Dqn_JsmnString key = Dqn_JsmnITerator_Key(&it); - if (key == DQN_STRING("test")) + Dqn_JsmnString key = Dqn_JsmnIteratorKey(&it); + 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; - 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); - if (obj_key == DQN_STRING("test2")) + Dqn_JsmnString obj_key = Dqn_JsmnIteratorKey(&obj_it); + 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; - uint64_t test_2_value = Dqn_Jsmn_IteratorU64(&obj_it); + Dqn_JsmnU64 test_2_value = Dqn_JsmnIteratorU64(&obj_it); + (void)test_2_value; } } } } #endif -bool Dqn_Jsmn_IteratorNext(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnIterator *prev_it); -Dqn_JsmnString Dqn_Jsmn_IteratorKey(Dqn_JsmnIterator *it); -Dqn_JsmnIterator Dqn_Jsmn_IteratorFindKey(Dqn_Jsmn *jsmn_state, Dqn_JsmnString key, Dqn_JsmnIterator *parent_it); +bool Dqn_JsmnIteratorNext(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnIterator *prev_it); +Dqn_JsmnString Dqn_JsmnIteratorKey(Dqn_JsmnIterator *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_Jsmn_IteratorExpectKey(it, expected, err_handle) Dqn_Jsmn_Iterator_ExpectKey(it, expected, err_handle, __FILE__, __LINE__) +#define Dqn_JsmnIteratorExpectValue(it, expected, err_handle) Dqn_Jsmn_IteratorExpectValue(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 // 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. -Dqn_JsmnString Dqn_Jsmn_IteratorString(Dqn_JsmnIterator const *it); -bool Dqn_Jsmn_IteratorBool(Dqn_JsmnIterator const *it); -uint64_t Dqn_Jsmn_IteratorU64(Dqn_JsmnIterator const *it); - -#define DQN_JSMN_ERROR_HANDLE_DUMP(handle) \ - for (Dqn_ListChunk *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))); \ - } \ - } - +Dqn_JsmnString Dqn_JsmnIteratorString(Dqn_JsmnIterator const *it); +bool Dqn_JsmnIteratorBool(Dqn_JsmnIterator const *it); +Dqn_JsmnU64 Dqn_JsmnIteratorU64(Dqn_JsmnIterator const *it); #endif // DQN_JSMN_H #if defined(DQN_JSMN_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; - 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; } -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; } -bool Dqn_Jsmn_IsDigit(char ch) +bool Dqn_JsmnIsDigit(char ch) { bool result = (ch >= '0' && ch <= '9'); return result; } -uint64_t Dqn_Jsmn_StringToU64(Dqn_JsmnString string) +Dqn_JsmnU64 Dqn_JsmnStringToU64(Dqn_JsmnString string) { - uint64_t result = 0; - if (!Dqn_Jsmn_StringIsValid(string)) + Dqn_JsmnU64 result = 0; + if (!Dqn_JsmnStringIsValid(string)) return result; for (int i = 0; i < string.size; i++) { - char ch = string.str[i]; - if (!Dqn_Jsmn_IsDigit(ch)) + char ch = string.buf.str[i]; + if (!Dqn_JsmnIsDigit(ch)) return result; - uint64_t digit = ch - '0'; + Dqn_JsmnU64 digit = ch - '0'; result = (result * 10) + digit; } return result; } +#if defined(__cplusplus) bool operator==(Dqn_JsmnString lhs, Dqn_JsmnString rhs) { - bool result = Dqn_Jsmn_StringEq(lhs, rhs); + bool result = Dqn_JsmnStringEq(lhs, rhs); 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_init(&parser); - int result = jsmn_parse(&parser, json, size, nullptr, 0); + unsigned int result = jsmn_parse(&parser, json, size, NULL, 0); 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) - return false; - - assert(jsmn->tokens_size == Dqn_Jsmn_TokensRequired(json, size)); - - *jsmn = {}; - jsmn_init(&jsmn->parser); - jsmn->valid = jsmn_parse(&jsmn->parser, jsmn->json.str, jsmn->json.size, jsmn->tokens, jsmn->tokens_size) > 0; - return jsmn->valid; + switch (enum_val) + { +#define DQN_JSMN_X_ENTRY(enum_val) case Dqn_JsmnTokenIs_##enum_val: return DQN_JSMN_STRING(#enum_val); + DQN_JSMN_X_MACRO +#undef DQN_JSMN_X_ENTRY + } + return DQN_JSMN_STRING("DQN_JSMN_UNHANDLED_ENUM"); } -#if defined(DQN_H) -Dqn_Jsmn Dqn_Jsmn_InitWithJSON(Dqn_JsmnString json, Dqn_ArenaAllocator *arena) +Dqn_Jsmn Dqn_JsmnInitWithJSONCString(char const *json, Dqn_JsmnI64 size, jsmntok_t *tokens, unsigned int tokens_size) { - Dqn_Jsmn result = {}; - result.tokens_size = Dqn_Jsmn_InitWithJSONCString(json.str, json.size, nullptr); - result.tokens = Dqn_ArenaAllocator_NewArray(arena, jsmntok_t, result.tokens_size, Dqn_ZeroMem::No); + Dqn_Jsmn result = DQN_JSMN_ZERO_INIT; + if (!tokens || tokens_size <= 0 || !json || size <= 0) + 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; } -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_Jsmn_InitWithJSON(json, arena); + Dqn_Jsmn result = Dqn_JsmnInitWithJSONCString(json.buf.str, json.size, tokens, size); 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; 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; } -bool Dqn_Jsmn_TokenBool(jsmntok_t token, Dqn_JsmnString json) +bool Dqn_JsmnTokenBool(jsmntok_t token, Dqn_JsmnString json) { - assert(token.start < json.size); - char ch = json.str[token.start]; + DQN_JSMN_ASSERT(token.start < json.size); + char ch = json.buf.str[token.start]; bool result = ch == 't'; - if (!result) { assert(ch == 'f'); } + if (!result) { DQN_JSMN_ASSERT(ch == 'f'); } 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_JsmnString string = {json.str + token.start, token.end - token.start}; - uint64_t result = Dqn_Jsmn_StringToU64(string); + DQN_JSMN_ASSERT(token.start < json.size); + Dqn_JsmnString string = {{json.buf.str + token.start}, token.end - token.start}; + Dqn_JsmnU64 result = Dqn_JsmnStringToU64(string); 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) return; @@ -535,13 +684,15 @@ void Dqn_JsmnErrorHandle__AddError(Dqn_JsmnErrorHandle *handle, jsmntok_t token, return; Dqn_JsmnError *error = handle->errors + handle->errors_size++; + error->token = token; + error->actual = actual; error->expected = expected; error->json = json; error->cpp_file = file; 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) { @@ -554,7 +705,7 @@ bool Dqn_Jsmn_IteratorNext(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnI } bool result = false; - if (!Dqn_Jsmn_StringIsValid(it->json) || it->json.size <= 0) { + if (!Dqn_JsmnStringIsValid(it->json) || it->json.size <= 0) { return result; } @@ -570,21 +721,21 @@ bool Dqn_Jsmn_IteratorNext(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnI 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) - 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; } -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 = {}; - for (Dqn_JsmnIterator it = {}; Dqn_Jsmn_IteratorNext(&it, jsmn_state, parent_it); ) + Dqn_JsmnIterator result = DQN_JSMN_ZERO_INIT; + 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); - if (it_key == key) + Dqn_JsmnString it_key = Dqn_JsmnTokenString(*it.key, jsmn_state->json); + if (Dqn_JsmnStringEq(it_key, key)) { result = it; break; @@ -594,67 +745,87 @@ Dqn_JsmnIterator Dqn_Jsmn_IteratorFindKey(Dqn_Jsmn *jsmn_state, Dqn_JsmnString k 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) { - case Dqn_JsmnTokenIs_Object: result = token.type == JSMN_OBJECT; break; - case Dqn_JsmnTokenIs_Array: result = token.type == JSMN_ARRAY; break; - case Dqn_JsmnTokenIs_String: result = token.type == JSMN_STRING; break; + case Dqn_JsmnTokenIs_Invalid: result = token.type == JSMN_UNDEFINED; break; + case Dqn_JsmnTokenIs_Object: result = token.type == JSMN_OBJECT; 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: { - assert(token.start < it->json.size); - char ch = it->json.str[token.start]; - result = token.type == JSMN_PRIMITIVE && (ch == '-' || Dqn_Jsmn_IsDigit(ch)); + DQN_JSMN_ASSERT(token.start < it->json.size); + char ch = it->json.buf.str[token.start]; + result = token.type == JSMN_PRIMITIVE && (ch == '-' || Dqn_JsmnIsDigit(ch)); } break; case Dqn_JsmnTokenIs_Bool: { - assert(token.start < it->json.size); - char ch = it->json.str[token.start]; + DQN_JSMN_ASSERT(token.start < it->json.size); + char ch = it->json.buf.str[token.start]; result = token.type == JSMN_PRIMITIVE && (ch == 't' || ch == 'f'); } 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) - Dqn_JsmnErrorHandle__AddError(err_handle, token, it->json, expected, file, line); + Dqn_JsmnErrorHandleAddError(err_handle, token, it->json, actual, expected, file, line); 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; } -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; } -Dqn_JsmnString Dqn_Jsmn_IteratorString(Dqn_JsmnIterator const *it) +Dqn_JsmnString Dqn_JsmnIteratorString(Dqn_JsmnIterator const *it) { - Dqn_JsmnString result = {}; - if (it->value && it->json.str) - result = {it->json.str + it->value->start, it->value->end - it->value->start}; + Dqn_JsmnString result = DQN_JSMN_ZERO_INIT; + if (it->value && it->json.buf.str) + result = DQN_JSMN_CLITERAL(Dqn_JsmnString){{it->json.buf.str + it->value->start}, it->value->end - it->value->start}; 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; } -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; } diff --git a/dqn_keccak.h b/dqn_keccak.h index 2c473e8..35a022f 100644 --- a/dqn_keccak.h +++ b/dqn_keccak.h @@ -39,9 +39,9 @@ // Define this in one and only one C++ file to enable the implementation // code of the header file. -#if !defined(DQN_KECCAK_MEMCOPY) +#if !defined(DQN_KECCAK_MEMCPY) #include - #define DQN_KECCAK_MEMCOPY(dest, src, count) memcpy(dest, src, count) + #define DQN_KECCAK_MEMCPY(dest, src, count) memcpy(dest, src, count) #endif #if !defined(DQN_KECCAK_MEMCMP) @@ -49,6 +49,12 @@ #define DQN_KECCAK_MEMCMP(dest, src, count) memcmp(dest, src, count) #endif +#if !defined(DQN_KECCAK_MEMSET) + #include + #define DQN_KECCAK_MEMSET(dest, byte, count) memset(dest, byte, count) +#endif + + #if !defined(DQN_KECCAK_ASSERT) #if defined(NDEBUG) #define DQN_KECCAK_ASSERT(expr) @@ -74,98 +80,128 @@ #define DQN_KECCAK_STRING96_FMT(string) 96, string #define DQN_KECCAK_STRING128_FMT(string) 128, string -// ----------------------------------------------------------------------------- -// NOTE: Data structures -// ----------------------------------------------------------------------------- -struct Dqn_KeccakBytes28 { char data[28]; }; // 224 bit -struct Dqn_KeccakBytes32 { char data[32]; }; // 256 bit -struct Dqn_KeccakBytes48 { char data[48]; }; // 384 bit -struct Dqn_KeccakBytes64 { char data[64]; }; // 512 bit - -struct Dqn_KeccakString56 { char str[(sizeof(Dqn_KeccakBytes28) * 2) + 1]; }; -struct Dqn_KeccakString64 { char str[(sizeof(Dqn_KeccakBytes32) * 2) + 1]; }; -struct Dqn_KeccakString96 { char str[(sizeof(Dqn_KeccakBytes48) * 2) + 1]; }; -struct Dqn_KeccakString128 { char str[(sizeof(Dqn_KeccakBytes64) * 2) + 1]; }; - // ----------------------------------------------------------------------------- // NOTE: API // ----------------------------------------------------------------------------- -// TODO(dqn): Write a streaming API -typedef unsigned char Dqn_Keccak_u8; -typedef unsigned short Dqn_Keccak_u16; -typedef unsigned int Dqn_Keccak_u32; -typedef unsigned int Dqn_Keccak_uint; +typedef unsigned char Dqn_KeccakU8; +typedef unsigned short Dqn_KeccakU16; +typedef unsigned int Dqn_KeccakU32; +typedef unsigned int Dqn_KeccakUint; #ifdef _MSC_VER - typedef unsigned __int64 Dqn_Keccak_u64; + typedef unsigned __int64 Dqn_KeccakU64; #else - typedef unsigned long long Dqn_Keccak_u64; + typedef unsigned long long Dqn_KeccakU64; #endif // ----------------------------------------------------------------------------- -// NOTE: SHA3 +// NOTE: Data structures // ----------------------------------------------------------------------------- -// Applies the FIPS 202 SHA3 algorithm on the supplied buffer. The size of -// the returned hash is (bit-rate/8) bytes, i.e. the dest_size must be atleast -// 32 bytes for SHA-256 and so forth. +typedef struct Dqn_KeccakBytes28 { char data[28]; } Dqn_KeccakBytes28; // 224 bit +typedef struct Dqn_KeccakBytes32 { char data[32]; } Dqn_KeccakBytes32; // 256 bit +typedef struct Dqn_KeccakBytes48 { char data[48]; } Dqn_KeccakBytes48; // 384 bit +typedef struct Dqn_KeccakBytes64 { char data[64]; } Dqn_KeccakBytes64; // 512 bit -void Dqn_Keccak_SHA3_224(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size); -Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_ToBytes28(void *bytes, Dqn_Keccak_u64 bytes_size); +typedef struct Dqn_KeccakString56 { char str[(sizeof(Dqn_KeccakBytes28) * 2) + 1]; } Dqn_KeccakString56; +typedef struct Dqn_KeccakString64 { char str[(sizeof(Dqn_KeccakBytes32) * 2) + 1]; } Dqn_KeccakString64; +typedef struct Dqn_KeccakString96 { char str[(sizeof(Dqn_KeccakBytes48) * 2) + 1]; } Dqn_KeccakString96; +typedef struct Dqn_KeccakString128 { char str[(sizeof(Dqn_KeccakBytes64) * 2) + 1]; } Dqn_KeccakString128; -void Dqn_Keccak_SHA3_256(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size); -Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_ToBytes32(void *bytes, Dqn_Keccak_u64 bytes_size); +#define DQN_KECCAK_LANE_SIZE_U64 5 +typedef struct Dqn_KeccakState +{ + Dqn_KeccakU8 state[DQN_KECCAK_LANE_SIZE_U64 * DQN_KECCAK_LANE_SIZE_U64 * sizeof(Dqn_KeccakU64)]; + int state_size; // The number of bytes written to the state + int absorb_size; // The amount of bytes to absorb/sponge in/from the state + int hash_size_bits; // The size of the hash the context was initialised for in bits + char delimited_suffix; // The delimited suffix of the current hash +} Dqn_KeccakState; -void Dqn_Keccak_SHA3_384(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size); -Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_ToBytes48(void *bytes, Dqn_Keccak_u64 bytes_size); +// ----------------------------------------------------------------------------- +// NOTE: SHA3/Keccak Streaming API +// ----------------------------------------------------------------------------- +// Setup a hashing state for either +// - FIPS 202 SHA3 +// - Non-finalized SHA3 (only difference is delimited suffix of 0x1 instead of 0x6 in SHA3) +// The non-finalized SHA3 version is the one adopted by many cryptocurrencies +// such as Ethereum and Monero as they adopted SHA3 before it was finalized. +// +// The state produced from this function is to be used alongside the +// 'KeccakUpdate' and 'KeccakFinish' functions. +// +// sha3: If true, setup the state for FIPS 202 SHA3, otherwise the non-finalized version +// hash_size_bits: The number of bits to setup the context for, available sizes are 224, 256, 384 and 512. +Dqn_KeccakState Dqn_KeccakSHA3Init(bool sha3, int hash_size_bits); -void Dqn_Keccak_SHA3_512(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size); -Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_ToBytes64(void *bytes, Dqn_Keccak_u64 bytes_size); +// After initialising a 'Dqn_KeccakState' via 'Dqn_KeccakSHA3Init', iteratively +// update the hash with new data by calling 'Dqn_KeccakUpdate'. On completion, +// call 'Dqn_KeccakFinish' to generate the hash from the state. The 'dest_size' +// must be at-least the (bit-size / 8), i.e. for 'Dqn_Keccak512Init' it must be +// atleast (512 / 8) bytes, 64 bytes. +void Dqn_KeccakUpdate(Dqn_KeccakState *keccak, void const *data, Dqn_KeccakU64 data_size); +void Dqn_KeccakFinish(Dqn_KeccakState *keccak, void *dest, int dest_size); + +// ----------------------------------------------------------------------------- +// NOTE: Simple API +// ----------------------------------------------------------------------------- +// Helper function that combines the Init, Update and Finish step in one shot, +// i.e. hashing a singlular contiguous buffer. Use the streaming API if data +// is split across different buffers. +void Dqn_KeccakSHA3Hash(bool sha3, int hash_size_bits, void const *src, Dqn_KeccakU64 src_size, void *dest, int dest_size); + +#define Dqn_SHA3Hash(hash_size_bits, src, src_size, dest, dest_size) Dqn_KeccakSHA3Hash(true /*sha3*/, hash_size_bits, src, src_size, dest, dest_size) +#define Dqn_SHA3_224(src, src_size, dest, dest_size) Dqn_SHA3Hash(224, src, src_size, dest, dest_size) +#define Dqn_SHA3_256(src, src_size, dest, dest_size) Dqn_SHA3Hash(256, src, src_size, dest, dest_size) +#define Dqn_SHA3_384(src, src_size, dest, dest_size) Dqn_SHA3Hash(384, src, src_size, dest, dest_size) +#define Dqn_SHA3_512(src, src_size, dest, dest_size) Dqn_SHA3Hash(512, src, src_size, dest, dest_size) + +#define Dqn_KeccakHash(hash_size_bits, src, src_size, dest, dest_size) Dqn_KeccakSHA3Hash(false /*sha3*/, hash_size_bits, src, src_size, dest, dest_size) +#define Dqn_Keccak224(src, src_size, dest, dest_size) Dqn_KeccakHash(224, src, src_size, dest, dest_size) +#define Dqn_Keccak256(src, src_size, dest, dest_size) Dqn_KeccakHash(256, src, src_size, dest, dest_size) +#define Dqn_Keccak384(src, src_size, dest, dest_size) Dqn_KeccakHash(384, src, src_size, dest, dest_size) +#define Dqn_Keccak512(src, src_size, dest, dest_size) Dqn_KeccakHash(512, src, src_size, dest, dest_size) + +// ----------------------------------------------------------------------------- +// NOTE: SHA3 Helpers +// ----------------------------------------------------------------------------- +Dqn_KeccakBytes28 Dqn_SHA3_224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size); +Dqn_KeccakBytes32 Dqn_SHA3_256ToBytes32(void *bytes, Dqn_KeccakU64 bytes_size); +Dqn_KeccakBytes48 Dqn_SHA3_384ToBytes48(void *bytes, Dqn_KeccakU64 bytes_size); +Dqn_KeccakBytes64 Dqn_SHA3_512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size); + +// ----------------------------------------------------------------------------- +// NOTE: Keccak Helpers +// ----------------------------------------------------------------------------- +Dqn_KeccakBytes28 Dqn_Keccak224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size); +Dqn_KeccakBytes32 Dqn_Keccak256ToBytes32(void *bytes, Dqn_KeccakU64 bytes_size); +Dqn_KeccakBytes48 Dqn_Keccak384ToBytes48(void *bytes, Dqn_KeccakU64 bytes_size); +Dqn_KeccakBytes64 Dqn_Keccak512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size); #if defined(DQN_H) // ----------------------------------------------------------------------------- // NOTE: SHA3 - Helpers for Dqn data structures // ----------------------------------------------------------------------------- -Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_StringToBytes28(Dqn_String string); -Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_StringToBytes32(Dqn_String string); -Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_StringToBytes48(Dqn_String string); -Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_StringToBytes64(Dqn_String string); +Dqn_KeccakBytes28 Dqn_SHA3_224StringToBytes28(Dqn_String string); +Dqn_KeccakBytes32 Dqn_SHA3_256StringToBytes32(Dqn_String string); +Dqn_KeccakBytes48 Dqn_SHA3_384StringToBytes48(Dqn_String string); +Dqn_KeccakBytes64 Dqn_SHA3_512StringToBytes64(Dqn_String string); -Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_U8ArrayToBytes28(Dqn_Array array); -Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_U8ArrayToBytes32(Dqn_Array array); -Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_U8ArrayToBytes48(Dqn_Array array); -Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_U8ArrayToBytes64(Dqn_Array array); -#endif // DQN_H +Dqn_KeccakBytes28 Dqn_SHA3_224_U8ArrayToBytes28(Dqn_Array array); +Dqn_KeccakBytes32 Dqn_SHA3_256_U8ArrayToBytes32(Dqn_Array array); +Dqn_KeccakBytes48 Dqn_SHA3_384_U8ArrayToBytes48(Dqn_Array array); +Dqn_KeccakBytes64 Dqn_SHA3_512_U8ArrayToBytes64(Dqn_Array array); -// ----------------------------------------------------------------------------- -// NOTE: Keccak -// ----------------------------------------------------------------------------- -// Applies the non-finalized SHA3 algorithm (i.e. a delimited suffix of 0x1 -// instead of 0x6 in SHA3). This is the version of the algorithm used by -// Ethereum and Monero as they adopted SHA3 before it was finalized. -void Dqn_Keccak_224(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size); -Dqn_KeccakBytes28 Dqn_Keccak_224_ToBytes28(void *bytes, Dqn_Keccak_u64 bytes_size); - -void Dqn_Keccak_256(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size); -Dqn_KeccakBytes32 Dqn_Keccak_256_ToBytes32(void *bytes, Dqn_Keccak_u64 bytes_size); - -void Dqn_Keccak_384(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size); -Dqn_KeccakBytes48 Dqn_Keccak_384_ToBytes48(void *bytes, Dqn_Keccak_u64 bytes_size); - -void Dqn_Keccak_512(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size); -Dqn_KeccakBytes64 Dqn_Keccak_512_ToBytes64(void *bytes, Dqn_Keccak_u64 bytes_size); - -#if defined(DQN_H) // ----------------------------------------------------------------------------- // NOTE: Keccak - Helpers for Dqn data structures // ----------------------------------------------------------------------------- -Dqn_KeccakBytes28 Dqn_Keccak_224_StringToBytes28(Dqn_String string); -Dqn_KeccakBytes32 Dqn_Keccak_256_StringToBytes32(Dqn_String string); -Dqn_KeccakBytes48 Dqn_Keccak_384_StringToBytes48(Dqn_String string); -Dqn_KeccakBytes64 Dqn_Keccak_512_StringToBytes64(Dqn_String string); +Dqn_KeccakBytes28 Dqn_Keccak224StringToBytes28(Dqn_String string); +Dqn_KeccakBytes32 Dqn_Keccak256StringToBytes32(Dqn_String string); +Dqn_KeccakBytes48 Dqn_Keccak384StringToBytes48(Dqn_String string); +Dqn_KeccakBytes64 Dqn_Keccak512StringToBytes64(Dqn_String string); -Dqn_KeccakBytes28 Dqn_Keccak_224_U8ArrayToBytes28(Dqn_Array array); -Dqn_KeccakBytes32 Dqn_Keccak_256_U8ArrayToBytes32(Dqn_Array array); -Dqn_KeccakBytes48 Dqn_Keccak_384_U8ArrayToBytes48(Dqn_Array array); -Dqn_KeccakBytes64 Dqn_Keccak_512_U8ArrayToBytes64(Dqn_Array array); +Dqn_KeccakBytes28 Dqn_Keccak224_U8ArrayToBytes28(Dqn_Array array); +Dqn_KeccakBytes32 Dqn_Keccak256_U8ArrayToBytes32(Dqn_Array array); +Dqn_KeccakBytes48 Dqn_Keccak384_U8ArrayToBytes48(Dqn_Array array); +Dqn_KeccakBytes64 Dqn_Keccak512_U8ArrayToBytes64(Dqn_Array array); #endif // DQN_H // ----------------------------------------------------------------------------- @@ -174,36 +210,36 @@ Dqn_KeccakBytes64 Dqn_Keccak_512_U8ArrayToBytes64(Dqn_Array array // Convert a binary buffer into its hex representation into dest. The dest // buffer must be large enough to contain the hex representation, i.e. // atleast src_size * 2). This function does *not* null-terminate the buffer. -void Dqn_Keccak_BytesToHex(void const *src, Dqn_Keccak_u64 src_size, char *dest, Dqn_Keccak_u64 dest_size); +void Dqn_KeccakBytesToHex(void const *src, Dqn_KeccakU64 src_size, char *dest, Dqn_KeccakU64 dest_size); // Converts a fixed amount of bytes into a hexadecimal string. Helper functions -// that call into Dqn_Keccak_BytesToHex. +// that call into Dqn_KeccakBytesToHex. // return: The hexadecimal string of the bytes, null-terminated. -Dqn_KeccakString56 Dqn_Keccak_Bytes28ToHex(Dqn_KeccakBytes28 const *bytes); -Dqn_KeccakString64 Dqn_Keccak_Bytes32ToHex(Dqn_KeccakBytes32 const *bytes); -Dqn_KeccakString96 Dqn_Keccak_Bytes48ToHex(Dqn_KeccakBytes48 const *bytes); -Dqn_KeccakString128 Dqn_Keccak_Bytes64ToHex(Dqn_KeccakBytes64 const *bytes); +Dqn_KeccakString56 Dqn_KeccakBytes28ToHex(Dqn_KeccakBytes28 const *bytes); +Dqn_KeccakString64 Dqn_KeccakBytes32ToHex(Dqn_KeccakBytes32 const *bytes); +Dqn_KeccakString96 Dqn_KeccakBytes48ToHex(Dqn_KeccakBytes48 const *bytes); +Dqn_KeccakString128 Dqn_KeccakBytes64ToHex(Dqn_KeccakBytes64 const *bytes); // Compares byte data structures for byte equality (via memcmp). // return: 1 if the contents are equal otherwise 0. -int Dqn_Keccak_Bytes28Equals(Dqn_KeccakBytes28 const *a, Dqn_KeccakBytes28 const *b); -int Dqn_Keccak_Bytes32Equals(Dqn_KeccakBytes32 const *a, Dqn_KeccakBytes32 const *b); -int Dqn_Keccak_Bytes48Equals(Dqn_KeccakBytes48 const *a, Dqn_KeccakBytes48 const *b); -int Dqn_Keccak_Bytes64Equals(Dqn_KeccakBytes64 const *a, Dqn_KeccakBytes64 const *b); +int Dqn_KeccakBytes28Equals(Dqn_KeccakBytes28 const *a, Dqn_KeccakBytes28 const *b); +int Dqn_KeccakBytes32Equals(Dqn_KeccakBytes32 const *a, Dqn_KeccakBytes32 const *b); +int Dqn_KeccakBytes48Equals(Dqn_KeccakBytes48 const *a, Dqn_KeccakBytes48 const *b); +int Dqn_KeccakBytes64Equals(Dqn_KeccakBytes64 const *a, Dqn_KeccakBytes64 const *b); -#if defined(DQN_H) +#if defined(DQN_H) && defined(DQN_WITH_HEX) // ----------------------------------------------------------------------------- // NOTE: Other helper functions for Dqn data structures // ----------------------------------------------------------------------------- // Converts a 64 character hex string into the 32 byte binary representation. // Invalid hex characters in the string will be represented as 0. // hex: Must be exactly a 64 character hex string. -Dqn_KeccakBytes32 Dqn_Keccak_Hex64StringToBytes(Dqn_String hex); -#endif // DQN_H +Dqn_KeccakBytes32 Dqn_KeccakHex64StringToBytes(Dqn_String hex); +#endif // DQN_H && DQN_WITH_HEX #endif // DQN_KECCAK_H #if defined(DQN_KECCAK_IMPLEMENTATION) -Dqn_Keccak_u64 const DQN_KECCAK_ROUNDS[] = { +Dqn_KeccakU64 const DQN_KECCAK_ROUNDS[] = { 0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, 0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A, 0x000000008000808B, 0x800000000000008B, 0x8000000000008089, @@ -211,7 +247,7 @@ Dqn_Keccak_u64 const DQN_KECCAK_ROUNDS[] = { 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, }; -Dqn_Keccak_u64 const DQN_KECCAK_ROTATIONS[][5] = +Dqn_KeccakU64 const DQN_KECCAK_ROTATIONS[][5] = { {0, 36, 3, 41, 18}, {1, 44, 10, 45, 2}, @@ -220,148 +256,14 @@ Dqn_Keccak_u64 const DQN_KECCAK_ROTATIONS[][5] = {27, 20, 39, 8, 14}, }; -#define DQN_KECCAK_LANE_SIZE_U64 5 #define DQN_KECCAK_ROL64(val, rotate) (((val) << (rotate)) | (((val) >> (64 - (rotate))))) -static void Dqn_Keccak__ReferencePermute(Dqn_Keccak_u64 *A) -{ - // TODO(dqn): Do some more tests and remove. - // NOTE: Reference permutation taken for cross-reference and back verifying - // against from digestpp by kerukuro - // https://github.com/kerukuro/digestpp/blob/master/algorithm/detail/sha3_provider.hpp - - for (int round = 0; round < 24; round++) - { - Dqn_Keccak_u64 C[5], D[5]; - C[0] = A[0 * 5 + 0] ^ A[1 * 5 + 0] ^ A[2 * 5 + 0] ^ A[3 * 5 + 0] ^ A[4 * 5 + 0]; - C[1] = A[0 * 5 + 1] ^ A[1 * 5 + 1] ^ A[2 * 5 + 1] ^ A[3 * 5 + 1] ^ A[4 * 5 + 1]; - C[2] = A[0 * 5 + 2] ^ A[1 * 5 + 2] ^ A[2 * 5 + 2] ^ A[3 * 5 + 2] ^ A[4 * 5 + 2]; - C[3] = A[0 * 5 + 3] ^ A[1 * 5 + 3] ^ A[2 * 5 + 3] ^ A[3 * 5 + 3] ^ A[4 * 5 + 3]; - C[4] = A[0 * 5 + 4] ^ A[1 * 5 + 4] ^ A[2 * 5 + 4] ^ A[3 * 5 + 4] ^ A[4 * 5 + 4]; - - D[0] = C[4] ^ DQN_KECCAK_ROL64(C[1], 1); - D[1] = C[0] ^ DQN_KECCAK_ROL64(C[2], 1); - D[2] = C[1] ^ DQN_KECCAK_ROL64(C[3], 1); - D[3] = C[2] ^ DQN_KECCAK_ROL64(C[4], 1); - D[4] = C[3] ^ DQN_KECCAK_ROL64(C[0], 1); - - Dqn_Keccak_u64 B00 = A[0 * 5 + 0] ^ D[0]; - Dqn_Keccak_u64 B10 = DQN_KECCAK_ROL64(A[0 * 5 + 1] ^ D[1], 1); - Dqn_Keccak_u64 B20 = DQN_KECCAK_ROL64(A[0 * 5 + 2] ^ D[2], 62); - Dqn_Keccak_u64 B5 = DQN_KECCAK_ROL64(A[0 * 5 + 3] ^ D[3], 28); - Dqn_Keccak_u64 B15 = DQN_KECCAK_ROL64(A[0 * 5 + 4] ^ D[4], 27); - - Dqn_Keccak_u64 B16 = DQN_KECCAK_ROL64(A[1 * 5 + 0] ^ D[0], 36); - Dqn_Keccak_u64 B1 = DQN_KECCAK_ROL64(A[1 * 5 + 1] ^ D[1], 44); - Dqn_Keccak_u64 B11 = DQN_KECCAK_ROL64(A[1 * 5 + 2] ^ D[2], 6); - Dqn_Keccak_u64 B21 = DQN_KECCAK_ROL64(A[1 * 5 + 3] ^ D[3], 55); - Dqn_Keccak_u64 B6 = DQN_KECCAK_ROL64(A[1 * 5 + 4] ^ D[4], 20); - - Dqn_Keccak_u64 B7 = DQN_KECCAK_ROL64(A[2 * 5 + 0] ^ D[0], 3); - Dqn_Keccak_u64 B17 = DQN_KECCAK_ROL64(A[2 * 5 + 1] ^ D[1], 10); - Dqn_Keccak_u64 B2 = DQN_KECCAK_ROL64(A[2 * 5 + 2] ^ D[2], 43); - Dqn_Keccak_u64 B12 = DQN_KECCAK_ROL64(A[2 * 5 + 3] ^ D[3], 25); - Dqn_Keccak_u64 B22 = DQN_KECCAK_ROL64(A[2 * 5 + 4] ^ D[4], 39); - - Dqn_Keccak_u64 B23 = DQN_KECCAK_ROL64(A[3 * 5 + 0] ^ D[0], 41); - Dqn_Keccak_u64 B8 = DQN_KECCAK_ROL64(A[3 * 5 + 1] ^ D[1], 45); - Dqn_Keccak_u64 B18 = DQN_KECCAK_ROL64(A[3 * 5 + 2] ^ D[2], 15); - Dqn_Keccak_u64 B3 = DQN_KECCAK_ROL64(A[3 * 5 + 3] ^ D[3], 21); - Dqn_Keccak_u64 B13 = DQN_KECCAK_ROL64(A[3 * 5 + 4] ^ D[4], 8); - - Dqn_Keccak_u64 B14 = DQN_KECCAK_ROL64(A[4 * 5 + 0] ^ D[0], 18); - Dqn_Keccak_u64 B24 = DQN_KECCAK_ROL64(A[4 * 5 + 1] ^ D[1], 2); - Dqn_Keccak_u64 B9 = DQN_KECCAK_ROL64(A[4 * 5 + 2] ^ D[2], 61); - Dqn_Keccak_u64 B19 = DQN_KECCAK_ROL64(A[4 * 5 + 3] ^ D[3], 56); - Dqn_Keccak_u64 B4 = DQN_KECCAK_ROL64(A[4 * 5 + 4] ^ D[4], 14); - -#if 0 - printf("B00: %024llu\n", A[0 * 5 + 0] ^ D[0]); - printf("B10: %024llu\n", DQN_KECCAK_ROL64(A[0 * 5 + 1] ^ D[1], 1)); - printf("B20: %024llu\n", DQN_KECCAK_ROL64(A[0 * 5 + 2] ^ D[2], 62)); - printf("B05: %024llu\n", DQN_KECCAK_ROL64(A[0 * 5 + 3] ^ D[3], 28)); - printf("B15: %024llu\n\n", DQN_KECCAK_ROL64(A[0 * 5 + 4] ^ D[4], 27)); - - printf("B16: %024llu\n", DQN_KECCAK_ROL64(A[1 * 5 + 0] ^ D[0], 36)); - printf("B01: %024llu\n", DQN_KECCAK_ROL64(A[1 * 5 + 1] ^ D[1], 44)); - printf("B11: %024llu\n", DQN_KECCAK_ROL64(A[1 * 5 + 2] ^ D[2], 6)); - printf("B21: %024llu\n", DQN_KECCAK_ROL64(A[1 * 5 + 3] ^ D[3], 55)); - printf("B06: %024llu\n\n", DQN_KECCAK_ROL64(A[1 * 5 + 4] ^ D[4], 20)); - - printf("B07: %024llu\n", DQN_KECCAK_ROL64(A[2 * 5 + 0] ^ D[0], 3)); - printf("B17: %024llu\n", DQN_KECCAK_ROL64(A[2 * 5 + 1] ^ D[1], 10)); - printf("B02: %024llu\n", DQN_KECCAK_ROL64(A[2 * 5 + 2] ^ D[2], 43)); - printf("B12: %024llu\n", DQN_KECCAK_ROL64(A[2 * 5 + 3] ^ D[3], 25)); - printf("B22: %024llu\n\n", DQN_KECCAK_ROL64(A[2 * 5 + 4] ^ D[4], 39)); - - printf("B23: %024llu\n", DQN_KECCAK_ROL64(A[3 * 5 + 0] ^ D[0], 41)); - printf("B08: %024llu\n", DQN_KECCAK_ROL64(A[3 * 5 + 1] ^ D[1], 45)); - printf("B18: %024llu\n", DQN_KECCAK_ROL64(A[3 * 5 + 2] ^ D[2], 15)); - printf("B03: %024llu\n", DQN_KECCAK_ROL64(A[3 * 5 + 3] ^ D[3], 21)); - printf("B13: %024llu\n\n", DQN_KECCAK_ROL64(A[3 * 5 + 4] ^ D[4], 8)); - - printf("B14: %024llu\n", DQN_KECCAK_ROL64(A[4 * 5 + 0] ^ D[0], 18)); - printf("B24: %024llu\n", DQN_KECCAK_ROL64(A[4 * 5 + 1] ^ D[1], 2)); - printf("B09: %024llu\n", DQN_KECCAK_ROL64(A[4 * 5 + 2] ^ D[2], 61)); - printf("B19: %024llu\n", DQN_KECCAK_ROL64(A[4 * 5 + 3] ^ D[3], 56)); - printf("B04: %024llu\n\n", DQN_KECCAK_ROL64(A[4 * 5 + 4] ^ D[4], 14)); -#endif - - A[0 * 5 + 0] = B00 ^ ((~B1) & B2); - A[0 * 5 + 1] = B1 ^ ((~B2) & B3); - A[0 * 5 + 2] = B2 ^ ((~B3) & B4); - A[0 * 5 + 3] = B3 ^ ((~B4) & B00); - A[0 * 5 + 4] = B4 ^ ((~B00) & B1); - - A[1 * 5 + 0] = B5 ^ ((~B6) & B7); - A[1 * 5 + 1] = B6 ^ ((~B7) & B8); - A[1 * 5 + 2] = B7 ^ ((~B8) & B9); - A[1 * 5 + 3] = B8 ^ ((~B9) & B5); - A[1 * 5 + 4] = B9 ^ ((~B5) & B6); - - A[2 * 5 + 0] = B10 ^ ((~B11) & B12); - A[2 * 5 + 1] = B11 ^ ((~B12) & B13); - A[2 * 5 + 2] = B12 ^ ((~B13) & B14); - A[2 * 5 + 3] = B13 ^ ((~B14) & B10); - A[2 * 5 + 4] = B14 ^ ((~B10) & B11); - - A[3 * 5 + 0] = B15 ^ ((~B16) & B17); - A[3 * 5 + 1] = B16 ^ ((~B17) & B18); - A[3 * 5 + 2] = B17 ^ ((~B18) & B19); - A[3 * 5 + 3] = B18 ^ ((~B19) & B15); - A[3 * 5 + 4] = B19 ^ ((~B15) & B16); - - A[4 * 5 + 0] = B20 ^ ((~B21) & B22); - A[4 * 5 + 1] = B21 ^ ((~B22) & B23); - A[4 * 5 + 2] = B22 ^ ((~B23) & B24); - A[4 * 5 + 3] = B23 ^ ((~B24) & B20); - A[4 * 5 + 4] = B24 ^ ((~B20) & B21); - - A[0] ^= DQN_KECCAK_ROUNDS[round]; - -#if 0 - for (int y = 0; y < 5; y++) - { - for (int x = 0; x < 5; x++) - { - Dqn_Keccak_u64 lane = A[x + (y * 5)]; - printf("[%d,%d] %024llu ", x, y, lane); - } - - printf("\n"); - } -#endif - - } -} - -void Dqn_Keccak__Permute(void *state) +static void Dqn_Keccak__Permute(void *state) { // TODO(dqn): Do some profiling on unrolling and can we SIMD some part of // this? Unroll loop, look at data dependencies and investigate. -#if 1 - Dqn_Keccak_u64 *lanes_u64 = (Dqn_Keccak_u64 *)state; + Dqn_KeccakU64 *lanes_u64 = (Dqn_KeccakU64 *)state; for (int round_index = 0; round_index < 24; round_index++) { #define LANE_INDEX(x, y) ((x) + ((y) * DQN_KECCAK_LANE_SIZE_U64)) @@ -369,7 +271,7 @@ void Dqn_Keccak__Permute(void *state) // θ step // --------------------------------------------------------------------- #if 1 - Dqn_Keccak_u64 c[DQN_KECCAK_LANE_SIZE_U64]; + Dqn_KeccakU64 c[DQN_KECCAK_LANE_SIZE_U64]; for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++) { c[x] = lanes_u64[LANE_INDEX(x, 0)] ^ @@ -379,7 +281,7 @@ void Dqn_Keccak__Permute(void *state) lanes_u64[LANE_INDEX(x, 4)]; } - Dqn_Keccak_u64 d[DQN_KECCAK_LANE_SIZE_U64]; + Dqn_KeccakU64 d[DQN_KECCAK_LANE_SIZE_U64]; for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++) d[x] = c[(x + 4) % DQN_KECCAK_LANE_SIZE_U64] ^ DQN_KECCAK_ROL64(c[(x + 1) % DQN_KECCAK_LANE_SIZE_U64], 1); @@ -387,7 +289,7 @@ void Dqn_Keccak__Permute(void *state) for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++) lanes_u64[LANE_INDEX(x, y)] ^= d[x]; #else - Dqn_Keccak_u64 c[5], d[5]; + Dqn_KeccakU64 c[5], d[5]; c[0] = lanes_u64[0 * 5 + 0] ^ lanes_u64[1 * 5 + 0] ^ lanes_u64[2 * 5 + 0] ^ lanes_u64[3 * 5 + 0] ^ lanes_u64[4 * 5 + 0]; c[1] = lanes_u64[0 * 5 + 1] ^ lanes_u64[1 * 5 + 1] ^ lanes_u64[2 * 5 + 1] ^ lanes_u64[3 * 5 + 1] ^ lanes_u64[4 * 5 + 1]; c[2] = lanes_u64[0 * 5 + 2] ^ lanes_u64[1 * 5 + 2] ^ lanes_u64[2 * 5 + 2] ^ lanes_u64[3 * 5 + 2] ^ lanes_u64[4 * 5 + 2]; @@ -404,24 +306,16 @@ void Dqn_Keccak__Permute(void *state) // --------------------------------------------------------------------- // ρ and π steps // --------------------------------------------------------------------- - Dqn_Keccak_u64 b[DQN_KECCAK_LANE_SIZE_U64 * DQN_KECCAK_LANE_SIZE_U64]; + Dqn_KeccakU64 b[DQN_KECCAK_LANE_SIZE_U64 * DQN_KECCAK_LANE_SIZE_U64]; for (int y = 0; y < DQN_KECCAK_LANE_SIZE_U64; y++) { for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++) { - Dqn_Keccak_u64 lane = lanes_u64[LANE_INDEX(x, y)]; - Dqn_Keccak_u64 rotate_count = DQN_KECCAK_ROTATIONS[x][y]; + Dqn_KeccakU64 lane = lanes_u64[LANE_INDEX(x, y)]; + Dqn_KeccakU64 rotate_count = DQN_KECCAK_ROTATIONS[x][y]; b[LANE_INDEX(y, (2 * x + 3 * y) % 5)] = DQN_KECCAK_ROL64(lane, rotate_count); -#if 0 - int index = LANE_INDEX(y, (2 * x + 3 * y) % 5); - printf("B%02d: %024llu\n", index, b[index]); -#endif } - -#if 0 - printf("\n"); -#endif } // --------------------------------------------------------------------- @@ -431,7 +325,7 @@ void Dqn_Keccak__Permute(void *state) { for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++) { - Dqn_Keccak_u64 rhs = ~b[LANE_INDEX((x + 1) % 5, y)] & + Dqn_KeccakU64 rhs = ~b[LANE_INDEX((x + 1) % 5, y)] & b[LANE_INDEX((x + 2) % 5, y)]; lanes_u64[LANE_INDEX(x, y)] = b[LANE_INDEX(x, y)] ^ rhs; @@ -442,362 +336,293 @@ void Dqn_Keccak__Permute(void *state) // ι step // --------------------------------------------------------------------- lanes_u64[LANE_INDEX(0, 0)] ^= DQN_KECCAK_ROUNDS[round_index]; - -#if 0 - for (int y = 0; y < 5; y++) - { - for (int x = 0; x < 5; x++) - { - Dqn_Keccak_u64 lane = lanes_u64[x + (y * 5)]; - printf("[%d,%d] %024llu ", x, y, lane); - } - - printf("\n"); - } -#endif - #undef LANE_INDEX #undef DQN_KECCAK_ROL64 } -#else - Dqn_Keccak__ReferencePermute((Dqn_Keccak_u64 *)state); -#endif } -static void Dqn_Keccak__Construction(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size, int bitrate, char delimited_suffix) +// ----------------------------------------------------------------------------- +// NOTE: Streaming API +// ----------------------------------------------------------------------------- +Dqn_KeccakState Dqn_KeccakSHA3Init(bool sha3, int hash_size_bits) { - int const ABSORB_SIZE = bitrate / 8; - int const ABSORB_SIZE_U64 = ABSORB_SIZE / 8; - Dqn_Keccak_u64 state_u64[DQN_KECCAK_LANE_SIZE_U64 * DQN_KECCAK_LANE_SIZE_U64] = {}; + char const SHA3_DELIMITED_SUFFIX = 0x06; + char const KECCAK_DELIMITED_SUFFIX = 0x01; + int const bitrate = 1600 - (hash_size_bits * 2); - // --------------------------------------------------------------------- - // Sponge Step: Absorb all the bytes into the state - // --------------------------------------------------------------------- - Dqn_Keccak_u64 const *block = (Dqn_Keccak_u64 const *)src; - int const absorb_count = src_size / ABSORB_SIZE; - for (int absorb_index = 0; absorb_index < absorb_count; absorb_index++, block += ABSORB_SIZE_U64) + Dqn_KeccakState result = {}; + result.hash_size_bits = hash_size_bits; + result.absorb_size = bitrate / 8; + result.delimited_suffix = sha3 ? SHA3_DELIMITED_SUFFIX : KECCAK_DELIMITED_SUFFIX; + DQN_KECCAK_ASSERT(bitrate + (hash_size_bits * 2) /*capacity*/ == 1600); + return result; +} + +void Dqn_KeccakUpdate(Dqn_KeccakState *keccak, void const *data, Dqn_KeccakU64 data_size) +{ + Dqn_KeccakU8 *state = keccak->state; + Dqn_KeccakU8 const *ptr = (Dqn_KeccakU8 *)data; + Dqn_KeccakU64 ptr_size = data_size; + while (ptr_size > 0) { - for (int index = 0; index < ABSORB_SIZE_U64; index++) - state_u64[index] ^= block[index]; - Dqn_Keccak__Permute(state_u64); + Dqn_KeccakU64 space = keccak->absorb_size - keccak->state_size; + int bytes_to_absorb = (int)(space < ptr_size ? space : ptr_size); + + for (int index = 0; index < bytes_to_absorb; index++) + state[keccak->state_size + index] ^= ptr[index]; + + ptr += bytes_to_absorb; + keccak->state_size += bytes_to_absorb; + ptr_size -= bytes_to_absorb; + + if (keccak->state_size >= keccak->absorb_size) + { + DQN_KECCAK_ASSERT(keccak->state_size == keccak->absorb_size); + Dqn_Keccak__Permute(state); + keccak->state_size = 0; + } } +} + +void Dqn_KeccakFinish(Dqn_KeccakState *keccak, void *dest, int dest_size) +{ + DQN_KECCAK_ASSERT(dest_size >= keccak->hash_size_bits / 8); // --------------------------------------------------------------------- - // Sponge Finalization Step: Remaining source bytes + padding + // Sponge Finalization Step: Final padding bit // --------------------------------------------------------------------- - // NOTE: Do the remainder bytes not divisible in the block, then, also, - // complete the sponge by adding the padding bits and delimited suffix. - { - Dqn_Keccak_u8 * state_u8 = (Dqn_Keccak_u8 *) state_u64; - Dqn_Keccak_u8 const *block_u8 = (Dqn_Keccak_u8 const *) block; - int const remainder = src_size % ABSORB_SIZE; - for (int index = 0; index < remainder; index++) - state_u8[index] ^= block_u8[index]; + int const INDEX_OF_0X80_BYTE = keccak->absorb_size - 1; + int const delimited_suffix_index = keccak->state_size; + DQN_KECCAK_ASSERT(delimited_suffix_index < keccak->absorb_size); - // NOTE: (remainder + 1) can never point out of array bounds, the - // remainder is guaranteed to be less than the ABSORB_SIZE, since we - // processed all the full blocks above. - int const delimited_suffix_index = remainder; - int const INDEX_OF_0X80_BYTE = ABSORB_SIZE - 1; - state_u8[delimited_suffix_index] ^= delimited_suffix; + Dqn_KeccakU8 *state = keccak->state; + state[delimited_suffix_index] ^= keccak->delimited_suffix; - // NOTE: In the reference implementation, it checks that if the - // delimited suffix is set to the padding bit (0x80), then we need to - // permute twice. Once for the delimited suffix, and a second time for - // the "padding" permute. - // - // However all standard algorithms either specify a 0x01, or 0x06, 0x04 - // delimited suffix and so forth- so this case is never hit. We can omit - // this from the implementation here. + // NOTE: In the reference implementation, it checks that if the + // delimited suffix is set to the padding bit (0x80), then we need to + // permute twice. Once for the delimited suffix, and a second time for + // the "padding" permute. + // + // However all standard algorithms either specify a 0x01, or 0x06, 0x04 + // delimited suffix and so forth- so this case is never hit. We can omit + // this from the implementation here. - state_u8[INDEX_OF_0X80_BYTE] ^= 0x80; - Dqn_Keccak__Permute(state_u64); - } + state[INDEX_OF_0X80_BYTE] ^= 0x80; + Dqn_Keccak__Permute(state); // --------------------------------------------------------------------- // Squeeze Step: Squeeze bytes from the state into our hash // --------------------------------------------------------------------- - Dqn_Keccak_u8 * dest_u8 = (Dqn_Keccak_u8 *) dest; - int const squeeze_count = dest_size / ABSORB_SIZE; - int squeeze_index = 0; + Dqn_KeccakU8 *dest_u8 = (Dqn_KeccakU8 *)dest; + int const squeeze_count = dest_size / keccak->absorb_size; + int squeeze_index = 0; for (; squeeze_index < squeeze_count; squeeze_index++) { - if (squeeze_index) Dqn_Keccak__Permute(state_u64); - DQN_KECCAK_MEMCOPY(dest_u8, state_u64, ABSORB_SIZE); - dest_u8 += ABSORB_SIZE; + if (squeeze_index) Dqn_Keccak__Permute(state); + DQN_KECCAK_MEMCPY(dest_u8, state, keccak->absorb_size); + dest_u8 += keccak->absorb_size; } // --------------------------------------------------------------------- // Squeeze Finalisation Step: Remainder bytes in hash // --------------------------------------------------------------------- + int const remainder = dest_size % keccak->absorb_size; + if (remainder) { - int const remainder = dest_size % ABSORB_SIZE; - if (remainder) - { - if (squeeze_index) Dqn_Keccak__Permute(state_u64); - DQN_KECCAK_MEMCOPY(dest_u8, state_u64, remainder); - } + if (squeeze_index) Dqn_Keccak__Permute(state); + DQN_KECCAK_MEMCPY(dest_u8, state, remainder); } } -#define DQN_KECCAK_SHA3_DELIMITED_SUFFIX 0x06 -#define DQN_KECCAK_DELIMITED_SUFFIX 0x01 - -// ----------------------------------------------------------------------------- -// NOTE: SHA3-224 -// ----------------------------------------------------------------------------- -void Dqn_Keccak_SHA3_224(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size) +void Dqn_KeccakSHA3Hash(bool sha3, int hash_size_bits, void const *src, Dqn_KeccakU64 src_size, void *dest, int dest_size) { - int const BITRATE = 1152; - DQN_KECCAK_ASSERT(dest_size >= 224 / 8); - Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_SHA3_DELIMITED_SUFFIX); + Dqn_KeccakState keccak = Dqn_KeccakSHA3Init(sha3, hash_size_bits); + Dqn_KeccakUpdate(&keccak, src, src_size); + Dqn_KeccakFinish(&keccak, dest, dest_size); } -Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_ToBytes28(void *bytes, Dqn_Keccak_u64 bytes_size) +// ----------------------------------------------------------------------------- +// NOTE: SHA3 Helpers +// ----------------------------------------------------------------------------- +Dqn_KeccakBytes28 Dqn_SHA3_224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size) { Dqn_KeccakBytes28 result; - Dqn_Keccak_SHA3_224(bytes, bytes_size, result.data, sizeof(result)); + Dqn_SHA3_224(bytes, bytes_size, result.data, sizeof(result)); return result; } -// ----------------------------------------------------------------------------- -// NOTE: SHA3-256 -// ----------------------------------------------------------------------------- -void Dqn_Keccak_SHA3_256(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size) -{ - int const BITRATE = 1088; - DQN_KECCAK_ASSERT(dest_size >= 256 / 8); - Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_SHA3_DELIMITED_SUFFIX); -} - -Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_ToBytes32(void *bytes, Dqn_Keccak_u64 bytes_size) +Dqn_KeccakBytes32 Dqn_SHA3_256ToBytes32(void *bytes, Dqn_KeccakU64 bytes_size) { Dqn_KeccakBytes32 result; - Dqn_Keccak_SHA3_256(bytes, bytes_size, result.data, sizeof(result)); + Dqn_SHA3_256(bytes, bytes_size, result.data, sizeof(result)); return result; } -// ----------------------------------------------------------------------------- -// NOTE: SHA3-384 -// ----------------------------------------------------------------------------- -void Dqn_Keccak_SHA3_384(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size) -{ - int const BITRATE = 832; - DQN_KECCAK_ASSERT(dest_size >= 384 / 8); - Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_SHA3_DELIMITED_SUFFIX); -} - -Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_ToBytes48(void *bytes, Dqn_Keccak_u64 bytes_size) +Dqn_KeccakBytes48 Dqn_SHA3_384ToBytes48(void *bytes, Dqn_KeccakU64 bytes_size) { Dqn_KeccakBytes48 result; - Dqn_Keccak_SHA3_384(bytes, bytes_size, result.data, sizeof(result)); + Dqn_SHA3_384(bytes, bytes_size, result.data, sizeof(result)); return result; } -// ----------------------------------------------------------------------------- -// NOTE: SHA3-512 -// ----------------------------------------------------------------------------- -void Dqn_Keccak_SHA3_512(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size) -{ - int const BITRATE = 576; - DQN_KECCAK_ASSERT(dest_size >= 512 / 8); - Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_SHA3_DELIMITED_SUFFIX); -} - -Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_ToBytes64(void *bytes, Dqn_Keccak_u64 bytes_size) +Dqn_KeccakBytes64 Dqn_SHA3_512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size) { Dqn_KeccakBytes64 result; - Dqn_Keccak_SHA3_512(bytes, bytes_size, result.data, sizeof(result)); + Dqn_SHA3_512(bytes, bytes_size, result.data, sizeof(result)); return result; } +// ----------------------------------------------------------------------------- +// NOTE: Keccak Helpers +// ----------------------------------------------------------------------------- +Dqn_KeccakBytes28 Dqn_Keccak224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size) +{ + Dqn_KeccakBytes28 result; + Dqn_Keccak224(bytes, bytes_size, result.data, sizeof(result)); + return result; +} + +Dqn_KeccakBytes32 Dqn_Keccak256ToBytes32(void *bytes, Dqn_KeccakU64 bytes_size) +{ + Dqn_KeccakBytes32 result; + Dqn_Keccak256(bytes, bytes_size, result.data, sizeof(result)); + return result; +} + +Dqn_KeccakBytes48 Dqn_Keccak384ToBytes48(void *bytes, Dqn_KeccakU64 bytes_size) +{ + Dqn_KeccakBytes48 result; + Dqn_Keccak384(bytes, bytes_size, result.data, sizeof(result)); + return result; +} + +Dqn_KeccakBytes64 Dqn_Keccak512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size) +{ + Dqn_KeccakBytes64 result; + Dqn_Keccak512(bytes, bytes_size, result.data, sizeof(result)); + return result; +} + + #if defined(DQN_H) // ----------------------------------------------------------------------------- // NOTE: SHA3 - Helpers for Dqn data structures // ----------------------------------------------------------------------------- -Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_StringToBytes28(Dqn_String string) +Dqn_KeccakBytes28 Dqn_SHA3_224StringToBytes28(Dqn_String string) { Dqn_KeccakBytes28 result; - Dqn_Keccak_SHA3_224(string.str, string.size, result.data, sizeof(result)); + Dqn_SHA3_224(string.str, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_U8ArrayToBytes28(Dqn_Array array) +Dqn_KeccakBytes28 Dqn_SHA3_224_U8ArrayToBytes28(Dqn_Array array) { Dqn_KeccakBytes28 result; - Dqn_Keccak_SHA3_224(array.data, array.size, result.data, sizeof(result)); + Dqn_SHA3_224(array.data, array.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_StringToBytes32(Dqn_String string) +Dqn_KeccakBytes32 Dqn_SHA3_256StringToBytes32(Dqn_String string) { Dqn_KeccakBytes32 result; - Dqn_Keccak_SHA3_256(string.str, string.size, result.data, sizeof(result)); + Dqn_SHA3_256(string.str, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_U8ArrayToBytes32(Dqn_Array array) +Dqn_KeccakBytes32 Dqn_SHA3_256_U8ArrayToBytes32(Dqn_Array array) { Dqn_KeccakBytes32 result; - Dqn_Keccak_SHA3_256(array.data, array.size, result.data, sizeof(result)); + Dqn_SHA3_256(array.data, array.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_StringToBytes48(Dqn_String string) +Dqn_KeccakBytes48 Dqn_SHA3_384StringToBytes48(Dqn_String string) { Dqn_KeccakBytes48 result; - Dqn_Keccak_SHA3_384(string.str, string.size, result.data, sizeof(result)); + Dqn_SHA3_384(string.str, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_U8ArrayToBytes48(Dqn_Array array) +Dqn_KeccakBytes48 Dqn_SHA3_384_U8ArrayToBytes48(Dqn_Array array) { Dqn_KeccakBytes48 result; - Dqn_Keccak_SHA3_384(array.data, array.size, result.data, sizeof(result)); + Dqn_SHA3_384(array.data, array.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_StringToBytes64(Dqn_String string) +Dqn_KeccakBytes64 Dqn_SHA3_512StringToBytes64(Dqn_String string) { Dqn_KeccakBytes64 result; - Dqn_Keccak_SHA3_512(string.str, string.size, result.data, sizeof(result)); + Dqn_SHA3_512(string.str, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_U8ArrayToBytes64(Dqn_Array array) +Dqn_KeccakBytes64 Dqn_SHA3_512_U8ArrayToBytes64(Dqn_Array array) { Dqn_KeccakBytes64 result; - Dqn_Keccak_SHA3_512(array.data, array.size, result.data, sizeof(result)); + Dqn_SHA3_512(array.data, array.size, result.data, sizeof(result)); return result; } #endif // DQN_H -// ----------------------------------------------------------------------------- -// NOTE: Keccak-224 -// ----------------------------------------------------------------------------- -void Dqn_Keccak_224(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size) -{ - int const BITRATE = 1152; - DQN_KECCAK_ASSERT(dest_size >= 224 / 8); - Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_DELIMITED_SUFFIX); -} - -Dqn_KeccakBytes28 Dqn_Keccak_224_ToBytes28(void *bytes, Dqn_Keccak_u64 bytes_size) -{ - Dqn_KeccakBytes28 result; - Dqn_Keccak_224(bytes, bytes_size, result.data, sizeof(result)); - return result; -} - -// ----------------------------------------------------------------------------- -// NOTE: Keccak-256 -// ----------------------------------------------------------------------------- -void Dqn_Keccak_256(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size) -{ - int const BITRATE = 1088; - DQN_KECCAK_ASSERT(dest_size >= 256 / 8); - Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_DELIMITED_SUFFIX); -} - -Dqn_KeccakBytes32 Dqn_Keccak_256_ToBytes32(void *bytes, Dqn_Keccak_u64 bytes_size) -{ - Dqn_KeccakBytes32 result; - Dqn_Keccak_256(bytes, bytes_size, result.data, sizeof(result)); - return result; -} - -// ----------------------------------------------------------------------------- -// NOTE: Keccak-384 -// ----------------------------------------------------------------------------- -void Dqn_Keccak_384(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size) -{ - int const BITRATE = 832; - DQN_KECCAK_ASSERT(dest_size >= 384 / 8); - Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_DELIMITED_SUFFIX); -} - -Dqn_KeccakBytes48 Dqn_Keccak_384_ToBytes48(void *bytes, Dqn_Keccak_u64 bytes_size) -{ - Dqn_KeccakBytes48 result; - Dqn_Keccak_384(bytes, bytes_size, result.data, sizeof(result)); - return result; -} - -// ----------------------------------------------------------------------------- -// NOTE: Keccak-512 -// ----------------------------------------------------------------------------- -void Dqn_Keccak_512(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size) -{ - int const BITRATE = 576; - DQN_KECCAK_ASSERT(dest_size >= 512 / 8); - Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_DELIMITED_SUFFIX); -} - -Dqn_KeccakBytes64 Dqn_Keccak_512_ToBytes64(void *bytes, Dqn_Keccak_u64 bytes_size) -{ - Dqn_KeccakBytes64 result; - Dqn_Keccak_512(bytes, bytes_size, result.data, sizeof(result)); - return result; -} - #if defined(DQN_H) // ----------------------------------------------------------------------------- // NOTE: Keccak - Helpers for Dqn data structures // ----------------------------------------------------------------------------- -Dqn_KeccakBytes28 Dqn_Keccak_224_StringToBytes28(Dqn_String string) +Dqn_KeccakBytes28 Dqn_Keccak224StringToBytes28(Dqn_String string) { Dqn_KeccakBytes28 result; - Dqn_Keccak_224(string.str, string.size, result.data, sizeof(result)); + Dqn_Keccak224(string.str, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes28 Dqn_Keccak_224_U8ArrayToBytes28(Dqn_Array array) +Dqn_KeccakBytes28 Dqn_Keccak224_U8ArrayToBytes28(Dqn_Array array) { Dqn_KeccakBytes28 result; - Dqn_Keccak_224(array.data, array.size, result.data, sizeof(result)); + Dqn_Keccak224(array.data, array.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes32 Dqn_Keccak_256_StringToBytes32(Dqn_String string) +Dqn_KeccakBytes32 Dqn_Keccak256StringToBytes32(Dqn_String string) { Dqn_KeccakBytes32 result; - Dqn_Keccak_256(string.str, string.size, result.data, sizeof(result)); + Dqn_Keccak256(string.str, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes32 Dqn_Keccak_256_U8ArrayToBytes32(Dqn_Array array) +Dqn_KeccakBytes32 Dqn_Keccak256_U8ArrayToBytes32(Dqn_Array array) { Dqn_KeccakBytes32 result; - Dqn_Keccak_256(array.data, array.size, result.data, sizeof(result)); + Dqn_Keccak256(array.data, array.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes48 Dqn_Keccak_384_StringToBytes48(Dqn_String string) +Dqn_KeccakBytes48 Dqn_Keccak384StringToBytes48(Dqn_String string) { Dqn_KeccakBytes48 result; - Dqn_Keccak_384(string.str, string.size, result.data, sizeof(result)); + Dqn_Keccak384(string.str, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes48 Dqn_Keccak_384_U8ArrayToBytes48(Dqn_Array array) +Dqn_KeccakBytes48 Dqn_Keccak384_U8ArrayToBytes48(Dqn_Array array) { Dqn_KeccakBytes48 result; - Dqn_Keccak_384(array.data, array.size, result.data, sizeof(result)); + Dqn_Keccak384(array.data, array.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes64 Dqn_Keccak_512_StringToBytes64(Dqn_String string) +Dqn_KeccakBytes64 Dqn_Keccak512StringToBytes64(Dqn_String string) { Dqn_KeccakBytes64 result; - Dqn_Keccak_512(string.str, string.size, result.data, sizeof(result)); + Dqn_Keccak512(string.str, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes64 Dqn_Keccak_512_U8ArrayToBytes64(Dqn_Array array) +Dqn_KeccakBytes64 Dqn_Keccak512_U8ArrayToBytes64(Dqn_Array array) { Dqn_KeccakBytes64 result; - Dqn_Keccak_512(array.data, array.size, result.data, sizeof(result)); + Dqn_Keccak512(array.data, array.size, result.data, sizeof(result)); return result; } #endif // DQN_H @@ -805,13 +630,13 @@ Dqn_KeccakBytes64 Dqn_Keccak_512_U8ArrayToBytes64(Dqn_Array array // ----------------------------------------------------------------------------- // NOTE: Helper functions // ----------------------------------------------------------------------------- -void Dqn_Keccak_BytesToHex(void const *src, Dqn_Keccak_u64 src_size, char *dest, Dqn_Keccak_u64 dest_size) +void Dqn_KeccakBytesToHex(void const *src, Dqn_KeccakU64 src_size, char *dest, Dqn_KeccakU64 dest_size) { (void)src_size; (void)dest_size; DQN_KECCAK_ASSERT(dest_size >= src_size * 2); unsigned char *src_u8 = (unsigned char *)src; - for (Dqn_Keccak_u64 src_index = 0, dest_index = 0; + for (Dqn_KeccakU64 src_index = 0, dest_index = 0; src_index < src_size; src_index += 1, dest_index += 2) { @@ -823,73 +648,72 @@ void Dqn_Keccak_BytesToHex(void const *src, Dqn_Keccak_u64 src_size, char *dest, } } -Dqn_KeccakString56 Dqn_Keccak_Bytes28ToHex(Dqn_KeccakBytes28 const *bytes) +Dqn_KeccakString56 Dqn_KeccakBytes28ToHex(Dqn_KeccakBytes28 const *bytes) { Dqn_KeccakString56 result; - Dqn_Keccak_BytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str)); + Dqn_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str)); result.str[sizeof(result.str) - 1] = 0; return result; } -Dqn_KeccakString64 Dqn_Keccak_Bytes32ToHex(Dqn_KeccakBytes32 const *bytes) +Dqn_KeccakString64 Dqn_KeccakBytes32ToHex(Dqn_KeccakBytes32 const *bytes) { Dqn_KeccakString64 result; - Dqn_Keccak_BytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str)); + Dqn_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str)); result.str[sizeof(result.str) - 1] = 0; return result; } -Dqn_KeccakString96 Dqn_Keccak_Bytes48ToHex(Dqn_KeccakBytes48 const *bytes) +Dqn_KeccakString96 Dqn_KeccakBytes48ToHex(Dqn_KeccakBytes48 const *bytes) { Dqn_KeccakString96 result; - Dqn_Keccak_BytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str)); + Dqn_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str)); result.str[sizeof(result.str) - 1] = 0; return result; } -Dqn_KeccakString128 Dqn_Keccak_Bytes64ToHex(Dqn_KeccakBytes64 const *bytes) +Dqn_KeccakString128 Dqn_KeccakBytes64ToHex(Dqn_KeccakBytes64 const *bytes) { Dqn_KeccakString128 result; - Dqn_Keccak_BytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str)); + Dqn_KeccakBytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str)); result.str[sizeof(result.str) - 1] = 0; return result; } -int Dqn_Keccak_Bytes28Equals(Dqn_KeccakBytes28 const *a, Dqn_KeccakBytes28 const *b) +int Dqn_KeccakBytes28Equals(Dqn_KeccakBytes28 const *a, Dqn_KeccakBytes28 const *b) { int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0; return result; } -int Dqn_Keccak_Bytes32Equals(Dqn_KeccakBytes32 const *a, Dqn_KeccakBytes32 const *b) +int Dqn_KeccakBytes32Equals(Dqn_KeccakBytes32 const *a, Dqn_KeccakBytes32 const *b) { int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0; return result; } -int Dqn_Keccak_Bytes48Equals(Dqn_KeccakBytes48 const *a, Dqn_KeccakBytes48 const *b) +int Dqn_KeccakBytes48Equals(Dqn_KeccakBytes48 const *a, Dqn_KeccakBytes48 const *b) { int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0; return result; } -int Dqn_Keccak_Bytes64Equals(Dqn_KeccakBytes64 const *a, Dqn_KeccakBytes64 const *b) +int Dqn_KeccakBytes64Equals(Dqn_KeccakBytes64 const *a, Dqn_KeccakBytes64 const *b) { int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0; return result; } -#if defined(DQN_H) +#if defined(DQN_H) && defined(DQN_WITH_HEX) // ----------------------------------------------------------------------------- // NOTE: Other helper functions for Dqn data structures // ----------------------------------------------------------------------------- -Dqn_KeccakBytes32 Dqn_Keccak_Hex64StringToBytes(Dqn_String hex) +Dqn_KeccakBytes32 Dqn_KeccakHex64StringToBytes(Dqn_String hex) { DQN_KECCAK_ASSERT(hex.size == 64); Dqn_KeccakBytes32 result; Dqn_HexToBytes(hex.str, hex.size, result.data, sizeof(result)); return result; } -#endif // DQN_H - +#endif // DQN_H && DQN_WITH_HEX #endif // DQN_KECCAK_IMPLEMENTATION diff --git a/dqn_metadesk.h b/dqn_metadesk.h deleted file mode 100644 index 9a69989..0000000 --- a/dqn_metadesk.h +++ /dev/null @@ -1,1399 +0,0 @@ -#if !defined(DQN_META_DESK_H) -#define DQN_META_DESK_H -// ----------------------------------------------------------------------------- -// NOTE: Dqn_MetaDesk -// ----------------------------------------------------------------------------- -// Contains helpers functions to code generate formatted CPP files using -// Dion-System's MetaDesk library. -// -// MetaDesk must be available under the include path at -// "metadesk/source/md.[c|h]" when this file is compiled. -// -// ----------------------------------------------------------------------------- -// NOTE: Configuration -// ----------------------------------------------------------------------------- -// #define DQN_META_DESK_IMPLEMENTATION -// Define this in one and only one C++ file to enable the implementation -// code of the header file. -// -// #define DQN_META_DESK_STANDALONE_PROGRAM -// Define this to enable a "main" function allowing this translation unit to -// be compiled into an executable, by default this program will generate all -// the possible code generation outputs. This file's implementation must -// also be available to this translation unit. -// -// ----------------------------------------------------------------------------- -// NOTE: Dqn_MetaDesk Header File -// ----------------------------------------------------------------------------- -#if !defined(_CRT_SECURE_NO_WARNINGS) - #define _CRT_SECURE_NO_WARNINGS - #define DQN_MD_UNDEFINE_CRT_WARNING -#endif - -#include -#include -#include "metadesk/source/md.h" - -#define DQN_MD_ASSERT_MSG(expr, fmt, ...) \ - if (!(expr)) \ - { \ - fprintf(stderr, "Assertion triggered at %s line %d. ", __FILE__, __LINE__); \ - fprintf(stderr, fmt, ##__VA_ARGS__); \ - fputc('\n', stderr); \ - *((int *)0); \ - } - -#define DQN_MD_INVALID_CODE_PATH do { DQN_MD_ASSERT_MSG(0, "Invalid code path") } while (0) -#define DQN_MD_INVALID_CODE_PATH_MSG(fmt, ...) do { DQN_MD_ASSERT_MSG(0, fmt, __VA_ARGS__) } while(0) -#define DQN_MD_CAST(Type) (Type) - -// ----------------------------------------------------------------------------- -// NOTE: Dqn_MDCppFile: Helper functions to generate formatted CPP files -// ----------------------------------------------------------------------------- -struct Dqn_MDCppFile -{ - FILE *file; - int indent; - int space_per_indent; - bool append_extra_new_line; -}; - -int Dqn_MDCppFile_SpacePerIndent(Dqn_MDCppFile *cpp); -void Dqn_MDCppFile_LineBeginV (Dqn_MDCppFile *cpp, char const *fmt, va_list args); -void Dqn_MDCppFile_LineBegin (Dqn_MDCppFile *cpp, char const *fmt, ...); -void Dqn_MDCppFile_LineEnd (Dqn_MDCppFile *cpp, char const *fmt, ...); -void Dqn_MDCppFile_LineAdd (Dqn_MDCppFile *cpp, char const *fmt, ...); -void Dqn_MDCppFile_LineV (Dqn_MDCppFile *cpp, char const *fmt, va_list args); -void Dqn_MDCppFile_Line (Dqn_MDCppFile *cpp, char const *fmt, ...); -void Dqn_MDCppFile_NewLine (Dqn_MDCppFile *cpp); -void Dqn_MDCppFile_Indent (Dqn_MDCppFile *cpp); -void Dqn_MDCppFile_Unindent (Dqn_MDCppFile *cpp); -void Dqn_MDCppFile_BeginBlock (Dqn_MDCppFile *cpp, char const *fmt, ...); -void Dqn_MDCppFile_EndBlock (Dqn_MDCppFile *cpp, bool trailing_semicolon, bool new_line_on_next_block); -#define Dqn_MDCppFile_EndEnumBlock(cpp) Dqn_MDCppFile_EndBlock(cpp, true /*trailing_semicolon*/, true /*new_line_on_next_block*/) -#define Dqn_MDCppFile_EndForBlock(cpp) Dqn_MDCppFile_EndBlock(cpp, false /*trailing_semicolon*/, false /*new_line_on_next_block*/) -#define Dqn_MDCppFile_EndIfBlock(cpp) Dqn_MDCppFile_EndBlock(cpp, false /*trailing_semicolon*/, false /*new_line_on_next_block*/) -#define Dqn_MDCppFile_EndFuncBlock(cpp) Dqn_MDCppFile_EndBlock(cpp, false /*trailing_semicolon*/, true /*new_line_on_next_block*/) -#define Dqn_MDCppFile_EndStructBlock(cpp) Dqn_MDCppFile_EndBlock(cpp, true /*trailing_semicolon*/, true /*new_line_on_next_block*/) - -// ----------------------------------------------------------------------------- -// NOTE: Dqn_MD: Code generating helper -// ----------------------------------------------------------------------------- -enum Dqn_MD_CodeGenFlag -{ - Dqn_MD_CodeGenFlag_CEnum = (1 << 0), - Dqn_MD_CodeGenFlag_CppStruct = (1 << 1), - Dqn_MD_CodeGenFlag_ImGuiFunction = (1 << 2), - Dqn_MD_CodeGenFlag_JsonParsingFunction = (1 << 3), - Dqn_MD_CodeGenFlag_CurlQueryFunction = (1 << 4), - Dqn_MD_CodeGenFlag_JsonEndpointURLFunction = (1 << 5), - Dqn_MD_CodeGenFlag_JsonRPCPostDataFunction = (1 << 5), - Dqn_MD_CodeGenFlag_PrintFunction = (1 << 6), - Dqn_MD_CodeGenFlag_All = (0xFFFFFFFF), -}; - -// file_path: The path of the file to feed into the code generator to parse and spit out code. -// output_name: The name to assign the .cpp/.h file that is generated from the -// function, i.e. .h and .cpp -void Dqn_MD_CodeGen(char const *file_path, char const *output_name, unsigned int flags); - -// ----------------------------------------------------------------------------- -// NOTE: Dqn_MD: Code generating functions -// ----------------------------------------------------------------------------- -/* -Generate a CPP struct for the given 'struct_node'. Children nodes of the -'struct_node' must have a 'type' child associated with it. - -Input - INS_CoinGecko_Coin: - { - id: {type: string}, - symbol: {type: string}, - name: {type: string}, - market_cap_rank: {type: u16}, - market_data: {type: INS_CoinGecko_CoinMarketData}, - } - -Output - struct CoinGecko_CoinMarketDataPrice - { - Dqn_f32 aud; - Dqn_f32 btc; - Dqn_f32 eth; - Dqn_f32 usd; - }; -*/ -void Dqn_MD_GenerateCppStruct(Dqn_MDCppFile *cpp, MD_Node const *struct_node); - -/* -Generate for any root MetaDesk node tagged with the 'required_tag' -required_tag: The tag that must be attached to the MetaDesk node for an enum to be generated for it -name: The name of the enum to generate - -Input: (With name = "INS_QueryType", required_tag = "json_endpoint") - char const EXAMPLE[] = R"(@json_endpoint Etherscan_TXListForAddress: {} - @json_endpoint CoinGecko_Coin: {}"); - -Output - enum INS_QueryType - { - INS_QueryType_Etherscan_TXListForAddress, - INS_QueryType_CoinGecko_Coin, - INS_QueryType_Count - }; -*/ -void Dqn_MD_GenerateCEnum(Dqn_MDCppFile *cpp, MD_ParseResult *parse, MD_String8 required_tag, char const *name); - -/* -Generate ImGui widgets for visualising the given 'struct_node'. If a custom type -is provided, we assume that an ImGui function has been previously generated for -the type already. - -Input - INS_CoinGecko_Coin: - { - id: {type: string, json_type: string}, - symbol: {type: string, json_type: string}, - name: {type: string, json_type: string}, - market_cap_rank: {type: u16, json_type: number}, - market_data: {type: INS_CoinGecko_CoinMarketData, json_type: object}, - } - -Output - void INS_CoinGecko_CoinImGui(INS_CoinGecko_Coin const *val){ - ImGui::Text("id: %.*s", DQN_STRING_FMT(val->id)); - ImGui::Text("symbol: %.*s", DQN_STRING_FMT(val->symbol)); - ImGui::Text("name: %.*s", DQN_STRING_FMT(val->name)); - ImGui::Text("market_cap_rank: %I64u", val->market_cap_rank); - INS_CoinGecko_CoinMarketDataImGui(&val->market_data); - } -*/ -void Dqn_MD_GenerateImGuiFunction(Dqn_MDCppFile *cpp, MD_Node const *struct_node, bool header_file); - -/* -Generate for any root MetaDesk node tagged with 'json_endpoint'. The MetaDesk -node containing the 'json_endpoint' can specify fields that the JSON parsing -function will attempt to pull out from the provided JSON string, powered by the -library jsmn.h. This function requires "Dqn_Jsmn.h/cpp" to be present in the -project using this generated code. - -Input - char const EXAMPLE[] = R"( - Etherscan_TXListForAddressEntry: - { - block_number: {type: u64, json_key: "blockNumber", json_type: string}, - nonce: {type: u64, json_type: string}, - block_hash: {type: string, json_key: "blockHash", json_type: string}, - } - - @json_endpoint(url: "http://api.etherscan.io/api?module=account&action=txlist&address=%.*s&startblock=0&endblock=99999999&sort=asc&apikey=%.*s" - params: {eth_address:string, api_key: string}) - Etherscan_TXListForAddress: - { - status: {type: bool, json_type: string}, - message: {type: string, json_type: string}, - result: {type: Etherscan_TXListForAddressEntry[], json_type: array}, - }"); - -Output: (Truncated for brevity) - Etherscan_TXListForAddress Etherscan_TXListForAddressParseTokens(jsmntok_t **start_it, Dqn_JsmnErrorHandle *err_handle, Dqn_String json, Dqn_ArenaAllocator *arena) - { - INS_Etherscan_TXListForAddress result = {}; - jsmntok_t *it = (*start_it); - if (!Dqn_JsmnToken_ExpectObject(*it, err_handle)) return result; - jsmntok_t *object = it++; - - for (int index = 0; index < object->size; index++) - { - jsmntok_t *key = it++; - jsmntok_t *value = it++; - Dqn_String key_str = Dqn_JsmnToken_String(json, *key); - Dqn_String value_str = Dqn_JsmnToken_String(json, *value); (void)value_str; - - if (key_str == DQN_STRING("status")) - { - if (Dqn_JsmnToken_ExpectString(*value, err_handle)) - { - result.status = DQN_CAST(Dqn_b32)Dqn_String_ToU64(value_str); - } - } - ... - else if (key_str == DQN_STRING("result")) - { - if (Dqn_JsmnToken_ExpectArray(*value, err_handle)) - { - result.result = Dqn_Array_InitWithArenaNoGrow(arena, INS_Etherscan_TXListForAddressEntry, value->size, 0, Dqn_ZeroMem::Yes); - for (int array_index = 0; array_index < value->size; array_index++) - { - auto *result_entry = Dqn_Array_Make(&result.result, 1); - *result_entry = INS_Etherscan_TXListForAddressEntryParseTokens(&it, err_handle, json, arena); - } - } - } - ... - } - *start_it = it; - return result; - } - -*/ -void Dqn_MD_GenerateJsonParsingFunction(Dqn_MDCppFile *cpp, MD_Node const *struct_node, bool header_file); -void Dqn_MD_GenerateCurlQueryFunction(Dqn_MDCppFile *cpp, MD_Node *struct_node, bool header_file); - - -/* -Generate for any root MetaDesk node with the following tags defined - @json_endpoint(url: <...>, params: {<...>}) - -Input - char const EXAMPLE[] = R"( - @json_endpoint(url: "http://api.etherscan.io/api?module=account&action=txlist&address=%.*s&startblock=0&endblock=99999999&sort=asc&apikey=%.*s" - params: {eth_address:string, api_key: string}) - Etherscan_TXListForAddress: - { - }"); - -Output - Dqn_String Etherscan_TXListForAddressURL(Dqn_ArenaAllocator *arena, Dqn_String eth_address, Dqn_String api_key) - { - Dqn_String result = Dqn_String_InitArenaFmt(arena, "http://api.etherscan.io/api?module=account&action=txlist&address=%.*s&startblock=0&endblock=99999999&sort=asc&apikey=%.*s", DQN_STRING_FMT(eth_address), DQN_STRING_FMT(api_key)); - DQN_HARD_ASSERT(result.str); - return result; - } -*/ -void Dqn_MD_GenerateJsonEndpointURLFunction(Dqn_MDCppFile *cpp, MD_Node *struct_node, bool header_file); -void Dqn_MD_GenerateJsonRPCPostDataFunction(Dqn_MDCppFile *cpp, MD_Node *root, MD_Node *struct_node, bool header_file); - -void Dqn_MD_GeneratePrintFunction(Dqn_MDCppFile *cpp, MD_Node const *struct_node, bool header_file); - -#if defined(DQN_MD_UNDEFINE_CRT_WARNING) - #undef _CRT_SECURE_NO_WARNINGS -#endif -#endif // DQN_META_DESK_H - -#if defined(DQN_META_DESK_IMPLEMENTATION) -// ----------------------------------------------------------------------------- -// NOTE: Implementation -// ----------------------------------------------------------------------------- -// ----------------------------------------------------------------------------- -// NOTE: Implementation Defines -// ----------------------------------------------------------------------------- -#if defined(__clang__) - #pragma clang diagnostic ignored "-Wwritable-strings" -#endif - -// ----------------------------------------------------------------------------- -// NOTE: MetaDesk Defines -// ----------------------------------------------------------------------------- -// NOTE: Warning! Order is important here, clang-cl on Windows defines _MSC_VER -#if defined(__clang__) -#elif defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable: 4244) // '=': conversion from 'MD_u64' to 'unsigned int', possible loss of data - #pragma warning(disable: 4101) // 'consume': unreferenced local variable -#endif - -#include "metadesk/source/md.c" - -#if defined(__clang__) -#elif defined(_MSC_VER) - #pragma warning(pop) -#endif - -// ----------------------------------------------------------------------------- -// NOTE: Dqn_MDCppFile Implementation -// ----------------------------------------------------------------------------- -int Dqn_MDCppFile_SpacePerIndent(Dqn_MDCppFile *cpp) -{ - int result = cpp->space_per_indent == 0 ? 4 : cpp->space_per_indent; - return result; -} - -void Dqn_MDCppFile_LineBeginV(Dqn_MDCppFile *cpp, char const *fmt, va_list args) -{ - int spaces = cpp->indent * Dqn_MDCppFile_SpacePerIndent(cpp); - fprintf(cpp->file, "%*s", spaces, ""); - vfprintf(cpp->file, fmt, args); -} - -void Dqn_MDCppFile_LineBegin(Dqn_MDCppFile *cpp, char const *fmt, ...) -{ - va_list args; - va_start(args, fmt); - Dqn_MDCppFile_LineBeginV(cpp, fmt, args); - va_end(args); -} - -void Dqn_MDCppFile_LineEnd(Dqn_MDCppFile *cpp, char const *fmt, ...) -{ - if (fmt) - { - va_list args; - va_start(args, fmt); - vfprintf(cpp->file, fmt, args); - va_end(args); - } - - fputc('\n', cpp->file); -} - -void Dqn_MDCppFile_LineAdd(Dqn_MDCppFile *cpp, char const *fmt, ...) -{ - va_list args; - va_start(args, fmt); - vfprintf(cpp->file, fmt, args); - va_end(args); -} - -void Dqn_MDCppFile_LineV(Dqn_MDCppFile *cpp, char const *fmt, va_list args) -{ - Dqn_MDCppFile_LineBeginV(cpp, fmt, args); - Dqn_MDCppFile_LineEnd(cpp, nullptr); -} - -void Dqn_MDCppFile_Line(Dqn_MDCppFile *cpp, char const *fmt, ...) -{ - va_list args; - va_start(args, fmt); - Dqn_MDCppFile_LineBeginV(cpp, fmt, args); - Dqn_MDCppFile_LineEnd(cpp, nullptr); - va_end(args); -} - -void Dqn_MDCppFile_NewLine(Dqn_MDCppFile *cpp) -{ - fputc('\n', cpp->file); -} - -void Dqn_MDCppFile_Indent(Dqn_MDCppFile *cpp) -{ - cpp->indent++; -} - -void Dqn_MDCppFile_Unindent(Dqn_MDCppFile *cpp) -{ - cpp->indent--; - MD_Assert(cpp->indent >= 0); -} - -void Dqn_MDCppFile_BeginBlock(Dqn_MDCppFile *cpp, char const *fmt, ...) -{ - if (fmt) - { - va_list args; - va_start(args, fmt); - Dqn_MDCppFile_LineV(cpp, fmt, args); - va_end(args); - } - - Dqn_MDCppFile_Line(cpp, "{"); - Dqn_MDCppFile_Indent(cpp); -} - -void Dqn_MDCppFile_EndBlock(Dqn_MDCppFile *cpp, bool trailing_semicolon, bool new_line_on_next_block) -{ - Dqn_MDCppFile_Unindent(cpp); - Dqn_MDCppFile_Line(cpp, trailing_semicolon ? "};" : "}"); - if (new_line_on_next_block) fputc('\n', cpp->file); -} - -// ----------------------------------------------------------------------------- -// MD Helpers -// ----------------------------------------------------------------------------- -static bool Dqn_MD__NodeHasArrayNotation(MD_Node const *node) -{ - unsigned node_brackets = MD_NodeFlag_HasBracketLeft | MD_NodeFlag_HasBracketRight; - bool result = (node->flags & node_brackets) == node_brackets; - return result; -} - -// ----------------------------------------------------------------------------- -// Code -// ----------------------------------------------------------------------------- -void Dqn_MD_CodeGen(char const *file_path, char const *output_name, unsigned int flags) -{ - MD_String8 h_file_name = MD_S8Fmt("%s.h", output_name); - MD_String8 cpp_file_name = MD_S8Fmt("%s.cpp", output_name); - - Dqn_MDCppFile cpp = {}; - Dqn_MDCppFile header = {}; - cpp.file = fopen(DQN_MD_CAST(char const *)cpp_file_name.str, "w+b"); - header.file = fopen(DQN_MD_CAST(char const *)h_file_name.str, "w+b"); - - fprintf(stdout, "Generating meta file from %s to %.*s and %.*s\n", file_path, MD_S8VArg(cpp_file_name), MD_S8VArg(h_file_name)); - - if (cpp.file && header.file) - { - MD_ParseResult parse = MD_ParseWholeFile(MD_S8CString(DQN_MD_CAST(char *)file_path)); - if (flags & Dqn_MD_CodeGenFlag_CEnum) - Dqn_MD_GenerateCEnum(&header, &parse, MD_S8Lit("json_endpoint"), "INS_QueryType"); - - for (MD_EachNode(struct_node, parse.node->first_child)) - { - if (flags & Dqn_MD_CodeGenFlag_CppStruct) - Dqn_MD_GenerateCppStruct(&header, struct_node); - - if (flags & Dqn_MD_CodeGenFlag_ImGuiFunction) - Dqn_MD_GenerateImGuiFunction(&header, struct_node, true /*header_file*/); - - if (flags & Dqn_MD_CodeGenFlag_JsonParsingFunction) - Dqn_MD_GenerateJsonParsingFunction(&header, struct_node, true /*header_file*/); - - if (flags & Dqn_MD_CodeGenFlag_CurlQueryFunction) - Dqn_MD_GenerateCurlQueryFunction(&header, struct_node, true /*header_file*/); - - if (flags & Dqn_MD_CodeGenFlag_JsonEndpointURLFunction) - Dqn_MD_GenerateJsonEndpointURLFunction(&header, struct_node, true /*header_file*/); - - if (flags & Dqn_MD_CodeGenFlag_JsonRPCPostDataFunction) - Dqn_MD_GenerateJsonRPCPostDataFunction(&header, parse.node, struct_node, true /*header_file*/); - - if (flags & Dqn_MD_CodeGenFlag_PrintFunction) - Dqn_MD_GeneratePrintFunction(&header, struct_node, true /*header_file*/); - - Dqn_MDCppFile_NewLine(&header); - - if (flags & Dqn_MD_CodeGenFlag_ImGuiFunction) - Dqn_MD_GenerateImGuiFunction(&cpp, struct_node, false /*header_file*/); - - if (flags & Dqn_MD_CodeGenFlag_JsonParsingFunction) - Dqn_MD_GenerateJsonParsingFunction(&cpp, struct_node, false /*header_file*/); - - if (flags & Dqn_MD_CodeGenFlag_CurlQueryFunction) - Dqn_MD_GenerateCurlQueryFunction(&cpp, struct_node, false /*header_file*/); - - if (flags & Dqn_MD_CodeGenFlag_JsonEndpointURLFunction) - Dqn_MD_GenerateJsonEndpointURLFunction(&cpp, struct_node, false /*header_file*/); - - if (flags & Dqn_MD_CodeGenFlag_JsonRPCPostDataFunction) - Dqn_MD_GenerateJsonRPCPostDataFunction(&cpp, parse.node, struct_node, false /*header_file*/); - - if (flags & Dqn_MD_CodeGenFlag_PrintFunction) - Dqn_MD_GeneratePrintFunction(&cpp, struct_node, false /*header_file*/); - } - } - - if (cpp.file) fclose(cpp.file); - if (header.file) fclose(header.file); -} - -MD_String8 Dqn_MD__ConvertTypeToCppType(MD_String8 type) -{ - MD_String8 result = type; - if (MD_S8Match(type, MD_S8Lit("u8"), 0)) { result = MD_S8Lit("Dqn_u8"); } - else if (MD_S8Match(type, MD_S8Lit("u16"), 0)) { result = MD_S8Lit("Dqn_u16"); } - else if (MD_S8Match(type, MD_S8Lit("u32"), 0)) { result = MD_S8Lit("Dqn_u32"); } - else if (MD_S8Match(type, MD_S8Lit("u64"), 0)) { result = MD_S8Lit("Dqn_u64"); } - else if (MD_S8Match(type, MD_S8Lit("u128"), 0)) { result = MD_S8Lit("intx::uint128"); } - else if (MD_S8Match(type, MD_S8Lit("i8"), 0)) { result = MD_S8Lit("Dqn_i8"); } - else if (MD_S8Match(type, MD_S8Lit("i16"), 0)) { result = MD_S8Lit("Dqn_i16"); } - else if (MD_S8Match(type, MD_S8Lit("i32"), 0)) { result = MD_S8Lit("Dqn_i32"); } - else if (MD_S8Match(type, MD_S8Lit("i64"), 0)) { result = MD_S8Lit("Dqn_i64"); } - else if (MD_S8Match(type, MD_S8Lit("f32"), 0)) { result = MD_S8Lit("Dqn_f32"); } - else if (MD_S8Match(type, MD_S8Lit("f64"), 0)) { result = MD_S8Lit("Dqn_f64"); } - else if (MD_S8Match(type, MD_S8Lit("string"), 0)) { result = MD_S8Lit("Dqn_String"); } - else if (MD_S8Match(type, MD_S8Lit("bool"), 0)) { result = MD_S8Lit("Dqn_b32"); } - return result; -} - -bool Dqn_MD__IsTypeCppPrimitive(MD_String8 type) -{ - MD_String8 cpp_type = Dqn_MD__ConvertTypeToCppType(type); - bool result = !MD_S8Match(cpp_type, type, 0); - // TODO(doyle): We weren't able to convert the type to our Dqn_* type/custom - // type, we assume that this is a custom CPP type we don't ordinarily - // recognize. - return result; -} - -bool Dqn_MD__TypeIsUnsignedInt(MD_String8 type) -{ - bool result = MD_S8Match(type, MD_S8Lit("u8"), 0) || - MD_S8Match(type, MD_S8Lit("u16"), 0) || - MD_S8Match(type, MD_S8Lit("u32"), 0) || - MD_S8Match(type, MD_S8Lit("u64"), 0) || - MD_S8Match(type, MD_S8Lit("u128"), 0); - return result; -} - -bool Dqn_MD__TypeIsSignedInt(MD_String8 type) -{ - bool result = MD_S8Match(type, MD_S8Lit("i8"), 0) || - MD_S8Match(type, MD_S8Lit("i16"), 0) || - MD_S8Match(type, MD_S8Lit("i32"), 0) || - MD_S8Match(type, MD_S8Lit("i64"), 0) || - MD_S8Match(type, MD_S8Lit("i128"), 0); - return result; -} - -MD_String8 Dqn_MD__ConvertTypeToFmtString(MD_String8 type) -{ - MD_String8 result = type; - if (MD_S8Match(type, MD_S8Lit("u8"), 0)) { result = MD_S8Lit("%I64u"); } - else if (MD_S8Match(type, MD_S8Lit("u16"), 0)) { result = MD_S8Lit("%I64u"); } - else if (MD_S8Match(type, MD_S8Lit("u32"), 0)) { result = MD_S8Lit("%I64u"); } - else if (MD_S8Match(type, MD_S8Lit("u64"), 0)) { result = MD_S8Lit("%I64u"); } - else if (MD_S8Match(type, MD_S8Lit("i8"), 0)) { result = MD_S8Lit("%I64d"); } - else if (MD_S8Match(type, MD_S8Lit("i16"), 0)) { result = MD_S8Lit("%I64d"); } - else if (MD_S8Match(type, MD_S8Lit("i32"), 0)) { result = MD_S8Lit("%I64d"); } - else if (MD_S8Match(type, MD_S8Lit("i64"), 0)) { result = MD_S8Lit("%I64d"); } - else if (MD_S8Match(type, MD_S8Lit("f32"), 0)) { result = MD_S8Lit("%f"); } - else if (MD_S8Match(type, MD_S8Lit("f64"), 0)) { result = MD_S8Lit("%f"); } - else if (MD_S8Match(type, MD_S8Lit("string"), 0)) { result = MD_S8Lit("%.*s"); } - else if (MD_S8Match(type, MD_S8Lit("bool"), 0)) { result = MD_S8Lit("%s"); } - return result; -} - -void Dqn_MD_GenerateCppStruct(Dqn_MDCppFile *cpp, MD_Node const *struct_node) -{ - if (!MD_NodeHasTag(DQN_MD_CAST(MD_Node *)struct_node, MD_S8Lit("cpp_struct"), DQN_MD_CAST(MD_MatchFlags)0)) - return; - - Dqn_MDCppFile_BeginBlock(cpp, "struct %.*s", MD_S8VArg(struct_node->string)); - for (MD_EachNode(field, struct_node->first_child)) - { - MD_Node *type_node = MD_NodeFromString(field->first_child, field->last_child + 1, MD_S8Lit("type"), (MD_MatchFlags)0); - MD_String8 type = type_node->first_child->string; - MD_Assert(!MD_NodeIsNil(type_node)); - - MD_String8 cpp_type = Dqn_MD__ConvertTypeToCppType(type); - bool const is_array = Dqn_MD__NodeHasArrayNotation(type_node->first_child->next); - if (is_array) - { - Dqn_MDCppFile_Line(cpp, "Dqn_Array<%.*s> %.*s;", MD_S8VArg(cpp_type), MD_S8VArg(field->string)); - } - else - { - Dqn_MDCppFile_Line(cpp, "%.*s %.*s;", MD_S8VArg(cpp_type), MD_S8VArg(field->string)); - } - } - Dqn_MDCppFile_EndStructBlock(cpp); -} - -void Dqn_MD_GenerateCEnum(Dqn_MDCppFile *cpp, MD_ParseResult *parse, MD_String8 required_tag, char const *name) -{ - Dqn_MDCppFile_BeginBlock(cpp, "enum %s", name); - for (MD_EachNode(struct_node, parse->node->first_child)) - { - if (MD_NodeHasTag(struct_node, required_tag, DQN_MD_CAST(MD_MatchFlags)0)) - Dqn_MDCppFile_Line(cpp, "%s_%.*s,", name, MD_S8VArg(struct_node->string)); - } - Dqn_MDCppFile_Line(cpp, "%s_Count", name); - Dqn_MDCppFile_EndEnumBlock(cpp); -} - -void Dqn_MD_GeneratePrintFunction(Dqn_MDCppFile *cpp, MD_Node const *struct_node, bool header_file) -{ - if (!MD_NodeHasTag(DQN_MD_CAST(MD_Node *)struct_node, MD_S8Lit("cpp_struct"), DQN_MD_CAST(MD_MatchFlags)0) || - !MD_NodeHasTag(DQN_MD_CAST(MD_Node *)struct_node, MD_S8Lit("cpp_print"), DQN_MD_CAST(MD_MatchFlags)0)) - { - return; - } - - const MD_String8 VAL_STR = MD_S8Lit("val"); - Dqn_MDCppFile_LineBegin(cpp, - "void %.*sPrint(FILE *file, %.*s const *%.*s", - MD_S8VArg(struct_node->string), - MD_S8VArg(struct_node->string), - MD_S8VArg(VAL_STR)); - - if (header_file) - { - Dqn_MDCppFile_LineEnd(cpp, ");"); - return; - } - - Dqn_MDCppFile_LineEnd(cpp, ")"); - Dqn_MDCppFile_BeginBlock(cpp, nullptr); - for (MD_EachNode(field_node, struct_node->first_child)) - { - MD_Node *type_node = MD_NodeFromString(field_node->first_child, field_node->last_child + 1, MD_S8Lit("type"), (MD_MatchFlags)0); - MD_String8 type = type_node->first_child->string; - MD_String8 field = field_node->string; - MD_Assert(!MD_NodeIsNil(type_node)); - - MD_String8 cpp_type = Dqn_MD__ConvertTypeToCppType(type); - bool is_array = Dqn_MD__NodeHasArrayNotation(type_node->first_child->next); - MD_String8 param = VAL_STR; - MD_String8 variable_to_print = is_array ? MD_S8Lit("it") : MD_S8Fmt("%.*s->%.*s", MD_S8VArg(param), MD_S8VArg(field)); - if (is_array) - Dqn_MDCppFile_BeginBlock(cpp, "for (%.*s const &it : %.*s->%.*s)", MD_S8VArg(cpp_type), MD_S8VArg(param), MD_S8VArg(field)); - - if (MD_S8Match(type, MD_S8Lit("u8"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%u\n", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("u16"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%u\n", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("u32"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%u\n", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("u64"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%zu\n", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("u128"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%.*s\n", Dqn_U128_String(%.*s, true /*separate*/, ','));)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("i8"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%d\n", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("i16"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%d\n", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("i32"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%d\n", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("i64"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%zd\n", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("f32"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%f\n", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("f64"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%f\n", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("string"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%.*s\n", DQN_STRING_FMT(%.*s));)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("bool"), 0)) Dqn_MDCppFile_Line(cpp, R"(fprintf(file, "%.*s: %%s\n", %.*s ? "true" : "false");)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else Dqn_MDCppFile_Line(cpp, R"(%.*sPrint(file, &%.*s);)", MD_S8VArg(type), MD_S8VArg(variable_to_print)); - - if (is_array) - Dqn_MDCppFile_EndForBlock(cpp); - - } - Dqn_MDCppFile_EndFuncBlock(cpp); -} - -void Dqn_MD_GenerateImGuiFunction(Dqn_MDCppFile *cpp, MD_Node const *struct_node, bool header_file) -{ - const MD_String8 VAL_STR = MD_S8Lit("val"); - Dqn_MDCppFile_LineBegin(cpp, - "void %.*sImGui(%.*s const *%.*s)", - MD_S8VArg(struct_node->string), - MD_S8VArg(struct_node->string), - MD_S8VArg(VAL_STR)); - - if (header_file) - { - Dqn_MDCppFile_LineEnd(cpp, ";"); - return; - } - - Dqn_MDCppFile_BeginBlock(cpp, nullptr); - for (MD_EachNode(field_node, struct_node->first_child)) - { - MD_Node *type_node = MD_NodeFromString(field_node->first_child, field_node->last_child + 1, MD_S8Lit("type"), (MD_MatchFlags)0); - MD_String8 type = type_node->first_child->string; - MD_String8 field = field_node->string; - MD_Assert(!MD_NodeIsNil(type_node)); - - MD_String8 cpp_type = Dqn_MD__ConvertTypeToCppType(type); - bool is_array = Dqn_MD__NodeHasArrayNotation(type_node->first_child->next); - MD_String8 param = VAL_STR; - MD_String8 variable_to_print = is_array ? MD_S8Lit("it") : MD_S8Fmt("%.*s->%.*s", MD_S8VArg(param), MD_S8VArg(field)); - if (is_array) - Dqn_MDCppFile_BeginBlock(cpp, "for (%.*s const &it : %.*s->%.*s)", MD_S8VArg(cpp_type), MD_S8VArg(param), MD_S8VArg(field)); - - if (MD_S8Match(type, MD_S8Lit("u8"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("u16"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("u32"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("u64"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("u128"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("i8"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("i16"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("i32"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("i64"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("f32"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%f", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("f64"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%f", %.*s);)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("string"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%.*s", DQN_STRING_FMT(%.*s));)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else if (MD_S8Match(type, MD_S8Lit("bool"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%s", %.*s ? "true" : "false");)", MD_S8VArg(field), MD_S8VArg(variable_to_print)); - else Dqn_MDCppFile_Line(cpp, R"(%.*sImGui(&%.*s);)", MD_S8VArg(type), MD_S8VArg(variable_to_print)); - - if (is_array) - Dqn_MDCppFile_EndForBlock(cpp); - } - Dqn_MDCppFile_EndFuncBlock(cpp); -} - -void Dqn_MD__CppCodeGenDeserializeJSONPrimitiveToken(Dqn_MDCppFile *cpp, - MD_Node *json_type_node, - MD_Node *type_node, - MD_String8 field, - MD_String8 dest, - MD_String8 json_value_variable_name) -{ - // NOTE: This function is a little bit more complicated than it'd like to be - // because we support the JSON specifiying a different type than the native - // storage stype (i.e. the json could specify a number as a json string i.e. - // "100" but our CPP struct wants a primitive integer. - // - // So we handle that and do that conversion step here. - - // ------------------------------------------------------------------------- - // Determine the source type we're converting from - // ------------------------------------------------------------------------- - MD_String8 json_type = json_type_node->first_child->string; - MD_String8 type = type_node->first_child->string; - enum JsonPrimitive - { - Number, - String, - Object, - Bool, - Array, - }; - - JsonPrimitive json_primitive = {}; - if (MD_S8Match(json_type, MD_S8Lit("number"), (MD_MatchFlags)0)) - { - json_primitive = JsonPrimitive::Number; - Dqn_MDCppFile_BeginBlock(cpp, "if (Dqn_JsmnToken_ExpectNumber(json, *%.*s, err_handle))", MD_S8VArg(json_value_variable_name)); - } - else if (MD_S8Match(json_type, MD_S8Lit("string"), (MD_MatchFlags)0)) - { - json_primitive = JsonPrimitive::String; - Dqn_MDCppFile_BeginBlock(cpp, "if (Dqn_JsmnToken_ExpectString(*%.*s, err_handle))", MD_S8VArg(json_value_variable_name)); - } - else if (MD_S8Match(json_type, MD_S8Lit("bool"), (MD_MatchFlags)0)) - { - json_primitive = JsonPrimitive::Bool; - Dqn_MDCppFile_BeginBlock(cpp, "if (Dqn_JsmnToken_ExpectBool(json, *%.*s, err_handle))", MD_S8VArg(json_value_variable_name)); - } - else if (MD_S8Match(json_type, MD_S8Lit("array"), (MD_MatchFlags)0)) - { - json_primitive = JsonPrimitive::Array; - Dqn_MDCppFile_BeginBlock(cpp, "if (Dqn_JsmnToken_ExpectArray(*%.*s, err_handle))", MD_S8VArg(json_value_variable_name)); - } - else if (MD_S8Match(json_type, MD_S8Lit("object"), (MD_MatchFlags)0)) - { - json_primitive = JsonPrimitive::Object; - Dqn_MDCppFile_BeginBlock(cpp, "if (Dqn_JsmnToken_ExpectObject(*%.*s, err_handle))", MD_S8VArg(json_value_variable_name)); - } - else - DQN_MD_INVALID_CODE_PATH_MSG("Unhandled json type: '%.*s'. Expected 'array', 'number', 'string' or 'object'", MD_S8VArg(json_type)); - - // ------------------------------------------------------------------------- - // Convert the JSON token to our CPP struct's expecting type - // ------------------------------------------------------------------------- - if (json_primitive == JsonPrimitive::Array) - { - MD_String8 cpp_type = Dqn_MD__ConvertTypeToCppType(type); - bool const custom_type = MD_S8Match(cpp_type, type, DQN_MD_CAST(MD_MatchFlags)0); // Type was not recognised, i.e we didn't convert it - - Dqn_MDCppFile_Line(cpp, R"(%.*s = Dqn_Array_InitWithArenaNoGrow(arena, %.*s, value->size, 0, Dqn_ZeroMem::Yes);)", MD_S8VArg(dest), MD_S8VArg(cpp_type)); - Dqn_MDCppFile_BeginBlock(cpp, "for (int array_index = 0; array_index < value->size; array_index++)"); - Dqn_MDCppFile_Line(cpp, - R"(auto *%.*s_entry = Dqn_Array_Make(&%.*s, 1);)", - MD_S8VArg(field), - MD_S8VArg(dest)); - if (custom_type) - { - Dqn_MDCppFile_Line(cpp, - R"(*%.*s_entry = %.*sParseTokens(&it, err_handle, json, arena);)", - MD_S8VArg(field), - MD_S8VArg(type)); - } - else - { - Dqn_MDCppFile_Line(cpp, "jsmntok_t *sub_value = it++;"); - Dqn_MDCppFile_Line(cpp, "Dqn_String sub_value_str = Dqn_JsmnToken_String(json, *sub_value); (void)sub_value_str;"); - - MD_String8 dest = MD_S8Fmt("*%.*s_entry", MD_S8VArg(field)); - Dqn_MD__CppCodeGenDeserializeJSONPrimitiveToken(cpp, type_node, type_node, field, dest, MD_S8Lit("sub_value")); - } - Dqn_MDCppFile_EndForBlock(cpp); - } - else if (json_primitive == JsonPrimitive::Object) - { - Dqn_MDCppFile_BeginBlock(cpp, "if (Dqn_JsmnToken_ExpectObject(*%.*s, err_handle))", MD_S8VArg(json_value_variable_name)); - Dqn_MDCppFile_Line(cpp, "it = %.*s; // Rewind the iterator to the object", MD_S8VArg(json_value_variable_name)); - Dqn_MDCppFile_Line(cpp, "%.*s = %.*sParseTokens(&it, err_handle, json, arena);", MD_S8VArg(dest), MD_S8VArg(type)); - Dqn_MDCppFile_EndIfBlock(cpp); - } - else - { - if (MD_S8Match(type, MD_S8Lit("f32"), (MD_MatchFlags)0)) - { - DQN_MD_ASSERT_MSG(json_primitive == JsonPrimitive::String || json_primitive == JsonPrimitive::Number, "Implement the other part"); - Dqn_MDCppFile_Line(cpp, "%.*s = DQN_CAST(Dqn_f32)atof(%.*s_str.str);", MD_S8VArg(dest), MD_S8VArg(json_value_variable_name)); - } - else if (MD_S8Match(type, MD_S8Lit("f64"), (MD_MatchFlags)0)) - { - DQN_MD_ASSERT_MSG(json_primitive == JsonPrimitive::String || json_primitive == JsonPrimitive::Number, "Implement the other part"); - Dqn_MDCppFile_Line(cpp, "%.*s = atof(%.*s_str.str);", MD_S8VArg(dest), MD_S8VArg(json_value_variable_name)); - } - else if (MD_S8Match(type, MD_S8Lit("u8"), (MD_MatchFlags)0) || - MD_S8Match(type, MD_S8Lit("u16"), (MD_MatchFlags)0) || - MD_S8Match(type, MD_S8Lit("u32"), (MD_MatchFlags)0) || - MD_S8Match(type, MD_S8Lit("u64"), (MD_MatchFlags)0)) - { - DQN_MD_ASSERT_MSG(json_primitive == JsonPrimitive::String || json_primitive == JsonPrimitive::Number, "Implement the other part"); - if (MD_S8Match(type, MD_S8Lit("u64"), (MD_MatchFlags)0)) - { - if (MD_NodeHasTag(json_type_node->first_child, MD_S8Lit("hex"), (MD_MatchFlags)0)) - { - Dqn_MDCppFile_Line( - cpp, - R"(%.*s = Dqn_Hex_StringToU64(%.*s_str);)", - MD_S8VArg(dest), MD_S8VArg(json_value_variable_name)); - } - else - { - Dqn_MDCppFile_Line( - cpp, - R"(%.*s = Dqn_String_ToU64(%.*s_str);)", - MD_S8VArg(dest), MD_S8VArg(json_value_variable_name)); - } - } - else - { - // We need to truncate the deserialized type to write it - // to our struct. - - // NOTE: This bit capitalises the types, i.e. turns u16 - // -> U16, which matches our function naming convention - // for Dqn_Safe_TruncateU64To and co. - char type_all_caps[4] = {}; - memcpy(type_all_caps, type.str, type.size); - type_all_caps[0] = MD_CharToUpper(type_all_caps[0]); - - Dqn_MDCppFile_Line( - cpp, - R"(%.*s = Dqn_Safe_TruncateU64To%s(Dqn_String_ToU64(%.*s_str));)", - MD_S8VArg(dest), - type_all_caps, - MD_S8VArg(json_value_variable_name)); - } - } - else if (MD_S8Match(type, MD_S8Lit("u128"), 0)) - { - DQN_MD_ASSERT_MSG(json_primitive == JsonPrimitive::String || json_primitive == JsonPrimitive::Number, "Implement the other part"); - Dqn_MDCppFile_Line( - cpp, - R"(%.*s = Dqn_U128_FromString(%.*s_str.str, Dqn_Safe_TruncateU64ToInt(%.*s_str.size));)", - MD_S8VArg(dest), - MD_S8VArg(json_value_variable_name), - MD_S8VArg(json_value_variable_name)); - } - else if (MD_S8Match(type, MD_S8Lit("i8"), 0) || - MD_S8Match(type, MD_S8Lit("i16"), 0) || - MD_S8Match(type, MD_S8Lit("i32"), 0) || - MD_S8Match(type, MD_S8Lit("i64"), 0)) - { - DQN_MD_ASSERT_MSG(json_primitive == JsonPrimitive::String || json_primitive == JsonPrimitive::Number, "Implement the other part"); - char type_all_caps[4] = {}; - memcpy(type_all_caps, type.str, type.size); - type_all_caps[0] = MD_CharToUpper(type_all_caps[0]); - - Dqn_MDCppFile_Line( - cpp, - R"(%.*s = Dqn_Safe_TruncateU64To%s(Dqn_String_ToU64(%.*s_str));)", - MD_S8VArg(dest), - type_all_caps, - MD_S8VArg(json_value_variable_name) - ); - } - else if (MD_S8Match(type, MD_S8Lit("string"), 0)) - { - DQN_MD_ASSERT_MSG(json_primitive == JsonPrimitive::String, "Implement the other part"); - Dqn_MDCppFile_Line( - cpp, - R"(%.*s = Dqn_String_Copy(%.*s_str, arena);)", - MD_S8VArg(dest), - MD_S8VArg(json_value_variable_name)); - } - else if (MD_S8Match(type, MD_S8Lit("bool"), 0)) - { - if (json_primitive == JsonPrimitive::Bool) - { - Dqn_MDCppFile_Line( - cpp, - R"(%.*s = Dqn_JsmnToken_Bool(json, *%.*s);)", - MD_S8VArg(dest), - MD_S8VArg(json_value_variable_name)); - } - else if (json_primitive == JsonPrimitive::Number) - { - Dqn_MDCppFile_Line( - cpp, - R"(%.*s = DQN_CAST(Dqn_b32)Dqn_String_ToU64(%.*s_str);)", - MD_S8VArg(dest), - MD_S8VArg(json_value_variable_name)); - } - else - { - DQN_MD_INVALID_CODE_PATH_MSG("Unhandled"); - } - } - else - { - // NOTE: Unhandled 'json_type' specified in the .mdesk file - DQN_MD_INVALID_CODE_PATH; - } - } - - Dqn_MDCppFile_EndIfBlock(cpp); -} - -void Dqn_MD_GenerateJsonParsingFunction(Dqn_MDCppFile *cpp, MD_Node const *struct_node, bool header_file) -{ - MD_Node *tag = MD_NodeFromString(struct_node->first_tag, struct_node->last_tag + 1, MD_S8Lit("json_parsing_function"), (MD_MatchFlags)0); - if (MD_NodeIsNil(tag)) - return; - - bool return_array_type = false; - MD_String8 return_type = struct_node->string; - - MD_Node *struct_type_node = MD_NodeFromString(struct_node->first_child, struct_node->last_child + 1, MD_S8Lit("type"), (MD_MatchFlags)0); - MD_Node *struct_json_type_node = MD_NodeFromString(struct_node->first_child, struct_node->last_child + 1, MD_S8Lit("json_type"), (MD_MatchFlags)0); - if (MD_NodeIsNil(struct_type_node) && MD_NodeIsNil(struct_json_type_node)) - { - } - else - { - return_array_type = true; - return_type = MD_S8Fmt("Dqn_Array<%.*s>", MD_S8VArg(struct_type_node->first_child->string)); - } - - // ------------------------------------------------------------------------- - // Write json helper parsing function - // ------------------------------------------------------------------------- - Dqn_MDCppFile_LineBegin(cpp, "%.*s %.*sParse(Dqn_String json, Dqn_JsmnErrorHandle *err_handle, Dqn_ArenaAllocator *temp_arena, Dqn_ArenaAllocator *arena", MD_S8VArg(return_type), MD_S8VArg(struct_node->string)); - if (header_file) - { - Dqn_MDCppFile_LineEnd(cpp, ");"); - } - else - { - Dqn_MDCppFile_LineEnd(cpp, ")"); - Dqn_MDCppFile_BeginBlock(cpp, nullptr); - Dqn_MDCppFile_Line(cpp, "(void)arena; // TODO(dqn): We don't always need an Arena, but we always have it in the prototype to make life easy if somewhere deep in the json heirarchy we need an arena"); - Dqn_MDCppFile_Line(cpp, "jsmn_parser parser = {};"); - Dqn_MDCppFile_Line(cpp, "jsmn_init(&parser);"); - Dqn_MDCppFile_NewLine(cpp); - Dqn_MDCppFile_Line(cpp, "int tokens_required = jsmn_parse(&parser, json.str, json.size, nullptr, 0);"); - Dqn_MDCppFile_Line(cpp, "DQN_HARD_ASSERT(tokens_required > 0);"); - Dqn_MDCppFile_Line(cpp, "auto *tokens = Dqn_ArenaAllocator_NewArray(temp_arena, jsmntok_t, tokens_required, Dqn_ZeroMem::No);"); - Dqn_MDCppFile_NewLine(cpp); - Dqn_MDCppFile_Line(cpp, "jsmn_init(&parser);"); - Dqn_MDCppFile_Line(cpp, "jsmn_parse(&parser, json.str, json.size, tokens, tokens_required);"); - Dqn_MDCppFile_Line(cpp, "jsmntok_t *it = tokens + 0;"); - Dqn_MDCppFile_NewLine(cpp); - Dqn_MDCppFile_Line(cpp, "%.*s result = %.*sParseTokens(&it, err_handle, json, arena);", MD_S8VArg(return_type), MD_S8VArg(struct_node->string)); - Dqn_MDCppFile_Line(cpp, "return result;"); - Dqn_MDCppFile_EndFuncBlock(cpp); - } - - // ------------------------------------------------------------------------- - // Write the main work horse code - // ------------------------------------------------------------------------- - Dqn_MDCppFile_LineBegin(cpp, "%.*s %.*sParseTokens(jsmntok_t **start_it, Dqn_JsmnErrorHandle *err_handle, Dqn_String json, Dqn_ArenaAllocator *arena", MD_S8VArg(return_type), MD_S8VArg(struct_node->string)); - if (header_file) - { - Dqn_MDCppFile_LineEnd(cpp, ");"); - return; - } - - Dqn_MDCppFile_LineEnd(cpp, ")"); - Dqn_MDCppFile_BeginBlock(cpp, nullptr); - Dqn_MDCppFile_Line(cpp, "(void)arena; // TODO(dqn): We don't always need an Arena, but we always have it in the prototype to make life easy if somewhere deep in the json heirarchy we need an arena"); - Dqn_MDCppFile_Line(cpp, "%.*s result = {};", MD_S8VArg(return_type)); - Dqn_MDCppFile_Line(cpp, "jsmntok_t *it = (*start_it);"); - - if (return_array_type) - { - MD_String8 struct_type = struct_type_node->first_child->string; - MD_String8 struct_json_type = struct_json_type_node->first_child->string; - Dqn_MDCppFile_Line(cpp, "jsmntok_t *value = it++;"); - Dqn_MDCppFile_NewLine(cpp); - Dqn_MD__CppCodeGenDeserializeJSONPrimitiveToken(cpp, struct_json_type_node, struct_type_node, MD_S8Lit("root"), MD_S8Lit("result"), MD_S8Lit("value")); - } - else - { - //---------------------------------------------------------------------- - // Convert the type's in our .mdesk struct into JSON parsing code - //---------------------------------------------------------------------- - Dqn_MDCppFile_Line(cpp, "if (!Dqn_JsmnToken_ExpectObject(*it, err_handle)) return result;"); - Dqn_MDCppFile_Line(cpp, "jsmntok_t *object = it++;"); - Dqn_MDCppFile_NewLine(cpp); - Dqn_MDCppFile_BeginBlock(cpp, "for (int index = 0; index < object->size; index++)"); - Dqn_MDCppFile_Line(cpp, "jsmntok_t *key = it++;"); - Dqn_MDCppFile_Line(cpp, "jsmntok_t *value = it++;"); - Dqn_MDCppFile_Line(cpp, "Dqn_String key_str = Dqn_JsmnToken_String(json, *key);"); - Dqn_MDCppFile_Line(cpp, "Dqn_String value_str = Dqn_JsmnToken_String(json, *value); (void)value_str;"); - Dqn_MDCppFile_NewLine(cpp); - - MD_String8 const IF_BRANCH = MD_S8Lit("if"); - MD_String8 const ELSE_IF_BRANCH = MD_S8Lit("else if"); - MD_String8 branch_str = IF_BRANCH; - for (MD_EachNode(field_node, struct_node->first_child)) - { - MD_Node *type_node = MD_NodeFromString(field_node->first_child, field_node->last_child + 1, MD_S8Lit("type"), (MD_MatchFlags)0); - MD_Node *json_type_node = MD_NodeFromString(field_node->first_child, field_node->last_child + 1, MD_S8Lit("json_type"), (MD_MatchFlags)0); - MD_Node *json_key_node = MD_NodeFromString(field_node->first_child, field_node->last_child + 1, MD_S8Lit("json_key"), (MD_MatchFlags)0); - - MD_String8 field = field_node->string; - MD_String8 type = type_node->first_child->string; - MD_String8 json_type = json_type_node->first_child->string; - MD_String8 json_key = MD_NodeIsNil(json_key_node) ? field : json_key_node->first_child->string; - MD_Assert(!MD_NodeIsNil(type_node)); - - Dqn_MDCppFile_BeginBlock( - cpp, "%.*s (key_str == DQN_STRING(\"%.*s\"))", MD_S8VArg(branch_str), MD_S8VArg(json_key)); - branch_str = ELSE_IF_BRANCH; - MD_String8 dest = MD_S8Fmt("result.%.*s", MD_S8VArg(field)); - Dqn_MD__CppCodeGenDeserializeJSONPrimitiveToken(cpp, json_type_node, type_node, field, dest, MD_S8Lit("value")); - Dqn_MDCppFile_EndIfBlock(cpp); - } - - if (struct_node->first_child) - { - Dqn_MDCppFile_BeginBlock(cpp, "else"); - Dqn_MDCppFile_Line (cpp, "// Potential unhandled json object and value, advance the iterator accordingly"); - Dqn_MDCppFile_Line (cpp, "DQN_ASSERT(key->type == JSMN_STRING);"); - Dqn_MDCppFile_BeginBlock(cpp, "if (value->type == JSMN_OBJECT)"); - Dqn_MDCppFile_Line (cpp, "it = Dqn_JsmnToken_AdvanceItPastObject(value, err_handle, json);"); - Dqn_MDCppFile_EndIfBlock(cpp); - Dqn_MDCppFile_BeginBlock(cpp, "else if (value->type == JSMN_ARRAY)"); - Dqn_MDCppFile_Line (cpp, "it = Dqn_JsmnToken_AdvanceItPastArray(value, err_handle, json);"); - Dqn_MDCppFile_EndIfBlock(cpp); - Dqn_MDCppFile_EndIfBlock(cpp); - } - Dqn_MDCppFile_EndForBlock(cpp); - } - - Dqn_MDCppFile_NewLine(cpp); - Dqn_MDCppFile_Line(cpp, "*start_it = it;"); - Dqn_MDCppFile_Line(cpp, "return result;"); - Dqn_MDCppFile_EndFuncBlock(cpp); -} - -struct Dqn_MD__Variable -{ - MD_Node *type_node; - MD_String8 type; - MD_Node *name_node; - MD_String8 name; - MD_String8 cpp_type; - bool is_array; -}; - -void Dqn_MD__PrintVariableAsFunctionArgument(Dqn_MDCppFile *cpp, Dqn_MD__Variable *variable) -{ - if (variable->is_array) - Dqn_MDCppFile_LineAdd(cpp, "%.*s const *%.*s, Dqn_isize %.*s_size", MD_S8VArg(variable->cpp_type), MD_S8VArg(variable->name), MD_S8VArg(variable->name)); - else - Dqn_MDCppFile_LineAdd(cpp, "%.*s %.*s", MD_S8VArg(variable->cpp_type), MD_S8VArg(variable->name)); -} - -Dqn_MD__Variable Dqn_MD__VariableFromNode(MD_Node *node) -{ - Dqn_MD__Variable result = {}; - result.type_node = MD_NodeFromString(node->first_child, node->last_child + 1, MD_S8Lit("type"), DQN_MD_CAST(MD_MatchFlags)0); - result.name = node->string; - result.type = result.type_node->first_child->string; - result.is_array = Dqn_MD__NodeHasArrayNotation(result.type_node->first_child->next); - result.cpp_type = Dqn_MD__ConvertTypeToCppType(result.type); - return result; -} - -void Dqn_MD_GenerateJsonEndpointURLFunction(Dqn_MDCppFile *cpp, MD_Node *struct_node, bool header_file) -{ - MD_Node *json_endpoint_node = MD_NodeFromString(struct_node->first_tag, struct_node->last_tag + 1, MD_S8Lit("json_endpoint"), (MD_MatchFlags)0); - if (MD_NodeIsNil(json_endpoint_node)) - return; - - if (MD_NodeIsNil(json_endpoint_node->first_child)) - { - DQN_MD_INVALID_CODE_PATH; - return; - } - - MD_Node *url_node = MD_NodeFromString(json_endpoint_node->first_child, json_endpoint_node->last_child + 1, MD_S8Lit("url"), (MD_MatchFlags)0); - MD_Node *params_node = MD_NodeFromString(json_endpoint_node->first_child, json_endpoint_node->last_child + 1, MD_S8Lit("params"), (MD_MatchFlags)0); - MD_String8 url = url_node->first_child->string; - - if (MD_NodeIsNil(url_node)) - { - DQN_MD_INVALID_CODE_PATH; - return; - } - - bool const need_arena = (!MD_NodeIsNil(params_node->first_child)); - - Dqn_MDCppFile_LineBegin(cpp, "Dqn_String %.*sURL(", MD_S8VArg(struct_node->string)); - if (need_arena) - { - Dqn_MDCppFile_LineBegin(cpp, "Dqn_ArenaAllocator *arena"); - if (!MD_NodeIsNil(params_node->first_child)) Dqn_MDCppFile_LineAdd(cpp, ", "); - for (MD_EachNode(param_node, params_node->first_child)) - { - MD_String8 name = param_node->string; - MD_String8 type = param_node->first_child->string; - MD_Assert(!MD_NodeIsNil(param_node->first_child)); - Dqn_MDCppFile_LineAdd(cpp, "%.*s %.*s", MD_S8VArg(Dqn_MD__ConvertTypeToCppType(type)), MD_S8VArg(name)); - if (!MD_NodeIsNil(param_node->next)) Dqn_MDCppFile_LineAdd(cpp, ", "); - } - } - - if (header_file) - { - Dqn_MDCppFile_LineEnd(cpp, ");"); - return; - } - - Dqn_MDCppFile_LineEnd(cpp, ")"); - Dqn_MDCppFile_BeginBlock(cpp, nullptr); - - if (need_arena) - { - Dqn_MDCppFile_LineBegin(cpp, R"(Dqn_String result = Dqn_String_Fmt(arena, "%.*s")", MD_S8VArg(url)); - if (!MD_NodeIsNil(params_node->first_child)) Dqn_MDCppFile_LineAdd(cpp, ", "); - for (MD_EachNode(param_node, params_node->first_child)) - { - MD_String8 name = param_node->string; - MD_String8 type = param_node->first_child->string; - Dqn_MD__ConvertTypeToFmtString(type); - if (MD_S8Match(type, MD_S8Lit("string"), 0)) - Dqn_MDCppFile_LineAdd(cpp, "DQN_STRING_FMT(%.*s)", MD_S8VArg(name)); - else - Dqn_MDCppFile_LineAdd(cpp, "%.*s", MD_S8VArg(name)); - - if (!MD_NodeIsNil(param_node->next)) Dqn_MDCppFile_LineAdd(cpp, ", "); - } - Dqn_MDCppFile_LineEnd(cpp, ");"); - Dqn_MDCppFile_Line(cpp, "DQN_HARD_ASSERT(result.str);"); - Dqn_MDCppFile_Line(cpp, "return result;"); - } - else - { - Dqn_MDCppFile_Line(cpp, R"(return DQN_STRING("%.*s");)", MD_S8VArg(url)); - } - Dqn_MDCppFile_EndFuncBlock(cpp); -} - -void Dqn_MD_GenerateJsonRPCPostDataFunction(Dqn_MDCppFile *cpp, MD_Node *root, MD_Node *struct_node, bool header_file) -{ - MD_Node *tag = MD_NodeFromString(struct_node->first_tag, struct_node->last_tag + 1, MD_S8Lit("json_rpc_post_data"), (MD_MatchFlags)0); - if (MD_NodeIsNil(tag)) - return; - - MD_Node *jsonrpc_node = MD_NodeFromString(tag->first_child, tag->last_child + 1, MD_S8Lit("jsonrpc"), (MD_MatchFlags)0); - MD_Node *method_node = MD_NodeFromString(tag->first_child, tag->last_child + 1, MD_S8Lit("method"), (MD_MatchFlags)0); - MD_Node *id_node = MD_NodeFromString(tag->first_child, tag->last_child + 1, MD_S8Lit("id"), (MD_MatchFlags)0); - MD_Node *params_node = MD_NodeFromString(tag->first_child, tag->last_child + 1, MD_S8Lit("params"), (MD_MatchFlags)0); - - if (MD_NodeIsNil(jsonrpc_node) || MD_NodeIsNil(method_node) || MD_NodeIsNil(id_node)) - { - DQN_MD_INVALID_CODE_PATH_MSG("Missing mandatory node in the RPC post data tag"); - return; - } - - bool need_tmp_arena = false; - bool const need_arena = (!MD_NodeIsNil(params_node->first_child)); - Dqn_MDCppFile_LineBegin(cpp, "Dqn_String %.*sJsonRPCPostData(", MD_S8VArg(struct_node->string)); - if (need_arena) - { - // --------------------------------------------------------------------- - // Add the required arenas to the function prototype - // --------------------------------------------------------------------- - Dqn_MDCppFile_LineBegin(cpp, "Dqn_ArenaAllocator *arena, "); - for (MD_EachNode(param_node, params_node->first_child)) - { - MD_Node *type_node = param_node->first_child; - if (Dqn_MD__NodeHasArrayNotation(type_node->first_child->next)) - { - // NOTE: We need a string builder to build this data, i.e. we - // need some temporary memory - need_tmp_arena = true; - Dqn_MDCppFile_LineBegin(cpp, "Dqn_ArenaAllocator *tmp_arena, "); - break; - } - } - - // --------------------------------------------------------------------- - // Add the parameters to the function prototype - // --------------------------------------------------------------------- - for (MD_EachNode(param_node, params_node->first_child)) - { - Dqn_MD__Variable variable = Dqn_MD__VariableFromNode(param_node); - Dqn_MD__PrintVariableAsFunctionArgument(cpp, &variable); - if (!MD_NodeIsNil(param_node->next)) Dqn_MDCppFile_LineAdd(cpp, ", "); - } - } - - if (header_file) - { - Dqn_MDCppFile_LineEnd(cpp, ");"); - return; - } - - Dqn_MDCppFile_LineEnd(cpp, ")"); - Dqn_MDCppFile_BeginBlock(cpp, nullptr); - - MD_String8 jsonrpc = jsonrpc_node->first_child->string; - MD_String8 method = method_node->first_child->string; - MD_String8 id = id_node->first_child->string; - - if (need_arena) - { - if (need_tmp_arena) - { - Dqn_MDCppFile_Line(cpp, "Dqn_ArenaAllocatorAutoRegion mem_region = Dqn_ArenaAllocator_AutoRegion(tmp_arena);"); - Dqn_MDCppFile_Line(cpp, "Dqn_StringBuilder<> builder = {};"); - Dqn_MDCppFile_Line(cpp, "Dqn_StringBuilder_InitWithArena(&builder, tmp_arena);"); - Dqn_MDCppFile_Line(cpp, R"FOO(Dqn_StringBuilder_AppendString(&builder, DQN_STRING(R"({"jsonrpc":"%.*s", "method":"%.*s", "id":%.*s, "params":[)"));)FOO", - MD_S8VArg(jsonrpc), - MD_S8VArg(method), - MD_S8VArg(id)); - - for (MD_EachNode(param_node, params_node->first_child)) - { - Dqn_MD__Variable variable = Dqn_MD__VariableFromNode(param_node); - if (variable.is_array) - { - Dqn_MDCppFile_BeginBlock(cpp, "for (Dqn_isize array_index = 0; array_index < %.*s_size; array_index++)", MD_S8VArg(variable.name)); - Dqn_MDCppFile_Line(cpp, "auto *%.*s_entry = %.*s + array_index;", MD_S8VArg(variable.name), MD_S8VArg(variable.name)); - if (Dqn_MD__IsTypeCppPrimitive(variable.type)) - { - DQN_MD_INVALID_CODE_PATH_MSG("Unimplemented code path"); - } - else - { - MD_Node *struct_node = MD_NodeFromString(root->first_child, root->last_child + 1, variable.type, DQN_MD_CAST(MD_MatchFlags)0); - DQN_MD_ASSERT_MSG(struct_node, "We don't know how to format print the parameter %.*s", MD_S8VArg(variable.type)); - - Dqn_MDCppFile_Line(cpp, "if (array_index) Dqn_StringBuilder_AppendString(&builder, DQN_STRING(\", \"));"); - Dqn_MDCppFile_Line(cpp, "Dqn_StringBuilder_AppendChar(&builder, '{');"); - for (MD_EachNode(struct_param, struct_node->first_child)) - { - // TODO(dqn): Only support strings atm - Dqn_MD__Variable struct_variable = Dqn_MD__VariableFromNode(struct_param); - DQN_MD_ASSERT_MSG(Dqn_MD__IsTypeCppPrimitive(struct_variable.type), "We don't know how to format print the parameter %.*s", MD_S8VArg(struct_variable.type)); - Dqn_MDCppFile_Line(cpp, R"FOO(Dqn_StringBuilder_AppendFmt(&builder, "\"%.*s\": \"%.*s\"%s", DQN_STRING_FMT(%.*s_entry->%.*s));)FOO", - MD_S8VArg(struct_variable.name), - MD_S8VArg(Dqn_MD__ConvertTypeToFmtString(struct_variable.type)), - MD_NodeIsNil(struct_param->next) ? "" : ", ", - MD_S8VArg(variable.name), - MD_S8VArg(struct_variable.name)); - - } - Dqn_MDCppFile_Line(cpp, "Dqn_StringBuilder_AppendChar(&builder, '}');"); - } - Dqn_MDCppFile_EndForBlock(cpp); - } - else - { - Dqn_MDCppFile_Line(cpp, "Dqn_StringBuilder_AppendFmt(&builder, \"%.*s%s\", %.*s);", - MD_S8VArg(Dqn_MD__ConvertTypeToFmtString(variable.type)), - MD_NodeIsNil(param_node->next) ? "" : ", ", - MD_S8VArg(variable.name)); - } - } - - Dqn_MDCppFile_Line(cpp, "Dqn_StringBuilder_AppendString(&builder, DQN_STRING(\"]}\"));"); - Dqn_MDCppFile_Line(cpp, "Dqn_String result = Dqn_StringBuilder_BuildStringWithArena(&builder, arena);"); - } - else - { - Dqn_MDCppFile_Line(cpp, "Dqn_String result = Dqn_String_Fmt("); - Dqn_MDCppFile_Indent(cpp); - Dqn_MDCppFile_Line(cpp, "arena,"); - Dqn_MDCppFile_LineBegin(cpp, - R"FOO(R"({"jsonrpc":"%.*s", "method":"%.*s", "id":%.*s, "params":[)FOO", - MD_S8VArg(jsonrpc), - MD_S8VArg(method), - MD_S8VArg(id)); - - for (MD_EachNode(param_node, params_node->first_child)) - { - Dqn_MDCppFile_LineAdd(cpp, R"("%%.*s")"); - if (!MD_NodeIsNil(param_node->next)) Dqn_MDCppFile_LineAdd(cpp, ", "); - } - Dqn_MDCppFile_LineEnd(cpp, R"FOO(]})",)FOO"); - - for (MD_EachNode(param_node, params_node->first_child)) - { - MD_String8 name = param_node->string; - MD_String8 type = param_node->first_child->string; - Dqn_MD__ConvertTypeToFmtString(type); - if (MD_S8Match(type, MD_S8Lit("string"), 0)) - Dqn_MDCppFile_LineBegin(cpp, "DQN_STRING_FMT(%.*s)", MD_S8VArg(name)); - else - Dqn_MDCppFile_LineBegin(cpp, "%.*s", MD_S8VArg(name)); - - if (!MD_NodeIsNil(param_node->next)) - { - Dqn_MDCppFile_LineAdd(cpp, ","); - Dqn_MDCppFile_LineEnd(cpp, nullptr); - } - } - Dqn_MDCppFile_LineEnd(cpp, ");"); - Dqn_MDCppFile_Unindent(cpp); - } - Dqn_MDCppFile_Line(cpp, "DQN_HARD_ASSERT(result.str);"); - Dqn_MDCppFile_Line(cpp, "return result;"); - } - else - { - Dqn_MDCppFile_Line(cpp, R"FOO(return DQN_STRING(R"({"jsonrpc":"%.*s", "method":"%.*s", "id":%.*s, "params":[]})");)FOO", - MD_S8VArg(jsonrpc), - MD_S8VArg(method), - MD_S8VArg(id)); - } - Dqn_MDCppFile_EndFuncBlock(cpp); -} - -void Dqn_MD_GenerateCurlQueryFunction(Dqn_MDCppFile *cpp, MD_Node *struct_node, bool header_file) -{ - MD_Node *json_endpoint_node = MD_NodeFromString(struct_node->first_tag, struct_node->last_tag + 1, MD_S8Lit("json_endpoint"), (MD_MatchFlags)0); - if (MD_NodeIsNil(json_endpoint_node)) - return; - - if (MD_NodeIsNil(json_endpoint_node->first_child)) - { - DQN_MD_INVALID_CODE_PATH; - return; - } - - MD_Node *url_node = MD_NodeFromString(json_endpoint_node->first_child, json_endpoint_node->last_child + 1, MD_S8Lit("url"), (MD_MatchFlags)0); - MD_Node *params_node = MD_NodeFromString(json_endpoint_node->first_child, json_endpoint_node->last_child + 1, MD_S8Lit("params"), (MD_MatchFlags)0); - MD_String8 url = url_node->first_child->string; - - if (MD_NodeIsNil(url_node) || MD_NodeIsNil(params_node)) - { - DQN_MD_INVALID_CODE_PATH; - return; - } - - Dqn_MDCppFile_LineBegin(cpp, "Dqn_b32 %.*sQuery(struct INS_Core *insight", MD_S8VArg(struct_node->string)); - if (!MD_NodeIsNil(params_node->first_child)) Dqn_MDCppFile_LineAdd(cpp, ", "); - for (MD_EachNode(param_node, params_node->first_child)) - { - MD_String8 name = param_node->string; - MD_String8 type = param_node->first_child->string; - MD_Assert(!MD_NodeIsNil(param_node->first_child)); - Dqn_MDCppFile_LineAdd(cpp, "%.*s %.*s", MD_S8VArg(Dqn_MD__ConvertTypeToCppType(type)), MD_S8VArg(name)); - if (!MD_NodeIsNil(param_node->next)) Dqn_MDCppFile_LineAdd(cpp, ", "); - } - - if (header_file) - { - Dqn_MDCppFile_LineEnd(cpp, ");"); - return; - } - - Dqn_MDCppFile_LineEnd(cpp, ")"); - Dqn_MDCppFile_BeginBlock(cpp, nullptr); - - Dqn_MDCppFile_Line(cpp, "Dqn_FixedString<1024> url = {};"); - Dqn_MDCppFile_LineBegin(cpp, R"(Dqn_FixedString_AppendFmt(&url, "%.*s")", MD_S8VArg(url)); - if (!MD_NodeIsNil(params_node->first_child)) Dqn_MDCppFile_LineAdd(cpp, ", "); - for (MD_EachNode(param_node, params_node->first_child)) - { - MD_String8 name = param_node->string; - Dqn_MDCppFile_LineAdd(cpp, "DQN_STRING_FMT(%.*s)", MD_S8VArg(name)); - if (!MD_NodeIsNil(param_node->next)) Dqn_MDCppFile_LineAdd(cpp, ", "); - } - Dqn_MDCppFile_LineEnd(cpp, ");"); - Dqn_MDCppFile_Line(cpp, "DQN_HARD_ASSERT(url.size);"); - - Dqn_MDCppFile_Line(cpp, "INS_CurlQuery *query = INS_Curl_QueueQuery(insight, INS_QueryType_%.*s, Dqn_FixedString_ToString(&url));", MD_S8VArg(struct_node->string)); - Dqn_MDCppFile_Line(cpp, "Dqn_b32 result = query != nullptr;"); - Dqn_MDCppFile_Line(cpp, "return result;"); - Dqn_MDCppFile_EndFuncBlock(cpp); -} -#endif // DQN_META_DESK_IMPLEMENTATION - -#if defined(DQN_META_DESK_STANDALONE_PROGRAM) -int main(int argc, char **argv) -{ - if (argc != 3) - { - fprintf(stdout, "Please pass a file name and output name\nUsage: metadesk \n"); - return -1; - } - - char *file = argv[1]; - char *output_name = argv[2]; - Dqn_MD_CodeGen(file, output_name, Dqn_MD_CodeGenFlag_All); - return 0; -} -#endif // DQN_META_DESK_STANDALONE_PROGRAM diff --git a/dqn_tester.h b/dqn_tester.h new file mode 100644 index 0000000..af86259 --- /dev/null +++ b/dqn_tester.h @@ -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 +#include + +#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 diff --git a/dqn_tests.cpp b/dqn_tests.cpp index 8d423b7..39946fa 100644 --- a/dqn_tests.cpp +++ b/dqn_tests.cpp @@ -266,13 +266,13 @@ Dqn_Test Dqn_Test_File() // NOTE: Write step 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_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, Dqn_FileExists(SRC_FILE)); // NOTE: Read step 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, 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)); @@ -987,13 +987,6 @@ Dqn_Test Dqn_Test_OS() 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"); 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: { - Dqn_KeccakBytes28 hash = Dqn_Keccak_SHA3_224_StringToBytes28(input); + Dqn_KeccakBytes28 hash = Dqn_SHA3_224StringToBytes28(input); Dqn_KeccakBytes28 expect; FIPS202_SHA3_224(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data); DQN_TEST_ASSERT_MSG((*test), - Dqn_Keccak_Bytes28Equals(&hash, &expect), + Dqn_KeccakBytes28Equals(&hash, &expect), "\ninput: %.*s" "\nhash: %.*s" "\nexpect: %.*s" , DQN_STRING_FMT(input_hex), - DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&hash).str), - DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&expect).str)); + DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&hash).str), + DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&expect).str)); } break; case Hash_SHA3_256: { - Dqn_KeccakBytes32 hash = Dqn_Keccak_SHA3_256_StringToBytes32(input); + Dqn_KeccakBytes32 hash = Dqn_SHA3_256StringToBytes32(input); Dqn_KeccakBytes32 expect; FIPS202_SHA3_256(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data); DQN_TEST_ASSERT_MSG((*test), - Dqn_Keccak_Bytes32Equals(&hash, &expect), + Dqn_KeccakBytes32Equals(&hash, &expect), "\ninput: %.*s" "\nhash: %.*s" "\nexpect: %.*s" , DQN_STRING_FMT(input_hex), - DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&hash).str), - DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&expect).str)); + DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&hash).str), + DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&expect).str)); } break; case Hash_SHA3_384: { - Dqn_KeccakBytes48 hash = Dqn_Keccak_SHA3_384_StringToBytes48(input); + Dqn_KeccakBytes48 hash = Dqn_SHA3_384StringToBytes48(input); Dqn_KeccakBytes48 expect; FIPS202_SHA3_384(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data); DQN_TEST_ASSERT_MSG((*test), - Dqn_Keccak_Bytes48Equals(&hash, &expect), + Dqn_KeccakBytes48Equals(&hash, &expect), "\ninput: %.*s" "\nhash: %.*s" "\nexpect: %.*s" , DQN_STRING_FMT(input_hex), - DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&hash).str), - DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&expect).str)); + DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&hash).str), + DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&expect).str)); } break; case Hash_SHA3_512: { - Dqn_KeccakBytes64 hash = Dqn_Keccak_SHA3_512_StringToBytes64(input); + Dqn_KeccakBytes64 hash = Dqn_SHA3_512StringToBytes64(input); Dqn_KeccakBytes64 expect; FIPS202_SHA3_512(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data); DQN_TEST_ASSERT_MSG((*test), - Dqn_Keccak_Bytes64Equals(&hash, &expect), + Dqn_KeccakBytes64Equals(&hash, &expect), "\ninput: %.*s" "\nhash: %.*s" "\nexpect: %.*s" , DQN_STRING_FMT(input_hex), - DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&hash).str), - DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&expect).str)); + DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&hash).str), + DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&expect).str)); } break; case Hash_Keccak_224: { - Dqn_KeccakBytes28 hash = Dqn_Keccak_224_StringToBytes28(input); + Dqn_KeccakBytes28 hash = Dqn_Keccak224StringToBytes28(input); Dqn_KeccakBytes28 expect; Keccak(1152, 448, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); DQN_TEST_ASSERT_MSG((*test), - Dqn_Keccak_Bytes28Equals(&hash, &expect), + Dqn_KeccakBytes28Equals(&hash, &expect), "\ninput: %.*s" "\nhash: %.*s" "\nexpect: %.*s" , DQN_STRING_FMT(input_hex), - DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&hash).str), - DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&expect).str)); + DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&hash).str), + DQN_KECCAK_STRING56_FMT(Dqn_KeccakBytes28ToHex(&expect).str)); } break; case Hash_Keccak_256: { - Dqn_KeccakBytes32 hash = Dqn_Keccak_256_StringToBytes32(input); + Dqn_KeccakBytes32 hash = Dqn_Keccak256StringToBytes32(input); Dqn_KeccakBytes32 expect; Keccak(1088, 512, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); DQN_TEST_ASSERT_MSG((*test), - Dqn_Keccak_Bytes32Equals(&hash, &expect), + Dqn_KeccakBytes32Equals(&hash, &expect), "\ninput: %.*s" "\nhash: %.*s" "\nexpect: %.*s" , DQN_STRING_FMT(input_hex), - DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&hash).str), - DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&expect).str)); + DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&hash).str), + DQN_KECCAK_STRING64_FMT(Dqn_KeccakBytes32ToHex(&expect).str)); } break; case Hash_Keccak_384: { - Dqn_KeccakBytes48 hash = Dqn_Keccak_384_StringToBytes48(input); + Dqn_KeccakBytes48 hash = Dqn_Keccak384StringToBytes48(input); Dqn_KeccakBytes48 expect; Keccak(832, 768, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); DQN_TEST_ASSERT_MSG((*test), - Dqn_Keccak_Bytes48Equals(&hash, &expect), + Dqn_KeccakBytes48Equals(&hash, &expect), "\ninput: %.*s" "\nhash: %.*s" "\nexpect: %.*s" , DQN_STRING_FMT(input_hex), - DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&hash).str), - DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&expect).str)); + DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&hash).str), + DQN_KECCAK_STRING96_FMT(Dqn_KeccakBytes48ToHex(&expect).str)); } break; case Hash_Keccak_512: { - Dqn_KeccakBytes64 hash = Dqn_Keccak_512_StringToBytes64(input); + Dqn_KeccakBytes64 hash = Dqn_Keccak512StringToBytes64(input); Dqn_KeccakBytes64 expect; Keccak(576, 1024, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); DQN_TEST_ASSERT_MSG((*test), - Dqn_Keccak_Bytes64Equals(&hash, &expect), + Dqn_KeccakBytes64Equals(&hash, &expect), "\ninput: %.*s" "\nhash: %.*s" "\nexpect: %.*s" , DQN_STRING_FMT(input_hex), - DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&hash).str), - DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&expect).str)); + DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&hash).str), + DQN_KECCAK_STRING128_FMT(Dqn_KeccakBytes64ToHex(&expect).str)); } break;