From f6dd762e8d585004d269ee8aabb5c2d59cac1992 Mon Sep 17 00:00:00 2001 From: doylet Date: Mon, 26 Feb 2024 00:15:09 +1100 Subject: [PATCH] Add dqn_cgen, reorganise files into standalone/external dirs --- b_stacktrace.h => External/b_stacktrace.h | 0 {Misc => Standalone}/dqn_cpp_file.h | 30 +- {Misc => Standalone}/dqn_keccak.h | 181 +++-- dqn_utest.h => Standalone/dqn_utest.h | 2 +- build.bat | 2 +- dqn.h | 48 ++ dqn_cgen.cpp | 804 ++++++++++++++++++++++ dqn_cgen.h | 166 +++++ dqn_cppbuild.h | 4 +- Misc/dqn_json.h => dqn_json.h | 0 dqn_unit_tests.cpp | 108 ++- 11 files changed, 1208 insertions(+), 137 deletions(-) rename b_stacktrace.h => External/b_stacktrace.h (100%) rename {Misc => Standalone}/dqn_cpp_file.h (88%) rename {Misc => Standalone}/dqn_keccak.h (77%) rename dqn_utest.h => Standalone/dqn_utest.h (99%) create mode 100644 dqn_cgen.cpp create mode 100644 dqn_cgen.h rename Misc/dqn_json.h => dqn_json.h (100%) diff --git a/b_stacktrace.h b/External/b_stacktrace.h similarity index 100% rename from b_stacktrace.h rename to External/b_stacktrace.h diff --git a/Misc/dqn_cpp_file.h b/Standalone/dqn_cpp_file.h similarity index 88% rename from Misc/dqn_cpp_file.h rename to Standalone/dqn_cpp_file.h index 328bc7e..6e4590e 100644 --- a/Misc/dqn_cpp_file.h +++ b/Standalone/dqn_cpp_file.h @@ -1,20 +1,34 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// $$$$$$\ $$$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$$$$$$$\ +// $$ __$$\ $$ __$$\ $$ __$$\ $$ _____|\_$$ _|$$ | $$ _____| +// $$ / \__|$$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ | +// $$ | $$$$$$$ |$$$$$$$ | $$$$$\ $$ | $$ | $$$$$\ +// $$ | $$ ____/ $$ ____/ $$ __| $$ | $$ | $$ __| +// $$ | $$\ $$ | $$ | $$ | $$ | $$ | $$ | +// \$$$$$$ |$$ | $$ | $$ | $$$$$$\ $$$$$$$$\ $$$$$$$$\ +// \______/ \__| \__| \__| \______|\________|\________| +// +// dqn_cpp_file.h -- Functions to emit C++ formatted code +// +//////////////////////////////////////////////////////////////////////////////// #if !defined(DQN_CPP_FILE_H) #define DQN_CPP_FILE_H -// NOTE: Dqn_CppFile: Helper functions to generate C++ files -// ///////////////////////////////////////////////////////////////////////////// #include /// printf, fputc #include /// va_list... #include /// assert typedef struct Dqn_CppFile { ///< Maintains state for printing C++ style formatted files - FILE *file; ///< (Write) File to print to - int indent; ///< Current indent level of the printer - int space_per_indent; ///< (Write) Number of spaces per indent - unsigned char if_chain[256]; /// - unsigned char if_chain_size; /// + FILE *file; ///< (Write) File to print to + int indent; ///< Current indent level of the printer + int space_per_indent; ///< (Write) Number of spaces per indent + unsigned char if_chain[256]; /// + unsigned char if_chain_size; /// } Dqn_CppFile; +#define Dqn_CppSpacePerIndent(cpp) ((cpp) && (cpp)->space_per_indent) ? ((cpp)->space_per_indent) : 4 + /// Print the format string indented and terminate the string with a new-line. void Dqn_CppLineV(Dqn_CppFile *cpp, char const *fmt, va_list args); void Dqn_CppLine(Dqn_CppFile *cpp, char const *fmt, ...); @@ -160,7 +174,7 @@ void Dqn_CppLine(Dqn_CppFile *cpp, char const *fmt, ...) void Dqn_CppPrintV(Dqn_CppFile *cpp, char const *fmt, va_list args) { - int space_per_indent = cpp->space_per_indent == 0 ? 4 : cpp->space_per_indent; + int space_per_indent = Dqn_CppSpacePerIndent(cpp); int spaces = fmt ? (cpp->indent * space_per_indent) : 0; fprintf(cpp->file, "%*s", spaces, ""); vfprintf(cpp->file, fmt, args); diff --git a/Misc/dqn_keccak.h b/Standalone/dqn_keccak.h similarity index 77% rename from Misc/dqn_keccak.h rename to Standalone/dqn_keccak.h index 14432f1..72723b1 100644 --- a/Misc/dqn_keccak.h +++ b/Standalone/dqn_keccak.h @@ -1,17 +1,30 @@ #if !defined(DQN_KECCAK_H) #define DQN_KECCAK_H -// ----------------------------------------------------------------------------- -// NOTE: Overview -// ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ +// $$ | $$ |$$ _____|$$ __$$\ $$ __$$\ $$ __$$\ $$ | $$ | +// $$ |$$ / $$ | $$ / \__|$$ / \__|$$ / $$ |$$ |$$ / +// $$$$$ / $$$$$\ $$ | $$ | $$$$$$$$ |$$$$$ / +// $$ $$< $$ __| $$ | $$ | $$ __$$ |$$ $$< +// $$ |\$$\ $$ | $$ | $$\ $$ | $$\ $$ | $$ |$$ |\$$\ +// $$ | \$$\ $$$$$$$$\ \$$$$$$ |\$$$$$$ |$$ | $$ |$$ | \$$\ +// \__| \__|\________| \______/ \______/ \__| \__|\__| \__| +// +// dqn_keccak.h -- FIPS202 SHA3 + non-finalized SHA3 (aka. Keccak) hashing algorithms +// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// // Implementation of the Keccak hashing algorithms from the Keccak and SHA3 // families (including the FIPS202 published algorithms and the non-finalized // ones, i.e. the ones used in Ethereum and Monero which adopted SHA3 before it // was finalized. The only difference between the 2 is a different delimited // suffix). // -// ----------------------------------------------------------------------------- -// NOTE: MIT License -// ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// MIT License +// // Copyright (c) 2021 github.com/doy-lee // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -32,12 +45,25 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // -// ----------------------------------------------------------------------------- -// NOTE: Macros -// ----------------------------------------------------------------------------- -// #define DQN_KECCAK_IMPLEMENTATION -// Define this in one and only one C++ file to enable the implementation -// code of the header file. +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// $$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\ +// $$ __$$\ $$ __$$\\__$$ __|\_$$ _|$$ __$$\ $$$\ $$ |$$ __$$\ +// $$ / $$ |$$ | $$ | $$ | $$ | $$ / $$ |$$$$\ $$ |$$ / \__| +// $$ | $$ |$$$$$$$ | $$ | $$ | $$ | $$ |$$ $$\$$ |\$$$$$$\ +// $$ | $$ |$$ ____/ $$ | $$ | $$ | $$ |$$ \$$$$ | \____$$\ +// $$ | $$ |$$ | $$ | $$ | $$ | $$ |$$ |\$$$ |$$\ $$ | +// $$$$$$ |$$ | $$ | $$$$$$\ $$$$$$ |$$ | \$$ |\$$$$$$ | +// \______/ \__| \__| \______| \______/ \__| \__| \______/ +// +// Options -- Compile time build customisation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// - Define this in one and only one C++ file to enable the implementation +// code of the header file. +// +// #define DQN_KECCAK_IMPLEMENTATION #if !defined(DQN_KECCAK_MEMCPY) #include @@ -54,7 +80,6 @@ #define DQN_KECCAK_MEMSET(dest, byte, count) memset(dest, byte, count) #endif - #if !defined(DQN_KECCAK_ASSERT) #if defined(NDEBUG) #define DQN_KECCAK_ASSERT(expr) @@ -80,9 +105,6 @@ #define DQN_KECCAK_STRING96_FMT(string) 96, string #define DQN_KECCAK_STRING128_FMT(string) 128, string -// ----------------------------------------------------------------------------- -// NOTE: API -// ----------------------------------------------------------------------------- typedef unsigned char Dqn_KeccakU8; typedef unsigned short Dqn_KeccakU16; typedef unsigned int Dqn_KeccakU32; @@ -93,9 +115,6 @@ typedef unsigned int Dqn_KeccakUint; typedef unsigned long long Dqn_KeccakU64; #endif -// ----------------------------------------------------------------------------- -// NOTE: Data structures -// ----------------------------------------------------------------------------- typedef struct Dqn_KeccakBytes28 { char data[28]; } Dqn_KeccakBytes28; // 224 bit typedef struct Dqn_KeccakBytes32 { char data[32]; } Dqn_KeccakBytes32; // 256 bit typedef struct Dqn_KeccakBytes48 { char data[48]; } Dqn_KeccakBytes48; // 384 bit @@ -116,9 +135,7 @@ typedef struct Dqn_KeccakState char delimited_suffix; // The delimited suffix of the current hash } Dqn_KeccakState; -// ----------------------------------------------------------------------------- -// NOTE: SHA3/Keccak Streaming API -// ----------------------------------------------------------------------------- +// NOTE: SHA3/Keccak Streaming API ///////////////////////////////////////////////////////////////// // Setup a hashing state for either // - FIPS 202 SHA3 // - Non-finalized SHA3 (only difference is delimited suffix of 0x1 instead of 0x6 in SHA3) @@ -140,9 +157,7 @@ Dqn_KeccakState Dqn_KeccakSHA3Init(bool sha3, int hash_size_bits); void Dqn_KeccakUpdate(Dqn_KeccakState *keccak, void const *data, Dqn_KeccakU64 data_size); void Dqn_KeccakFinish(Dqn_KeccakState *keccak, void *dest, int dest_size); -// ----------------------------------------------------------------------------- -// NOTE: Simple API -// ----------------------------------------------------------------------------- +// NOTE: Simple API //////////////////////////////////////////////////////////////////////////////// // Helper function that combines the Init, Update and Finish step in one shot, // i.e. hashing a singlular contiguous buffer. Use the streaming API if data // is split across different buffers. @@ -160,43 +175,33 @@ void Dqn_KeccakSHA3Hash(bool sha3, int hash_size_bits, void const *sr #define Dqn_Keccak384(src, src_size, dest, dest_size) Dqn_KeccakHash(384, src, src_size, dest, dest_size) #define Dqn_Keccak512(src, src_size, dest, dest_size) Dqn_KeccakHash(512, src, src_size, dest, dest_size) -// ----------------------------------------------------------------------------- -// NOTE: SHA3 Helpers -// ----------------------------------------------------------------------------- +// NOTE: SHA3 Helpers ////////////////////////////////////////////////////////////////////////////// Dqn_KeccakBytes28 Dqn_SHA3_224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size); Dqn_KeccakBytes32 Dqn_SHA3_256ToBytes32(void *bytes, Dqn_KeccakU64 bytes_size); Dqn_KeccakBytes48 Dqn_SHA3_384ToBytes48(void *bytes, Dqn_KeccakU64 bytes_size); Dqn_KeccakBytes64 Dqn_SHA3_512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size); -// ----------------------------------------------------------------------------- -// NOTE: Keccak Helpers -// ----------------------------------------------------------------------------- +// NOTE: Keccak Helpers //////////////////////////////////////////////////////////////////////////// Dqn_KeccakBytes28 Dqn_Keccak224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size); Dqn_KeccakBytes32 Dqn_Keccak256ToBytes32(void *bytes, Dqn_KeccakU64 bytes_size); Dqn_KeccakBytes48 Dqn_Keccak384ToBytes48(void *bytes, Dqn_KeccakU64 bytes_size); Dqn_KeccakBytes64 Dqn_Keccak512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size); #if defined(DQN_H) -// ----------------------------------------------------------------------------- -// NOTE: SHA3 - Helpers for Dqn data structures -// ----------------------------------------------------------------------------- -Dqn_KeccakBytes28 Dqn_SHA3_224StringToBytes28(Dqn_String8 string); -Dqn_KeccakBytes32 Dqn_SHA3_256StringToBytes32(Dqn_String8 string); -Dqn_KeccakBytes48 Dqn_SHA3_384StringToBytes48(Dqn_String8 string); -Dqn_KeccakBytes64 Dqn_SHA3_512StringToBytes64(Dqn_String8 string); +// NOTE: SHA3 - Helpers for Dqn data structures //////////////////////////////////////////////////// +Dqn_KeccakBytes28 Dqn_SHA3_224StringToBytes28(Dqn_Str8 string); +Dqn_KeccakBytes32 Dqn_SHA3_256StringToBytes32(Dqn_Str8 string); +Dqn_KeccakBytes48 Dqn_SHA3_384StringToBytes48(Dqn_Str8 string); +Dqn_KeccakBytes64 Dqn_SHA3_512StringToBytes64(Dqn_Str8 string); -// ----------------------------------------------------------------------------- -// NOTE: Keccak - Helpers for Dqn data structures -// ----------------------------------------------------------------------------- -Dqn_KeccakBytes28 Dqn_Keccak224StringToBytes28(Dqn_String8 string); -Dqn_KeccakBytes32 Dqn_Keccak256StringToBytes32(Dqn_String8 string); -Dqn_KeccakBytes48 Dqn_Keccak384StringToBytes48(Dqn_String8 string); -Dqn_KeccakBytes64 Dqn_Keccak512StringToBytes64(Dqn_String8 string); +// NOTE: Keccak - Helpers for Dqn data structures ////////////////////////////////////////////////// +Dqn_KeccakBytes28 Dqn_Keccak224StringToBytes28(Dqn_Str8 string); +Dqn_KeccakBytes32 Dqn_Keccak256StringToBytes32(Dqn_Str8 string); +Dqn_KeccakBytes48 Dqn_Keccak384StringToBytes48(Dqn_Str8 string); +Dqn_KeccakBytes64 Dqn_Keccak512StringToBytes64(Dqn_Str8 string); #endif // DQN_H -// ----------------------------------------------------------------------------- -// NOTE: Helper functions -// ----------------------------------------------------------------------------- +// NOTE: Helper functions ////////////////////////////////////////////////////////////////////////// // Convert a binary buffer into its hex representation into dest. The dest // buffer must be large enough to contain the hex representation, i.e. // atleast src_size * 2). This function does *not* null-terminate the buffer. @@ -218,13 +223,11 @@ int Dqn_KeccakBytes48Equals(Dqn_KeccakBytes48 const *a, Dqn_KeccakBytes48 const int Dqn_KeccakBytes64Equals(Dqn_KeccakBytes64 const *a, Dqn_KeccakBytes64 const *b); #if defined(DQN_H) && defined(DQN_WITH_HEX) -// ----------------------------------------------------------------------------- -// NOTE: Other helper functions for Dqn data structures -// ----------------------------------------------------------------------------- +// NOTE: Other helper functions for Dqn data structures //////////////////////////////////////////// // Converts a 64 character hex string into the 32 byte binary representation. // Invalid hex characters in the string will be represented as 0. // hex: Must be exactly a 64 character hex string. -Dqn_KeccakBytes32 Dqn_KeccakHex64StringToBytes(Dqn_String8 hex); +Dqn_KeccakBytes32 Dqn_KeccakHex64StringToBytes(Dqn_Str8 hex); #endif // DQN_H && DQN_WITH_HEX #endif // DQN_KECCAK_H @@ -257,9 +260,7 @@ static void Dqn_Keccak__Permute(void *state) for (int round_index = 0; round_index < 24; round_index++) { #define LANE_INDEX(x, y) ((x) + ((y) * DQN_KECCAK_LANE_SIZE_U64)) - // --------------------------------------------------------------------- - // θ step - // --------------------------------------------------------------------- + // θ step ////////////////////////////////////////////////////////////////////////////////// #if 1 Dqn_KeccakU64 c[DQN_KECCAK_LANE_SIZE_U64]; for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++) @@ -293,9 +294,7 @@ static void Dqn_Keccak__Permute(void *state) d[4] = c[3] ^ DQN_KECCAK_ROL64(c[0], 1); #endif - // --------------------------------------------------------------------- - // ρ and π steps - // --------------------------------------------------------------------- + // ρ and π steps /////////////////////////////////////////////////////////////////////////// Dqn_KeccakU64 b[DQN_KECCAK_LANE_SIZE_U64 * DQN_KECCAK_LANE_SIZE_U64]; for (int y = 0; y < DQN_KECCAK_LANE_SIZE_U64; y++) { @@ -308,9 +307,7 @@ static void Dqn_Keccak__Permute(void *state) } } - // --------------------------------------------------------------------- - // χ step - // --------------------------------------------------------------------- + // χ step ////////////////////////////////////////////////////////////////////////////////// for (int y = 0; y < DQN_KECCAK_LANE_SIZE_U64; y++) { for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++) @@ -322,18 +319,14 @@ static void Dqn_Keccak__Permute(void *state) } } - // --------------------------------------------------------------------- - // ι step - // --------------------------------------------------------------------- + // ι step ////////////////////////////////////////////////////////////////////////////////// lanes_u64[LANE_INDEX(0, 0)] ^= DQN_KECCAK_ROUNDS[round_index]; #undef LANE_INDEX #undef DQN_KECCAK_ROL64 } } -// ----------------------------------------------------------------------------- -// NOTE: Streaming API -// ----------------------------------------------------------------------------- +// NOTE: Streaming API ///////////////////////////////////////////////////////////////////////////// Dqn_KeccakState Dqn_KeccakSHA3Init(bool sha3, int hash_size_bits) { char const SHA3_DELIMITED_SUFFIX = 0x06; @@ -382,9 +375,7 @@ void Dqn_KeccakFinish(Dqn_KeccakState *keccak, void *dest, int dest_size) { DQN_KECCAK_ASSERT(dest_size >= keccak->hash_size_bits / 8); - // --------------------------------------------------------------------- - // Sponge Finalization Step: Final padding bit - // --------------------------------------------------------------------- + // Sponge Finalization Step: Final padding bit ///////////////////////////////////////////////// int const INDEX_OF_0X80_BYTE = keccak->absorb_size - 1; int const delimited_suffix_index = keccak->state_size; DQN_KECCAK_ASSERT(delimited_suffix_index < keccak->absorb_size); @@ -404,9 +395,7 @@ void Dqn_KeccakFinish(Dqn_KeccakState *keccak, void *dest, int dest_size) state[INDEX_OF_0X80_BYTE] ^= 0x80; Dqn_Keccak__Permute(state); - // --------------------------------------------------------------------- - // Squeeze Step: Squeeze bytes from the state into our hash - // --------------------------------------------------------------------- + // Squeeze Step: Squeeze bytes from the state into our hash //////////////////////////////////// Dqn_KeccakU8 *dest_u8 = (Dqn_KeccakU8 *)dest; int const squeeze_count = dest_size / keccak->absorb_size; int squeeze_index = 0; @@ -417,9 +406,7 @@ void Dqn_KeccakFinish(Dqn_KeccakState *keccak, void *dest, int dest_size) dest_u8 += keccak->absorb_size; } - // --------------------------------------------------------------------- - // Squeeze Finalisation Step: Remainder bytes in hash - // --------------------------------------------------------------------- + // Squeeze Finalisation Step: Remainder bytes in hash ////////////////////////////////////////// int const remainder = dest_size % keccak->absorb_size; if (remainder) { @@ -435,9 +422,7 @@ void Dqn_KeccakSHA3Hash(bool sha3, int hash_size_bits, void const *src, Dqn_Kecc Dqn_KeccakFinish(&keccak, dest, dest_size); } -// ----------------------------------------------------------------------------- -// NOTE: SHA3 Helpers -// ----------------------------------------------------------------------------- +// NOTE: SHA3 Helpers ////////////////////////////////////////////////////////////////////////////// Dqn_KeccakBytes28 Dqn_SHA3_224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size) { Dqn_KeccakBytes28 result; @@ -466,9 +451,7 @@ Dqn_KeccakBytes64 Dqn_SHA3_512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size) return result; } -// ----------------------------------------------------------------------------- -// NOTE: Keccak Helpers -// ----------------------------------------------------------------------------- +// NOTE: Keccak Helpers //////////////////////////////////////////////////////////////////////////// Dqn_KeccakBytes28 Dqn_Keccak224ToBytes28(void *bytes, Dqn_KeccakU64 bytes_size) { Dqn_KeccakBytes28 result; @@ -499,31 +482,29 @@ Dqn_KeccakBytes64 Dqn_Keccak512ToBytes64(void *bytes, Dqn_KeccakU64 bytes_size) #if defined(DQN_H) -// ----------------------------------------------------------------------------- -// NOTE: SHA3 - Helpers for Dqn data structures -// ----------------------------------------------------------------------------- -Dqn_KeccakBytes28 Dqn_SHA3_224StringToBytes28(Dqn_String8 string) +// NOTE: SHA3 - Helpers for Dqn data structures //////////////////////////////////////////////////// +Dqn_KeccakBytes28 Dqn_SHA3_224StringToBytes28(Dqn_Str8 string) { Dqn_KeccakBytes28 result; Dqn_SHA3_224(string.data, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes32 Dqn_SHA3_256StringToBytes32(Dqn_String8 string) +Dqn_KeccakBytes32 Dqn_SHA3_256StringToBytes32(Dqn_Str8 string) { Dqn_KeccakBytes32 result; Dqn_SHA3_256(string.data, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes48 Dqn_SHA3_384StringToBytes48(Dqn_String8 string) +Dqn_KeccakBytes48 Dqn_SHA3_384StringToBytes48(Dqn_Str8 string) { Dqn_KeccakBytes48 result; Dqn_SHA3_384(string.data, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes64 Dqn_SHA3_512StringToBytes64(Dqn_String8 string) +Dqn_KeccakBytes64 Dqn_SHA3_512StringToBytes64(Dqn_Str8 string) { Dqn_KeccakBytes64 result; Dqn_SHA3_512(string.data, string.size, result.data, sizeof(result)); @@ -532,31 +513,29 @@ Dqn_KeccakBytes64 Dqn_SHA3_512StringToBytes64(Dqn_String8 string) #endif // DQN_H #if defined(DQN_H) -// ----------------------------------------------------------------------------- -// NOTE: Keccak - Helpers for Dqn data structures -// ----------------------------------------------------------------------------- -Dqn_KeccakBytes28 Dqn_Keccak224StringToBytes28(Dqn_String8 string) +// NOTE: Keccak - Helpers for Dqn data structures ////////////////////////////////////////////////// +Dqn_KeccakBytes28 Dqn_Keccak224StringToBytes28(Dqn_Str8 string) { Dqn_KeccakBytes28 result; Dqn_Keccak224(string.data, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes32 Dqn_Keccak256StringToBytes32(Dqn_String8 string) +Dqn_KeccakBytes32 Dqn_Keccak256StringToBytes32(Dqn_Str8 string) { Dqn_KeccakBytes32 result; Dqn_Keccak256(string.data, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes48 Dqn_Keccak384StringToBytes48(Dqn_String8 string) +Dqn_KeccakBytes48 Dqn_Keccak384StringToBytes48(Dqn_Str8 string) { Dqn_KeccakBytes48 result; Dqn_Keccak384(string.data, string.size, result.data, sizeof(result)); return result; } -Dqn_KeccakBytes64 Dqn_Keccak512StringToBytes64(Dqn_String8 string) +Dqn_KeccakBytes64 Dqn_Keccak512StringToBytes64(Dqn_Str8 string) { Dqn_KeccakBytes64 result; Dqn_Keccak512(string.data, string.size, result.data, sizeof(result)); @@ -564,9 +543,7 @@ Dqn_KeccakBytes64 Dqn_Keccak512StringToBytes64(Dqn_String8 string) } #endif // DQN_H -// ----------------------------------------------------------------------------- -// NOTE: Helper functions -// ----------------------------------------------------------------------------- +// NOTE: Helper functions ////////////////////////////////////////////////////////////////////////// void Dqn_KeccakBytesToHex(void const *src, Dqn_KeccakU64 src_size, char *dest, Dqn_KeccakU64 dest_size) { (void)src_size; (void)dest_size; @@ -642,10 +619,8 @@ int Dqn_KeccakBytes64Equals(Dqn_KeccakBytes64 const *a, Dqn_KeccakBytes64 const } #if defined(DQN_H) && defined(DQN_WITH_HEX) -// ----------------------------------------------------------------------------- -// NOTE: Other helper functions for Dqn data structures -// ----------------------------------------------------------------------------- -Dqn_KeccakBytes32 Dqn_KeccakHex64StringToBytes(Dqn_String8 hex) +// NOTE: Other helper functions for Dqn data structures //////////////////////////////////////////// +Dqn_KeccakBytes32 Dqn_KeccakHex64StringToBytes(Dqn_Str8 hex) { DQN_KECCAK_ASSERT(hex.size == 64); Dqn_KeccakBytes32 result; diff --git a/dqn_utest.h b/Standalone/dqn_utest.h similarity index 99% rename from dqn_utest.h rename to Standalone/dqn_utest.h index 2a3fcdd..9524f9b 100644 --- a/dqn_utest.h +++ b/Standalone/dqn_utest.h @@ -147,7 +147,7 @@ typedef struct Dqn_UTest { int num_tests_ok_in_group; Dqn_UTestState state; bool finished; - char name[1024]; + char name[256]; size_t name_size; } Dqn_UTest; diff --git a/build.bat b/build.bat index 49253eb..9e77f5c 100644 --- a/build.bat +++ b/build.bat @@ -13,7 +13,7 @@ pushd Build REM O2 Optimisation Level 2 REM Oi Use CPU Intrinsics REM Z7 Combine multi-debug files to one debug file - set common_flags=-D DQN_TEST_WITH_MAIN -D DQN_IMPLEMENTATION -D DQN_USE_STD_PRINTF /Tp %script_dir%\dqn.h + set common_flags=-D DQN_UNIT_TESTS_WITH_MAIN -D DQN_UNIT_TESTS_WITH_KECCAK -D DQN_IMPLEMENTATION -D DQN_USE_STD_PRINTF /Tp %script_dir%\dqn.h set msvc_driver_flags=%common_flags% -MT -EHa -GR- -Od -Oi -Z7 -wd4201 -W4 -nologo diff --git a/dqn.h b/dqn.h index 71983d9..49b0a47 100644 --- a/dqn.h +++ b/dqn.h @@ -184,6 +184,36 @@ // // #define DQN_ASAN_POISON_GUARD_SIZE 128 // +// - Enable 'Dqn_CGen' a parser that can emit run-time type information and +// allow arbitrary querying of data definitions expressed in Excel-like tables +// using text files encoded in Dion-System's Metadesk grammar. +// +// This option requires the standalone 'dqn_cpp_file.h' to be included prior +// to 'dqn.h' as well as Metadesk's 'md.h' and similarly the implementation of +// these files to be defined before the implementation of 'dqn.h' is defined. +// +// #define DQN_WITH_CGEN +// +// For example in your header file +// +// #include "metadesk/md.h" +// #include "dqn/Standalone/dqn_cpp_file.h" +// #define DQN_STB_SPRINTF_HEADER_ONLY // Metadesk includes 'stb_sprintf.h' already +// #define DQN_WITH_CGEN +// #include "dqn.h" +// +// Then in your implementation file +// +// #include "metadesk/md.c" +// #define DQN_CPP_FILE_IMPLEMENTATION +// #include "dqn/Standalone/dqn_cpp_file.h" +// #define DQN_IMPLEMENTATION +// #include "dqn.h" +// +// - Enable 'Dqn_JSON' a json parser. This option requires Sheredom's 'json.h' +// to be included prior to this file. +// +// #define DQN_WITH_JSON #if defined(DQN_ONLY_VARRAY) || \ defined(DQN_ONLY_SARRAY) || \ @@ -285,6 +315,14 @@ #include "dqn_hash.h" #include "dqn_helpers.h" #include "dqn_type_info.h" + +#if defined(DQN_WITH_CGEN) +#include "dqn_cgen.h" +#endif + +#if defined(DQN_WITH_JSON) +#include "dqn_json.h" +#endif #endif // DQN_H #if defined(DQN_IMPLEMENTATION) @@ -323,6 +361,16 @@ #include "dqn_math.cpp" #include "dqn_hash.cpp" #include "dqn_helpers.cpp" + +#if defined(DQN_WITH_CGEN) +#include "dqn_cgen.cpp" +#endif + +#if defined(DQN_WITH_JSON) +#define DQN_JSON_IMPLEMENTATION +#include "dqn_json.h" +#endif + #include "dqn_unit_tests.cpp" #include "dqn_docs.cpp" #endif // DQN_IMPLEMENTATION diff --git a/dqn_cgen.cpp b/dqn_cgen.cpp new file mode 100644 index 0000000..a466c0b --- /dev/null +++ b/dqn_cgen.cpp @@ -0,0 +1,804 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\ +// $$ __$$\ $$ __$$\ $$ _____|$$$\ $$ | +// $$ / \__|$$ / \__|$$ | $$$$\ $$ | +// $$ | $$ |$$$$\ $$$$$\ $$ $$\$$ | +// $$ | $$ |\_$$ |$$ __| $$ \$$$$ | +// $$ | $$\ $$ | $$ |$$ | $$ |\$$$ | +// \$$$$$$ |\$$$$$$ |$$$$$$$$\ $$ | \$$ | +// \______/ \______/ \________|\__| \__| +// +// dqn_cgen.cpp +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +Dqn_CGenMapNodeToEnum const DQN_CGEN_TABLE_KEY_LIST[] = +{ + {Dqn_CGenTableKeyType_Name, DQN_STR8("name")}, + {Dqn_CGenTableKeyType_Type, DQN_STR8("type")}, +}; + +Dqn_CGenMapNodeToEnum const DQN_CGEN_TABLE_TYPE_LIST[] = +{ + {Dqn_CGenTableType_Data, DQN_STR8("data") }, + {Dqn_CGenTableType_CodeGenBuiltinTypes, DQN_STR8("code_gen_builtin_types")}, + {Dqn_CGenTableType_CodeGenStruct, DQN_STR8("code_gen_struct") }, + {Dqn_CGenTableType_CodeGenEnum, DQN_STR8("code_gen_enum") }, +}; + +Dqn_CGenMapNodeToEnum const DQN_CGEN_TABLE_ROW_TAG_LIST[] = +{ + {Dqn_CGenTableRowTagType_CommentDivider, DQN_STR8("comment_divider")}, + {Dqn_CGenTableRowTagType_EmptyLine, DQN_STR8("empty_line")}, +}; + +Dqn_CGenMapNodeToEnum const DQN_CGEN_TABLE_ROW_TAG_COMMENT_DIVIDER_KEY_LIST[] = +{ + {Dqn_CGenTableRowTagCommentDivider_Label, DQN_STR8("label")}, +}; + +Dqn_CGenTableHeaderType const DQN_CGEN_TABLE_CODE_GEN_STRUCT_HEADER_LIST[] = +{ + Dqn_CGenTableHeaderType_Name, + Dqn_CGenTableHeaderType_Table, + Dqn_CGenTableHeaderType_CppType, + Dqn_CGenTableHeaderType_CppName, + Dqn_CGenTableHeaderType_CppIsPtr, + Dqn_CGenTableHeaderType_CppArraySize, + Dqn_CGenTableHeaderType_CppArraySizeField, + Dqn_CGenTableHeaderType_GenTypeInfo, +}; + +Dqn_CGenTableHeaderType const DQN_CGEN_TABLE_CODE_GEN_ENUM_HEADER_LIST[] = +{ + Dqn_CGenTableHeaderType_Name, + Dqn_CGenTableHeaderType_Table, + Dqn_CGenTableHeaderType_CppName, + Dqn_CGenTableHeaderType_CppValue, + Dqn_CGenTableHeaderType_GenTypeInfo, + Dqn_CGenTableHeaderType_GenEnumCount, +}; + +Dqn_CGenTableHeaderType const DQN_CGEN_TABLE_CODE_GEN_BUILTIN_TYPES_HEADER_LIST[] = +{ + Dqn_CGenTableHeaderType_Name, +}; + +static bool Dqn_CGen_GatherTables_(Dqn_CGen *cgen, Dqn_ErrorSink *error) +{ + bool result = false; + if (!cgen || !cgen->file_list || !cgen->arena) + return result; + + // NOTE: Gather the tables ///////////////////////////////////////////////////////////////////// + for (MD_EachNode(ref, cgen->file_list->first_child)) { + MD_Node *root = MD_ResolveNodeFromReference(ref); + for (MD_EachNode(node, root->first_child)) { + + MD_Node *table_tag = MD_TagFromString(node, MD_S8Lit("table"), 0); + if (MD_NodeIsNil(table_tag)) + continue; + + Dqn_CGenTable *table = MD_PushArray(cgen->arena, Dqn_CGenTable, 1); + table->node = node; + table->name = table_tag->first_child->first_child->string; + MD_MapInsert(cgen->arena, &cgen->table_map, MD_MapKeyStr(table->name), table); + MD_QueuePush(cgen->first_table, cgen->last_table, table); + + for (MD_EachNode(key, table_tag->first_child)) { + Dqn_CGenMapNodeToEnum key_mapping = Dqn_CGen_MapNodeToEnumOrExit(key, + DQN_CGEN_TABLE_KEY_LIST, + DQN_ARRAY_UCOUNT(DQN_CGEN_TABLE_KEY_LIST), + "Table specified invalid key"); + switch (DQN_CAST(Dqn_CGenTableKeyType)key_mapping.enum_val) { + case Dqn_CGenTableKeyType_Nil: DQN_INVALID_CODE_PATH; + + case Dqn_CGenTableKeyType_Name: { + table->name = key->first_child->string; + } break; + + case Dqn_CGenTableKeyType_Type: { + MD_Node *table_type_value = key->first_child; + Dqn_CGenMapNodeToEnum table_type_validator = Dqn_CGen_MapNodeToEnumOrExit(table_type_value, + DQN_CGEN_TABLE_TYPE_LIST, + DQN_ARRAY_UCOUNT(DQN_CGEN_TABLE_TYPE_LIST), + "Table 'type' specified invalid value"); + table->type = DQN_CAST(Dqn_CGenTableType) table_type_validator.enum_val; + } break; + } + } + } + } + + // NOTE: Parse the tables ////////////////////////////////////////////////////////////////////// + Dqn_usize const BEGIN_COLUMN_INDEX = 1; /*Reserve 0th slot for nil-entry*/ + for (Dqn_CGenTable *table = cgen->first_table; table; table = table->next) { + table->column_count = BEGIN_COLUMN_INDEX; + table->headers_node = table->node->first_child; + for (MD_EachNode(column_node, table->headers_node->first_child)) + table->column_count++; + + if (table->column_count == BEGIN_COLUMN_INDEX) + continue; + + MD_Node *row_it = table->headers_node->next; + for (MD_EachNode(row_node, row_it)) + table->row_count++; + + table->rows = MD_PushArray(cgen->arena, Dqn_CGenTableRow, table->row_count); + table->headers = MD_PushArray(cgen->arena, Dqn_CGenTableHeader, table->column_count); + for (Dqn_usize index = 0; index < table->row_count; index++) { + Dqn_CGenTableRow *row = table->rows + index; + row->columns = MD_PushArray(cgen->arena, Dqn_CGenTableColumn, table->column_count); + } + + // NOTE: Collect table headers ///////////////////////////////////////////////////////////// + table->headers_map = MD_MapMake(cgen->arena); + Dqn_usize column_index = BEGIN_COLUMN_INDEX; + for (MD_EachNode(header_column, table->headers_node->first_child)) { + DQN_ASSERT(column_index < table->column_count); + + // NOTE: Detect builtin headers and cache the index for that table ///////////////////// + for (Dqn_usize enum_index = 0; enum_index < Dqn_CGenTableHeaderType_Count; enum_index++) { + Dqn_Str8 decl_str8 = Dqn_CGen_TableHeaderTypeToDeclStr8(DQN_CAST(Dqn_CGenTableHeaderType)enum_index); + if (decl_str8 != Dqn_Str8_Init(header_column->string.str, header_column->string.size)) + continue; + table->column_indexes[enum_index] = column_index; + break; + } + + MD_MapInsert(cgen->arena, &table->headers_map, MD_MapKeyStr(header_column->string), DQN_CAST(void *)column_index); + table->headers[column_index++].name = header_column->string; + } + + // NOTE: Validate table headers //////////////////////////////////////////////////////////// + switch (table->type) { + case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH; + + case Dqn_CGenTableType_Data: { + } break; + + case Dqn_CGenTableType_CodeGenStruct: { + for (Dqn_CGenTableHeaderType enum_val : DQN_CGEN_TABLE_CODE_GEN_STRUCT_HEADER_LIST) { + if (table->column_indexes[enum_val] == 0) { + Dqn_Str8 expected_value = Dqn_CGen_TableHeaderTypeToDeclStr8(enum_val); + Dqn_CGen_LogF(MD_MessageKind_Error, table->headers_node, error, "Struct code generation table is missing column '%.*s'", DQN_STR_FMT(expected_value)); + return false; + } + } + } break; + + case Dqn_CGenTableType_CodeGenEnum: { + for (Dqn_CGenTableHeaderType enum_val : DQN_CGEN_TABLE_CODE_GEN_ENUM_HEADER_LIST) { + if (table->column_indexes[enum_val] == 0) { + Dqn_Str8 expected_value = Dqn_CGen_TableHeaderTypeToDeclStr8(enum_val); + Dqn_CGen_LogF(MD_MessageKind_Error, table->headers_node, error, "Enum code generation table is missing column '%.*s'", DQN_STR_FMT(expected_value)); + return false; + } + } + } break; + + case Dqn_CGenTableType_CodeGenBuiltinTypes: { + for (Dqn_CGenTableHeaderType enum_val : DQN_CGEN_TABLE_CODE_GEN_BUILTIN_TYPES_HEADER_LIST) { + if (table->column_indexes[enum_val] == 0) { + Dqn_Str8 expected_value = Dqn_CGen_TableHeaderTypeToDeclStr8(enum_val); + Dqn_CGen_LogF(MD_MessageKind_Error, table->headers_node, error, "Enum code generation table is missing column '%.*s'", DQN_STR_FMT(expected_value)); + return false; + } + } + } break; + } + + // NOTE: Parse each row in table /////////////////////////////////////////////////////////// + Dqn_usize row_index = 0; + for (MD_EachNode(row_node, row_it)) { + column_index = BEGIN_COLUMN_INDEX; + DQN_ASSERT(row_index < table->row_count); + + // NOTE: Parse any tags set on the row ///////////////////////////////////////////////// + Dqn_CGenTableRow *row = table->rows + row_index++; + for (MD_EachNode(row_tag, row_node->first_tag)) { + Dqn_CGenMapNodeToEnum row_mapping = Dqn_CGen_MapNodeToEnumOrExit(row_tag, + DQN_CGEN_TABLE_ROW_TAG_LIST, + DQN_ARRAY_UCOUNT(DQN_CGEN_TABLE_ROW_TAG_LIST), + "Table specified invalid row tag"); + Dqn_CGenTableRowTag *tag = MD_PushArray(cgen->arena, Dqn_CGenTableRowTag, 1); + tag->type = DQN_CAST(Dqn_CGenTableRowTagType) row_mapping.enum_val; + MD_QueuePush(row->first_tag, row->last_tag, tag); + + switch (tag->type) { + case Dqn_CGenTableRowTagType_Nil: DQN_INVALID_CODE_PATH; + + case Dqn_CGenTableRowTagType_CommentDivider: { + for (MD_EachNode(tag_key, row_tag->first_child)) { + Dqn_CGenMapNodeToEnum tag_mapping = Dqn_CGen_MapNodeToEnumOrExit(tag_key, + DQN_CGEN_TABLE_ROW_TAG_COMMENT_DIVIDER_KEY_LIST, + DQN_ARRAY_UCOUNT(DQN_CGEN_TABLE_ROW_TAG_COMMENT_DIVIDER_KEY_LIST), + "Table specified invalid row tag"); + switch (DQN_CAST(Dqn_CGenTableRowTagCommentDivider)tag_mapping.enum_val) { + case Dqn_CGenTableRowTagCommentDivider_Nil: DQN_INVALID_CODE_PATH; + case Dqn_CGenTableRowTagCommentDivider_Label: { + tag->comment = tag_key->first_child->string; + } break; + } + } + } break; + + case Dqn_CGenTableRowTagType_EmptyLine: break; + } + } + + for (MD_EachNode(column_node, row_node->first_child)) { + table->headers[column_index].longest_string = DQN_MAX(table->headers[column_index].longest_string, DQN_CAST(int) column_node->string.size); + row->columns[column_index].string = Dqn_CGen_MDToDqnStr8(column_node->string); + row->columns[column_index].node = column_node; + column_index++; + } + } + } + + // NOTE: Validate codegen ////////////////////////////////////////////////////////////////////// + Dqn_CGenTableHeaderType const CHECK_COLUMN_LINKS[] = { + Dqn_CGenTableHeaderType_CppName, + Dqn_CGenTableHeaderType_CppType, + Dqn_CGenTableHeaderType_CppValue, + Dqn_CGenTableHeaderType_CppIsPtr, + Dqn_CGenTableHeaderType_CppArraySize, + Dqn_CGenTableHeaderType_CppArraySizeField, + }; + + result = true; + for (Dqn_CGenTable *table = cgen->first_table; table; table = table->next) { + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { + for (Dqn_CGenTableHeaderType check_column : CHECK_COLUMN_LINKS) { + Dqn_CGenTableColumn column = it.cgen_table_row->columns[table->column_indexes[check_column]]; + if (column.string.size == 0) { + // NOTE: The code generation table did not bind a code generation parameter to + // a column in the target table. We skip it. + continue; + } + + // NOTE: Check if the column to bind to exists in the target table. + if (!MD_MapLookup(&it.table->headers_map, MD_MapKeyStr(Dqn_CGen_DqnToMDStr8(column.string)))) { + result = false; + Dqn_Str8 header_type_str8 = Dqn_CGen_TableHeaderTypeToDeclStr8(check_column); + Dqn_CGen_LogF(MD_MessageKind_Error, column.node, error, + "Code generation table binds '%.*s' to '%.*s', but the column '%.*s' does not exist in table '%.*s'\n" + "NOTE: If you want '%.*s' to omit the column '%.*s' you can bind to the empty string `` to skip it, otherwise, please ensure the table '%.*s' has the column '%.*s'" + , + DQN_STR_FMT(column.string), + DQN_STR_FMT(header_type_str8), + DQN_STR_FMT(column.string), + MD_S8VArg(it.table->name), + MD_S8VArg(it.table->name), + DQN_STR_FMT(header_type_str8), + MD_S8VArg(it.table->name), + DQN_STR_FMT(header_type_str8)); + } + } + } + } + + return result; +} + +DQN_API Dqn_CGen Dqn_CGen_InitFilesArgV(int argc, char const **argv, Dqn_ErrorSink *error) +{ + Dqn_CGen result = {}; + result.arena = MD_ArenaAlloc(); + result.file_list = MD_MakeList(result.arena); + result.table_map = MD_MapMake(result.arena); + + bool has_error = false; + for (Dqn_isize arg_index = 0; arg_index < argc; arg_index++) { + MD_String8 file_name = MD_S8CString(DQN_CAST(char *)argv[arg_index]); + MD_ParseResult parse_result = MD_ParseWholeFile(result.arena, file_name); + for (MD_Message *message = parse_result.errors.first; message != 0; message = message->next) { + MD_CodeLoc code_loc = MD_CodeLocFromNode(message->node); + has_error = true; + Dqn_CGen_LogF(message->kind, message->node, error, "%.*s", MD_S8VArg(message->string)); + } + MD_PushNewReference(result.arena, result.file_list, parse_result.node); + } + + if (!has_error) + Dqn_CGen_GatherTables_(&result, error); + return result; +} + +DQN_API Dqn_Str8 Dqn_CGen_TableHeaderTypeToDeclStr8(Dqn_CGenTableHeaderType type) +{ + Dqn_Str8 result = {}; + switch (type) { + case Dqn_CGenTableHeaderType_Name: result = DQN_STR8("name"); break; + case Dqn_CGenTableHeaderType_Table: result = DQN_STR8("table"); break; + case Dqn_CGenTableHeaderType_CppType: result = DQN_STR8("cpp_type"); break; + case Dqn_CGenTableHeaderType_CppName: result = DQN_STR8("cpp_name"); break; + case Dqn_CGenTableHeaderType_CppValue: result = DQN_STR8("cpp_value"); break; + case Dqn_CGenTableHeaderType_CppIsPtr: result = DQN_STR8("cpp_is_ptr"); break; + case Dqn_CGenTableHeaderType_CppArraySize: result = DQN_STR8("cpp_array_size"); break; + case Dqn_CGenTableHeaderType_CppArraySizeField: result = DQN_STR8("cpp_array_size_field"); break; + case Dqn_CGenTableHeaderType_GenTypeInfo: result = DQN_STR8("gen_type_info"); break; + case Dqn_CGenTableHeaderType_GenEnumCount: result = DQN_STR8("gen_enum_count"); break; + case Dqn_CGenTableHeaderType_Count: result = DQN_STR8("XX BAD ENUM VALUE XX"); break; + default: result = DQN_STR8("XX INVALID ENUM VALUE XX"); break; + } + return result; +} + +DQN_API Dqn_CGenMapNodeToEnum Dqn_CGen_MapNodeToEnumOrExit(MD_Node const *node, Dqn_CGenMapNodeToEnum const *valid_keys, Dqn_usize valid_keys_size, char const *fmt, ...) +{ + Dqn_CGenMapNodeToEnum result = {}; + for (Dqn_usize index = 0; index < valid_keys_size; index++) { + Dqn_CGenMapNodeToEnum const *validator = valid_keys + index; + if (Dqn_Str8_Init(node->string.str, node->string.size) == validator->node_string) { + result = *validator; + break; + } + } + + if (result.enum_val == 0) { + MD_CodeLoc loc = MD_CodeLocFromNode(DQN_CAST(MD_Node *)node); + Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + va_list args; + va_start(args, fmt); + Dqn_Str8 user_msg = Dqn_Str8_InitFV(scratch.arena, fmt, args); + va_end(args); + + Dqn_Str8Builder builder = {}; + builder.arena = scratch.arena; + + Dqn_Str8Builder_AppendF(&builder, "%.*s: '%.*s' is not recognised, the supported values are ", DQN_STR_FMT(user_msg), MD_S8VArg(node->string)); + for (Dqn_usize index = 0; index < valid_keys_size; index++) { + Dqn_CGenMapNodeToEnum const *validator = valid_keys + index; + Dqn_Str8Builder_AppendF(&builder, DQN_CAST(char *)"%s'%.*s'", index ? ", " : "", DQN_STR_FMT(validator->node_string)); + } + + Dqn_Str8 error_msg = Dqn_Str8Builder_Build(&builder, scratch.arena); + MD_PrintMessageFmt(stderr, loc, MD_MessageKind_Error, DQN_CAST(char *) "%.*s", DQN_STR_FMT(error_msg)); + Dqn_OS_Exit(DQN_CAST(uint32_t)-1); + } + return result; +} + +DQN_API void Dqn_CGen_LogF(MD_MessageKind kind, MD_Node *node, Dqn_ErrorSink *error, char const *fmt, ...) +{ + if (!error) + return; + + Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_Str8Builder builder = {}; + builder.arena = scratch.arena; + + MD_String8 kind_string = MD_StringFromMessageKind(kind); + MD_CodeLoc loc = MD_CodeLocFromNode(node); + Dqn_Str8Builder_AppendF(&builder, "" MD_FmtCodeLoc " %.*s: ", MD_CodeLocVArg(loc), MD_S8VArg(kind_string)); + + va_list args; + va_start(args, fmt); + Dqn_Str8Builder_AppendFV(&builder, fmt, args); + va_end(args); + + Dqn_Str8 msg = Dqn_Str8Builder_Build(&builder, scratch.arena); + Dqn_ErrorSink_MakeF(error, DQN_CAST(uint32_t)-1, "%.*s", DQN_STR_FMT(msg)); +} + +DQN_API bool Dqn_CGen_TableHasHeaders(Dqn_CGenTable const *table, Dqn_Str8 const *headers, Dqn_usize header_count, Dqn_ErrorSink *error) +{ + bool result = true; + Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_Str8Builder builder = {}; + builder.arena = scratch.arena; + + for (Dqn_usize index = 0; index < header_count; index++) { + Dqn_Str8 header = headers[index]; + MD_String8 header_md = {DQN_CAST(MD_u8 *) header.data, header.size}; + MD_MapSlot *slot = MD_MapLookup(DQN_CAST(MD_Map *)&table->headers_map, MD_MapKeyStr(header_md)); + if (!slot) { + result = false; + Dqn_Str8Builder_AppendF(&builder, "%s%.*s", builder.count ? ", " : "", DQN_STR_FMT(header)); + } + } + + if (!result) { + Dqn_Str8 missing_headers = Dqn_Str8Builder_Build(&builder, scratch.arena); + Dqn_CGen_LogF(MD_MessageKind_Error, table->headers_node, error, "Table '%.*s' is missing the header(s): %.*s", MD_S8VArg(table->name), DQN_STR_FMT(missing_headers)); + } + + return result; +} + +DQN_API Dqn_CGenLookupColumnAtHeader Dqn_CGen_LookupColumnAtHeader(Dqn_CGenTable *table, Dqn_Str8 header, Dqn_CGenTableRow const *row) +{ + Dqn_CGenLookupColumnAtHeader result = {}; + if (!table || !row) + return result; + + MD_String8 header_md = {DQN_CAST(MD_u8 *) header.data, header.size}; + MD_MapSlot *slot = MD_MapLookup(&table->headers_map, MD_MapKeyStr(header_md)); + if (!slot) + return result; + + Dqn_usize column_index = DQN_CAST(Dqn_usize) slot->val; + DQN_ASSERT(column_index < table->column_count); + { + Dqn_usize begin = DQN_CAST(uintptr_t)(table->rows); + Dqn_usize end = DQN_CAST(uintptr_t)(table->rows + table->row_count); + Dqn_usize ptr = DQN_CAST(uintptr_t)row; + DQN_ASSERTF(ptr >= begin && ptr <= end, "The row to lookup does not belong to the table passed in"); + } + + result.index = column_index; + result.column = row->columns[column_index]; + result.header = table->headers[column_index]; + return result; +} + +DQN_API bool Dqn_CGen_LookupNextTableInCodeGenTable(Dqn_CGen *cgen, Dqn_CGenTable *cgen_table, Dqn_CGenLookupTableIterator *it) +{ + if (!cgen_table) + return false; + + if (it->row_index >= cgen_table->row_count) + return false; + + if (cgen_table->type != Dqn_CGenTableType_CodeGenEnum && cgen_table->type != Dqn_CGenTableType_CodeGenStruct && cgen_table->type != Dqn_CGenTableType_CodeGenBuiltinTypes) + return false; + + // NOTE: Lookup the table in this row that we will code generate from. Not + // applicable when we are doing builtin types as the types are just put + // in-line into the code generation table itself. + it->cgen_table_row = cgen_table->rows + it->row_index++; + if (cgen_table->type != Dqn_CGenTableType_CodeGenBuiltinTypes) { + Dqn_CGenTableColumn cgen_table_column = it->cgen_table_row->columns[cgen_table->column_indexes[Dqn_CGenTableHeaderType_Table]]; + MD_String8 key = {DQN_CAST(MD_u8 *)cgen_table_column.string.data, cgen_table_column.string.size}; + MD_MapSlot *table_slot = MD_MapLookup(&cgen->table_map, MD_MapKeyStr(key)); + if (!table_slot) { + MD_CodeLoc loc = MD_CodeLocFromNode(cgen_table_column.node); + MD_PrintMessageFmt(stderr, + loc, + MD_MessageKind_Warning, + DQN_CAST(char *) "Code generation table references non-existent table '%.*s'", + DQN_STR_FMT(cgen_table_column.string)); + return false; + } + it->table = DQN_CAST(Dqn_CGenTable *) table_slot->val; + } + + for (Dqn_usize type = 0; type < Dqn_CGenTableHeaderType_Count; type++) + it->cgen_table_column[type] = it->cgen_table_row->columns[cgen_table->column_indexes[type]]; + return true; +} + +static void Dqn_CGen_EmitRowWhitespace_(Dqn_CGenTableRow const *row, Dqn_CppFile *cpp) +{ + for (Dqn_CGenTableRowTag *tag = row->first_tag; tag; tag = tag->next) { + switch (tag->type) { + case Dqn_CGenTableRowTagType_Nil: DQN_INVALID_CODE_PATH; + + case Dqn_CGenTableRowTagType_CommentDivider: { + if (tag->comment.size <= 0) + break; + + Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_Str8 prefix = Dqn_Str8_InitF(scratch.arena, "// NOTE: %.*s ", MD_S8VArg(tag->comment)); + int line_padding = DQN_MAX(100 - (DQN_CAST(int) prefix.size + (Dqn_CppSpacePerIndent(cpp) * cpp->indent)), 0); + Dqn_CppPrint(cpp, "%.*s", DQN_STR_FMT(prefix)); + for (int index = 0; index < line_padding; index++) + Dqn_CppAppend(cpp, "/"); + Dqn_CppAppend(cpp, "\n"); + } break; + + case Dqn_CGenTableRowTagType_EmptyLine: { + Dqn_CppAppend(cpp, "\n"); + } break; + } + } +} + +DQN_API void Dqn_CGen_EmitCodeForTables(Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_CppFile *cpp, Dqn_Str8 emit_prefix) +{ + DQN_ASSERT(Dqn_Str8_HasData(emit_prefix)); + + if (emit & Dqn_CGenEmit_Prototypes) { + // NOTE: Generate type info enums ////////////////////////////////////////////////////////// + Dqn_CppEnumBlock(cpp, "%.*s_Type", DQN_STR_FMT(emit_prefix)) { + Dqn_CppLine(cpp, "%.*s_Type_Nil,", DQN_STR_FMT(emit_prefix)); + for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) + Dqn_CppLine(cpp, "%.*s_Type_%.*s,", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)); + } + Dqn_CppLine(cpp, "%.*s_Type_Count,", DQN_STR_FMT(emit_prefix)); + } + + // NOTE: Generate structs + enums ////////////////////////////////////////////////////////////// + for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { + switch (table->type) { + case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH; + case Dqn_CGenTableType_Data: continue; + + case Dqn_CGenTableType_CodeGenStruct: { + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) { + // TODO(doyle): Verify the codegen table has the headers from the table it references + Dqn_CppStructBlock(cpp, "%.*s", DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)) { + for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { + Dqn_CGenTableRow const *row = it.table->rows + row_index; + Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); + Dqn_CGenLookupColumnAtHeader cpp_type = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppType].string, row); + Dqn_CGenLookupColumnAtHeader cpp_array_size = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySize].string, row); + int name_to_type_padding = 1 + it.table->headers[cpp_type.index].longest_string - DQN_CAST(int) cpp_type.column.string.size; + if (cpp_name.column.string.size <= 0 || cpp_type.column.string.size <= 0) + continue; + Dqn_CGen_EmitRowWhitespace_(row, cpp); + + Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_Str8 array_size = {}; + if (cpp_array_size.column.string.size) + array_size = Dqn_Str8_InitF(scratch.arena, "[%.*s]", DQN_STR_FMT(cpp_array_size.column.string)); + + Dqn_CppLine(cpp, + "%.*s%*s%.*s%.*s;", + DQN_STR_FMT(cpp_type.column.string), + name_to_type_padding, + "", + DQN_STR_FMT(cpp_name.column.string), + DQN_STR_FMT(array_size)); + } + } + } + + } break; + + case Dqn_CGenTableType_CodeGenEnum: { + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) { + Dqn_CppEnumBlock(cpp, "%.*s", DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string)) { + Dqn_usize enum_count = 0; + for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { + Dqn_CGenTableRow const *row = it.table->rows + row_index; + Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); + Dqn_CGenLookupColumnAtHeader cpp_value = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppValue].string, row); + + if (cpp_name.column.string.size <= 0) + continue; + + Dqn_CGen_EmitRowWhitespace_(row, cpp); + if (cpp_value.column.string.size) { + Dqn_CppLine(cpp, + "%.*s_%.*s = %.*s,", + DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string), + DQN_STR_FMT(cpp_name.column.string), + DQN_STR_FMT(cpp_value.column.string)); + } else { + Dqn_CppLine(cpp, + "%.*s_%.*s = %zu,", + DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string), + DQN_STR_FMT(cpp_name.column.string), + row_index); + } + enum_count++; + } + + Dqn_CGenTableColumn gen_enum_count_column = it.cgen_table_column[Dqn_CGenTableHeaderType_GenEnumCount]; + if (gen_enum_count_column.string.size) + Dqn_CppLine(cpp, + "%.*s_%.*s = %zu,", + DQN_STR_FMT(it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string), + DQN_STR_FMT(gen_enum_count_column.string), + enum_count); + } + } + } break; + } + } + + // NOTE: Generate enums for struct fields ////////////////////////////////////////////////// + for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { + switch (table->type) { + case Dqn_CGenTableType_Nil: DQN_INVALID_CODE_PATH; + case Dqn_CGenTableType_Data: continue; + case Dqn_CGenTableType_CodeGenEnum: continue; + + case Dqn_CGenTableType_CodeGenStruct: { + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it); ) { + Dqn_Str8 struct_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; + Dqn_CppEnumBlock(cpp, "%.*sTypeField", DQN_STR_FMT(struct_name)) { + for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { + Dqn_CGenTableRow const *row = it.table->rows + row_index; + Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); + if (cpp_name.column.string.size <= 0) + continue; + Dqn_CGen_EmitRowWhitespace_(row, cpp); + Dqn_CppLine(cpp, "%.*sTypeField_%.*s,", DQN_STR_FMT(struct_name), DQN_STR_FMT(cpp_name.column.string)); + } + Dqn_CppLine(cpp, "%.*sTypeField_Count,", DQN_STR_FMT(struct_name)); + } + } + } break; + } + } + } + + if (emit & Dqn_CGenEmit_Implementation) { + // NOTE: Generate type info //////////////////////////////////////////////////////////////////// + for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { + + if (table->type == Dqn_CGenTableType_CodeGenBuiltinTypes) + continue; + + Dqn_Str8 struct_or_enum_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; + Dqn_CppBlock(cpp, ";\n\n", "Dqn_TypeField const g_%.*s_type_fields[] =", DQN_STR_FMT(struct_or_enum_name)) { + if (table->type == Dqn_CGenTableType_CodeGenStruct) { + for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { + Dqn_CGenTableRow const *row = it.table->rows + row_index; + Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); + Dqn_CGenLookupColumnAtHeader cpp_type = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppType].string, row); + Dqn_CGenLookupColumnAtHeader cpp_is_ptr = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppIsPtr].string, row); + Dqn_CGenLookupColumnAtHeader cpp_array_size = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySize].string, row); + Dqn_CGenLookupColumnAtHeader cpp_array_size_field = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppArraySizeField].string, row); + + bool cpp_is_ptr_b32 = cpp_is_ptr.column.string == DQN_STR8("true"); + Dqn_Str8 cpp_array_size_str8 = Dqn_Str8_HasData(cpp_array_size.column.string) ? cpp_array_size.column.string : DQN_STR8("0"); + + Dqn_CGenTableColumn struct_name = it.cgen_table_row->columns[table->column_indexes[Dqn_CGenTableHeaderType_Name]]; + Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_Str8 cpp_array_size_field_str8 = DQN_STR8("NULL"); + if (cpp_array_size_field.column.string.size) { + + // TODO(doyle): Check that array_size_field references a valid field in the table + // NOTE: We use a raw index for the reference because the struct might + // not have type info being generated so we can't rely on the enum. + Dqn_usize index_the_field_references = 0; + for (Dqn_usize sub_row_index = 0; sub_row_index < it.table->row_count; sub_row_index++) { + Dqn_CGenTableRow const *sub_row = it.table->rows + sub_row_index; + Dqn_CGenTableColumn sub_cpp_name = sub_row->columns[cpp_name.index]; + if (sub_cpp_name.string == cpp_array_size_field.column.string) + index_the_field_references = sub_row_index; + } + cpp_array_size_field_str8 = Dqn_Str8_InitF(scratch.arena, + "&g_%.*s_type_fields[%zu]", + DQN_STR_FMT(struct_name.string), + index_the_field_references); + } + + Dqn_Str8 orig_cpp_type_info = Dqn_Str8_InitF(scratch.arena, "%.*s", DQN_STR_FMT(cpp_type.column.string)); + Dqn_Str8 cpp_type_info = orig_cpp_type_info; + cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("*"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive); + cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("constexpr"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive); + cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("const"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive); + cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8("static"), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive); + cpp_type_info = Dqn_Str8_Replace(cpp_type_info, /*find*/ DQN_STR8(" "), /*replace*/ DQN_STR8(""), /*start_index*/ 0, scratch.arena, Dqn_Str8EqCase_Sensitive); + + Dqn_Str8 cpp_type_enum = Dqn_Str8_InitF(scratch.arena, "%.*s_Type_%.*s", DQN_STR_FMT(emit_prefix), DQN_STR_FMT(cpp_type_info)); + Dqn_usize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size; + Dqn_usize cpp_type_padding = 1 + it.table->headers[cpp_type.index].longest_string - cpp_type.column.string.size; + Dqn_usize cpp_type_enum_padding = cpp_type_padding + (orig_cpp_type_info.size - cpp_type_info.size); + + Dqn_CppLine(cpp, + "{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ 0, offsetof(%.*s, %.*s),%*ssizeof(((%.*s*)0)->%.*s),%*sDQN_STR8(\"%.*s\"),%*s%.*s,%*s/*is_pointer*/ %s,%s /*array_size*/ %.*s, /*array_size_field*/ %.*s},", + row_index, + DQN_STR_FMT(cpp_name.column.string), + cpp_name_padding, "", + + // NOTE: offsetof(a, b) + DQN_STR_FMT(struct_or_enum_name), + DQN_STR_FMT(cpp_name.column.string), + cpp_name_padding, "", + + // NOTE: sizeof(a->b) + DQN_STR_FMT(struct_or_enum_name), + DQN_STR_FMT(cpp_name.column.string), + cpp_name_padding, "", + + // NOTE: Type string + DQN_STR_FMT(cpp_type.column.string), + cpp_type_padding, "", + + // NOTE: Type as enum + DQN_STR_FMT(cpp_type_enum), + cpp_type_enum_padding, "", + + cpp_is_ptr_b32 ? "true" : "false", + cpp_is_ptr_b32 ? " " : "", + DQN_STR_FMT(cpp_array_size_str8), + DQN_STR_FMT(cpp_array_size_field_str8)); + } + } else { + DQN_ASSERT(table->type == Dqn_CGenTableType_CodeGenEnum); + for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { + Dqn_CGenTableRow const *row = it.table->rows + row_index; + Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); + Dqn_CGenLookupColumnAtHeader cpp_value = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppValue].string, row); + if (cpp_name.column.string.size <= 0) + continue; + + Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_usize cpp_name_padding = 1 + it.table->headers[cpp_name.index].longest_string - cpp_name.column.string.size; + Dqn_Str8 cpp_value_str8 = Dqn_Str8_HasData(cpp_value.column.string) ? cpp_value.column.string : Dqn_Str8_InitF(scratch.arena, "%zu", row_index); + Dqn_CppLine(cpp, + "{%2d, DQN_STR8(\"%.*s\"),%*s/*value*/ %.*s, /*offset_of*/ 0, sizeof(%.*s), DQN_STR8(\"\"), %.*s_Type_%.*s, /*is_pointer*/ false, /*array_size*/ 0, /*array_size_field*/ NULL},", + row_index, + + // NOTE: Name string + DQN_STR_FMT(cpp_name.column.string), + cpp_name_padding, "", + + // NOTE: Value + DQN_STR_FMT(cpp_value_str8), + + // NOTE: sizeof(a) + DQN_STR_FMT(struct_or_enum_name), + + // NOTE: ..._Type_... + DQN_STR_FMT(emit_prefix), + DQN_STR_FMT(struct_or_enum_name)); + } + } + } + } + } + + int longest_name_across_all_tables = 0; + for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { + longest_name_across_all_tables = DQN_MAX(longest_name_across_all_tables, DQN_CAST(int)it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string.size); + } + } + + Dqn_CppBlock(cpp, ";\n\n", "Dqn_TypeInfo const g_%.*s_types[] =", DQN_STR_FMT(emit_prefix)) { + Dqn_CppLine(cpp, "{DQN_STR8(\"\"),%*sDqn_TypeKind_Nil, /*fields*/ NULL, /*count*/ 0},", 1 + longest_name_across_all_tables, ""); + for (Dqn_CGenTable *table = cgen->first_table; table != 0; table = table->next) { + for (Dqn_CGenLookupTableIterator it = {}; Dqn_CGen_LookupNextTableInCodeGenTable(cgen, table, &it);) { + Dqn_Str8 type_name = it.cgen_table_column[Dqn_CGenTableHeaderType_Name].string; + Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + int name_padding = 1 + longest_name_across_all_tables - DQN_CAST(int) type_name.size; + + Dqn_Str8 type_info_kind = {}; + char const *type_info_kind_padding = ""; + if (table->type == Dqn_CGenTableType_CodeGenEnum) { + type_info_kind = DQN_STR8("Dqn_TypeKind_Enum"); + type_info_kind_padding = " "; + } else if (table->type == Dqn_CGenTableType_CodeGenStruct) { + type_info_kind = DQN_STR8("Dqn_TypeKind_Struct"); + } else { + DQN_ASSERT(table->type == Dqn_CGenTableType_CodeGenBuiltinTypes); + type_info_kind = DQN_STR8("Dqn_TypeKind_Basic"); + type_info_kind_padding = " "; + } + + Dqn_Str8 fields_count = {}; + if (table->type == Dqn_CGenTableType_CodeGenBuiltinTypes) { + fields_count = DQN_STR8("0"); + } else { + DQN_ASSERT(table->type == Dqn_CGenTableType_CodeGenStruct || table->type == Dqn_CGenTableType_CodeGenEnum); + int fields_count_int = 0; + for (Dqn_usize row_index = 0; row_index < it.table->row_count; row_index++) { + Dqn_CGenTableRow const *row = it.table->rows + row_index; + Dqn_CGenLookupColumnAtHeader cpp_name = Dqn_CGen_LookupColumnAtHeader(it.table, it.cgen_table_column[Dqn_CGenTableHeaderType_CppName].string, row); + fields_count_int += Dqn_Str8_HasData(cpp_name.column.string); + } + fields_count = Dqn_Str8_InitF(scratch.arena, "%d", fields_count_int); + } + + Dqn_Str8 fields = DQN_STR8("NULL"); + int fields_padding = 1; + if (table->type != Dqn_CGenTableType_CodeGenBuiltinTypes) { + fields_padding = name_padding; + fields = Dqn_Str8_InitF(scratch.arena, "g_%.*s_type_fields", DQN_STR_FMT(type_name)); + } + + Dqn_CppLine(cpp, + "{DQN_STR8(\"%.*s\"),%*s%.*s, %s/*fields*/ %.*s,%*s/*count*/ %.*s},", + DQN_STR_FMT(type_name), + name_padding, "", + DQN_STR_FMT(type_info_kind), + type_info_kind_padding, + DQN_STR_FMT(fields), + fields_padding, "", + DQN_STR_FMT(fields_count)); + } + } + } + } +} diff --git a/dqn_cgen.h b/dqn_cgen.h new file mode 100644 index 0000000..a004851 --- /dev/null +++ b/dqn_cgen.h @@ -0,0 +1,166 @@ +#if !defined(DQN_CGEN_H) +#define DQN_CGEN_H + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// $$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\ +// $$ __$$\ $$ __$$\ $$ _____|$$$\ $$ | +// $$ / \__|$$ / \__|$$ | $$$$\ $$ | +// $$ | $$ |$$$$\ $$$$$\ $$ $$\$$ | +// $$ | $$ |\_$$ |$$ __| $$ \$$$$ | +// $$ | $$\ $$ | $$ |$$ | $$ |\$$$ | +// \$$$$$$ |\$$$$$$ |$$$$$$$$\ $$ | \$$ | +// \______/ \______/ \________|\__| \__| +// +// dqn_cgen.h -- C/C++ code generation from table data in Metadesk files +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +// NOTE: [$CGEN] Dqn_CGen ////////////////////////////////////////////////////////////////////////// +#if !defined(MD_H) +#error Metadesk 'md.h' must be included before 'dqn_cgen.h' +#endif + +#if !defined(DQN_H) +#error 'dqn.h' must be included before 'dqn_cgen.h' +#endif + +#if !defined(DQN_CPP_FILE_H) +#error 'dqn_cpp_file.h' must be included before 'dqn_cgen.h' +#endif + +enum Dqn_CGenTableKeyType +{ + Dqn_CGenTableKeyType_Nil, + Dqn_CGenTableKeyType_Name, + Dqn_CGenTableKeyType_Type, +}; + +enum Dqn_CGenTableType +{ + Dqn_CGenTableType_Nil, + Dqn_CGenTableType_Data, + Dqn_CGenTableType_CodeGenBuiltinTypes, + Dqn_CGenTableType_CodeGenStruct, + Dqn_CGenTableType_CodeGenEnum, +}; + +enum Dqn_CGenTableRowTagType +{ + Dqn_CGenTableRowTagType_Nil, + Dqn_CGenTableRowTagType_CommentDivider, + Dqn_CGenTableRowTagType_EmptyLine, +}; + +enum Dqn_CGenTableRowTagCommentDivider +{ + Dqn_CGenTableRowTagCommentDivider_Nil, + Dqn_CGenTableRowTagCommentDivider_Label, +}; + +enum Dqn_CGenTableHeaderType +{ + Dqn_CGenTableHeaderType_Name, + Dqn_CGenTableHeaderType_Table, + Dqn_CGenTableHeaderType_CppType, + Dqn_CGenTableHeaderType_CppName, + Dqn_CGenTableHeaderType_CppValue, + Dqn_CGenTableHeaderType_CppIsPtr, + Dqn_CGenTableHeaderType_CppArraySize, + Dqn_CGenTableHeaderType_CppArraySizeField, + Dqn_CGenTableHeaderType_GenTypeInfo, + Dqn_CGenTableHeaderType_GenEnumCount, + Dqn_CGenTableHeaderType_Count, +}; + +struct Dqn_CGenTableHeader +{ + MD_String8 name; + int longest_string; +}; + +struct Dqn_CGenTableRowTag +{ + Dqn_CGenTableRowTagType type; + MD_String8 comment; + Dqn_CGenTableRowTag *next; +}; + + +struct Dqn_CGenTableColumn +{ + MD_Node *node; + Dqn_Str8 string; +}; + +struct Dqn_CGenTableRow +{ + Dqn_CGenTableRowTag *first_tag; + Dqn_CGenTableRowTag *last_tag; + Dqn_CGenTableColumn *columns; +}; + +struct Dqn_CGenTable +{ + Dqn_CGenTableType type; + MD_String8 name; + MD_Map headers_map; + Dqn_CGenTableHeader *headers; + Dqn_CGenTableRow *rows; + size_t column_count; + size_t row_count; + + MD_Node *node; + MD_Node *headers_node; + Dqn_usize column_indexes[Dqn_CGenTableHeaderType_Count]; + Dqn_CGenTable *next; +}; + +struct Dqn_CGen +{ + MD_Arena *arena; + MD_Node *file_list; + MD_Map table_map; + Dqn_CGenTable *first_table; + Dqn_CGenTable *last_table; +}; + +struct Dqn_CGenMapNodeToEnum +{ + uint32_t enum_val; + Dqn_Str8 node_string; +}; + +struct Dqn_CGenLookupTableIterator +{ + Dqn_CGenTableRow *cgen_table_row; + Dqn_CGenTableColumn cgen_table_column[Dqn_CGenTableHeaderType_Count]; + Dqn_CGenTable *table; + Dqn_usize row_index; +}; + +struct Dqn_CGenLookupColumnAtHeader +{ + Dqn_usize index; + Dqn_CGenTableHeader header; + Dqn_CGenTableColumn column; +}; + +enum Dqn_CGenEmit +{ + Dqn_CGenEmit_Prototypes = 1 << 0, + Dqn_CGenEmit_Implementation = 1 << 1, +}; + +// NOTE: [$CGEN] Dqn_CGen ////////////////////////////////////////////////////////////////////////// +#define Dqn_CGen_MDToDqnStr8(str8) Dqn_Str8_Init((str8).str, (str8).size) +#define Dqn_CGen_DqnToMDStr8(str8) {DQN_CAST(MD_u8 *)(str8).data, (str8).size} +DQN_API Dqn_CGen Dqn_CGen_InitFilesArgV (int argc, char const **argv, Dqn_ErrorSink *error); +DQN_API Dqn_Str8 Dqn_CGen_TableHeaderTypeToDeclStr8 (Dqn_CGenTableHeaderType type); +DQN_API Dqn_CGenMapNodeToEnum Dqn_CGen_MapNodeToEnumOrExit (MD_Node const *node, Dqn_CGenMapNodeToEnum const *valid_keys, Dqn_usize valid_keys_size, char const *fmt, ...); +DQN_API void Dqn_CGen_LogF (MD_MessageKind kind, MD_Node *node, Dqn_ErrorSink *error, char const *fmt, ...); +DQN_API bool Dqn_CGen_TableHasHeaders (Dqn_CGenTable const *table, Dqn_Str8 const *headers, Dqn_usize header_count, Dqn_ErrorSink *error); +DQN_API Dqn_CGenLookupColumnAtHeader Dqn_CGen_LookupColumnAtHeader (Dqn_CGenTable *table, Dqn_Str8 header, Dqn_CGenTableRow const *row); +DQN_API bool Dqn_CGen_LookupNextTableInCodeGenTable(Dqn_CGen *cgen, Dqn_CGenTable *cgen_table, Dqn_CGenLookupTableIterator *it); +DQN_API void Dqn_CGen_EmitCodeForTables (Dqn_CGen *cgen, Dqn_CGenEmit emit, Dqn_CppFile *cpp, Dqn_Str8 emit_prefix); +#endif // DQN_CGEN_H diff --git a/dqn_cppbuild.h b/dqn_cppbuild.h index 3e8b4ca..96ca1c7 100644 --- a/dqn_cppbuild.h +++ b/dqn_cppbuild.h @@ -16,7 +16,9 @@ // //////////////////////////////////////////////////////////////////////////////////////////////////// -#include // exit +#if !defined(DQN_H) +#error 'dqn.h' must be included before 'dqn_cppbuild.h' +#endif struct Dqn_CPPBuildCompileFile { diff --git a/Misc/dqn_json.h b/dqn_json.h similarity index 100% rename from Misc/dqn_json.h rename to dqn_json.h diff --git a/dqn_unit_tests.cpp b/dqn_unit_tests.cpp index 1653730..7ef66f2 100644 --- a/dqn_unit_tests.cpp +++ b/dqn_unit_tests.cpp @@ -1,5 +1,5 @@ #define DQN_UTEST_IMPLEMENTATION -#include "dqn_utest.h" +#include "Standalone/dqn_utest.h" static Dqn_UTest Dqn_Test_Arena() { @@ -875,7 +875,71 @@ static Dqn_UTest Dqn_Test_Intrinsics() return test; } -#if defined(DQN_TEST_WITH_KECCAK) +#if defined(DQN_UNIT_TESTS_WITH_KECCAK) +DQN_GCC_WARNING_PUSH +DQN_GCC_WARNING_DISABLE(-Wunused-parameter) +DQN_GCC_WARNING_DISABLE(-Wsign-compare) + +DQN_MSVC_WARNING_PUSH +DQN_MSVC_WARNING_DISABLE(4244) +DQN_MSVC_WARNING_DISABLE(4100) +DQN_MSVC_WARNING_DISABLE(6385) +// NOTE: Keccak Reference Implementation /////////////////////////////////////////////////////////// +// A very compact Keccak implementation taken from the reference implementation +// repository +// +// https://github.com/XKCP/XKCP/blob/master/Standalone/CompactFIPS202/C/Keccak-more-compact.c + +#define FOR(i,n) for(i=0; i>1; } +#define ROL(a,o) ((((uint64_t)a)<>(64-o))) + +static uint64_t Dqn_RefImpl_load64_ (const uint8_t *x) { int i; uint64_t u=0; FOR(i,8) { u<<=8; u|=x[7-i]; } return u; } +static void Dqn_RefImpl_store64_(uint8_t *x, uint64_t u) { int i; FOR(i,8) { x[i]=u; u>>=8; } } +static void Dqn_RefImpl_xor64_ (uint8_t *x, uint64_t u) { int i; FOR(i,8) { x[i]^=u; u>>=8; } } + +#define rL(x,y) Dqn_RefImpl_load64_((uint8_t*)s+8*(x+5*y)) +#define wL(x,y,l) Dqn_RefImpl_store64_((uint8_t*)s+8*(x+5*y),l) +#define XL(x,y,l) Dqn_RefImpl_xor64_((uint8_t*)s+8*(x+5*y),l) + +void Dqn_RefImpl_Keccak_F1600(void *s) +{ + int r,x,y,i,j,Y; uint8_t R=0x01; uint64_t C[5],D; + for(i=0; i<24; i++) { + /*θ*/ FOR(x,5) C[x]=rL(x,0)^rL(x,1)^rL(x,2)^rL(x,3)^rL(x,4); FOR(x,5) { D=C[(x+4)%5]^ROL(C[(x+1)%5],1); FOR(y,5) XL(x,y,D); } + /*ρπ*/ x=1; y=r=0; D=rL(x,y); FOR(j,24) { r+=j+1; Y=(2*x+3*y)%5; x=y; y=Y; C[0]=rL(x,y); wL(x,y,ROL(D,r%64)); D=C[0]; } + /*χ*/ FOR(y,5) { FOR(x,5) C[x]=rL(x,y); FOR(x,5) wL(x,y,C[x]^((~C[(x+1)%5])&C[(x+2)%5])); } + /*ι*/ FOR(j,7) if (Dqn_RefImpl_LFSR86540_(&R)) XL(0,0,(uint64_t)1<<((1<0) { b=(inLen0) { b=(outLen0) Dqn_RefImpl_Keccak_F1600(s); } +} + +#undef XL +#undef wL +#undef rL +#undef ROL +#undef FOR +DQN_MSVC_WARNING_POP +DQN_GCC_WARNING_POP + +#define DQN_KECCAK_IMPLEMENTATION +#include "Standalone/dqn_keccak.h" + #define DQN_UTEST_HASH_X_MACRO \ DQN_UTEST_HASH_X_ENTRY(SHA3_224, "SHA3-224") \ DQN_UTEST_HASH_X_ENTRY(SHA3_256, "SHA3-256") \ @@ -903,8 +967,8 @@ Dqn_Str8 const DQN_UTEST_HASH_STRING_[] = void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { - Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); - Dqn_Str8 input_hex = Dqn_Hex_BytesToStr8Arena(scratch.arena, input.data, input.size); + Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr); + Dqn_Str8 input_hex = Dqn_Bin_BytesToHexArena(scratch.arena, input.data, input.size); switch(hash_type) { @@ -912,7 +976,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { Dqn_KeccakBytes28 hash = Dqn_SHA3_224StringToBytes28(input); Dqn_KeccakBytes28 expect; - FIPS202_SHA3_224(DQN_CAST(u8 *)input.data, input.size, (u8 *)expect.data); + Dqn_RefImpl_FIPS202_SHA3_224_(DQN_CAST(uint8_t *)input.data, input.size, (uint8_t *)expect.data); DQN_UTEST_ASSERTF(test, Dqn_KeccakBytes28Equals(&hash, &expect), "\ninput: %.*s" @@ -929,7 +993,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { Dqn_KeccakBytes32 hash = Dqn_SHA3_256StringToBytes32(input); Dqn_KeccakBytes32 expect; - FIPS202_SHA3_256(DQN_CAST(u8 *)input.data, input.size, (u8 *)expect.data); + Dqn_RefImpl_FIPS202_SHA3_256_(DQN_CAST(uint8_t *)input.data, input.size, (uint8_t *)expect.data); DQN_UTEST_ASSERTF(test, Dqn_KeccakBytes32Equals(&hash, &expect), "\ninput: %.*s" @@ -946,7 +1010,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { Dqn_KeccakBytes48 hash = Dqn_SHA3_384StringToBytes48(input); Dqn_KeccakBytes48 expect; - FIPS202_SHA3_384(DQN_CAST(u8 *)input.data, input.size, (u8 *)expect.data); + Dqn_RefImpl_FIPS202_SHA3_384_(DQN_CAST(uint8_t *)input.data, input.size, (uint8_t *)expect.data); DQN_UTEST_ASSERTF(test, Dqn_KeccakBytes48Equals(&hash, &expect), "\ninput: %.*s" @@ -963,7 +1027,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { Dqn_KeccakBytes64 hash = Dqn_SHA3_512StringToBytes64(input); Dqn_KeccakBytes64 expect; - FIPS202_SHA3_512(DQN_CAST(u8 *)input.data, input.size, (u8 *)expect.data); + Dqn_RefImpl_FIPS202_SHA3_512_(DQN_CAST(uint8_t *)input.data, input.size, (uint8_t *)expect.data); DQN_UTEST_ASSERTF(test, Dqn_KeccakBytes64Equals(&hash, &expect), "\ninput: %.*s" @@ -980,7 +1044,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { Dqn_KeccakBytes28 hash = Dqn_Keccak224StringToBytes28(input); Dqn_KeccakBytes28 expect; - Keccak(1152, 448, DQN_CAST(u8 *)input.data, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); + Dqn_RefImpl_Keccak_(1152, 448, DQN_CAST(uint8_t *)input.data, input.size, 0x01, (uint8_t *)expect.data, sizeof(expect)); DQN_UTEST_ASSERTF(test, Dqn_KeccakBytes28Equals(&hash, &expect), "\ninput: %.*s" @@ -997,7 +1061,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { Dqn_KeccakBytes32 hash = Dqn_Keccak256StringToBytes32(input); Dqn_KeccakBytes32 expect; - Keccak(1088, 512, DQN_CAST(u8 *)input.data, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); + Dqn_RefImpl_Keccak_(1088, 512, DQN_CAST(uint8_t *)input.data, input.size, 0x01, (uint8_t *)expect.data, sizeof(expect)); DQN_UTEST_ASSERTF(test, Dqn_KeccakBytes32Equals(&hash, &expect), "\ninput: %.*s" @@ -1014,7 +1078,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { Dqn_KeccakBytes48 hash = Dqn_Keccak384StringToBytes48(input); Dqn_KeccakBytes48 expect; - Keccak(832, 768, DQN_CAST(u8 *)input.data, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); + Dqn_RefImpl_Keccak_(832, 768, DQN_CAST(uint8_t *)input.data, input.size, 0x01, (uint8_t *)expect.data, sizeof(expect)); DQN_UTEST_ASSERTF(test, Dqn_KeccakBytes48Equals(&hash, &expect), "\ninput: %.*s" @@ -1031,7 +1095,7 @@ void Dqn_Test_KeccakDispatch_(Dqn_UTest *test, int hash_type, Dqn_Str8 input) { Dqn_KeccakBytes64 hash = Dqn_Keccak512StringToBytes64(input); Dqn_KeccakBytes64 expect; - Keccak(576, 1024, DQN_CAST(u8 *)input.data, input.size, 0x01, (u8 *)expect.data, sizeof(expect)); + Dqn_RefImpl_Keccak_(576, 1024, DQN_CAST(uint8_t *)input.data, input.size, 0x01, (uint8_t *)expect.data, sizeof(expect)); DQN_UTEST_ASSERTF(test, Dqn_KeccakBytes64Equals(&hash, &expect), "\ninput: %.*s" @@ -1061,9 +1125,7 @@ Dqn_UTest Dqn_Test_Keccak() DQN_UTEST_GROUP(test, "Dqn_Keccak") { for (int hash_type = 0; hash_type < Hash_Count; hash_type++) { - pcg32_random_t rng = {}; - pcg32_srandom_r(&rng, 0xd48e'be21'2af8'733d, 0x3f89'3bd2'd6b0'4eef); - + Dqn_PCG32 rng = Dqn_PCG32_Init(0xd48e'be21'2af8'733d); for (Dqn_Str8 input : INPUTS) { Dqn_UTest_Begin(&test, "%.*s - Input: %.*s", DQN_STR_FMT(DQN_UTEST_HASH_STRING_[hash_type]), DQN_CAST(int)DQN_MIN(input.size, 54), input.data); Dqn_Test_KeccakDispatch_(&test, hash_type, input); @@ -1071,22 +1133,22 @@ Dqn_UTest Dqn_Test_Keccak() } Dqn_UTest_Begin(&test, "%.*s - Deterministic random inputs", DQN_STR_FMT(DQN_UTEST_HASH_STRING_[hash_type])); - for (int index = 0; index < 128; index++) { + for (Dqn_usize index = 0; index < 128; index++) { char src[4096] = {}; - uint32_t src_size = pcg32_boundedrand_r(&rng, sizeof(src)); + uint32_t src_size = Dqn_PCG32_Range(&rng, 0, sizeof(src)); - for (int src_index = 0; src_index < src_size; src_index++) - src[src_index] = pcg32_boundedrand_r(&rng, 255); + for (Dqn_usize src_index = 0; src_index < src_size; src_index++) + src[src_index] = DQN_CAST(char)Dqn_PCG32_Range(&rng, 0, 255); Dqn_Str8 input = Dqn_Str8_Init(src, src_size); - TestKeccakDispatch_(&test, hash_type, input); + Dqn_Test_KeccakDispatch_(&test, hash_type, input); } Dqn_UTest_End(&test); } } return test; } -#endif // defined(DQN_TEST_WITH_KECCAK) +#endif // defined(DQN_UNIT_TESTS_WITH_KECCAK) static Dqn_UTest Dqn_Test_M4() { @@ -1836,7 +1898,7 @@ void Dqn_Test_RunSuite() Dqn_Test_Fs(), Dqn_Test_FixedArray(), Dqn_Test_Intrinsics(), - #if defined(DQN_TEST_WITH_KECCAK) + #if defined(DQN_UNIT_TESTS_WITH_KECCAK) Dqn_Test_Keccak(), #endif Dqn_Test_M4(), @@ -1860,7 +1922,7 @@ void Dqn_Test_RunSuite() fprintf(stdout, "Summary: %d/%d tests succeeded\n", total_good_tests, total_tests); } -#if defined(DQN_TEST_WITH_MAIN) +#if defined(DQN_UNIT_TESTS_WITH_MAIN) int main(int argc, char *argv[]) { (void)argv; (void)argc;