Move source code into Source folder and add a single header generator"
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
#if !defined(DN_CPP_FILE_H)
|
||||
#define DN_CPP_FILE_H
|
||||
|
||||
#include <stdio.h> /// printf, fputc
|
||||
#include <stdarg.h> /// va_list...
|
||||
#include <assert.h> /// assert
|
||||
|
||||
typedef struct DN_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; ///
|
||||
} DN_CppFile;
|
||||
|
||||
#define DN_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 DN_CppLineV(DN_CppFile *cpp, char const *fmt, va_list args);
|
||||
void DN_CppLine(DN_CppFile *cpp, char const *fmt, ...);
|
||||
|
||||
/// Print the format string indented
|
||||
void DN_CppPrintV(DN_CppFile *cpp, char const *fmt, va_list args);
|
||||
void DN_CppPrint(DN_CppFile *cpp, char const *fmt, ...);
|
||||
|
||||
/// Print the format string
|
||||
#define DN_CppAppend(cpp, fmt, ...) fprintf((cpp)->file, fmt, ##__VA_ARGS__)
|
||||
#define DN_CppAppendV(cpp, fmt, args) vfprintf((cpp)->file, fmt, args)
|
||||
|
||||
/// End the current line, useful after CppPrint and CppAppend
|
||||
#define DN_CppNewLine(cpp) fputc('\n', (cpp)->file)
|
||||
|
||||
/// Manually modify the indent level
|
||||
#define DN_CppIndent(cpp) (cpp)->indent++
|
||||
#define DN_CppUnindent(cpp) (cpp)->indent--; assert((cpp)->indent >= 0)
|
||||
|
||||
/// Block scope functions that execute a function on entry and exit of the
|
||||
/// scope by exploiting the comma operator and a for loop.
|
||||
///
|
||||
/// @code
|
||||
/// DN_CppEnumBlock(cpp, "abc") {
|
||||
/// printf("Hello world!");
|
||||
/// }
|
||||
///
|
||||
/// // Is equivalent to
|
||||
///
|
||||
/// DN_CppBeginBlock(cpp, "abc");
|
||||
/// printf("Hello world!");
|
||||
/// DN_CppEndEnumBlock(cpp);
|
||||
/// @endcode
|
||||
|
||||
#define DN_CppEnumBlock(cpp, fmt, ...) \
|
||||
for (bool DN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(DN_CppBeginEnumBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__) = (DN_CppEndEnumBlock(cpp), false))
|
||||
|
||||
#define DN_CppForBlock(cpp, fmt, ...) \
|
||||
for (bool DN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(DN_CppBeginForBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__) = (DN_CppEndForBlock(cpp), false))
|
||||
|
||||
#define DN_CppWhileBlock(cpp, fmt, ...) \
|
||||
for (bool DN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(DN_CppBeginWhileBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__) = (DN_CppEndWhileBlock(cpp), false))
|
||||
|
||||
#define DN_CppIfOrElseIfBlock(cpp, fmt, ...) \
|
||||
for (bool DN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(DN_CppBeginIfOrElseIfBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__) = (DN_CppEndIfOrElseIfBlock(cpp), false))
|
||||
|
||||
#define DN_CppElseBlock(cpp) \
|
||||
for (bool DN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(DN_CppBeginElseBlock(cpp), true); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__) = (DN_CppEndElseBlock(cpp), false))
|
||||
|
||||
#define DN_CppFuncBlock(cpp, fmt, ...) \
|
||||
for (bool DN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(DN_CppBeginFuncBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__) = (DN_CppEndFuncBlock(cpp), false))
|
||||
|
||||
#define DN_CppStructBlock(cpp, fmt, ...) \
|
||||
for (bool DN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(DN_CppBeginStructBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__) = (DN_CppEndStructBlock(cpp), false))
|
||||
|
||||
#define DN_CppSwitchBlock(cpp, fmt, ...) \
|
||||
for (bool DN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(DN_CppBeginSwitchBlock(cpp, fmt, ##__VA_ARGS__), true); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__) = (DN_CppEndSwitchBlock(cpp), false))
|
||||
|
||||
#define DN_CppBlock(cpp, ending, fmt, ...) \
|
||||
for (bool DN_CPP_TOKEN_PASTE_(once_, __LINE__) = \
|
||||
(DN_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__), true); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__) = (DN_CppEndBlock(cpp, ending), false))
|
||||
|
||||
#define DN_CppIfChain(cpp) \
|
||||
for (bool DN_CPP_TOKEN_PASTE_(once_, __LINE__) = (DN_CppBeginIfChain(cpp), true); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__); \
|
||||
DN_CPP_TOKEN_PASTE_(once_, __LINE__) = (DN_CppEndIfChain(cpp), false))
|
||||
|
||||
/// Print the format string followed by a "{" and enter a new line whilst
|
||||
/// increasing the indent level after the brace.
|
||||
void DN_CppBeginBlock (DN_CppFile *cpp, bool append, char const *fmt, ...);
|
||||
void DN_CppBeginBlockV(DN_CppFile *cpp, bool append, char const *fmt, va_list args);
|
||||
void DN_CppEndBlock (DN_CppFile *cpp, char const *ending);
|
||||
|
||||
/// Begin/End a block, specifically for the following language constructs.
|
||||
#define DN_CppBeginEnumBlock(cpp, fmt, ...) DN_CppBeginBlock(cpp, false /*append*/, "enum " fmt, ##__VA_ARGS__)
|
||||
#define DN_CppEndEnumBlock(cpp) DN_CppEndBlock(cpp, ";\n")
|
||||
|
||||
#define DN_CppBeginWhileBlock(cpp, fmt, ...) DN_CppBeginBlock(cpp, false /*append*/, "while (" fmt ")", ##__VA_ARGS__)
|
||||
#define DN_CppEndWhileBlock(cpp) DN_CppEndBlock(cpp, "\n")
|
||||
|
||||
#define DN_CppBeginForBlock(cpp, fmt, ...) DN_CppBeginBlock(cpp, false /*append*/, "for (" fmt ")", ##__VA_ARGS__)
|
||||
#define DN_CppEndForBlock(cpp) DN_CppEndBlock(cpp, "\n")
|
||||
|
||||
#define DN_CppBeginFuncBlock(cpp, fmt, ...) DN_CppBeginBlock(cpp, false /*append*/, fmt, ##__VA_ARGS__)
|
||||
#define DN_CppEndFuncBlock(cpp) DN_CppEndBlock(cpp, "\n")
|
||||
|
||||
#define DN_CppBeginStructBlock(cpp, fmt, ...) DN_CppBeginBlock(cpp, false /*append*/, "struct " fmt, ##__VA_ARGS__)
|
||||
#define DN_CppEndStructBlock(cpp) DN_CppEndBlock(cpp, ";\n")
|
||||
|
||||
#define DN_CppBeginSwitchBlock(cpp, fmt, ...) DN_CppBeginBlock(cpp, false /*append*/, "switch (" fmt ")", ##__VA_ARGS__)
|
||||
#define DN_CppEndSwitchBlock(cpp) DN_CppEndBlock(cpp, "\n")
|
||||
|
||||
void DN_CppBeginIfOrElseIfBlock (DN_CppFile *cpp, char const *fmt, ...);
|
||||
#define DN_CppEndIfOrElseIfBlock(cpp) DN_CppEndBlock(cpp, "")
|
||||
|
||||
void DN_CppBeginElseBlock (DN_CppFile *cpp);
|
||||
void DN_CppEndElseBlock (DN_CppFile *cpp);
|
||||
|
||||
#define DN_CPP_TOKEN_PASTE2_(x, y) x ## y
|
||||
#define DN_CPP_TOKEN_PASTE_(x, y) DN_CPP_TOKEN_PASTE2_(x, y)
|
||||
#endif // DN_CPP_FILE_H
|
||||
|
||||
#if defined(DN_CPP_FILE_IMPLEMENTATION)
|
||||
void DN_CppLineV(DN_CppFile *cpp, char const *fmt, va_list args)
|
||||
{
|
||||
DN_CppPrintV(cpp, fmt, args);
|
||||
DN_CppNewLine(cpp);
|
||||
}
|
||||
|
||||
void DN_CppLine(DN_CppFile *cpp, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_CppLineV(cpp, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void DN_CppPrintV(DN_CppFile *cpp, char const *fmt, va_list args)
|
||||
{
|
||||
int space_per_indent = DN_CppSpacePerIndent(cpp);
|
||||
int spaces = fmt ? (cpp->indent * space_per_indent) : 0;
|
||||
fprintf(cpp->file, "%*s", spaces, "");
|
||||
vfprintf(cpp->file, fmt, args);
|
||||
}
|
||||
|
||||
void DN_CppPrint(DN_CppFile *cpp, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_CppPrintV(cpp, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void DN_CppBeginBlock(DN_CppFile *cpp, bool append, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_CppBeginBlockV(cpp, append, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void DN_CppBeginBlockV(DN_CppFile *cpp, bool append, char const *fmt, va_list args)
|
||||
{
|
||||
if (append)
|
||||
DN_CppAppendV(cpp, fmt, args);
|
||||
else
|
||||
DN_CppPrintV(cpp, fmt, args);
|
||||
|
||||
bool empty_fmt = fmt == nullptr || strlen(fmt) == 0;
|
||||
DN_CppAppend(cpp, "%s{\n", empty_fmt ? "" : " ");
|
||||
DN_CppIndent(cpp);
|
||||
}
|
||||
|
||||
void DN_CppEndBlock(DN_CppFile *cpp, char const *ending)
|
||||
{
|
||||
DN_CppUnindent(cpp);
|
||||
DN_CppPrint(cpp, "}%s", ending);
|
||||
}
|
||||
|
||||
void DN_CppBeginIfOrElseIfBlock(DN_CppFile *cpp, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
assert(cpp->if_chain_size);
|
||||
if (cpp->if_chain[cpp->if_chain_size - 1] == 0)
|
||||
DN_CppPrint(cpp, "if");
|
||||
else
|
||||
DN_CppAppend(cpp, " else if");
|
||||
|
||||
DN_CppAppend(cpp, " (");
|
||||
DN_CppAppendV(cpp, fmt, args);
|
||||
DN_CppAppend(cpp, ") {\n");
|
||||
DN_CppIndent(cpp);
|
||||
va_end(args);
|
||||
cpp->if_chain[cpp->if_chain_size - 1]++;
|
||||
}
|
||||
|
||||
void DN_CppBeginElseBlock(DN_CppFile *cpp)
|
||||
{
|
||||
assert(cpp->if_chain_size);
|
||||
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1)
|
||||
DN_CppBeginBlock(cpp, true /*append*/, " else");
|
||||
}
|
||||
|
||||
void DN_CppEndElseBlock(DN_CppFile *cpp)
|
||||
{
|
||||
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1)
|
||||
DN_CppEndBlock(cpp, "");
|
||||
}
|
||||
|
||||
void DN_CppBeginIfChain(DN_CppFile *cpp)
|
||||
{
|
||||
assert(cpp->if_chain_size < sizeof(cpp->if_chain)/sizeof(cpp->if_chain[0]));
|
||||
cpp->if_chain_size++;
|
||||
}
|
||||
|
||||
void DN_CppEndIfChain(DN_CppFile *cpp)
|
||||
{
|
||||
assert(cpp->if_chain_size);
|
||||
if (cpp->if_chain[cpp->if_chain_size - 1] >= 1) {
|
||||
DN_CppNewLine(cpp);
|
||||
}
|
||||
cpp->if_chain[cpp->if_chain_size - 1] = 0;
|
||||
cpp->if_chain_size--;
|
||||
}
|
||||
|
||||
#endif // DN_CPP_FILE_IMPLEMENTATION
|
||||
@@ -0,0 +1,666 @@
|
||||
#if !defined(DN_KC_H)
|
||||
#define DN_KC_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\
|
||||
// $$ | $$ |$$ _____|$$ __$$\ $$ __$$\ $$ __$$\ $$ | $$ |
|
||||
// $$ |$$ / $$ | $$ / \__|$$ / \__|$$ / $$ |$$ |$$ /
|
||||
// $$$$$ / $$$$$\ $$ | $$ | $$$$$$$$ |$$$$$ /
|
||||
// $$ $$< $$ __| $$ | $$ | $$ __$$ |$$ $$<
|
||||
// $$ |\$$\ $$ | $$ | $$\ $$ | $$\ $$ | $$ |$$ |\$$\
|
||||
// $$ | \$$\ $$$$$$$$\ \$$$$$$ |\$$$$$$ |$$ | $$ |$$ | \$$\
|
||||
// \__| \__|\________| \______/ \______/ \__| \__|\__| \__|
|
||||
//
|
||||
// dn_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).
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2021 github.com/doy-lee
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\
|
||||
// $$ __$$\ $$ __$$\\__$$ __|\_$$ _|$$ __$$\ $$$\ $$ |$$ __$$\
|
||||
// $$ / $$ |$$ | $$ | $$ | $$ | $$ / $$ |$$$$\ $$ |$$ / \__|
|
||||
// $$ | $$ |$$$$$$$ | $$ | $$ | $$ | $$ |$$ $$\$$ |\$$$$$$\
|
||||
// $$ | $$ |$$ ____/ $$ | $$ | $$ | $$ |$$ \$$$$ | \____$$\
|
||||
// $$ | $$ |$$ | $$ | $$ | $$ | $$ |$$ |\$$$ |$$\ $$ |
|
||||
// $$$$$$ |$$ | $$ | $$$$$$\ $$$$$$ |$$ | \$$ |\$$$$$$ |
|
||||
// \______/ \__| \__| \______| \______/ \__| \__| \______/
|
||||
//
|
||||
// Options -- Compile time build customisation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// - Define this in one and only one C++ file to enable the implementation
|
||||
// code of the header file.
|
||||
//
|
||||
// #define DN_KC_IMPLEMENTATION
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(DN_KC_MEMCPY)
|
||||
#include <string.h>
|
||||
#define DN_KC_MEMCPY(dest, src, count) memcpy(dest, src, count)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_KC_MEMCMP)
|
||||
#include <string.h>
|
||||
#define DN_KC_MEMCMP(dest, src, count) memcmp(dest, src, count)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_KC_MEMSET)
|
||||
#include <string.h>
|
||||
#define DN_KC_MEMSET(dest, byte, count) memset(dest, byte, count)
|
||||
#endif
|
||||
|
||||
#if !defined(DN_KC_ASSERT)
|
||||
#if defined(NDEBUG)
|
||||
#define DN_KC_ASSERT(expr)
|
||||
#else
|
||||
#define DN_KC_ASSERT(expr) \
|
||||
do \
|
||||
{ \
|
||||
if (!(expr)) \
|
||||
{ \
|
||||
(*(volatile int *)0) = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
// Use this macro in a printf-like function,
|
||||
/*
|
||||
DN_KCString64 string = {};
|
||||
printf("%.*s\n", DN_KC_STRING64_FMT(string));
|
||||
*/
|
||||
#define DN_KC_STRING56_FMT(string) 56, string
|
||||
#define DN_KC_STRING64_FMT(string) 64, string
|
||||
#define DN_KC_STRING96_FMT(string) 96, string
|
||||
#define DN_KC_STRING128_FMT(string) 128, string
|
||||
|
||||
typedef struct DN_KCBytes28 { char data[28]; } DN_KCBytes28; // 224 bit
|
||||
typedef struct DN_KCBytes32 { char data[32]; } DN_KCBytes32; // 256 bit
|
||||
typedef struct DN_KCBytes48 { char data[48]; } DN_KCBytes48; // 384 bit
|
||||
typedef struct DN_KCBytes64 { char data[64]; } DN_KCBytes64; // 512 bit
|
||||
|
||||
typedef struct DN_KCString56 { char data[(sizeof(DN_KCBytes28) * 2) + 1]; } DN_KCString56;
|
||||
typedef struct DN_KCString64 { char data[(sizeof(DN_KCBytes32) * 2) + 1]; } DN_KCString64;
|
||||
typedef struct DN_KCString96 { char data[(sizeof(DN_KCBytes48) * 2) + 1]; } DN_KCString96;
|
||||
typedef struct DN_KCString128 { char data[(sizeof(DN_KCBytes64) * 2) + 1]; } DN_KCString128;
|
||||
|
||||
#define DN_KC_LANE_SIZE_U64 5
|
||||
typedef struct DN_KCState {
|
||||
uint8_t state[DN_KC_LANE_SIZE_U64 * DN_KC_LANE_SIZE_U64 * sizeof(uint64_t)];
|
||||
int state_size; // The number of bytes written to the state
|
||||
int absorb_size; // The amount of bytes to absorb/sponge in/from the state
|
||||
int hash_size_bits; // The size of the hash the context was initialised for in bits
|
||||
char delimited_suffix; // The delimited suffix of the current hash
|
||||
} DN_KCState;
|
||||
|
||||
// NOTE: SHA3/Keccak Streaming API /////////////////////////////////////////////////////////////////
|
||||
// Setup a hashing state for either
|
||||
// - FIPS 202 SHA3
|
||||
// - Non-finalized SHA3 (only difference is delimited suffix of 0x1 instead of 0x6 in SHA3)
|
||||
// The non-finalized SHA3 version is the one adopted by many cryptocurrencies
|
||||
// such as Ethereum and Monero as they adopted SHA3 before it was finalized.
|
||||
//
|
||||
// The state produced from this function is to be used alongside the
|
||||
// 'KeccakUpdate' and 'KC_HashFinish' functions.
|
||||
//
|
||||
// sha3: If true, setup the state for FIPS 202 SHA3, otherwise the non-finalized version
|
||||
// hash_size_bits: The number of bits to setup the context for, available sizes are 224, 256, 384 and 512.
|
||||
DN_KCState DN_KC_HashInit(bool sha3, uint16_t hash_size_bits);
|
||||
|
||||
// After initialising a 'DN_KCState' via 'DN_KeccakSHA3Init', iteratively
|
||||
// update the hash with new data by calling 'DN_KC_HashUpdate'. On completion,
|
||||
// call 'DN_KC_HashFinish' to generate the hash from the state. The 'dest_size'
|
||||
// must be at-least the (bit-size / 8), i.e. for 'DN_Keccak512Init' it must be
|
||||
// atleast (512 / 8) bytes, 64 bytes.
|
||||
void DN_KC_HashUpdate(DN_KCState *keccak, void const *data, size_t data_size);
|
||||
DN_KCBytes32 DN_KC_HashFinish(DN_KCState const *keccak);
|
||||
void DN_KC_HashFinishPtr(DN_KCState *keccak, void *dest, size_t dest_size);
|
||||
|
||||
// NOTE: Simple API ////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper function that combines the Init, Update and Finish step in one shot,
|
||||
// i.e. hashing a singlular contiguous buffer. Use the streaming API if data
|
||||
// is split across different buffers.
|
||||
void DN_KC_Hash(bool sha3, uint16_t hash_size_bits, void const *src, uint64_t src_size, void *dest, int dest_size);
|
||||
|
||||
// NOTE: SHA3 Helpers //////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: SHA3 Streaming API
|
||||
#define DN_KC_SHA3_224Init(src, size, dest, dest_size) DN_KC_HashInit(true /*sha3*/, 224)
|
||||
#define DN_KC_SHA3_224Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_SHA3_224Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_SHA3_224FinishPtr(state, dest, size) DN_KC_HashFinish(state, dest, size)
|
||||
|
||||
#define DN_KC_SHA3_256Init(src, size, dest, dest_size) DN_KC_HashInit(true /*sha3*/, 256)
|
||||
#define DN_KC_SHA3_256Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_SHA3_256Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_SHA3_256FinishPtr(state, dest, size) DN_KC_HashFinish(state, dest, size)
|
||||
|
||||
#define DN_KC_SHA3_384Init(src, size, dest, dest_size) DN_KC_HashInit(true /*sha3*/, 384)
|
||||
#define DN_KC_SHA3_384Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_SHA3_384Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_SHA3_384FinishPtr(state, dest, size) DN_KC_HashFinish(state, dest, size)
|
||||
|
||||
#define DN_KC_SHA3_512Init(src, size, dest, dest_size) DN_KC_HashInit(true /*sha3*/, 512)
|
||||
#define DN_KC_SHA3_512Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_SHA3_512Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_SHA3_512FinishPtr(state, dest, size) DN_KC_HashFinishPtr(state, dest, size)
|
||||
|
||||
// NOTE: SHA3 one-shot API
|
||||
#define DN_KC_SHA3_224Ptr(src, size, dest, dest_size) DN_KC_Hash(true /*sha3*/, 224, src, size, dest, dest_size)
|
||||
#define DN_KC_SHA3_256Ptr(src, size, dest, dest_size) DN_KC_Hash(true /*sha3*/, 256, src, size, dest, dest_size)
|
||||
#define DN_KC_SHA3_384Ptr(src, size, dest, dest_size) DN_KC_Hash(true /*sha3*/, 384, src, size, dest, dest_size)
|
||||
#define DN_KC_SHA3_512Ptr(src, size, dest, dest_size) DN_KC_Hash(true /*sha3*/, 512, src, size, dest, dest_size)
|
||||
|
||||
DN_KCBytes28 DN_KC_SHA3_224(void const *src, uint64_t size);
|
||||
DN_KCBytes32 DN_KC_SHA3_256(void const *src, uint64_t size);
|
||||
DN_KCBytes48 DN_KC_SHA3_384(void const *src, uint64_t size);
|
||||
DN_KCBytes64 DN_KC_SHA3_512(void const *src, uint64_t size);
|
||||
|
||||
// NOTE: Keccak Helpers ////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: SHA3 Streaming API
|
||||
#define DN_KC_Keccak224Init() DN_KC_HashInit(false /*sha3*/, 224)
|
||||
#define DN_KC_Keccak224Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_Keccak224Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_Keccak224FinishPtr(state, dest, size) DN_KC_HashFinishPtr(state, dest, size)
|
||||
|
||||
#define DN_KC_Keccak256Init() DN_KC_HashInit(false /*sha3*/, 256)
|
||||
#define DN_KC_Keccak256Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_Keccak256Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_Keccak256FinishPtr(state, dest, size) DN_KC_HashFinishPtr(state, dest, size)
|
||||
|
||||
#define DN_KC_Keccak384Init() DN_KC_HashInit(false /*sha3*/, 384)
|
||||
#define DN_KC_Keccak384Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_Keccak384Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_Keccak384FinishPtr(state, dest, size) DN_KC_HashFinishPtr(state, dest, size)
|
||||
|
||||
#define DN_KC_Keccak512Init() DN_KC_HashInit(false /*sha3*/, 512)
|
||||
#define DN_KC_Keccak512Update(state, src, size) DN_KC_HashUpdate(state, src, size)
|
||||
#define DN_KC_Keccak512Finish(state) DN_KC_HashFinish(state)
|
||||
#define DN_KC_Keccak512FinishPtr(state, dest, size) DN_KC_HashFinishPtr(state, dest, size)
|
||||
|
||||
// NOTE: SHA3 one-shot API
|
||||
#define DN_KC_Keccak224Ptr(src, size, dest, dest_size) DN_KC_Hash(false /*sha3*/, 224, src, size, dest, dest_size)
|
||||
#define DN_KC_Keccak256Ptr(src, size, dest, dest_size) DN_KC_Hash(false /*sha3*/, 256, src, size, dest, dest_size)
|
||||
#define DN_KC_Keccak384Ptr(src, size, dest, dest_size) DN_KC_Hash(false /*sha3*/, 384, src, size, dest, dest_size)
|
||||
#define DN_KC_Keccak512Ptr(src, size, dest, dest_size) DN_KC_Hash(false /*sha3*/, 512, src, size, dest, dest_size)
|
||||
|
||||
DN_KCBytes28 DN_KC_Keccak224(void const *src, uint64_t size);
|
||||
DN_KCBytes32 DN_KC_Keccak256(void const *src, uint64_t size);
|
||||
DN_KCBytes48 DN_KC_Keccak384(void const *src, uint64_t size);
|
||||
DN_KCBytes64 DN_KC_Keccak512(void const *src, uint64_t size);
|
||||
|
||||
#if defined(DN_BASE_STRING_H)
|
||||
// NOTE: SHA3 - Helpers for DN data structures ////////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_SHA3_224Str8(DN_Str8 string);
|
||||
DN_KCBytes32 DN_KC_SHA3_256Str8(DN_Str8 string);
|
||||
DN_KCBytes48 DN_KC_SHA3_384Str8(DN_Str8 string);
|
||||
DN_KCBytes64 DN_KC_SHA3_512Str8(DN_Str8 string);
|
||||
|
||||
// NOTE: Keccak - Helpers for DN data structures //////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_Keccak224Str8(DN_Str8 string);
|
||||
DN_KCBytes32 DN_KC_Keccak256Str8(DN_Str8 string);
|
||||
DN_KCBytes48 DN_KC_Keccak384Str8(DN_Str8 string);
|
||||
DN_KCBytes64 DN_KC_Keccak512Str8(DN_Str8 string);
|
||||
#endif // DN_BASE_STRING_H
|
||||
|
||||
// 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.
|
||||
void DN_KC_BytesToHex(void const *src, uint64_t src_size, char *dest, uint64_t dest_size);
|
||||
|
||||
// Converts a fixed amount of bytes into a hexadecimal string. Helper functions
|
||||
// that call into DN_KCBytesToHex.
|
||||
// return: The hexadecimal string of the bytes, null-terminated.
|
||||
DN_KCString56 DN_KC_Bytes28ToHex(DN_KCBytes28 const *bytes);
|
||||
DN_KCString64 DN_KC_Bytes32ToHex(DN_KCBytes32 const *bytes);
|
||||
DN_KCString96 DN_KC_Bytes48ToHex(DN_KCBytes48 const *bytes);
|
||||
DN_KCString128 DN_KC_Bytes64ToHex(DN_KCBytes64 const *bytes);
|
||||
|
||||
// Compares byte data structures for byte equality (via memcmp).
|
||||
// return: 1 if the contents are equal otherwise 0.
|
||||
int DN_KC_Bytes28Equals(DN_KCBytes28 const *a, DN_KCBytes28 const *b);
|
||||
int DN_KC_Bytes32Equals(DN_KCBytes32 const *a, DN_KCBytes32 const *b);
|
||||
int DN_KC_Bytes48Equals(DN_KCBytes48 const *a, DN_KCBytes48 const *b);
|
||||
int DN_KC_Bytes64Equals(DN_KCBytes64 const *a, DN_KCBytes64 const *b);
|
||||
|
||||
#if defined(DN_BASE_STRING_H)
|
||||
// NOTE: Other helper functions for DN 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.
|
||||
DN_KCBytes32 DN_KC_Hex64ToBytes(DN_Str8 hex);
|
||||
#endif // DN_BASE_STRING_H
|
||||
#endif // DN_KC_H
|
||||
|
||||
#if defined(DN_KC_IMPLEMENTATION)
|
||||
uint64_t const DN_KC_ROUNDS[] = {
|
||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, 0x000000000000808B,
|
||||
0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, 0x0000000000000088,
|
||||
0x0000000080008009, 0x000000008000000A, 0x000000008000808B, 0x800000000000008B, 0x8000000000008089,
|
||||
0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
|
||||
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
||||
};
|
||||
|
||||
uint64_t const DN_KC_ROTATIONS[][5] =
|
||||
{
|
||||
{0, 36, 3, 41, 18},
|
||||
{1, 44, 10, 45, 2},
|
||||
{62, 6, 43, 15, 61},
|
||||
{28, 55, 25, 21, 56},
|
||||
{27, 20, 39, 8, 14},
|
||||
};
|
||||
|
||||
#define DN_KC_ROL64(val, rotate) (((val) << (rotate)) | (((val) >> (64 - (rotate)))))
|
||||
|
||||
static void DN_KC_Permute_(void *state)
|
||||
{
|
||||
// TODO(dn): Do some profiling on unrolling and can we SIMD some part of
|
||||
// this? Unroll loop, look at data dependencies and investigate.
|
||||
|
||||
uint64_t *lanes_u64 = (uint64_t *)state;
|
||||
for (int round_index = 0; round_index < 24; round_index++)
|
||||
{
|
||||
#define LANE_INDEX(x, y) ((x) + ((y) * DN_KC_LANE_SIZE_U64))
|
||||
// ?? step //////////////////////////////////////////////////////////////////////////////////
|
||||
#if 1
|
||||
uint64_t c[DN_KC_LANE_SIZE_U64];
|
||||
for (int x = 0; x < DN_KC_LANE_SIZE_U64; x++) {
|
||||
c[x] = lanes_u64[LANE_INDEX(x, 0)] ^
|
||||
lanes_u64[LANE_INDEX(x, 1)] ^
|
||||
lanes_u64[LANE_INDEX(x, 2)] ^
|
||||
lanes_u64[LANE_INDEX(x, 3)] ^
|
||||
lanes_u64[LANE_INDEX(x, 4)];
|
||||
}
|
||||
|
||||
uint64_t d[DN_KC_LANE_SIZE_U64];
|
||||
for (int x = 0; x < DN_KC_LANE_SIZE_U64; x++)
|
||||
d[x] = c[(x + 4) % DN_KC_LANE_SIZE_U64] ^ DN_KC_ROL64(c[(x + 1) % DN_KC_LANE_SIZE_U64], 1);
|
||||
|
||||
for (int y = 0; y < DN_KC_LANE_SIZE_U64; y++)
|
||||
for (int x = 0; x < DN_KC_LANE_SIZE_U64; x++)
|
||||
lanes_u64[LANE_INDEX(x, y)] ^= d[x];
|
||||
#else
|
||||
uint64_t c[5], d[5];
|
||||
c[0] = lanes_u64[0 * 5 + 0] ^ lanes_u64[1 * 5 + 0] ^ lanes_u64[2 * 5 + 0] ^ lanes_u64[3 * 5 + 0] ^ lanes_u64[4 * 5 + 0];
|
||||
c[1] = lanes_u64[0 * 5 + 1] ^ lanes_u64[1 * 5 + 1] ^ lanes_u64[2 * 5 + 1] ^ lanes_u64[3 * 5 + 1] ^ lanes_u64[4 * 5 + 1];
|
||||
c[2] = lanes_u64[0 * 5 + 2] ^ lanes_u64[1 * 5 + 2] ^ lanes_u64[2 * 5 + 2] ^ lanes_u64[3 * 5 + 2] ^ lanes_u64[4 * 5 + 2];
|
||||
c[3] = lanes_u64[0 * 5 + 3] ^ lanes_u64[1 * 5 + 3] ^ lanes_u64[2 * 5 + 3] ^ lanes_u64[3 * 5 + 3] ^ lanes_u64[4 * 5 + 3];
|
||||
c[4] = lanes_u64[0 * 5 + 4] ^ lanes_u64[1 * 5 + 4] ^ lanes_u64[2 * 5 + 4] ^ lanes_u64[3 * 5 + 4] ^ lanes_u64[4 * 5 + 4];
|
||||
|
||||
d[0] = c[4] ^ DN_KC_ROL64(c[1], 1);
|
||||
d[1] = c[0] ^ DN_KC_ROL64(c[2], 1);
|
||||
d[2] = c[1] ^ DN_KC_ROL64(c[3], 1);
|
||||
d[3] = c[2] ^ DN_KC_ROL64(c[4], 1);
|
||||
d[4] = c[3] ^ DN_KC_ROL64(c[0], 1);
|
||||
#endif
|
||||
|
||||
// ?? and ?? steps ///////////////////////////////////////////////////////////////////////////
|
||||
uint64_t b[DN_KC_LANE_SIZE_U64 * DN_KC_LANE_SIZE_U64];
|
||||
for (int y = 0; y < DN_KC_LANE_SIZE_U64; y++) {
|
||||
for (int x = 0; x < DN_KC_LANE_SIZE_U64; x++) {
|
||||
uint64_t lane = lanes_u64[LANE_INDEX(x, y)];
|
||||
uint64_t rotate_count = DN_KC_ROTATIONS[x][y];
|
||||
b[LANE_INDEX(y, (2 * x + 3 * y) % 5)] = DN_KC_ROL64(lane, rotate_count);
|
||||
}
|
||||
}
|
||||
|
||||
// ?? step //////////////////////////////////////////////////////////////////////////////////
|
||||
for (int y = 0; y < DN_KC_LANE_SIZE_U64; y++) {
|
||||
for (int x = 0; x < DN_KC_LANE_SIZE_U64; x++) {
|
||||
uint64_t rhs = ~b[LANE_INDEX((x + 1) % 5, y)] & b[LANE_INDEX((x + 2) % 5, y)];
|
||||
|
||||
lanes_u64[LANE_INDEX(x, y)] = b[LANE_INDEX(x, y)] ^ rhs;
|
||||
}
|
||||
}
|
||||
|
||||
// ?? step //////////////////////////////////////////////////////////////////////////////////
|
||||
lanes_u64[LANE_INDEX(0, 0)] ^= DN_KC_ROUNDS[round_index];
|
||||
#undef LANE_INDEX
|
||||
#undef DN_KC_ROL64
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Streaming API /////////////////////////////////////////////////////////////////////////////
|
||||
DN_KCState DN_KC_HashInit(bool sha3, uint16_t hash_size_bits)
|
||||
{
|
||||
char const SHA3_DELIMITED_SUFFIX = 0x06;
|
||||
char const KECCAK_DELIMITED_SUFFIX = 0x01;
|
||||
int const bitrate = 1600 - (hash_size_bits * 2);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
DN_KCState result = {};
|
||||
#else
|
||||
DN_KCState result = {0};
|
||||
#endif
|
||||
result.hash_size_bits = hash_size_bits;
|
||||
result.absorb_size = bitrate / 8;
|
||||
result.delimited_suffix = sha3 ? SHA3_DELIMITED_SUFFIX : KECCAK_DELIMITED_SUFFIX;
|
||||
DN_KC_ASSERT(bitrate + (hash_size_bits * 2) /*capacity*/ == 1600);
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_KC_HashUpdate(DN_KCState *keccak, void const *data, size_t data_size)
|
||||
{
|
||||
uint8_t *state = keccak->state;
|
||||
uint8_t const *ptr = (uint8_t *)data;
|
||||
size_t ptr_size = data_size;
|
||||
while (ptr_size > 0) {
|
||||
size_t space = keccak->absorb_size - keccak->state_size;
|
||||
int bytes_to_absorb = (int)(space < ptr_size ? space : ptr_size);
|
||||
|
||||
for (int index = 0; index < bytes_to_absorb; index++)
|
||||
state[keccak->state_size + index] ^= ptr[index];
|
||||
|
||||
ptr += bytes_to_absorb;
|
||||
keccak->state_size += bytes_to_absorb;
|
||||
ptr_size -= bytes_to_absorb;
|
||||
|
||||
if (keccak->state_size >= keccak->absorb_size) {
|
||||
DN_KC_ASSERT(keccak->state_size == keccak->absorb_size);
|
||||
DN_KC_Permute_(state);
|
||||
keccak->state_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DN_KCBytes32 DN_KC_HashFinish(DN_KCState *keccak)
|
||||
{
|
||||
DN_KCBytes32 result = {};
|
||||
DN_KC_HashFinishPtr(keccak, result.data, sizeof(result.data));
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_KC_HashFinishPtr(DN_KCState *keccak, void *dest, size_t dest_size)
|
||||
{
|
||||
DN_KC_ASSERT(dest_size >= (size_t)(keccak->hash_size_bits / 8));
|
||||
|
||||
// Sponge Finalization Step: Final padding bit /////////////////////////////////////////////////
|
||||
int const INDEX_OF_0X80_BYTE = keccak->absorb_size - 1;
|
||||
int const delimited_suffix_index = keccak->state_size;
|
||||
DN_KC_ASSERT(delimited_suffix_index < keccak->absorb_size);
|
||||
|
||||
uint8_t *state = keccak->state;
|
||||
state[delimited_suffix_index] ^= keccak->delimited_suffix;
|
||||
|
||||
// NOTE: In the reference implementation, it checks that if the
|
||||
// delimited suffix is set to the padding bit (0x80), then we need to
|
||||
// permute twice. Once for the delimited suffix, and a second time for
|
||||
// the "padding" permute.
|
||||
//
|
||||
// However all standard algorithms either specify a 0x01, or 0x06, 0x04
|
||||
// delimited suffix and so forth- so this case is never hit. We can omit
|
||||
// this from the implementation here.
|
||||
|
||||
state[INDEX_OF_0X80_BYTE] ^= 0x80;
|
||||
DN_KC_Permute_(state);
|
||||
|
||||
// Squeeze Step: Squeeze bytes from the state into our hash ////////////////////////////////////
|
||||
uint8_t *dest_u8 = (uint8_t *)dest;
|
||||
size_t const squeeze_count = dest_size / keccak->absorb_size;
|
||||
size_t squeeze_index = 0;
|
||||
for (; squeeze_index < squeeze_count; squeeze_index++) {
|
||||
if (squeeze_index)
|
||||
DN_KC_Permute_(state);
|
||||
DN_KC_MEMCPY(dest_u8, state, keccak->absorb_size);
|
||||
dest_u8 += keccak->absorb_size;
|
||||
}
|
||||
|
||||
// Squeeze Finalisation Step: Remainder bytes in hash //////////////////////////////////////////
|
||||
int const remainder = dest_size % keccak->absorb_size;
|
||||
if (remainder) {
|
||||
if (squeeze_index)
|
||||
DN_KC_Permute_(state);
|
||||
DN_KC_MEMCPY(dest_u8, state, remainder);
|
||||
}
|
||||
}
|
||||
|
||||
void DN_KC_Hash(bool sha3, uint16_t hash_size_bits, void const *src, size_t src_size, void *dest, size_t dest_size)
|
||||
{
|
||||
DN_KCState state = DN_KC_HashInit(sha3, hash_size_bits);
|
||||
DN_KC_HashUpdate(&state, src, src_size);
|
||||
DN_KC_HashFinishPtr(&state, dest, dest_size);
|
||||
}
|
||||
|
||||
// NOTE: SHA3 Helpers //////////////////////////////////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_SHA3_224(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes28 result;
|
||||
DN_KC_SHA3_224Ptr(src, size, result.data, sizeof(result.data));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes32 DN_KC_SHA3_256(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes32 result;
|
||||
DN_KC_SHA3_256Ptr(src, size, result.data, sizeof(result.data));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes48 DN_KC_SHA3_384(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes48 result;
|
||||
DN_KC_SHA3_384Ptr(src, size, result.data, sizeof(result.data));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes64 DN_KC_SHA3_512(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes64 result;
|
||||
DN_KC_SHA3_512Ptr(src, size, result.data, sizeof(result.data));
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Keccak Helpers ////////////////////////////////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_Keccak224(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes28 result;
|
||||
DN_KC_Keccak224Ptr(src, size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes32 DN_KC_Keccak256(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes32 result;
|
||||
DN_KC_Keccak256Ptr(src, size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes48 DN_KC_Keccak384(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes48 result;
|
||||
DN_KC_Keccak384Ptr(src, size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes64 DN_KC_Keccak512(void const *src, size_t size)
|
||||
{
|
||||
DN_KCBytes64 result;
|
||||
DN_KC_Keccak512Ptr(src, size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(DN_BASE_STRING_H)
|
||||
// NOTE: SHA3 - Helpers for DN data structures ////////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_SHA3_224Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes28 result;
|
||||
DN_KC_SHA3_224Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes32 DN_KC_SHA3_256Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes32 result;
|
||||
DN_KC_SHA3_256Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes48 DN_KC_SHA3_384Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes48 result;
|
||||
DN_KC_SHA3_384Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes64 DN_KC_SHA3_512Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes64 result;
|
||||
DN_KC_SHA3_512Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
#endif // DN_BASE_STRING_H
|
||||
|
||||
#if defined(DN_BASE_STRING_H)
|
||||
// NOTE: Keccak - Helpers for DN data structures //////////////////////////////////////////////////
|
||||
DN_KCBytes28 DN_KC_Keccak224Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes28 result;
|
||||
DN_KC_Keccak224Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes32 DN_KC_Keccak256Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes32 result;
|
||||
DN_KC_Keccak256Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes48 DN_KC_Keccak384Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes48 result;
|
||||
DN_KC_Keccak384Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCBytes64 DN_KC_Keccak512Str8(DN_Str8 string)
|
||||
{
|
||||
DN_KCBytes64 result;
|
||||
DN_KC_Keccak512Ptr(string.data, string.size, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
#endif // DN_BASE_STRING_H
|
||||
|
||||
// NOTE: Helper functions //////////////////////////////////////////////////////////////////////////
|
||||
void DN_KC_BytesToHex(void const *src, size_t src_size, char *dest, size_t dest_size)
|
||||
{
|
||||
(void)src_size; (void)dest_size;
|
||||
DN_KC_ASSERT(dest_size >= src_size * 2);
|
||||
|
||||
unsigned char *src_u8 = (unsigned char *)src;
|
||||
for (size_t src_index = 0, dest_index = 0; src_index < src_size;
|
||||
src_index += 1, dest_index += 2) {
|
||||
char byte = src_u8[src_index];
|
||||
char hex01 = (byte >> 4) & 0b1111;
|
||||
char hex02 = (byte >> 0) & 0b1111;
|
||||
dest[dest_index + 0] = hex01 < 10 ? (hex01 + '0') : (hex01 - 10) + 'a';
|
||||
dest[dest_index + 1] = hex02 < 10 ? (hex02 + '0') : (hex02 - 10) + 'a';
|
||||
}
|
||||
}
|
||||
|
||||
DN_KCString56 DN_KC_Bytes28ToHex(DN_KCBytes28 const *bytes)
|
||||
{
|
||||
DN_KCString56 result;
|
||||
DN_KC_BytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCString64 DN_KC_Bytes32ToHex(DN_KCBytes32 const *bytes)
|
||||
{
|
||||
DN_KCString64 result;
|
||||
DN_KC_BytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCString96 DN_KC_Bytes48ToHex(DN_KCBytes48 const *bytes)
|
||||
{
|
||||
DN_KCString96 result;
|
||||
DN_KC_BytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_KCString128 DN_KC_Bytes64ToHex(DN_KCBytes64 const *bytes)
|
||||
{
|
||||
DN_KCString128 result;
|
||||
DN_KC_BytesToHex(bytes->data, sizeof(bytes->data), result.data, sizeof(result.data));
|
||||
result.data[sizeof(result.data) - 1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KC_Bytes28Equals(DN_KCBytes28 const *a, DN_KCBytes28 const *b)
|
||||
{
|
||||
int result = DN_KC_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KC_Bytes32Equals(DN_KCBytes32 const *a, DN_KCBytes32 const *b)
|
||||
{
|
||||
int result = DN_KC_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KC_Bytes48Equals(DN_KCBytes48 const *a, DN_KCBytes48 const *b)
|
||||
{
|
||||
int result = DN_KC_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int DN_KC_Bytes64Equals(DN_KCBytes64 const *a, DN_KCBytes64 const *b)
|
||||
{
|
||||
int result = DN_KC_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(DN_BASE_STRING_H)
|
||||
// NOTE: Other helper functions for DN data structures ////////////////////////////////////////////
|
||||
DN_KCBytes32 DN_KC_Hex64ToBytes(DN_Str8 hex)
|
||||
{
|
||||
DN_KC_ASSERT(hex.size == 64);
|
||||
DN_KCBytes32 result;
|
||||
DN_CVT_HexToBytesPtr(hex, result.data, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
#endif // DN_BASE_STRING_H
|
||||
#endif // DN_KC_IMPLEMENTATION
|
||||
@@ -0,0 +1,330 @@
|
||||
#if !defined(DN_UT_H)
|
||||
#define DN_UT_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$$\
|
||||
// $$ | $$ |\__$$ __|$$ _____|$$ __$$\\__$$ __|
|
||||
// $$ | $$ | $$ | $$ | $$ / \__| $$ |
|
||||
// $$ | $$ | $$ | $$$$$\ \$$$$$$\ $$ |
|
||||
// $$ | $$ | $$ | $$ __| \____$$\ $$ |
|
||||
// $$ | $$ | $$ | $$ | $$\ $$ | $$ |
|
||||
// \$$$$$$ | $$ | $$$$$$$$\ \$$$$$$ | $$ |
|
||||
// \______/ \__| \________| \______/ \__|
|
||||
//
|
||||
// dn_utest.h -- Extremely minimal unit testing framework
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// A super minimal testing framework, most of the logic here is the pretty
|
||||
// printing of test results.
|
||||
//
|
||||
// NOTE: Configuration /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// #define DN_UT_IMPLEMENTATION
|
||||
// Define this in one and only one C++ file to enable the implementation
|
||||
// code of the header file. This will also automatically enable the JSMN
|
||||
// implementation.
|
||||
//
|
||||
// #define DN_UT_RESULT_LPAD
|
||||
// Define this to a number to specify how much to pad the output of the test
|
||||
// result line before the test result is printed.
|
||||
//
|
||||
// #define DN_UT_RESULT_PAD_CHAR
|
||||
// Define this to a character to specify the default character to use for
|
||||
// padding. By default this is '.'
|
||||
//
|
||||
// #define DN_UT_SPACING
|
||||
// Define this to a number to specify the number of spaces between the group
|
||||
// declaration and the test output in the group.
|
||||
//
|
||||
// #define DN_UT_BAD_COLOR
|
||||
// Define this to a terminal color code to specify what color errors will be
|
||||
// presented as.
|
||||
//
|
||||
// #define DN_UT_GOOD_COLOR
|
||||
// Define this to a terminal color code to specify what color sucess will be
|
||||
// presented as.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: Macros ////////////////////////////////////////////////////////////////////////////////////
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if !defined(DN_UT_RESULT_LPAD)
|
||||
#define DN_UT_RESULT_LPAD 90
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UT_RESULT_PAD_CHAR)
|
||||
#define DN_UT_RESULT_PAD_CHAR '.'
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UT_SPACING)
|
||||
#define DN_UT_SPACING 2
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UT_BAD_COLOR)
|
||||
#define DN_UT_BAD_COLOR "\x1b[31m"
|
||||
#endif
|
||||
|
||||
#if !defined(DN_UT_GOOD_COLOR)
|
||||
#define DN_UT_GOOD_COLOR "\x1b[32m"
|
||||
#endif
|
||||
|
||||
#define DN_UT_COLOR_RESET "\x1b[0m"
|
||||
|
||||
#define DN_UT_Test(test, fmt, ...) \
|
||||
int dummy_ = (DN_UT_BeginF((test), fmt, ##__VA_ARGS__), 0); \
|
||||
(void)dummy_, (test)->state == DN_UTState_TestBegun; \
|
||||
DN_UT_End(test)
|
||||
|
||||
#define DN_UT_AssertF(test, expr, fmt, ...) \
|
||||
DN_UT_AssertAtF((test), __FILE__, __LINE__, (expr), fmt, ##__VA_ARGS__)
|
||||
|
||||
#define DN_UT_Assert(test, expr) \
|
||||
DN_UT_AssertAt((test), __FILE__, __LINE__, (expr))
|
||||
|
||||
// TODO: Fix the logs. They print before the tests, we should accumulate logs
|
||||
// per test, then, dump them on test on. But to do this nicely without crappy
|
||||
// mem management we need to implement an arena.
|
||||
#define DN_UT_Log(test, fmt, ...) \
|
||||
DN_UT_LogF(test, "%*s" fmt "\n", DN_UT_SPACING * 2, "", ##__VA_ARGS__)
|
||||
|
||||
#define DN_UT_AssertAtF(test, file, line, expr, fmt, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
(test)->state = DN_UTState_TestFailed; \
|
||||
DN_UT_LogInsideTestF(test, \
|
||||
"%*sAssertion File: %s:%d\n" \
|
||||
"%*sExpression: [" #expr \
|
||||
"]\n" \
|
||||
"%*sReason: " fmt "\n", \
|
||||
DN_UT_SPACING * 2, \
|
||||
"", \
|
||||
file, \
|
||||
line, \
|
||||
DN_UT_SPACING * 2, \
|
||||
"", \
|
||||
DN_UT_SPACING * 2, \
|
||||
"", \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DN_UT_AssertAt(test, file, line, expr) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
(test)->state = DN_UTState_TestFailed; \
|
||||
DN_UT_LogInsideTestF(test, \
|
||||
"%*sAssertion File: %s:%d\n" \
|
||||
"%*sExpression: [" #expr "]\n", \
|
||||
DN_UT_SPACING * 2, \
|
||||
"", \
|
||||
file, \
|
||||
line, \
|
||||
DN_UT_SPACING * 2, \
|
||||
""); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// NOTE: Header ////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef enum DN_UTState
|
||||
{
|
||||
DN_UTState_Nil,
|
||||
DN_UTState_TestBegun,
|
||||
DN_UTState_TestFailed,
|
||||
} DN_UTState;
|
||||
|
||||
typedef struct DN_UTStr8Link
|
||||
{
|
||||
char *data;
|
||||
size_t size;
|
||||
DN_UTStr8Link *next;
|
||||
DN_UTStr8Link *prev;
|
||||
} DN_UTStr8Link;
|
||||
|
||||
typedef struct DN_UTCore
|
||||
{
|
||||
int num_tests_in_group;
|
||||
int num_tests_ok_in_group;
|
||||
DN_UTState state;
|
||||
char name[256];
|
||||
size_t name_size;
|
||||
DN_UTStr8Link *curr_test_messages;
|
||||
DN_UTStr8Link *output;
|
||||
} DN_UTCore;
|
||||
|
||||
void DN_UT_BeginFV(DN_UTCore *test, char const *fmt, va_list args);
|
||||
void DN_UT_BeginF(DN_UTCore *test, char const *fmt, ...);
|
||||
void DN_UT_End(DN_UTCore *test);
|
||||
|
||||
void DN_UT_LogF(DN_UTCore *test, char const *fmt, ...);
|
||||
void DN_UT_LogInsideTestF(DN_UTCore *test, char const *fmt, ...);
|
||||
|
||||
bool DN_UT_AllTestsPassed(DN_UTCore const *test);
|
||||
void DN_UT_PrintTests(DN_UTCore const *test);
|
||||
#endif // DN_UT_H
|
||||
|
||||
// NOTE: Implementation ////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(DN_UT_IMPLEMENTATION)
|
||||
DN_UTCore DN_UT_Init()
|
||||
{
|
||||
DN_UTCore result = {};
|
||||
result.output = (DN_UTStr8Link *)calloc(1, sizeof(*result.output));
|
||||
result.curr_test_messages = (DN_UTStr8Link *)calloc(1, sizeof(*result.curr_test_messages));
|
||||
assert(result.output);
|
||||
assert(result.curr_test_messages);
|
||||
result.output->next = result.output->prev = result.output;
|
||||
result.curr_test_messages->next = result.curr_test_messages->prev = result.curr_test_messages;
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_UT_Deinit(DN_UTCore *ut)
|
||||
{
|
||||
for (DN_UTStr8Link *it = ut->output->next; it != ut->output; it = ut->output->next) {
|
||||
it->next->prev = it->prev;
|
||||
it->prev->next = it->next;
|
||||
free(it);
|
||||
}
|
||||
free(ut->output);
|
||||
|
||||
for (DN_UTStr8Link *it = ut->curr_test_messages->next; it != ut->curr_test_messages; it = ut->curr_test_messages->next) {
|
||||
it->next->prev = it->prev;
|
||||
it->prev->next = it->next;
|
||||
free(it);
|
||||
}
|
||||
free(ut->curr_test_messages);
|
||||
}
|
||||
|
||||
void DN_UT_BeginFV(DN_UTCore *ut, char const *fmt, va_list args)
|
||||
{
|
||||
assert(ut->output && ut->curr_test_messages && "Test must be initialised by calling DN_UT_Init()");
|
||||
assert(ut->state == DN_UTState_Nil &&
|
||||
"Nesting a unit ut within another unit test is not allowed, ensure"
|
||||
"the first test has finished by calling DN_UT_End");
|
||||
|
||||
ut->num_tests_in_group++;
|
||||
ut->state = DN_UTState_TestBegun;
|
||||
ut->name_size = 0;
|
||||
{
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
ut->name_size = vsnprintf(NULL, 0, fmt, args_copy);
|
||||
va_end(args_copy);
|
||||
}
|
||||
|
||||
assert(ut->name_size < sizeof(ut->name));
|
||||
vsnprintf(ut->name, sizeof(ut->name), fmt, args);
|
||||
}
|
||||
|
||||
void DN_UT_BeginF(DN_UTCore *ut, char const *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_UT_BeginFV(ut, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static DN_UTStr8Link *DN_UT_AllocStr8LinkFV(char const *fmt, va_list args)
|
||||
{
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
size_t size = vsnprintf(nullptr, 0, fmt, args_copy) + 1;
|
||||
va_end(args_copy);
|
||||
|
||||
DN_UTStr8Link *result = (DN_UTStr8Link *)malloc(sizeof(*result) + size);
|
||||
if (result) {
|
||||
result->data = (char *)result + sizeof(*result);
|
||||
result->size = vsnprintf(result->data, size, fmt, args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_UT_End(DN_UTCore *ut)
|
||||
{
|
||||
assert(ut->state != DN_UTState_Nil && "Test was marked as ended but a ut was never commenced using DN_UT_Begin");
|
||||
size_t pad_size = DN_UT_RESULT_LPAD - (DN_UT_SPACING + ut->name_size);
|
||||
if (pad_size < 0)
|
||||
pad_size = 0;
|
||||
|
||||
char pad_buffer[DN_UT_RESULT_LPAD] = {};
|
||||
memset(pad_buffer, DN_UT_RESULT_PAD_CHAR, pad_size);
|
||||
|
||||
DN_UT_LogF(ut, "%*s%.*s%.*s", DN_UT_SPACING, "", (int)ut->name_size, ut->name, (int)pad_size, pad_buffer);
|
||||
if (ut->state == DN_UTState_TestFailed) {
|
||||
DN_UT_LogF(ut, DN_UT_BAD_COLOR " FAILED");
|
||||
} else {
|
||||
DN_UT_LogF(ut, DN_UT_GOOD_COLOR " OK");
|
||||
ut->num_tests_ok_in_group++;
|
||||
}
|
||||
DN_UT_LogF(ut, DN_UT_COLOR_RESET "\n");
|
||||
ut->state = DN_UTState_Nil;
|
||||
|
||||
// NOTE: Append any test messages (like assertions) into the main output buffer
|
||||
for (DN_UTStr8Link *it = ut->curr_test_messages->next; it != ut->curr_test_messages; it = ut->curr_test_messages->next) {
|
||||
// NOTE: Detach
|
||||
it->next->prev = it->prev;
|
||||
it->prev->next = it->next;
|
||||
|
||||
// NOTE: Attach
|
||||
it->next = ut->output;
|
||||
it->prev = ut->output->prev;
|
||||
it->next->prev = it;
|
||||
it->prev->next = it;
|
||||
}
|
||||
}
|
||||
|
||||
void DN_UT_LogF(DN_UTCore *ut, char const *fmt, ...)
|
||||
{
|
||||
assert(ut->output && ut->curr_test_messages && "UT was not initialised by calling UT_Init yet");
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_UTStr8Link *result = DN_UT_AllocStr8LinkFV(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
result->next = ut->output;
|
||||
result->prev = ut->output->prev;
|
||||
result->next->prev = result;
|
||||
result->prev->next = result;
|
||||
}
|
||||
|
||||
void DN_UT_LogInsideTestF(DN_UTCore *ut, char const *fmt, ...)
|
||||
{
|
||||
assert(ut->state >= DN_UTState_TestBegun && "");
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
DN_UTStr8Link *result = DN_UT_AllocStr8LinkFV(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
result->next = ut->curr_test_messages;
|
||||
result->prev = ut->curr_test_messages->prev;
|
||||
result->next->prev = result;
|
||||
result->prev->next = result;
|
||||
}
|
||||
|
||||
bool DN_UT_AllTestsPassed(DN_UTCore const *ut)
|
||||
{
|
||||
bool result = ut->num_tests_ok_in_group == ut->num_tests_in_group;
|
||||
return result;
|
||||
}
|
||||
|
||||
void DN_UT_PrintTests(DN_UTCore const *ut)
|
||||
{
|
||||
for (DN_UTStr8Link *it = ut->output->next; it != ut->output; it = it->next)
|
||||
fprintf(stdout, "%.*s", (int)it->size, it->data);
|
||||
|
||||
bool all_clear = DN_UT_AllTestsPassed(ut);
|
||||
fprintf(stdout,
|
||||
"%s\n %02d/%02d tests passed -- %s\n\n" DN_UT_COLOR_RESET,
|
||||
all_clear ? DN_UT_GOOD_COLOR : DN_UT_BAD_COLOR,
|
||||
ut->num_tests_ok_in_group,
|
||||
ut->num_tests_in_group,
|
||||
all_clear ? "OK" : "FAILED");
|
||||
}
|
||||
#endif // DN_UT_IMPLEMENTATION
|
||||
Reference in New Issue
Block a user