From 0ae5331d21aeee16cd82adf8c4c0a2ca061129e4 Mon Sep 17 00:00:00 2001 From: doyle Date: Sat, 7 Aug 2021 00:54:53 +1000 Subject: [PATCH] win32: Fix error messages for WinINet, add DSMap --- Code/Dqn.h | 574 ++++++++++++++++++-------- Code/Dqn_Curl.h | 9 + Code/Dqn_MetaDesk.h | 969 ++++++++++++++++++++++++++++++++------------ Code/Dqn_Tests.cpp | 273 ++++++++----- 4 files changed, 1293 insertions(+), 532 deletions(-) diff --git a/Code/Dqn.h b/Code/Dqn.h index f6e65f1..60a95c5 100644 --- a/Code/Dqn.h +++ b/Code/Dqn.h @@ -186,11 +186,19 @@ b = tmp; \ } while (0) -#define DQN_LEN_AND_STR(string) Dqn_CharCount(str), string -#define DQN_STR_AND_LEN(string) string, Dqn_CharCount(string) -#define DQN_STR_AND_LEN_I(string) string, (int)Dqn_CharCount(string) +// NOTE: Prefer the templated Dqn_CharCount function for type-safety. I prefer +// the macro version for embedding within macros for debuggers. When we step +// into a function call using the macro like, DQN_STRING("...") which is very +// common, the debugger jumps into the templated functions which is a waste of +// time (they're bug free by virtue of templatizing). +#define DQN_CHAR_COUNT(string) (sizeof(string) - 1) + +#define DQN_LEN_AND_STR(string) DQN_CHAR_COUNT(str), string +#define DQN_STR_AND_LEN(string) string, DQN_CHAR_COUNT(string) +#define DQN_STR_AND_LEN_I(string) string, (int)DQN_CHAR_COUNT(string) #define DQN_FOR_EACH(i, limit) for (Dqn_isize i = 0; i < (Dqn_isize)(limit); ++i) + #define DQN_BYTES(val) (val) #define DQN_KILOBYTES(val) (1024ULL * DQN_BYTES(val)) #define DQN_MEGABYTES(val) (1024ULL * DQN_KILOBYTES(val)) @@ -277,8 +285,11 @@ #define DQN_ASSERT_MSG(expr, fmt, ...) DQN_HARD_ASSERT_MSG(expr, fmt, ##__VA_ARGS__) #endif -#define DQN_INVALID_CODE_PATH DQN_ASSERT(0) -#define DQN_HARD_INVALID_CODE_PATH DQN_HARD_ASSERT(0 && "Invalid Code Path") +#define DQN_INVALID_CODE_PATH_MSG(fmt, ...) DQN_ASSERT_MSG(0, fmt, ##__VA_ARGS__) +#define DQN_INVALID_CODE_PATH DQN_INVALID_CODE_PATH_MSG("Invalid code path triggered") + +#define DQN_HARD_INVALID_CODE_PATH_MSG(fmt, ...) DQN_HARD_ASSERT_MSG(0, fmt, ##__VA_ARGS__) +#define DQN_HARD_INVALID_CODE_PATH DQN_HARD_INVALID_CODE_PATH_MSG("Invalid code path triggered") // ------------------------------------------------------------------------------------------------ // NOTE: Typedefs @@ -927,6 +938,106 @@ DQN_API void Dqn_CRTAllocator_Free (Dqn_CRTAllocator *alloc DQN_API void *Dqn_CRTAllocator__Malloc (Dqn_CRTAllocator *allocator, Dqn_usize size DQN_CALL_SITE_ARGS); DQN_API void *Dqn_CRTAllocator__Realloc (Dqn_CRTAllocator *allocator, void *ptr, Dqn_usize size DQN_CALL_SITE_ARGS); +// ------------------------------------------------------------------------------------------------- +// NOTE: Dqn_ArenaAllocator +// ------------------------------------------------------------------------------------------------- +struct Dqn_ArenaAllocatorBlock +{ + Dqn_AllocationTracer *tracer; + void *memory; + Dqn_isize size; + Dqn_isize used; + Dqn_ArenaAllocatorBlock *prev; + Dqn_ArenaAllocatorBlock *next; +}; + +enum struct Dqn_ArenaAllocatorMemProvider +{ + CRT, + Virtual, + UserMemory, +}; + +Dqn_usize const DQN_MEM_ARENA_DEFAULT_MIN_BLOCK_SIZE = DQN_KILOBYTES(4); +struct Dqn_ArenaAllocator +{ + Dqn_ArenaAllocatorMemProvider mem_provider; + + // NOTE: Read/Write + Dqn_isize min_block_size; // (Optional): When 0, DQN_MEM_ARENA_DEFAULT_MIN_BLOCK_SIZE is used. Otherwise every new block will at minimum be sized to this value. + + // The following fields are should be set once after zero initialisation + Dqn_AllocationTracer *tracer; + + // NOTE: Read Only + Dqn_ArenaAllocatorBlock *curr_mem_block; + Dqn_ArenaAllocatorBlock *top_mem_block; + Dqn_isize highest_used_mark; // TODO(dqn): This is not implemented yet + int total_allocated_mem_blocks; // Total throughout the life-time of the arena + Dqn_isize usage_before_last_reset; + Dqn_isize wastage_before_last_reset; +}; + +struct Dqn_ArenaAllocatorRegion +{ + Dqn_ArenaAllocator *arena; + Dqn_ArenaAllocatorBlock *curr_mem_block; + Dqn_isize curr_mem_block_used; + Dqn_ArenaAllocatorBlock *top_mem_block; +}; + +struct Dqn_ArenaAllocatorAutoRegion +{ + Dqn_ArenaAllocatorAutoRegion(Dqn_ArenaAllocator *arena); + ~Dqn_ArenaAllocatorAutoRegion(); + Dqn_ArenaAllocatorRegion region; +}; + +struct Dqn_ArenaAllocatorStats +{ + Dqn_isize total_used; + Dqn_isize total_allocated; + Dqn_isize total_wasted; + Dqn_isize total_blocks; +}; + +// NOTE: Dqn_ArenaAllocator can also be zero initialised and will default to the heap allocator with 0 size. +DQN_API Dqn_ArenaAllocator Dqn_ArenaAllocator_InitWithMemory(void *memory, Dqn_isize size); +DQN_API Dqn_ArenaAllocator Dqn_ArenaAllocator_InitWithCRT (Dqn_isize size DQN_CALL_SITE_ARGS); +DQN_API void Dqn_ArenaAllocator_Free (Dqn_ArenaAllocator *arena); +DQN_API Dqn_b32 Dqn_ArenaAllocator_Reserve (Dqn_ArenaAllocator *arena, Dqn_isize size DQN_CALL_SITE_ARGS); +DQN_API void Dqn_ArenaAllocator_ResetUsage (Dqn_ArenaAllocator *arena, Dqn_ZeroMem zero_mem); +DQN_API Dqn_ArenaAllocatorRegion Dqn_ArenaAllocator_BeginRegion (Dqn_ArenaAllocator *arena); +DQN_API void Dqn_ArenaAllocator_EndRegion (Dqn_ArenaAllocatorRegion region); +DQN_API Dqn_ArenaAllocatorAutoRegion Dqn_ArenaAllocator_AutoRegion (Dqn_ArenaAllocator *arena); + +#define Dqn_ArenaAllocator_TaggedAllocate(arena, size, alignment, zero_mem, tag) Dqn_ArenaAllocator__Allocate(arena, size, alignment, zero_mem DQN_CALL_SITE(tag)) +#define Dqn_ArenaAllocator_Allocate(arena, size, alignment, zero_mem) Dqn_ArenaAllocator__Allocate(arena, size, alignment, zero_mem DQN_CALL_SITE("")) + +#define Dqn_ArenaAllocator_TaggedNew(arena, Type, zero_mem, tag) (Type *)Dqn_ArenaAllocator__Allocate(arena, sizeof(Type), alignof(Type), zero_mem DQN_CALL_SITE(tag)) +#define Dqn_ArenaAllocator_New(arena, Type, zero_mem) (Type *)Dqn_ArenaAllocator__Allocate(arena, sizeof(Type), alignof(Type), zero_mem DQN_CALL_SITE("")) + +#define Dqn_ArenaAllocator_TaggedNewArray(arena, Type, count, zero_mem, tag) (Type *)Dqn_ArenaAllocator__Allocate(arena, sizeof(Type) * count, alignof(Type), zero_mem DQN_CALL_SITE(tag)) +#define Dqn_ArenaAllocator_NewArray(arena, Type, count, zero_mem) (Type *)Dqn_ArenaAllocator__Allocate(arena, sizeof(Type) * count, alignof(Type), zero_mem DQN_CALL_SITE("")) + +#define Dqn_ArenaAllocator_TaggedCopyNullTerminate(arena, Type, src, count, tag) (Type *)Dqn_ArenaAllocator__CopyNullTerminate(arena, src, sizeof(*src) * count, alignof(Type) DQN_CALL_SITE(tag)) +#define Dqn_ArenaAllocator_CopyNullTerminate(arena, Type, src, count) (Type *)Dqn_ArenaAllocator__CopyNullTerminate(arena, src, sizeof(*src) * count, alignof(Type) DQN_CALL_SITE("")) + +#define Dqn_ArenaAllocator_TaggedCopy(arena, Type, src, count, tag) (Type *)Dqn_ArenaAllocator__Copy(arena, src, sizeof(*src) * count, alignof(Type) DQN_CALL_SITE(tag)) +#define Dqn_ArenaAllocator_Copy(arena, Type, src, count) (Type *)Dqn_ArenaAllocator__Copy(arena, src, sizeof(*src) * count, alignof(Type) DQN_CALL_SITE("")) +DQN_API void *Dqn_ArenaAllocator__Copy (Dqn_ArenaAllocator *arena, void *src, Dqn_isize size, Dqn_u8 alignment DQN_CALL_SITE_ARGS); +DQN_API void *Dqn_ArenaAllocator__CopyNullTerminate (Dqn_ArenaAllocator *arena, void *src, Dqn_isize size, Dqn_u8 alignment DQN_CALL_SITE_ARGS); +DQN_API void *Dqn_ArenaAllocator__Allocate (Dqn_ArenaAllocator *arena, Dqn_isize size, Dqn_u8 alignment, Dqn_ZeroMem zero_mem DQN_CALL_SITE_ARGS); + +DQN_API Dqn_ArenaAllocatorStats Dqn_ArenaAllocator_GetStats (Dqn_ArenaAllocator const *arena); +DQN_API void Dqn_ArenaAllocator_DumpStatsToLog (Dqn_ArenaAllocator const *arena, char const *label); + +// Macros to print the string, i.e. +// fprintf(stdout, "Arena Dump - " DQN_ARENA_ALLOCATOR_FMT_STRING, DQN_ARENA_ALLOCATOR_FMT(stats, "Global Arena")); +#define DQN_ARENA_ALLOCATOR_FMT_STRING "%s: %$$.3d/%$$.3d (wasted %$$.3d - %d blks)" +#define DQN_ARENA_ALLOCATOR_FMT(stats, arena_label) arena_label, stats.total_used, stats.total_allocated, stats.total_wasted, stats.total_blocks + + // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_Map // ------------------------------------------------------------------------------------------------- @@ -944,8 +1055,8 @@ template struct Dqn_Map { Dqn_ArenaAllocator *arena; - Dqn_MapEntry **values; - Dqn_isize size; // The size of the 'values' list + Dqn_MapEntry **slots; + Dqn_isize size; // The number of slots // NOTE: Sum count and chain_count for total items in the list. Dqn_isize count; // The total number of top-level slots in the 'values' list occupied @@ -967,6 +1078,42 @@ template Dqn_MapEntry *Dqn_Map_AddCopy (Dqn_Map *map, Dq template Dqn_MapEntry *Dqn_Map_Get (Dqn_Map *map, Dqn_u64 hash); template void Dqn_Map_Erase (Dqn_Map *map, Dqn_u64 hash, Dqn_ZeroMem zero_mem); +// Demitri Spanos (HMN) Hash Table +// 70% Max Load, PoT size, Linear Probing, Tombstoneless Deletes + +#if !defined(DQN_DS_MAP_MIN_SIZE) + #define DQN_DS_MAP_MIN_SIZE 4096 +#endif + +template +struct Dqn_DSMapEntry +{ + Dqn_u64 hash; + T value; + Dqn_u8 occupied; +}; + +template +struct Dqn_DSMap +{ + Dqn_ArenaAllocator arena; + Dqn_DSMapEntry *slots; + Dqn_isize size; // The number of slots + Dqn_isize count; // The number of slots occupied in the list +}; + +// (Optional) DSMap can be zero initialised, it will default to a size of +// DQN_DS_MAP_MIN_SIZE elements, but if an initial size use the init function. +// size: A power of 2 size. +template Dqn_DSMap Dqn_DSMap_Init (Dqn_isize size); +template void Dqn_DSMap_Free (Dqn_DSMap *map); + +template Dqn_DSMapEntry *Dqn_DSMap_FindOrAdd(Dqn_DSMap *map, Dqn_u64 hash, Dqn_b32 find_only); +template Dqn_DSMapEntry *Dqn_DSMap_Add (Dqn_DSMap *map, Dqn_u64 hash, T &value); +template Dqn_DSMapEntry *Dqn_DSMap_AddCopy (Dqn_DSMap *map, Dqn_u64 hash, T const &value); +template Dqn_DSMapEntry *Dqn_DSMap_Get (Dqn_DSMap *map, Dqn_u64 hash); +template void Dqn_DSMap_Erase (Dqn_DSMap *map, Dqn_u64 hash, Dqn_ZeroMem zero_mem); + // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_Array // ------------------------------------------------------------------------------------------------- @@ -1006,7 +1153,7 @@ template DQN_API T * Dqn_Array_Peek (Dqn // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_String // ------------------------------------------------------------------------------------------------- -#define DQN_STRING(string) Dqn_String_Init(string, Dqn_CharCountI(string)) +#define DQN_STRING(string) Dqn_String{(char *)string, (Dqn_isize)DQN_CHAR_COUNT(string), (Dqn_isize)DQN_CHAR_COUNT(string)} #define DQN_STRING_FMT(string) (int)((string).size), (string).str struct Dqn_String { @@ -1047,12 +1194,16 @@ DQN_API Dqn_String Dqn_String_InitMemory(char *buf, Dqn_isize capacity); #define Dqn_String_TaggedAllocate(arena, size, zero_mem, tag) Dqn_String__Allocate(arena, size, zero_mem DQN_CALL_SITE(tag)); #define Dqn_String_Allocate(arena, size, zero_mem) Dqn_String__Allocate(arena, size, zero_mem DQN_CALL_SITE("")); +#define Dqn_String_TaggedCopyCString(src, size, arena, tag) Dqn_String__CopyCString(src, size, arena DQN_CALL_SITE(tag)) +#define Dqn_String_CopyCString(src, size, arena) Dqn_String__CopyCString(src, size, arena DQN_CALL_SITE("")) + #define Dqn_String_TaggedCopy(src, arena, tag) Dqn_String__Copy(src, arena DQN_CALL_SITE(tag)) #define Dqn_String_Copy(src, arena) Dqn_String__Copy(src, arena DQN_CALL_SITE("")) DQN_API Dqn_String Dqn_String__Fmt(Dqn_ArenaAllocator *arena DQN_CALL_SITE_ARGS, char const *fmt, ...); DQN_API Dqn_String Dqn_String__FmtV(Dqn_ArenaAllocator *arena, char const *fmt, va_list va DQN_CALL_SITE_ARGS); DQN_API Dqn_String Dqn_String__Allocate(Dqn_ArenaAllocator *arena, Dqn_isize size, Dqn_ZeroMem zero_mem); +DQN_API Dqn_String Dqn_String__CopyCString(char const *string, Dqn_isize size, Dqn_ArenaAllocator *arena DQN_CALL_SITE_ARGS); DQN_API Dqn_String Dqn_String__Copy(Dqn_String const src, Dqn_ArenaAllocator *arena DQN_CALL_SITE_ARGS); DQN_API Dqn_String Dqn_String_TrimWhitespaceAround(Dqn_String src); @@ -1077,7 +1228,6 @@ DQN_API Dqn_Array Dqn_String_Split (Dqn_String src, D DQN_API Dqn_u64 Dqn_String_ToU64 (Dqn_String str); DQN_API Dqn_i64 Dqn_String_ToI64 (Dqn_String str); - // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_FixedString // ------------------------------------------------------------------------------------------------- @@ -1327,100 +1477,6 @@ DQN_API Dqn_V2I Dqn_RectI32_Size (Dqn_RectI32 rect); DQN_API Dqn_V2 Dqn_LerpV2 (Dqn_V2 a, Dqn_f32 t, Dqn_V2 b); DQN_API Dqn_f32 Dqn_LerpF32(Dqn_f32 a, Dqn_f32 t, Dqn_f32 b); -// ------------------------------------------------------------------------------------------------- -// NOTE: Dqn_ArenaAllocator -// ------------------------------------------------------------------------------------------------- -struct Dqn_ArenaAllocatorBlock -{ - Dqn_AllocationTracer *tracer; - void *memory; - Dqn_isize size; - Dqn_isize used; - Dqn_ArenaAllocatorBlock *prev; - Dqn_ArenaAllocatorBlock *next; -}; - -enum struct Dqn_ArenaAllocatorMemProvider -{ - CRT, - Virtual, - UserMemory, -}; - -Dqn_usize const DQN_MEM_ARENA_DEFAULT_MIN_BLOCK_SIZE = DQN_KILOBYTES(4); -struct Dqn_ArenaAllocator -{ - Dqn_ArenaAllocatorMemProvider mem_provider; - - // NOTE: Read/Write - Dqn_isize min_block_size; // (Optional): When 0, DQN_MEM_ARENA_DEFAULT_MIN_BLOCK_SIZE is used. Otherwise every new block will at minimum be sized to this value. - - // The following fields are should be set once after zero initialisation - Dqn_AllocationTracer *tracer; - - // NOTE: Read Only - Dqn_ArenaAllocatorBlock *curr_mem_block; - Dqn_ArenaAllocatorBlock *top_mem_block; - Dqn_isize highest_used_mark; // TODO(dqn): This is not implemented yet - int total_allocated_mem_blocks; // Total throughout the life-time of the arena - Dqn_isize usage_before_last_reset; - Dqn_isize wastage_before_last_reset; -}; - -struct Dqn_ArenaAllocatorRegion -{ - Dqn_ArenaAllocator *arena; - Dqn_ArenaAllocatorBlock *curr_mem_block; - Dqn_isize curr_mem_block_used; - Dqn_ArenaAllocatorBlock *top_mem_block; -}; - -struct Dqn_ArenaAllocatorAutoRegion -{ - Dqn_ArenaAllocatorAutoRegion(Dqn_ArenaAllocator *arena); - ~Dqn_ArenaAllocatorAutoRegion(); - Dqn_ArenaAllocatorRegion region; -}; - -struct Dqn_ArenaAllocatorStats -{ - Dqn_isize total_used; - Dqn_isize total_allocated; - Dqn_isize total_wasted; - Dqn_isize total_blocks; -}; - -// NOTE: Dqn_ArenaAllocator can also be zero initialised and will default to the heap allocator with 0 size. -DQN_API Dqn_ArenaAllocator Dqn_ArenaAllocator_InitWithMemory(void *memory, Dqn_isize size); -DQN_API Dqn_ArenaAllocator Dqn_ArenaAllocator_InitWithCRT (Dqn_isize size DQN_CALL_SITE_ARGS); -DQN_API void Dqn_ArenaAllocator_Free (Dqn_ArenaAllocator *arena); -DQN_API Dqn_b32 Dqn_ArenaAllocator_Reserve (Dqn_ArenaAllocator *arena, Dqn_isize size DQN_CALL_SITE_ARGS); -DQN_API void Dqn_ArenaAllocator_ResetUsage (Dqn_ArenaAllocator *arena, Dqn_ZeroMem zero_mem); -DQN_API Dqn_ArenaAllocatorRegion Dqn_ArenaAllocator_BeginRegion (Dqn_ArenaAllocator *arena); -DQN_API void Dqn_ArenaAllocator_EndRegion (Dqn_ArenaAllocatorRegion region); -DQN_API Dqn_ArenaAllocatorAutoRegion Dqn_ArenaAllocator_AutoRegion (Dqn_ArenaAllocator *arena); - -#define Dqn_ArenaAllocator_TaggedAllocate(arena, size, alignment, zero_mem, tag) Dqn_ArenaAllocator__Allocate(arena, size, alignment, zero_mem DQN_CALL_SITE(tag)) -#define Dqn_ArenaAllocator_Allocate(arena, size, alignment, zero_mem) Dqn_ArenaAllocator__Allocate(arena, size, alignment, zero_mem DQN_CALL_SITE("")) - -#define Dqn_ArenaAllocator_TaggedNew(arena, Type, zero_mem, tag) (Type *)Dqn_ArenaAllocator__Allocate(arena, sizeof(Type), alignof(Type), zero_mem DQN_CALL_SITE(tag)) -#define Dqn_ArenaAllocator_New(arena, Type, zero_mem) (Type *)Dqn_ArenaAllocator__Allocate(arena, sizeof(Type), alignof(Type), zero_mem DQN_CALL_SITE("")) - -#define Dqn_ArenaAllocator_TaggedNewArray(arena, Type, count, zero_mem, tag) (Type *)Dqn_ArenaAllocator__Allocate(arena, sizeof(Type) * count, alignof(Type), zero_mem DQN_CALL_SITE(tag)) -#define Dqn_ArenaAllocator_NewArray(arena, Type, count, zero_mem) (Type *)Dqn_ArenaAllocator__Allocate(arena, sizeof(Type) * count, alignof(Type), zero_mem DQN_CALL_SITE("")) - -#define Dqn_ArenaAllocator_TaggedCopyNullTerminate(arena, Type, src, count, tag) (Type *)Dqn_ArenaAllocator__CopyNullTerminate(arena, src, sizeof(*src) * count, alignof(Type) DQN_CALL_SITE(tag)) -#define Dqn_ArenaAllocator_CopyNullTerminate(arena, Type, src, count) (Type *)Dqn_ArenaAllocator__CopyNullTerminate(arena, src, sizeof(*src) * count, alignof(Type) DQN_CALL_SITE("")) - -#define Dqn_ArenaAllocator_TaggedCopy(arena, Type, src, count, tag) (Type *)Dqn_ArenaAllocator__Copy(arena, src, sizeof(*src) * count, alignof(Type) DQN_CALL_SITE(tag)) -#define Dqn_ArenaAllocator_Copy(arena, Type, src, count) (Type *)Dqn_ArenaAllocator__Copy(arena, src, sizeof(*src) * count, alignof(Type) DQN_CALL_SITE("")) -DQN_API void *Dqn_ArenaAllocator__Copy (Dqn_ArenaAllocator *arena, void *src, Dqn_isize size, Dqn_u8 alignment DQN_CALL_SITE_ARGS); -DQN_API void *Dqn_ArenaAllocator__CopyNullTerminate (Dqn_ArenaAllocator *arena, void *src, Dqn_isize size, Dqn_u8 alignment DQN_CALL_SITE_ARGS); -DQN_API void *Dqn_ArenaAllocator__Allocate (Dqn_ArenaAllocator *arena, Dqn_isize size, Dqn_u8 alignment, Dqn_ZeroMem zero_mem DQN_CALL_SITE_ARGS); -DQN_API Dqn_ArenaAllocatorStats Dqn_ArenaAllocator_GetStats (Dqn_ArenaAllocator const *arena); -DQN_API void Dqn_ArenaAllocator_DumpStatsToLog (Dqn_ArenaAllocator const *arena, char const *label); -DQN_API Dqn_FixedString<512> Dqn_ArenaAllocator_StatsString (Dqn_ArenaAllocator const *arena, char const *label); - // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_Bit // ------------------------------------------------------------------------------------------------- @@ -1478,6 +1534,9 @@ DQN_API Dqn_u8 *Dqn_Hex_CStringToU8BytesUnchecked(char const *hex, Dq DQN_API Dqn_Array Dqn_Hex_CStringToU8ArrayUnchecked(char const *hex, Dqn_isize size, Dqn_ArenaAllocator *arena); DQN_API Dqn_Array Dqn_Hex_StringToU8ArrayUnchecked (Dqn_String const hex, Dqn_ArenaAllocator *arena); +DQN_API Dqn_u64 Dqn_Hex_CStringToU64(char const *hex, Dqn_isize size); +DQN_API Dqn_u64 Dqn_Hex_StringToU64(Dqn_String hex); + // Convert a series of bytes into a string DQN_API char *Dqn_Hex_U8BytesToCString(char const *bytes, Dqn_isize size, Dqn_ArenaAllocator *arena); DQN_API Dqn_String Dqn_Hex_U8ArrayToString (Dqn_Array const bytes, Dqn_ArenaAllocator *arena); @@ -1586,9 +1645,15 @@ DQN_API char *Dqn_U64ToTempStr (Dqn_u64 val, Dqn_b32 comma_sep = true); // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_Win // ------------------------------------------------------------------------------------------------- -// last_error: (Optional) The error code associated with the last error will be written into this value -DQN_API Dqn_String Dqn_Win_LastError (Dqn_ArenaAllocator *arena, int *last_error); -DQN_API void Dqn_Win_DumpLastError(Dqn_ArenaAllocator *tmp_arena, Dqn_String prefix); +struct Dqn_WinErrorMsg +{ + DWORD code; + char str[DQN_KILOBYTES(64) - 1]; // Maximum error size + DWORD size; +}; +DQN_API Dqn_WinErrorMsg Dqn_Win_LastError (); +DQN_API void Dqn_Win_DumpLastError(Dqn_String prefix); // Automatically dumps to DQN_LOG_E + DQN_API Dqn_StringW Dqn_Win_UTF8ToWChar (Dqn_String src, Dqn_ArenaAllocator *arena); DQN_API Dqn_String Dqn_Win_WCharToUTF8 (Dqn_StringW src, Dqn_ArenaAllocator *arena); @@ -1846,9 +1911,9 @@ Dqn_Map Dqn_Map_InitWithArena(Dqn_ArenaAllocator *arena, Dqn_isize size) Dqn_Map result = {}; result.arena = arena; - Dqn_isize final_size = size == 0 ? 4096 : size; - result.values = Dqn_ArenaAllocator_NewArray(arena, Dqn_MapEntry *, final_size, Dqn_ZeroMem::Yes); - if (result.values) result.size = final_size; + Dqn_isize final_size = size == 0 ? 4096 : size; + result.slots = Dqn_ArenaAllocator_NewArray(arena, Dqn_MapEntry *, final_size, Dqn_ZeroMem::Yes); + if (result.slots) result.size = final_size; return result; } @@ -1856,7 +1921,7 @@ template Dqn_MapEntry *Dqn_Map_FindOrAdd(Dqn_Map *map, Dqn_u64 hash, Dqn_MapCollideRule rule) { Dqn_isize index = hash % map->size; - Dqn_MapEntry *result = map->values[index]; + Dqn_MapEntry *result = map->slots[index]; if (result) { @@ -1884,7 +1949,7 @@ Dqn_MapEntry *Dqn_Map_FindOrAdd(Dqn_Map *map, Dqn_u64 hash, Dqn_MapCollide { result = Dqn_ArenaAllocator_New(map->arena, Dqn_MapEntry, Dqn_ZeroMem::Yes); map->count++; - map->values[index] = result; + map->slots[index] = result; } if (result) @@ -1919,7 +1984,7 @@ Dqn_MapEntry *Dqn_Map_Get(Dqn_Map *map, Dqn_u64 hash) Dqn_isize index = hash % map->size; Dqn_MapEntry *result = nullptr; - for (Dqn_MapEntry *entry = map->values[index]; entry; entry = entry->next) + for (Dqn_MapEntry *entry = map->slots[index]; entry; entry = entry->next) { if (entry->hash == hash) { @@ -1935,7 +2000,7 @@ template void Dqn_Map_Erase(Dqn_Map *map, Dqn_u64 hash, Dqn_ZeroMem zero_mem) { Dqn_isize index = hash % map->size; - Dqn_MapEntry **entry = &(map->values[index]); + Dqn_MapEntry **entry = &(map->slots[index]); Dqn_b32 is_chain_entry = *entry && (*entry)->next; while ((*entry) && (*entry)->hash != hash) @@ -1956,6 +2021,143 @@ void Dqn_Map_Erase(Dqn_Map *map, Dqn_u64 hash, Dqn_ZeroMem zero_mem) } } +// ------------------------------------------------------------------------------------------------- +// NOTE: Dqn_DSMap Template Implementation +// ------------------------------------------------------------------------------------------------- +template +Dqn_DSMap Dqn_DSMap_Init(Dqn_isize size) +{ + DQN_ASSERT_MSG(((size & (size - 1)) == 0), "Require non-zero power of 2 table size"); + Dqn_DSMap result = {}; + result.slots = Dqn_ArenaAllocator_NewArray(&result.arena, Dqn_DSMapEntry, size, Dqn_ZeroMem::Yes); + if (result.slots) result.size = size; + return result; +} + +template +void Dqn_DSMap_Free(Dqn_DSMap *map) +{ + Dqn_ArenaAllocator_Free(&map->arena); + *map = {}; +} + +template +Dqn_DSMapEntry *Dqn_DSMap_FindOrAdd(Dqn_DSMap *map, Dqn_u64 hash, Dqn_b32 find_only) +{ + if (!map->slots) + { + if (!find_only) *map = Dqn_DSMap_Init(DQN_DS_MAP_MIN_SIZE); + return nullptr; + } + + 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; + } + + if (result->occupied) + { + DQN_ASSERT_MSG(result->hash == hash, + "We have a max load factor of 70%% so we should never get an occupied slot that doesn't match " + "the hash we were searching for"); + } + else if (find_only) + { + result = nullptr; + } + else + { + result->hash = hash; + result->occupied = true; + + Dqn_f32 load_factor = ++map->count / DQN_CAST(Dqn_f32)map->size; + if (load_factor >= 0.7f) + { + auto new_map = Dqn_DSMap_Init(map->size << 1); + for (Dqn_isize map_index = 0; map_index < map->size; map_index++) + { + Dqn_DSMapEntry *entry = map->slots + map_index; + if (entry->occupied) + { + Dqn_DSMapEntry *new_entry = Dqn_DSMap_AddCopy(&new_map, entry->hash, entry->value); + if (new_entry->hash == hash) + result = new_entry; + } + } + + Dqn_DSMap_Free(map); + *map = new_map; + } + } + + return result; +} + +template +Dqn_DSMapEntry *Dqn_DSMap_Add(Dqn_DSMap *map, Dqn_u64 hash, T &value) +{ + Dqn_DSMapEntry *result = Dqn_DSMap_FindOrAdd(map, hash, false /*find_only*/); + if (result) + result->value = value; + + return result; +} + +template +Dqn_DSMapEntry *Dqn_DSMap_AddCopy(Dqn_DSMap *map, Dqn_u64 hash, T const &value) +{ + Dqn_DSMapEntry *result = Dqn_DSMap_FindOrAdd(map, hash, false /*find_only*/); + if (result) + result->value = value; + + return result; +} + +template +Dqn_DSMapEntry *Dqn_DSMap_Get(Dqn_DSMap *map, Dqn_u64 hash) +{ + Dqn_DSMapEntry *result = Dqn_DSMap_FindOrAdd(map, hash, true /*find_only*/); + return result; +} + +template +void Dqn_DSMap_Erase(Dqn_DSMap *map, Dqn_u64 hash, Dqn_ZeroMem zero_mem) +{ + Dqn_isize index = hash % map->size; + Dqn_DSMapEntry *result = map->slots + index; + if (!result || !result->occupied) + return; + + Dqn_isize start_index = index; + Dqn_isize probe_index = index; + for (;;) + { + probe_index = (probe_index + 1) % map->size; + Dqn_DSMapEntry *probe = map->slots + probe_index; + if (!probe->occupied) break; + + Dqn_isize desired_index = probe->hash % map->size; + if (desired_index != probe_index) + { + map->slots[start_index] = map->slots[probe_index]; + start_index = probe_index; + DQN_ASSERT(map->slots[start_index].occupied); + DQN_ASSERT(map->slots[probe_index].occupied); + } + } + + DQN_ASSERT(map->slots[start_index].occupied); + map->slots[start_index].occupied = false; + map->count -= 1; + + Dqn__ZeroMemBytes(map->slots + start_index, + sizeof(map->slots[start_index]), + zero_mem); +} + // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_FixedString Template Implementation // ------------------------------------------------------------------------------------------------- @@ -2596,6 +2798,7 @@ Dqn_b32 Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *iterator) // NOTE: FormatMessageA #define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000 #define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200 + #define FORMAT_MESSAGE_FROM_HMODULE 0x00000800 #define MAKELANGID(p, s) ((((unsigned short )(s)) << 10) | (unsigned short )(p)) #define SUBLANG_DEFAULT 0x01 // user default #define LANG_NEUTRAL 0x00 @@ -2740,6 +2943,8 @@ Dqn_b32 Dqn_List_Iterate(Dqn_List *list, Dqn_ListIterator *iterator) BOOL FreeLibrary (void *lib_module); void *GetProcAddress (void *hmodule, char const *proc_name); unsigned int GetWindowModuleFileNameA (void *hwnd, char *file_name, unsigned int file_name_max); + HMODULE GetModuleHandleA (char const *lpModuleName); + DWORD WaitForSingleObject (HANDLE handle, DWORD milliseconds); BOOL QueryPerformanceCounter (LARGE_INTEGER *performance_count); @@ -3212,13 +3417,26 @@ DQN_API Dqn_String Dqn_String__Allocate(Dqn_ArenaAllocator *arena, Dqn_isize siz return result; } +DQN_API Dqn_String Dqn_String__CopyCString(char const *string, Dqn_isize size, Dqn_ArenaAllocator *arena DQN_CALL_SITE_ARGS) +{ + Dqn_String result = {}; + if (size < 0 || !string || !arena) + { + DQN_INVALID_CODE_PATH; + return result; + } + + char *copy = DQN_CAST(char *)Dqn_ArenaAllocator__Allocate(arena, size + 1, alignof(char), Dqn_ZeroMem::No DQN_CALL_SITE_ARGS_INPUT); + DQN_MEMCOPY(copy, string, DQN_CAST(size_t)size); + copy[size] = 0; + + result = Dqn_String_Init(copy, size); + return result; +} + DQN_API Dqn_String Dqn_String__Copy(Dqn_String const src, Dqn_ArenaAllocator *arena DQN_CALL_SITE_ARGS) { - Dqn_String result = src; - result.str = DQN_CAST(char *)Dqn_ArenaAllocator__Allocate(arena, result.size + 1, alignof(char), Dqn_ZeroMem::No DQN_CALL_SITE_ARGS_INPUT); - result.cap = result.size; - DQN_MEMCOPY(result.str, src.str, DQN_CAST(size_t)result.size); - result.str[result.size] = 0; + Dqn_String result = Dqn_String__CopyCString(src.str, src.size, arena DQN_CALL_SITE_ARGS_INPUT); return result; } @@ -3293,7 +3511,7 @@ DQN_API Dqn_b32 Dqn_String_StartsWith(Dqn_String string, Dqn_String prefix, Dqn_ if (prefix.size > string.size) return result; - Dqn_String substring = {string.str, prefix.size}; + Dqn_String substring = Dqn_String{string.str, prefix.size, prefix.size}; result = Dqn_String_Eq(substring, prefix, eq_case); return result; } @@ -3617,28 +3835,10 @@ DQN_API Dqn_ArenaAllocatorStats Dqn_ArenaAllocator_GetStats(Dqn_ArenaAllocator c return result; } -Dqn_String const DQN_ARENA_ALLOCATOR__STATS_FMT = DQN_STRING("%s: %$$.3d/%$$.3d (wasted %$$.3d - %d blks)"); DQN_API void Dqn_ArenaAllocator_DumpStatsToLog(Dqn_ArenaAllocator const *arena, char const *label) { Dqn_ArenaAllocatorStats stats = Dqn_ArenaAllocator_GetStats(arena); - DQN_LOG_M(DQN_ARENA_ALLOCATOR__STATS_FMT.str, - label, - stats.total_used, - stats.total_allocated, - stats.total_wasted, - stats.total_blocks); -} - -DQN_API Dqn_FixedString<512> Dqn_ArenaAllocator_StatsString(Dqn_ArenaAllocator const *arena, char const *label) -{ - Dqn_ArenaAllocatorStats stats = Dqn_ArenaAllocator_GetStats(arena); - auto result = Dqn_FixedString_Fmt<512>(DQN_ARENA_ALLOCATOR__STATS_FMT.str, - label, - stats.total_used, - stats.total_allocated, - stats.total_wasted, - stats.total_blocks); - return result; + DQN_LOG_M(DQN_ARENA_ALLOCATOR_FMT_STRING, DQN_ARENA_ALLOCATOR_FMT(stats, label)); } // ------------------------------------------------------------------------------------------------- @@ -4285,7 +4485,7 @@ DQN_API Dqn_b32 Dqn_Char_IsWhitespace(char ch) DQN_API Dqn_b32 Dqn_Char_IsHex(char ch) { - Dqn_b32 result = ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') || (ch >= '0' || ch <= '9')); + Dqn_b32 result = ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') || (ch >= '0' && ch <= '9')); return result; } @@ -4394,6 +4594,42 @@ DQN_API Dqn_Array Dqn_Hex_StringToU8ArrayUnchecked(Dqn_String const hex, return result; } +DQN_API Dqn_u64 Dqn_Hex_CStringToU64(char const *hex, Dqn_isize size) +{ + Dqn_isize trim_size = size; + char const *trim_hex = hex; + if (trim_size >= 2) + { + if (trim_hex[0] == '0' && (trim_hex[1] == 'x' || trim_hex[1] == 'X')) + { + trim_size -= 2; + trim_hex += 2; + } + } + + DQN_ASSERT(DQN_CAST(Dqn_usize)(trim_size * 4 / 8) /*maximum amount of bytes represented in the hex string*/ <= sizeof(Dqn_u64)); + + Dqn_u64 result = 0; + Dqn_usize bits_written = 0; + for (Dqn_isize hex_index = 0; hex_index < size; hex_index++, bits_written += 4) + { + char ch = trim_hex[hex_index]; + if (!Dqn_Char_IsHex(ch)) break; + Dqn_u8 val = Dqn_Char_HexToU8(ch); + Dqn_usize bit_shift = 60 - bits_written; + result |= (DQN_CAST(Dqn_u64)val << bit_shift); + } + + result >>= (64 - bits_written); // Shift the remainder digits to the end. + return result; +} + +DQN_API Dqn_u64 Dqn_Hex_StringToU64(Dqn_String hex) +{ + Dqn_u64 result = Dqn_Hex_CStringToU64(hex.str, hex.size); + return result; +} + DQN_API char *Dqn_Hex_U8BytesToCString(char const *bytes, Dqn_isize size, Dqn_ArenaAllocator *arena) { char *result = size > 0 ? Dqn_ArenaAllocator_NewArray(arena, char, size * 2, Dqn_ZeroMem::No) : nullptr; @@ -5032,37 +5268,40 @@ DQN_API char *Dqn_U64ToTempStr(Dqn_u64 val, Dqn_b32 comma_sep) // ------------------------------------------------------------------------------------------------- // NOTE: Dqn_Win Implementation // ------------------------------------------------------------------------------------------------- -DQN_API Dqn_String Dqn_Win_LastError(Dqn_ArenaAllocator *arena, int *last_error) +DQN_API Dqn_WinErrorMsg Dqn_Win_LastError() { - DWORD error = GetLastError(); - int const MSG_SIZE_BYTES = DQN_KILOBYTES(64) - 1; // Maximum error size - int const MSG_SIZE = MSG_SIZE_BYTES / sizeof(char); - auto * msg = DQN_CAST(char *) Dqn_ArenaAllocator_NewArray(arena, char, MSG_SIZE_BYTES, Dqn_ZeroMem::No); + Dqn_WinErrorMsg result; + result.code = GetLastError(); + result.str[0] = 0; - DWORD msg_length = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // DWORD dwFlags, - nullptr, // LPCVOID lpSource, - error, // DWORD dwMessageId, - 0, // DWORD dwLanguageId, - msg, // LPSTR lpBuffer, - MSG_SIZE, // DWORD nSize, - nullptr // va_list * Arguments); + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + HMODULE module_to_get_errors_from = nullptr; + + if (result.code >= 12000 && result.code <= 12175) // WinINET Errors + { + flags |= FORMAT_MESSAGE_FROM_HMODULE; + module_to_get_errors_from = GetModuleHandleA("wininet.dll"); + } + + result.size = FormatMessageA(flags, + module_to_get_errors_from, // LPCVOID lpSource, + result.code, // DWORD dwMessageId, + 0, // DWORD dwLanguageId, + result.str, // LPSTR lpBuffer, + DQN_CAST(DWORD) Dqn_ArrayCountI(result.str), // DWORD nSize, + nullptr // va_list * Arguments); ); - Dqn_String result = Dqn_String_Init(msg, msg_length); - if (last_error) *last_error = error; return result; } -DQN_API void Dqn_Win_DumpLastError(Dqn_ArenaAllocator *tmp_arena, Dqn_String prefix) +DQN_API void Dqn_Win_DumpLastError(Dqn_String prefix) { - Dqn_ArenaAllocatorAutoRegion mem_region = Dqn_ArenaAllocator_AutoRegion(tmp_arena); - int last_error = 0; - Dqn_String msg = Dqn_Win_LastError(tmp_arena, &last_error); - + Dqn_WinErrorMsg msg = Dqn_Win_LastError(); if (msg.size) - DQN_LOG_E("%.*s. Error: %.*s", DQN_STRING_FMT(prefix), DQN_STRING_FMT(msg)); + DQN_LOG_E("%.*s. Error: %.*s", DQN_STRING_FMT(prefix), msg.size, msg.str); else - DQN_LOG_E("%.*s. FormatMessage failed on error: %d with error code: %d\n", DQN_STRING_FMT(prefix), last_error, GetLastError()); + DQN_LOG_E("%.*s. FormatMessage error: %d. No error message for: %d\n", DQN_STRING_FMT(prefix), GetLastError(), msg.code); } DQN_API Dqn_StringW Dqn_Win_UTF8ToWChar(Dqn_String src, Dqn_ArenaAllocator *arena) @@ -5075,11 +5314,10 @@ DQN_API Dqn_StringW Dqn_Win_UTF8ToWChar(Dqn_String src, Dqn_ArenaAllocator *aren int required = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, src.str, size_int, nullptr, 0); if (required == 0) { - Dqn_ArenaAllocatorAutoRegion mem_region = Dqn_ArenaAllocator_AutoRegion(arena); - Dqn_String error = Dqn_Win_LastError(arena, nullptr); + Dqn_WinErrorMsg error = Dqn_Win_LastError(); DQN_LOG_W("Failed to convert string '%.*s' to wide string: %.*s", DQN_STRING_FMT(src), - DQN_STRING_FMT(error)); + error.size, error.str); return result; } @@ -5096,7 +5334,7 @@ DQN_API Dqn_StringW Dqn_Win_UTF8ToWChar(Dqn_String src, Dqn_ArenaAllocator *aren "the character before is not a null character."); } - result = {string, string_size}; + result = Dqn_StringW{string, string_size, string_size}; return result; } @@ -5111,10 +5349,10 @@ DQN_API Dqn_String Dqn_Win_WCharToUTF8(Dqn_StringW src, Dqn_ArenaAllocator *aren if (required == 0) { Dqn_ArenaAllocatorAutoRegion mem_region = Dqn_ArenaAllocator_AutoRegion(arena); - Dqn_String error = Dqn_Win_LastError(arena, nullptr); + Dqn_WinErrorMsg error = Dqn_Win_LastError(); DQN_LOG_W("Failed to convert wide string '%.*s' to UTF8 string: %.*s", DQN_STRING_FMT(src), - DQN_STRING_FMT(error)); + error.size, error.str); return result; } @@ -5155,7 +5393,7 @@ DQN_API Dqn_StringW Dqn_Win_CurrentDirW(Dqn_ArenaAllocator *arena, Dqn_StringW s wchar_t *w_path = Dqn_ArenaAllocator_NewArray(arena, wchar_t, desired_size, Dqn_ZeroMem::No); if (!w_path) - result; + return result; DWORD bytes_written_wo_null_terminator = GetCurrentDirectoryW(desired_size, w_path); if ((bytes_written_wo_null_terminator + 1) != required_size) @@ -5171,7 +5409,7 @@ DQN_API Dqn_StringW Dqn_Win_CurrentDirW(Dqn_ArenaAllocator *arena, Dqn_StringW s w_path[desired_size] = 0; } - result = {w_path, desired_size - 1}; + result = Dqn_StringW{w_path, desired_size - 1, desired_size - 1}; return result; } diff --git a/Code/Dqn_Curl.h b/Code/Dqn_Curl.h index 75006e6..0826a44 100644 --- a/Code/Dqn_Curl.h +++ b/Code/Dqn_Curl.h @@ -47,6 +47,8 @@ struct Dqn_CurlProcs }; Dqn_CurlProcs Dqn_CurlProcs_Init(); +void Dqn_Curl_SetPostData(CURL *curl, char const *post_data, int post_size); + #endif // DQN_CURL_H #if defined(DQN_CURL_IMPLEMENTATION) @@ -77,4 +79,11 @@ Dqn_CurlProcs Dqn_CurlProcs_Init() return result; } + +void Dqn_Curl_SetPostData(CURL *curl, char const *post_data, int post_size) +{ + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, post_size); +} #endif // DQN_CURL_IMPLEMENTATION diff --git a/Code/Dqn_MetaDesk.h b/Code/Dqn_MetaDesk.h index 095aafd..9a69989 100644 --- a/Code/Dqn_MetaDesk.h +++ b/Code/Dqn_MetaDesk.h @@ -1,4 +1,4 @@ -#ifndef DQN_META_DESK_H +#if !defined(DQN_META_DESK_H) #define DQN_META_DESK_H // ----------------------------------------------------------------------------- // NOTE: Dqn_MetaDesk @@ -25,11 +25,26 @@ // ----------------------------------------------------------------------------- // 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_INVALID_CODE_PATH MD_Assert(0) +#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) // ----------------------------------------------------------------------------- @@ -72,6 +87,8 @@ enum Dqn_MD_CodeGenFlag 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), }; @@ -83,7 +100,7 @@ void Dqn_MD_CodeGen(char const *file_path, char const *output_name, unsigned int // ----------------------------------------------------------------------------- // NOTE: Dqn_MD: Code generating functions // ----------------------------------------------------------------------------- -#if 0 +/* Generate a CPP struct for the given 'struct_node'. Children nodes of the 'struct_node' must have a 'type' child associated with it. @@ -105,11 +122,10 @@ Output Dqn_f32 eth; Dqn_f32 usd; }; +*/ +void Dqn_MD_GenerateCppStruct(Dqn_MDCppFile *cpp, MD_Node const *struct_node); -#endif -void Dqn_MD_GenerateCppStruct(Dqn_MDCppFile *cpp, MD_Node const *struct_node, bool *requires_arena); - -#if 0 +/* 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 @@ -125,11 +141,10 @@ Output INS_QueryType_CoinGecko_Coin, INS_QueryType_Count }; - -#endif +*/ void Dqn_MD_GenerateCEnum(Dqn_MDCppFile *cpp, MD_ParseResult *parse, MD_String8 required_tag, char const *name); -#if 0 +/* 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. @@ -152,11 +167,10 @@ Output ImGui::Text("market_cap_rank: %I64u", val->market_cap_rank); INS_CoinGecko_CoinMarketDataImGui(&val->market_data); } - -#endif +*/ void Dqn_MD_GenerateImGuiFunction(Dqn_MDCppFile *cpp, MD_Node const *struct_node, bool header_file); -#if 0 +/* 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 @@ -222,10 +236,12 @@ Output: (Truncated for brevity) return result; } -#endif -void Dqn_MD_GenerateJsonParsingFunction(Dqn_MDCppFile *cpp, MD_Node const *struct_node, bool requires_arena, bool header_file); +*/ +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); -#if 0 + +/* Generate for any root MetaDesk node with the following tags defined @json_endpoint(url: <...>, params: {<...>}) @@ -244,22 +260,45 @@ Output DQN_HARD_ASSERT(result.str); return result; } - -#endif +*/ 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_GenerateCurlQueryFunction(Dqn_MDCppFile *cpp, 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 // ----------------------------------------------------------------------------- -#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 +// ----------------------------------------------------------------------------- +// 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" -#pragma warning(pop) + +#if defined(__clang__) +#elif defined(_MSC_VER) + #pragma warning(pop) +#endif // ----------------------------------------------------------------------------- // NOTE: Dqn_MDCppFile Implementation @@ -361,7 +400,7 @@ void Dqn_MDCppFile_EndBlock(Dqn_MDCppFile *cpp, bool trailing_semicolon, bool ne // ----------------------------------------------------------------------------- // MD Helpers // ----------------------------------------------------------------------------- -static bool MD_NodeHasArrayNotation(MD_Node const *node) +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; @@ -391,15 +430,14 @@ void Dqn_MD_CodeGen(char const *file_path, char const *output_name, unsigned int for (MD_EachNode(struct_node, parse.node->first_child)) { - bool requires_arena = false; if (flags & Dqn_MD_CodeGenFlag_CppStruct) - Dqn_MD_GenerateCppStruct(&header, struct_node, &requires_arena); + 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, requires_arena, true /*header_file*/); + Dqn_MD_GenerateJsonParsingFunction(&header, struct_node, true /*header_file*/); if (flags & Dqn_MD_CodeGenFlag_CurlQueryFunction) Dqn_MD_GenerateCurlQueryFunction(&header, struct_node, true /*header_file*/); @@ -407,19 +445,31 @@ void Dqn_MD_CodeGen(char const *file_path, char const *output_name, unsigned int 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, requires_arena, false /*header_file*/); + 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*/); } } @@ -446,6 +496,16 @@ MD_String8 Dqn_MD__ConvertTypeToCppType(MD_String8 type) 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) || @@ -479,13 +539,16 @@ MD_String8 Dqn_MD__ConvertTypeToFmtString(MD_String8 type) 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("DQN_STRING_FMT(%.*s)"); } + 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, bool *requires_arena) +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)) { @@ -494,11 +557,9 @@ void Dqn_MD_GenerateCppStruct(Dqn_MDCppFile *cpp, MD_Node const *struct_node, bo MD_Assert(!MD_NodeIsNil(type_node)); MD_String8 cpp_type = Dqn_MD__ConvertTypeToCppType(type); - if (MD_S8Match(type, MD_S8Lit("string"), 0)) *requires_arena = true; - bool const is_array = MD_NodeHasArrayNotation(type_node->first_child->next); + bool const is_array = Dqn_MD__NodeHasArrayNotation(type_node->first_child->next); if (is_array) { - *requires_arena = true; Dqn_MDCppFile_Line(cpp, "Dqn_Array<%.*s> %.*s;", MD_S8VArg(cpp_type), MD_S8VArg(field->string)); } else @@ -521,6 +582,65 @@ void Dqn_MD_GenerateCEnum(Dqn_MDCppFile *cpp, MD_ParseResult *parse, MD_String8 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"); @@ -544,47 +664,27 @@ void Dqn_MD_GenerateImGuiFunction(Dqn_MDCppFile *cpp, MD_Node const *struct_node MD_String8 field = field_node->string; MD_Assert(!MD_NodeIsNil(type_node)); - bool is_array = MD_NodeHasArrayNotation(type_node->first_child->next); + 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 : val->%.*s)", MD_S8VArg(type), MD_S8VArg(field)); - } + Dqn_MDCppFile_BeginBlock(cpp, "for (%.*s const &it : %.*s->%.*s)", MD_S8VArg(cpp_type), MD_S8VArg(param), MD_S8VArg(field)); - if (is_array) - { - if (MD_S8Match(type, MD_S8Lit("u8"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", *it);)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("u16"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", *it);)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("u32"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", *it);)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("u64"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", *it);)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("u128"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", *it);)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("i8"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", *it);)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("i16"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", *it);)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("i32"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", *it);)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("i64"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", *it);)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("f32"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%f", *it);)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("f64"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%f", *it);)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("string"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%.*s", DQN_STRING_FMT(*it));)", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("bool"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%s", *it ? "true" : "false");)", MD_S8VArg(field)); - else Dqn_MDCppFile_Line(cpp, R"(%.*sImGui(&it);)", MD_S8VArg(type)); - } - else - { - if (MD_S8Match(type, MD_S8Lit("u8"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", %.*s->%.*s);)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("u16"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", %.*s->%.*s);)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("u32"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", %.*s->%.*s);)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("u64"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", %.*s->%.*s);)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("u128"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64u", %.*s->%.*s);)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("i8"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", %.*s->%.*s);)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("i16"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", %.*s->%.*s);)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("i32"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", %.*s->%.*s);)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("i64"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%I64d", %.*s->%.*s);)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("f32"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%f", %.*s->%.*s);)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("f64"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%f", %.*s->%.*s);)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("string"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%.*s", DQN_STRING_FMT(%.*s->%.*s));)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("bool"), 0)) Dqn_MDCppFile_Line(cpp, R"(ImGui::Text("%.*s: %%s", %.*s->%.*s ? "true" : "false");)", MD_S8VArg(field), MD_S8VArg(param), MD_S8VArg(field)); - else Dqn_MDCppFile_Line(cpp, R"(%.*sImGui(&%.*s->%.*s);)", MD_S8VArg(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); @@ -592,12 +692,250 @@ void Dqn_MD_GenerateImGuiFunction(Dqn_MDCppFile *cpp, MD_Node const *struct_node Dqn_MDCppFile_EndFuncBlock(cpp); } -void Dqn_MD_GenerateJsonParsingFunction(Dqn_MDCppFile *cpp, MD_Node const *struct_node, bool requires_arena, bool header_file) +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) { - Dqn_MDCppFile_LineBegin(cpp, "%.*s %.*sParse(Dqn_String json, Dqn_JsmnErrorHandle *err_handle, Dqn_ArenaAllocator *temp_arena", MD_S8VArg(struct_node->string), MD_S8VArg(struct_node->string)); - if (requires_arena) - Dqn_MDCppFile_LineAdd(cpp, ", Dqn_ArenaAllocator *arena"); + // 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, ");"); @@ -606,6 +944,7 @@ void Dqn_MD_GenerateJsonParsingFunction(Dqn_MDCppFile *cpp, MD_Node const *struc { 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); @@ -617,15 +956,15 @@ void Dqn_MD_GenerateJsonParsingFunction(Dqn_MDCppFile *cpp, MD_Node const *struc 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(struct_node->string), MD_S8VArg(struct_node->string)); + 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); } - Dqn_MDCppFile_LineBegin(cpp, "%.*s %.*sParseTokens(jsmntok_t **start_it, Dqn_JsmnErrorHandle *err_handle, Dqn_String json", MD_S8VArg(struct_node->string), MD_S8VArg(struct_node->string)); - if (requires_arena) - Dqn_MDCppFile_LineAdd(cpp, ", Dqn_ArenaAllocator *arena"); - + // ------------------------------------------------------------------------- + // 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, ");"); @@ -634,180 +973,107 @@ void Dqn_MD_GenerateJsonParsingFunction(Dqn_MDCppFile *cpp, MD_Node const *struc Dqn_MDCppFile_LineEnd(cpp, ")"); Dqn_MDCppFile_BeginBlock(cpp, nullptr); - Dqn_MDCppFile_Line(cpp, "%.*s result = {};", MD_S8VArg(struct_node->string)); + 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);"); - 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)) + if (return_array_type) { - 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)); - - //------------------------------------------------------------------ + 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_BeginBlock( - cpp, "%.*s (key_str == DQN_STRING(\"%.*s\"))", MD_S8VArg(branch_str), MD_S8VArg(json_key)); - branch_str = ELSE_IF_BRANCH; + //---------------------------------------------------------------------- + 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); - if (MD_S8Match(json_type, MD_S8Lit("array"), (MD_MatchFlags)0)) + 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)) { - Dqn_MDCppFile_BeginBlock(cpp, "if (Dqn_JsmnToken_ExpectArray(*value, err_handle))"); - Dqn_MDCppFile_Line(cpp, R"(result.%.*s = Dqn_Array_InitWithArenaNoGrow(arena, %.*s, value->size, 0, Dqn_ZeroMem::Yes);)", MD_S8VArg(field), MD_S8VArg(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(&result.%.*s, 1);)", - MD_S8VArg(field), - MD_S8VArg(field)); - // TODO(dqn): This doesn't work if the type is not a custom type ... - Dqn_MDCppFile_Line(cpp, - R"(*%.*s_entry = %.*sParseTokens(&it, err_handle, json, arena);)", - MD_S8VArg(field), - MD_S8VArg(type)); - Dqn_MDCppFile_EndIfBlock(cpp); - Dqn_MDCppFile_EndForBlock(cpp); - } - else if (MD_S8Match(json_type, MD_S8Lit("object"), (MD_MatchFlags)0)) - { - Dqn_MDCppFile_BeginBlock(cpp, "if (Dqn_JsmnToken_ExpectObject(*value, err_handle))"); - Dqn_MDCppFile_Line(cpp, "it = value; // Rewind the iterator to the object"); - Dqn_MDCppFile_Line(cpp, "result.%.*s = %.*sParseTokens(&it, err_handle, json);", MD_S8VArg(field), MD_S8VArg(type)); + 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); } - else + + if (struct_node->first_child) { - //-------------------------------------------------------------- - // Enforce that the JSON token matches the expected type - //-------------------------------------------------------------- - if (MD_S8Match(json_type, MD_S8Lit("number"), (MD_MatchFlags)0)) - Dqn_MDCppFile_BeginBlock(cpp, "if (Dqn_JsmnToken_ExpectNumber(json, *value, err_handle))"); - else if (MD_S8Match(json_type, MD_S8Lit("string"), (MD_MatchFlags)0)) - Dqn_MDCppFile_BeginBlock(cpp, "if (Dqn_JsmnToken_ExpectString(*value, err_handle))"); - else - DQN_MD_INVALID_CODE_PATH; - - //-------------------------------------------------------------- - // Convert JSON token to our struct's expecting type - //-------------------------------------------------------------- - if (MD_S8Match(type, MD_S8Lit("f32"), (MD_MatchFlags)0)) - Dqn_MDCppFile_Line(cpp, "result.%.*s = DQN_CAST(Dqn_f32)atof(value_str.str);", MD_S8VArg(field)); - else if (MD_S8Match(type, MD_S8Lit("f64"), (MD_MatchFlags)0)) - Dqn_MDCppFile_Line(cpp, "result.%.*s = atof(value_str.str);", MD_S8VArg(field)); - 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)) - { - if (MD_S8Match(type, MD_S8Lit("u64"), (MD_MatchFlags)0)) - { - Dqn_MDCppFile_Line( - cpp, - R"(result.%.*s = Dqn_String_ToU64(value_str);)", - MD_S8VArg(field)); - } - 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"(result.%.*s = Dqn_Safe_TruncateU64To%s(Dqn_String_ToU64(value_str));)", - MD_S8VArg(field), - type_all_caps); - } - } - else if (MD_S8Match(type, MD_S8Lit("u128"), 0)) - { - Dqn_MDCppFile_Line( - cpp, - R"(result.%.*s = Dqn_U128_FromString(value_str.str, Dqn_Safe_TruncateU64ToInt(value_str.size));)", - MD_S8VArg(field)); - } - 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)) - { - 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"(result.%.*s = Dqn_Safe_TruncateU64To%s(Dqn_String_ToU64(value_str));)", - MD_S8VArg(field), - type_all_caps); - } - else if (MD_S8Match(type, MD_S8Lit("string"), 0)) - { - Dqn_MDCppFile_Line( - cpp, - R"(result.%.*s = Dqn_String_Copy(value_str, arena);)", - MD_S8VArg(field)); - } - else if (MD_S8Match(type, MD_S8Lit("bool"), 0)) - { - Dqn_MDCppFile_Line( - cpp, - R"(result.%.*s = DQN_CAST(Dqn_b32)Dqn_String_ToU64(value_str);)", - MD_S8VArg(field)); - } - else - { - // NOTE: Unhandled 'json_type' specified in the .mdesk file - DQN_MD_INVALID_CODE_PATH; - } + 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_EndIfBlock(cpp); + Dqn_MDCppFile_EndForBlock(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); @@ -824,21 +1090,27 @@ void Dqn_MD_GenerateJsonEndpointURLFunction(Dqn_MDCppFile *cpp, MD_Node *struct_ 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)) + if (MD_NodeIsNil(url_node)) { DQN_MD_INVALID_CODE_PATH; return; } - Dqn_MDCppFile_LineBegin(cpp, "Dqn_String %.*sURL(Dqn_ArenaAllocator *arena", 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)) + 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) { - 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, ", "); + 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) @@ -850,23 +1122,202 @@ void Dqn_MD_GenerateJsonEndpointURLFunction(Dqn_MDCppFile *cpp, MD_Node *struct_ Dqn_MDCppFile_LineEnd(cpp, ")"); Dqn_MDCppFile_BeginBlock(cpp, nullptr); - 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)) + if (need_arena) { - 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)); + 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, ", "); + 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_LineEnd(cpp, ");"); - Dqn_MDCppFile_Line(cpp, "DQN_HARD_ASSERT(result.str);"); - Dqn_MDCppFile_Line(cpp, "return result;"); Dqn_MDCppFile_EndFuncBlock(cpp); } diff --git a/Code/Dqn_Tests.cpp b/Code/Dqn_Tests.cpp index c1f25e5..5393de3 100644 --- a/Code/Dqn_Tests.cpp +++ b/Code/Dqn_Tests.cpp @@ -80,7 +80,7 @@ static int g_dqn_test_total_tests; if (!(expr)) \ { \ testing_state.test.fail_expr = DQN_STRING(#expr); \ - testing_state.test.fail_msg = Dqn_String_Fmt(&testing_state.arena, msg, ##__VA_ARGS__); \ + testing_state.test.fail_msg = Dqn_String_Fmt(&testing_state.arena, msg, ##__VA_ARGS__); \ } #define DQN_TEST_EXPECT(testing_state, expr) DQN_TEST_EXPECT_MSG(testing_state, expr, "") @@ -131,109 +131,6 @@ void Dqn_TestState_PrintResult(Dqn_TestState const *result) } } -void Dqn_Test_Allocator() -{ -#if 0 - Dqn_TestingState testing_state = {}; - DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Allocator"); - - // NOTE: Various allocator test - { - { - DQN_TEST_START_SCOPE(testing_state, "HeapAllocator - Allocate Small"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); - char const EXPECT[] = "hello_world"; - char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char), Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); - DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); - } - - { - DQN_TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocate Small"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithXHeap(); - char const EXPECT[] = "hello_world"; - char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char), Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); - DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); - } - - { - DQN_TEST_START_SCOPE(testing_state, "ArenaAllocator - Allocate Small"); - Dqn_ArenaAllocator arena = Dqn_ArenaAllocator_InitWithNewAllocator(Dqn_Allocator_InitWithHeap(), 0, nullptr); - Dqn_Allocator allocator = Dqn_Allocator_InitWithArena(&arena); - char const EXPECT[] = "hello_world"; - char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, Dqn_ArrayCount(EXPECT), alignof(char), Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - memcpy(buf, EXPECT, Dqn_ArrayCount(EXPECT)); - DQN_TEST_EXPECT_MSG(testing_state, memcmp(EXPECT, buf, Dqn_ArrayCount(EXPECT)) == 0, "buf: %s, expect: %s", buf, EXPECT); - } - } - - // NOTE: Alignment Test - { - Dqn_u8 const ALIGNMENT3 = 4; - Dqn_u8 const NUM_BYTES = sizeof(Dqn_u32); - { - DQN_TEST_START_SCOPE(testing_state, "HeapAllocator - Align to 32 bytes"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); - auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3); - DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); - } - - { - DQN_TEST_START_SCOPE(testing_state, "XHeapAllocator - Align to 32 bytes"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithXHeap(); - auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3); - DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); - } - - { - DQN_TEST_START_SCOPE(testing_state, "ArenaAllocator - Align to 32 bytes"); - Dqn_ArenaAllocator arena = {}; - if (arena.backup_allocator.type == Dqn_AllocatorType::Null) - arena.backup_allocator = Dqn_Allocator_InitWithHeap(); - - Dqn_Allocator allocator = Dqn_Allocator_InitWithArena(&arena); - auto *buf = DQN_CAST(Dqn_u32 *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); - int buf_mod_alignment = DQN_CAST(int)(DQN_CAST(uintptr_t)buf % ALIGNMENT3); - DQN_TEST_EXPECT_MSG(testing_state, buf_mod_alignment == 0, "buf_mod_alignment: %d", buf_mod_alignment); - } - } - - // NOTE: Dqn_PointerMetadata tests - { - Dqn_u8 const ALIGNMENT3 = 4; - Dqn_u8 const NUM_BYTES = 4; - Dqn_u8 const MAX_OFFSET = (ALIGNMENT3 - 1) + sizeof(Dqn_PointerMetadata); - { - DQN_TEST_START_SCOPE(testing_state, "HeapAllocator - Allocation metadata initialised"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithHeap(); - char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - Dqn_PointerMetadata metadata = Dqn_PointerMetadata_Get(buf); - DQN_TEST_EXPECT_MSG(testing_state, metadata.alignment == ALIGNMENT3, "metadata.alignment: %u, ALIGNMENT3: %u", metadata.alignment, ALIGNMENT3); - DQN_TEST_EXPECT_MSG(testing_state, metadata.offset <= MAX_OFFSET, "metadata.offset: %u, MAX_OFFSET: %u", metadata.offset, MAX_OFFSET); - } - - { - DQN_TEST_START_SCOPE(testing_state, "XHeapAllocator - Allocation metadata initialised"); - Dqn_Allocator allocator = Dqn_Allocator_InitWithXHeap(); - char *buf = DQN_CAST(char *)Dqn_Allocator_Allocate(&allocator, NUM_BYTES, ALIGNMENT3, Dqn_ZeroMem::Yes); - DQN_DEFER { Dqn_Allocator_Free(&allocator, buf); }; - Dqn_PointerMetadata metadata = Dqn_PointerMetadata_Get(buf); - DQN_TEST_EXPECT_MSG(testing_state, metadata.alignment == ALIGNMENT3, "metadata.alignment: %u, ALIGNMENT3: %u", metadata.alignment, ALIGNMENT3); - DQN_TEST_EXPECT_MSG(testing_state, metadata.offset <= MAX_OFFSET, "metadata.offset: %u, MAX_OFFSET: %u", metadata.offset, MAX_OFFSET); - } - } -#endif -} - void Dqn_Test_Array() { Dqn_TestingState testing_state = {}; @@ -431,6 +328,77 @@ void Dqn_Test_FixedString() } } +void Dqn_Test_Hex() +{ + Dqn_TestingState testing_state = {}; + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_Hex"); + { + DQN_TEST_START_SCOPE(testing_state, "Convert 0x123"); + Dqn_u64 result = Dqn_Hex_StringToU64(DQN_STRING("0x123")); + DQN_TEST_EXPECT_MSG(testing_state, result == 0x123, "result: %I64u", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Convert 0xFFFF"); + Dqn_u64 result = Dqn_Hex_StringToU64(DQN_STRING("0xFFFF")); + DQN_TEST_EXPECT_MSG(testing_state, result == 0xFFFF, "result: %I64u", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Convert FFFF"); + Dqn_u64 result = Dqn_Hex_StringToU64(DQN_STRING("FFFF")); + DQN_TEST_EXPECT_MSG(testing_state, result == 0xFFFF, "result: %I64u", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Convert abCD"); + Dqn_u64 result = Dqn_Hex_StringToU64(DQN_STRING("abCD")); + DQN_TEST_EXPECT_MSG(testing_state, result == 0xabCD, "result: %I64u", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Convert 0xabCD"); + Dqn_u64 result = Dqn_Hex_StringToU64(DQN_STRING("0xabCD")); + DQN_TEST_EXPECT_MSG(testing_state, result == 0xabCD, "result: %I64u", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Convert 0x"); + Dqn_u64 result = Dqn_Hex_StringToU64(DQN_STRING("0x")); + DQN_TEST_EXPECT_MSG(testing_state, result == 0x0, "result: %I64u", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Convert 0X"); + Dqn_u64 result = Dqn_Hex_StringToU64(DQN_STRING("0X")); + DQN_TEST_EXPECT_MSG(testing_state, result == 0x0, "result: %I64u", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Convert 3"); + Dqn_u64 result = Dqn_Hex_StringToU64(DQN_STRING("3")); + DQN_TEST_EXPECT_MSG(testing_state, result == 3, "result: %I64u", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Convert f"); + Dqn_u64 result = Dqn_Hex_StringToU64(DQN_STRING("f")); + DQN_TEST_EXPECT_MSG(testing_state, result == 0xf, "result: %I64u", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Convert g"); + Dqn_u64 result = Dqn_Hex_StringToU64(DQN_STRING("g")); + DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %I64u", result); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Convert -0x3"); + Dqn_u64 result = Dqn_Hex_StringToU64(DQN_STRING("-0x3")); + DQN_TEST_EXPECT_MSG(testing_state, result == 0, "result: %I64u", result); + } +} + void Dqn_Test_M4() { Dqn_TestingState testing_state = {}; @@ -456,6 +424,99 @@ void Dqn_Test_M4() } } +void Dqn_Test_DSMap() +{ + Dqn_TestingState testing_state = {}; + DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_DSMap"); + { + DQN_TEST_START_SCOPE(testing_state, "Add r-value item to map"); + Dqn_DSMap map = Dqn_DSMap_Init(128); + Dqn_DSMapEntry *entry = Dqn_DSMap_AddCopy(&map, 3 /*hash*/, 5 /*value*/); + DQN_TEST_EXPECT_MSG(testing_state, map.size == 128, "size: %I64d", map.size); + DQN_TEST_EXPECT_MSG(testing_state, map.count == 1, "count: %I64u", map.count); + DQN_TEST_EXPECT_MSG(testing_state, entry->hash == 3, "hash: %I64u", entry->hash); + DQN_TEST_EXPECT_MSG(testing_state, entry->value == 5, "value: %d", entry->value); + Dqn_DSMap_Free(&map); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Add l-value item to map"); + Dqn_DSMap map = Dqn_DSMap_Init(128); + int value = 5; + Dqn_DSMapEntry *entry = Dqn_DSMap_Add(&map, 3 /*hash*/, value); + DQN_TEST_EXPECT_MSG(testing_state, map.size == 128, "size: %I64d", map.size); + DQN_TEST_EXPECT_MSG(testing_state, map.count == 1, "count: %I64u", map.count); + DQN_TEST_EXPECT_MSG(testing_state, entry->hash == 3, "hash: %I64u", entry->hash); + DQN_TEST_EXPECT_MSG(testing_state, entry->value == 5, "value: %d", entry->value); + Dqn_DSMap_Free(&map); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Get item from map"); + Dqn_DSMap map = Dqn_DSMap_Init(128); + Dqn_DSMapEntry *entry = Dqn_DSMap_AddCopy(&map, 3 /*hash*/, 5 /*value*/); + Dqn_DSMapEntry *get_entry = Dqn_DSMap_Get(&map, 3 /*hash*/); + DQN_TEST_EXPECT_MSG(testing_state, get_entry == entry, "get_entry: %p, entry: %p", get_entry, entry); + Dqn_DSMap_Free(&map); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Get non-existent item from map"); + Dqn_DSMap map = Dqn_DSMap_Init(128); + Dqn_DSMapEntry *entry = Dqn_DSMap_Get(&map, 3 /*hash*/); + DQN_TEST_EXPECT(testing_state, entry == nullptr); + Dqn_DSMap_Free(&map); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Erase item from map"); + Dqn_DSMap map = Dqn_DSMap_Init(128); + Dqn_DSMap_AddCopy(&map, 3 /*hash*/, 5 /*value*/); + DQN_TEST_EXPECT_MSG(testing_state, map.count == 1, "count: %I64d", map.count); + Dqn_DSMap_Erase(&map, 3 /*hash*/, Dqn_ZeroMem::No); + DQN_TEST_EXPECT_MSG(testing_state, map.count == 0, "count: %I64d", map.count); + Dqn_DSMap_Free(&map); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Erase non-existent item from map"); + Dqn_DSMap map = Dqn_DSMap_Init(128); + Dqn_DSMap_Erase(&map, 3 /*hash*/, Dqn_ZeroMem::No); + DQN_TEST_EXPECT_MSG(testing_state, map.count == 0, "count: %I64d", map.count); + Dqn_DSMap_Free(&map); + } + + { + DQN_TEST_START_SCOPE(testing_state, "Test resize on maximum load"); + const Dqn_isize INIT_SIZE = 4; + Dqn_DSMap map = Dqn_DSMap_Init(INIT_SIZE); + Dqn_DSMap_AddCopy(&map, 0 /*hash*/, 5 /*value*/); + Dqn_DSMap_AddCopy(&map, 1 /*hash*/, 5 /*value*/); + DQN_TEST_EXPECT_MSG(testing_state, map.count == 2, "count: %I64d", map.count); + + // This *should* cause a resize because 3/4 slots filled is 75% load + Dqn_DSMap_AddCopy(&map, 6 /*hash*/, 5 /*value*/); + DQN_TEST_EXPECT_MSG(testing_state, map.count == 3, "count: %I64d", map.count); + DQN_TEST_EXPECT_MSG(testing_state, map.size == INIT_SIZE * 2, "size: %I64d", map.size); + + // Check that the elements are rehashed where we expected them to be + DQN_TEST_EXPECT (testing_state, map.slots[0].occupied == DQN_CAST(Dqn_u8)true); + DQN_TEST_EXPECT (testing_state, map.slots[1].occupied == DQN_CAST(Dqn_u8)true); + DQN_TEST_EXPECT (testing_state, map.slots[2].occupied == DQN_CAST(Dqn_u8)false); + DQN_TEST_EXPECT (testing_state, map.slots[3].occupied == DQN_CAST(Dqn_u8)false); + DQN_TEST_EXPECT (testing_state, map.slots[4].occupied == DQN_CAST(Dqn_u8)false); + DQN_TEST_EXPECT (testing_state, map.slots[5].occupied == DQN_CAST(Dqn_u8)false); + DQN_TEST_EXPECT (testing_state, map.slots[6].occupied == DQN_CAST(Dqn_u8)true); + DQN_TEST_EXPECT (testing_state, map.slots[7].occupied == DQN_CAST(Dqn_u8)false); + + DQN_TEST_EXPECT_MSG(testing_state, map.slots[0].value == 5, "value: %d", map.slots[0].value); + DQN_TEST_EXPECT_MSG(testing_state, map.slots[1].value == 5, "value: %d", map.slots[1].value); + DQN_TEST_EXPECT_MSG(testing_state, map.slots[6].value == 5, "value: %d", map.slots[6].value); + + Dqn_DSMap_Free(&map); + } +} + void Dqn_Test_Map() { Dqn_TestingState testing_state = {}; @@ -1182,18 +1243,20 @@ void Dqn_Test_TicketMutex() void Dqn_Test_RunSuite() { - Dqn_Test_Allocator(); Dqn_Test_Array(); Dqn_Test_FixedArray(); Dqn_Test_FixedString(); + Dqn_Test_Hex(); Dqn_Test_Intrinsics(); Dqn_Test_M4(); + Dqn_Test_DSMap(); Dqn_Test_Map(); Dqn_Test_Rect(); Dqn_Test_Str(); Dqn_Test_String(); Dqn_Test_StringBuilder(); Dqn_Test_TicketMutex(); + fprintf(stdout, "Summary: %d/%d tests succeeded\n", g_dqn_test_total_good_tests, g_dqn_test_total_tests); }