Update dqn and add some new libraries from internal projects

This commit is contained in:
doyle 2021-09-19 21:49:56 +10:00
parent 7f082c9395
commit 86391f6a73
8 changed files with 2902 additions and 1889 deletions

1244
Dqn.h

File diff suppressed because it is too large Load Diff

View File

@ -3,9 +3,8 @@
// -----------------------------------------------------------------------------
// NOTE: Dqn_Curl
// -----------------------------------------------------------------------------
// A wrapper over CURL that is primarily used for hot-code reloading by
// packaging the CURL api into a struct that can be passed across DLL
// boundaries.
// Define DQN_CURL_IMPLEMENTATION in one and only one file to enable the
// implementation in translation unit.
//
// curl_global_init(CURL_GLOBAL_ALL) must return CURLE_OK before anything
// in this file is used. You may cleanup curl on exit via curl_global_cleanup()
@ -14,15 +13,56 @@
// An easy way to generate the curl commands required to query a url is to use
// CURL itself and the option to dump the command to a C-compatible file, i.e.
//
// curl --libcurl RequestToCCode.c -X POST -H "Content-Type: application/json" --data-binary "{\"jsonrpc\": \"2.0\", \"id\": \"0\", \"method\": \"get_service_nodes\", \"params\": []}" oxen.observer:22023/json_rpc
// curl --libcurl RequestToCCode.c -X POST -H "Content-Type: application/json" --data-binary "{\"jsonrpc\": \"2.0\", \"id\": \"0\", \"method\": \"get_block_count\", \"params\": []}" oxen.observer:22023/json_rpc
//
// -----------------------------------------------------------------------------
// NOTE: Configuration
// NOTE: Example
// -----------------------------------------------------------------------------
// #define DQN_CURL_IMPLEMENTATION
// Define this in one and only one C++ file to enable the implementation
// code of the header file.
#if 0
struct CurlWriteFunctionUserData
{
Dqn_ArenaAllocator *arena;
Dqn_StringList data;
};
size_t CurlWriteFunction(char *ptr, size_t size, size_t nmemb, void *userdata)
{
auto *user_data = DQN_CAST(CurlWriteFunctionUserData *)userdata;
Dqn_StringList_AppendStringCopy(&user_data->data, user_data->arena, Dqn_String_Init(ptr, nmemb));
DQN_ASSERT(size == 1);
return nmemb;
}
void main()
{
// NOTE: Setup Curl handle
CURL *handle = curl_easy_init();
struct curl_slist *header_list = nullptr;
header_list = curl_slist_append(header_list, "Content-Type: application/json");
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header_list);
Dqn_String post_data = DQN_STRING("{\"jsonrpc\": \"2.0\", \"id\": \"0\", \"method\": \"get_block_count\", \"params\": []}");
Dqn_Curl_SetHTTPPost(handle, "oxen.observer:22023/json_rpc", post_data.str, DQN_CAST(int)post_data.size);
// NOTE: Set write callback
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch();
CurlWriteFunctionUserData user_data = {};
user_data.arena = scratch.arena;
Dqn_Curl_SetWriteCallback(handle, CurlWriteFunction, &user_data);
// NOTE: Execute CURL query
curl_easy_perform(handle);
Dqn_String output = Dqn_StringList_Build(&user_data.string_list, scoped_arena.arena);
DQN_LOG_I("%.*s", DQN_STRING_FMT(output));
// NOTE: Cleanup
curl_slist_free_all(header_list);
curl_easy_cleanup(handle);
}
#endif
// NOTE: Warning this causes Windows.h to be included.
#define CURL_STATICLIB
#define NOMINMAX
#include <curl-7.72.0/include/curl/curl.h>
@ -47,8 +87,9 @@ struct Dqn_CurlProcs
};
Dqn_CurlProcs Dqn_CurlProcs_Init();
void Dqn_Curl_SetPostData(CURL *curl, char const *post_data, int post_size);
void Dqn_Curl_SetURL(CURL *handle, char const *url);
void Dqn_Curl_SetHTTPPost(CURL *handle, char const *url, char const *post_data, int post_data_size);
void Dqn_Curl_SetWriteCallback(CURL *handle, size_t (*curl_write_callback)(char *ptr, size_t size, size_t nmemb, void *userdata), void *user_data);
#endif // DQN_CURL_H
#if defined(DQN_CURL_IMPLEMENTATION)
@ -80,10 +121,29 @@ Dqn_CurlProcs Dqn_CurlProcs_Init()
return result;
}
void Dqn_Curl_SetPostData(CURL *curl, char const *post_data, int post_size)
void Dqn_Curl_SetURL(CURL *handle, char const *url)
{
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, post_size);
curl_easy_setopt(handle, CURLOPT_URL, url);
}
void Dqn_Curl_SetHTTPPost(CURL *handle, char const *url, char const *post_data, int post_data_size)
{
curl_easy_setopt(handle, CURLOPT_BUFFERSIZE, 102400L);
curl_easy_setopt(handle, CURLOPT_URL, url);
curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, post_data);
curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)post_data_size);
curl_easy_setopt(handle, CURLOPT_USERAGENT, "curl/7.55.1");
curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(handle, CURLOPT_TCP_KEEPALIVE, 1L);
}
void Dqn_Curl_SetWriteCallback(CURL *handle,
size_t (*curl_write_callback)(char *ptr, size_t size, size_t nmemb, void *userdata),
void *user_data)
{
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curl_write_callback);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, user_data);
}
#endif // DQN_CURL_IMPLEMENTATION

View File

@ -1,4 +1,4 @@
#ifndef DQN_JSMN_H
#if !defined(DQN_JSMN_H)
#define DQN_JSMN_H
// -----------------------------------------------------------------------------
// NOTE: Dqn_Jsmn
@ -16,7 +16,7 @@
//
#if !defined(DQN_H)
#error You must include "Dqn.h" before including "Dqn_Jsmn.h"
#error You must include "dqn.h" before including "dqn_jsmn.h"
#endif // DQN_H
// -----------------------------------------------------------------------------
@ -135,13 +135,147 @@ JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
#endif
#endif // JSMN_H
// NOTE: Iterator copied from: https://github.com/zserge/jsmn/pull/69
// TODO(dqn): Write our own iterator logic in a manner that is more stateful
// than the current implementation, we should not have pass information back and
// forth between iterators, i.e. see my iterator abstraction that sits on top of
// this.
#ifndef __JSMN_ITERATOR_H__
#define __JSMN_ITERATOR_H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* Error return codes for jsmn iterator
*/
enum {
/* Input parameter error */
JSMNITER_ERR_PARAMETER = -1,
/* JSMN index doesn't point at an Array/Object */
JSMNITER_ERR_TYPE = -2,
/* Group item misses string identifier */
JSMNITER_ERR_NOIDENT = -3,
/* Broken JSON */
JSMNITER_ERR_BROKEN = -4,
};
/**
* Struct with state information for jsmn iterator
* - When the no more items for iterator the parser_pos value will point to
* JSMN index for next object after current Array/Object
*/
typedef struct {
jsmntok_t *jsmn_tokens;
unsigned int jsmn_len;
unsigned int parent_pos;
unsigned int parser_pos;
unsigned int index;
} jsmn_iterator_t;
/**
* @brief Takes an JSMN Array/Object and locates index for last item in collection
* @details Iterates over JSMN Array/Object until last item is found
*
* @param jsmn_tokens JSMN tokens
* @param jsmn_len JSMN token count
* @param parser_pos Current JSMN token
*
* @return < 0 - Error has occured, corresponds to one of JSMNITER_ERR_*
* >=0 - JSMN index for last item in Array/Object
*/
int jsmn_iterator_find_last( jsmntok_t *jsmn_tokens, unsigned int jsmn_len, unsigned int parser_pos );
/**
* @brief Initialize iterator
* @details Set initial value for iterator struct
*
* @param iterator Iterator struct
* @param jsmn_tokens JSMN tokens
* @param jsmn_len JSMN token count
* @param parser_pos Current JSMN token
*
* @return < 0 - Error has occured, corresponds to one of JSMNITER_ERR_*
* >=0 - Ok
*/
int jsmn_iterator_init( jsmn_iterator_t *iterator, jsmntok_t *jsmn_tokens, unsigned int jsmn_len,
unsigned int parser_pos );
/**
* @brief Get next item in JSMN Array/Object
* @details Gets JSMN position for next identifier and value in Array/Object
*
* @param iterator Iterator struct
* @param jsmn_identifier Return pointer for identifier, NULL for Array
* @param jsmn_value Return pointer for value
* @param next_value_index Possible to indicate where next value begins, allows determine end of sub
* Array/Object withouth manually searching for it
*
* @return < 0 - Error has occured, corresponds to one of JSMNITER_ERR_*
* 0 - No more values
* > 0 - Value (and identifier) has been returned
*/
int jsmn_iterator_next( jsmn_iterator_t *iterator, jsmntok_t **jsmn_identifier, jsmntok_t **jsmn_value,
unsigned int next_value_index );
/**
* @brief Return current parser position
* @details For Array the parser point to current value index
* For Object the parser points to the identifier
*
* @param iterator [description]
* @return [description]
*/
#define jsmn_iterator_position(_iterator_) ((_iterator_)->parser_pos)
#ifdef __cplusplus
}
#endif
#endif /*__JSMN_ITERATOR_H__*/
// -----------------------------------------------------------------------------
// Header File
// -----------------------------------------------------------------------------
#define DQN_JSMN_X_MACRO \
DQN_JSMN_X_ENTRY(Object) \
DQN_JSMN_X_ENTRY(Array) \
DQN_JSMN_X_ENTRY(String) \
DQN_JSMN_X_ENTRY(Number) \
DQN_JSMN_X_ENTRY(Bool)
enum struct Dqn_JsmnTokenIs
{
#define DQN_JSMN_X_ENTRY(enum_val) enum_val,
DQN_JSMN_X_MACRO
#undef DQN_JSMN_X_ENTRY
};
inline Dqn_String const Dqn_JsmnTokenIsEnumString(Dqn_JsmnTokenIs token)
{
switch (token)
{
#define DQN_JSMN_X_ENTRY(enum_val) case Dqn_JsmnTokenIs::enum_val: return DQN_STRING(#enum_val);
DQN_JSMN_X_MACRO
#undef DQN_JSMN_X_ENTRY
}
return DQN_STRING("DQN_JSMN_UNHANDLED_ENUM");
};
struct Dqn_JsmnError
{
jsmntok_t token;
char const *cpp_func;
Dqn_String json;
Dqn_JsmnTokenIs expected;
char const *cpp_file; // The file of the .cpp/h source code that triggered the error
int cpp_line; // The line of the .cpp/h source code that triggered the error
};
@ -152,28 +286,94 @@ struct Dqn_JsmnErrorHandle
Dqn_List<Dqn_JsmnError> list;
};
void Dqn_JsmnErrorHandle_AddError (Dqn_JsmnErrorHandle *err_handle, char const *func, char const *file, int line);
Dqn_String Dqn_JsmnToken_String (Dqn_String json, jsmntok_t token);
Dqn_b32 Dqn_JsmnToken_Bool (Dqn_String json, jsmntok_t token);
Dqn_u64 Dqn_JsmnToken_U64 (Dqn_String json, jsmntok_t token);
jsmntok_t *Dqn_JsmnToken_AdvanceItPastObject(jsmntok_t *start_it, Dqn_JsmnErrorHandle *err_handle, Dqn_String json);
jsmntok_t *Dqn_JsmnToken_AdvanceItPastArray (jsmntok_t *start_it, Dqn_JsmnErrorHandle *err_handle, Dqn_String json);
struct Dqn_Jsmn
{
jsmn_parser parser;
Dqn_String json;
int tokens_size;
jsmntok_t *tokens;
};
#define Dqn_JsmnToken_ExpectBool(json, token, err_handle) Dqn_JsmnToken__ExpectBool(json, token, err_handle, __FILE__, __LINE__)
#define Dqn_JsmnToken_ExpectNumber(json, token, err_handle) Dqn_JsmnToken__ExpectNumber(json, token, err_handle, __FILE__, __LINE__)
#define Dqn_JsmnToken_ExpectString(token, err_handle) Dqn_JsmnToken__ExpectString(token, err_handle, __FILE__, __LINE__)
#define Dqn_JsmnToken_ExpectArray(token, err_handle) Dqn_JsmnToken__ExpectArray(token, err_handle, __FILE__, __LINE__)
#define Dqn_JsmnToken_ExpectObject(token, err_handle) Dqn_JsmnToken__ExpectObject(token, err_handle, __FILE__, __LINE__)
struct Dqn_JsmnIterator
{
Dqn_b32 init;
jsmn_iterator_t jsmn_it;
Dqn_String json;
jsmntok_t *key;
jsmntok_t *value;
// When obj/array iteration is finished, we set the token_index_hint to the
// parent iterator so that it knows where to continue off from and to skip
// over the object/array we just iterated.
int token_index_hint;
};
Dqn_Jsmn Dqn_Jsmn_InitWithJSON (Dqn_String json, Dqn_ArenaAllocator *arena);
Dqn_Jsmn Dqn_Jsmn_InitWithJSONFile (Dqn_String file, Dqn_ArenaAllocator *arena);
// return: If the token is an array, return the size of the array otherwise -1.
int Dqn_Jsmn_TokenArraySize(jsmntok_t token);
Dqn_String Dqn_Jsmn_TokenString(jsmntok_t token, Dqn_String json);
Dqn_b32 Dqn_Jsmn_TokenBool(jsmntok_t token, Dqn_String json);
Dqn_u64 Dqn_Jsmn_TokenU64(jsmntok_t token, Dqn_String json);
// Iterator abstraction over jsmn_iterator_t, example on how to use this is
// shown below. The goal here is to minimise the amount of state the user has to
// manage.
#if 0
Dqn_ArenaAllocator arena = {};
Dqn_String json = DQN_STRING(R"({
"test": {
"test2": 0
}
})");
Dqn_Jsmn jsmn_state = Dqn_Jsmn_InitWithJSON(json, &arena);
for (Dqn_JsmnIterator it = {}; Dqn_JsmnIterator_Next(&it, &jsmn_state, nullptr /*prev_it*/); )
{
Dqn_String key_str = Dqn_Jsmn_TokenString(*it.key, jsmn_state.json);
if (Dqn_JsmnIterator_Key(&it) == DQN_STRING("test"))
{
if (!Dqn_JsmnIterator_ExpectValue(&it, Dqn_JsmnTokenIs::Object, nullptr))
continue;
for (Dqn_JsmnIterator obj_it = {}; Dqn_JsmnIterator_Next(&obj_it, &jsmn_state, &it); )
{
if (Dqn_JsmnIterator_Key(&it) == DQN_STRING("test2"))
{
if (!Dqn_JsmnIterator_ExpectValue(&it, Dqn_JsmnTokenIs::Number, nullptr))
continue;
Dqn_u64 test_2_value = Dqn_JsmnIterator_U64(&obj_it);
}
}
}
}
#endif
Dqn_b32 Dqn_JsmnIterator_Next(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnIterator *prev_it);
Dqn_String Dqn_JsmnIterator_Key (Dqn_JsmnIterator *it);
Dqn_JsmnIterator Dqn_JsmnIterator_FindKey(Dqn_Jsmn *jsmn_state, Dqn_String key, Dqn_JsmnIterator *parent_it);
#define Dqn_JsmnIterator_ExpectValue(it, expected, err_handle) Dqn_JsmnIterator__ExpectValue(it, expected, err_handle, __FILE__, __LINE__)
#define Dqn_JsmnIterator_ExpectKey(it, expected, err_handle) Dqn_JsmnIterator__ExpectKey(it, expected, err_handle, __FILE__, __LINE__)
// Convert the value part of the key-value JSON pair the iterator is currently
// pointing to, to a string/bool/u64. If the iterator's value does not point to
// the type requested, a zero initialised value is returned.
Dqn_String Dqn_JsmnIterator_String(Dqn_JsmnIterator const *it);
Dqn_b32 Dqn_JsmnIterator_Bool (Dqn_JsmnIterator const *it);
Dqn_u64 Dqn_JsmnIterator_U64 (Dqn_JsmnIterator const *it);
#define DQN_JSMN_ERROR_HANDLE_DUMP(handle) \
for (Dqn_ListChunk<Dqn_JsmnError> *chunk = handle.list.head; chunk; chunk = chunk->next) \
{ \
for (auto *error = chunk->data; error != (chunk->data + chunk->count); error++) \
{ \
DQN_LOG_E("Json parsing error at %s from %s:%d", \
error->cpp_func, \
DQN_LOG_E("Json parsing error in %s:%d, expected token type: %.*s, token was: %.*s", \
Dqn_Str_FileNameFromPath(error->cpp_file), \
error->cpp_line); \
error->cpp_line, \
DQN_STRING_FMT(Dqn_JsmnTokenIsEnumString(error->expected)), \
DQN_STRING_FMT(Dqn_Jsmn_TokenString(error->token, error->json))); \
} \
}
@ -183,7 +383,58 @@ jsmntok_t *Dqn_JsmnToken_AdvanceItPastArray (jsmntok_t *start_it, Dqn_JsmnError
// -----------------------------------------------------------------------------
// Implementation
// -----------------------------------------------------------------------------
void Dqn_JsmnErrorHandle_AddError(Dqn_JsmnErrorHandle *err_handle, char const *func, char const *file, int line)
Dqn_Jsmn Dqn_Jsmn_InitWithJSON(Dqn_String json, Dqn_ArenaAllocator *arena)
{
Dqn_Jsmn result = {};
result.json = json;
jsmn_init(&result.parser);
result.tokens_size = jsmn_parse(&result.parser, result.json.str, result.json.size, nullptr, 0);
result.tokens = Dqn_ArenaAllocator_NewArray(arena, jsmntok_t, result.tokens_size, Dqn_ZeroMem::No);
jsmn_init(&result.parser);
result.tokens_size = jsmn_parse(&result.parser, result.json.str, result.json.size, result.tokens, result.tokens_size);
return result;
}
Dqn_Jsmn Dqn_Jsmn_InitWithJSONFile(Dqn_String file, Dqn_ArenaAllocator *arena)
{
Dqn_String json = Dqn_File_ArenaReadFileToString(file.str, arena);
Dqn_Jsmn result = Dqn_Jsmn_InitWithJSON(json, arena);
return result;
}
int Dqn_Jsmn_TokenArraySize(jsmntok_t token)
{
int result = token.type == JSMN_ARRAY ? token.size : -1;
return result;
}
Dqn_String Dqn_Jsmn_TokenString(jsmntok_t token, Dqn_String json)
{
Dqn_String result = Dqn_String_Init(json.str + token.start, token.end - token.start);
return result;
}
Dqn_b32 Dqn_Jsmn_TokenBool(jsmntok_t token, Dqn_String json)
{
DQN_ASSERT_MSG(token.start < json.size, "%I64d < %I64u", token.start, json.size);
char ch = json.str[token.start];
Dqn_b32 result = ch == 't';
if (!result) { DQN_ASSERT(ch == 'f'); }
return result;
}
Dqn_u64 Dqn_Jsmn_TokenU64(jsmntok_t token, Dqn_String json)
{
DQN_ASSERT_MSG(token.start < json.size, "%I64d < %I64u", token.start, json.size);
Dqn_String string = Dqn_String_Init(json.str + token.start, token.end - token.start);
Dqn_u64 result = Dqn_String_ToU64(string);
return result;
}
void Dqn_JsmnErrorHandle__AddError(Dqn_JsmnErrorHandle *err_handle, jsmntok_t token, Dqn_String json, Dqn_JsmnTokenIs expected, char const *file, int line)
{
if (!err_handle)
return;
@ -194,120 +445,123 @@ void Dqn_JsmnErrorHandle_AddError(Dqn_JsmnErrorHandle *err_handle, char const *f
Dqn_JsmnError *error = Dqn_List_Make(&err_handle->list, 1);
if (error)
{
error->cpp_func = func;
error->expected = expected;
error->json = json;
error->cpp_file = file;
error->cpp_line = line;
}
}
Dqn_b32 Dqn_JsmnToken__ExpectBool(Dqn_String json, jsmntok_t token, Dqn_JsmnErrorHandle *err_handle, char const *file, int line)
Dqn_b32 Dqn_JsmnIterator_Next(Dqn_JsmnIterator *it, Dqn_Jsmn *jsmn_state, Dqn_JsmnIterator *prev_it)
{
DQN_ASSERT_MSG(token.start < json.size, "%I64d < %I64u", token.start, json.size);
char ch = json.str[token.start];
Dqn_b32 result = token.type == JSMN_PRIMITIVE && (ch == 't' || ch == 'f');
if (!result) Dqn_JsmnErrorHandle_AddError(err_handle, __func__, file, line);
if (!it->init)
{
it->init = true;
it->json = jsmn_state->json;
jsmn_iterator_init(&it->jsmn_it, jsmn_state->tokens, jsmn_state->tokens_size, prev_it ? jsmn_iterator_position(&prev_it->jsmn_it) : 0);
}
Dqn_b32 result = false;
if (!Dqn_String_IsValid(it->json) || it->json.size <= 0) {
return result;
}
Dqn_b32 Dqn_JsmnToken__ExpectNumber(Dqn_String json, jsmntok_t token, Dqn_JsmnErrorHandle *err_handle, char const *file, int line)
result = jsmn_iterator_next(&it->jsmn_it, &it->key, &it->value, it->token_index_hint) > 0;
if (!result)
{
DQN_ASSERT_MSG(token.start < json.size, "%I64d < %I64u", token.start, json.size);
char ch = json.str[token.start];
Dqn_b32 result = token.type == JSMN_PRIMITIVE && (ch == '-' || Dqn_Char_IsDigit(ch));
if (!result) Dqn_JsmnErrorHandle_AddError(err_handle, __func__, file, line);
// NOTE: Iterator has finished object/array, previous iterator will
// continue off where this iterator left off.
if (prev_it)
prev_it->token_index_hint = jsmn_iterator_position(&it->jsmn_it);
}
return result;
}
Dqn_b32 Dqn_JsmnToken__ExpectString(jsmntok_t token, Dqn_JsmnErrorHandle *err_handle, char const *file, int line)
Dqn_String Dqn_JsmnIterator_Key(Dqn_JsmnIterator *it)
{
Dqn_b32 result = token.type == JSMN_STRING;
if (!result) Dqn_JsmnErrorHandle_AddError(err_handle, __func__, file, line);
Dqn_String result = {};
if (it && it->key)
result = Dqn_String_Init(it->json.str + it->key->start, it->key->end - it->key->start);
return result;
}
Dqn_b32 Dqn_JsmnToken__ExpectArray(jsmntok_t token, Dqn_JsmnErrorHandle *err_handle, char const *file, int line)
Dqn_JsmnIterator Dqn_JsmnIterator_FindKey(Dqn_Jsmn *jsmn_state, Dqn_String key, Dqn_JsmnIterator *parent_it)
{
Dqn_b32 result = token.type == JSMN_ARRAY;
if (!result) Dqn_JsmnErrorHandle_AddError(err_handle, __func__, file, line);
return result;
}
Dqn_b32 Dqn_JsmnToken__ExpectObject(jsmntok_t token, Dqn_JsmnErrorHandle *err_handle, char const *file, int line)
Dqn_JsmnIterator result = {};
for (Dqn_JsmnIterator it = {}; Dqn_JsmnIterator_Next(&it, jsmn_state, parent_it); )
{
Dqn_b32 result = token.type == JSMN_OBJECT;
if (!result) Dqn_JsmnErrorHandle_AddError(err_handle, __func__, file, line);
return result;
}
Dqn_String Dqn_JsmnToken_String(Dqn_String json, jsmntok_t token)
Dqn_String it_key = Dqn_Jsmn_TokenString(*it.key, jsmn_state->json);
if (it_key == key)
{
Dqn_String result = Dqn_String_Init(json.str + token.start, token.end - token.start);
return result;
}
Dqn_b32 Dqn_JsmnToken_Bool(Dqn_String json, jsmntok_t token)
{
DQN_ASSERT_MSG(token.start < json.size, "%I64d < %I64u", token.start, json.size);
char ch = json.str[token.start];
Dqn_b32 result = ch == 't';
if (!result) { DQN_ASSERT(ch == 'f'); }
return result;
}
Dqn_u64 Dqn_JsmnToken_U64(Dqn_String json, jsmntok_t token)
{
DQN_ASSERT_MSG(token.start < json.size, "%I64d < %I64u", token.start, json.size);
Dqn_String string = Dqn_JsmnToken_String(json, token);
Dqn_u64 result = Dqn_String_ToU64(string);
return result;
}
jsmntok_t *Dqn_JsmnToken_AdvanceItPastObject(jsmntok_t *start_it, Dqn_JsmnErrorHandle *err_handle, Dqn_String json)
{
jsmntok_t *result = start_it;
if (!Dqn_JsmnToken_ExpectObject(*result, err_handle)) return result;
jsmntok_t *object = result++;
for (int index = 0; index < object->size; index++)
{
jsmntok_t *key = result++;
jsmntok_t *value = result++;
Dqn_String key_str = Dqn_JsmnToken_String(json, *key);
Dqn_String value_str = Dqn_JsmnToken_String(json, *value); (void)value_str;
if (value->type == JSMN_OBJECT)
{
result = Dqn_JsmnToken_AdvanceItPastObject(value, err_handle, json);
}
else if (value->type == JSMN_ARRAY)
{
result = Dqn_JsmnToken_AdvanceItPastArray(value, err_handle, json);
result = it;
break;
}
}
return result;
}
jsmntok_t *Dqn_JsmnToken_AdvanceItPastArray(jsmntok_t *start_it, Dqn_JsmnErrorHandle *err_handle, Dqn_String json)
static Dqn_b32 Dqn_JsmnIterator__Expect(Dqn_JsmnIterator *it, Dqn_JsmnTokenIs expected, jsmntok_t token, Dqn_JsmnErrorHandle *err_handle, char const *file, unsigned int line)
{
jsmntok_t *result = start_it;
if (!Dqn_JsmnToken_ExpectArray(*result, err_handle)) return result;
jsmntok_t *array = result++;
Dqn_b32 result = false;
switch (expected)
{
case Dqn_JsmnTokenIs::Object: result = token.type == JSMN_OBJECT; break;
case Dqn_JsmnTokenIs::Array: result = token.type == JSMN_ARRAY; break;
case Dqn_JsmnTokenIs::String: result = token.type == JSMN_STRING; break;
for (int index = 0; index < array->size; index++)
case Dqn_JsmnTokenIs::Number:
{
jsmntok_t *value = result++;
Dqn_String value_str = Dqn_JsmnToken_String(json, *value); (void)value_str;
if (value->type == JSMN_OBJECT)
{
Dqn_JsmnToken_AdvanceItPastObject(start_it, err_handle, json);
DQN_ASSERT_MSG(token.start < it->json.size, "%I64d < %I64u", token.start, it->json.size);
char ch = it->json.str[token.start];
result = token.type == JSMN_PRIMITIVE && (ch == '-' || Dqn_Char_IsDigit(ch));
}
else if (value->type == JSMN_ARRAY)
break;
case Dqn_JsmnTokenIs::Bool:
{
Dqn_JsmnToken_AdvanceItPastArray(start_it, err_handle, json);
DQN_ASSERT_MSG(token.start < it->json.size, "%I64d < %I64u", token.start, it->json.size);
char ch = it->json.str[token.start];
result = token.type == JSMN_PRIMITIVE && (ch == 't' || ch == 'f');
}
break;
}
if (!result)
Dqn_JsmnErrorHandle__AddError(err_handle, token, it->json, expected, file, line);
return result;
}
Dqn_b32 Dqn_JsmnIterator__ExpectValue(Dqn_JsmnIterator *it, Dqn_JsmnTokenIs expected, Dqn_JsmnErrorHandle *err_handle, char const *file, unsigned int line)
{
Dqn_b32 result = it->value && Dqn_JsmnIterator__Expect(it, expected, *it->value, err_handle, file, line);
return result;
}
Dqn_b32 Dqn_JsmnIterator__ExpectKey(Dqn_JsmnIterator *it, Dqn_JsmnTokenIs expected, Dqn_JsmnErrorHandle *err_handle, char const *file, unsigned int line)
{
Dqn_b32 result = it->key && Dqn_JsmnIterator__Expect(it, expected, *it->key, err_handle, file, line);
return result;
}
Dqn_String Dqn_JsmnIterator_String(Dqn_JsmnIterator const *it)
{
Dqn_String result = {};
if (it->value && it->json.str) result = Dqn_String_Init(it->json.str + it->value->start, it->value->end - it->value->start);
return result;
}
Dqn_b32 Dqn_JsmnIterator_Bool(Dqn_JsmnIterator const *it)
{
Dqn_b32 result = it->value && Dqn_Jsmn_TokenBool(*it->value, it->json);
return result;
}
Dqn_u64 Dqn_JsmnIterator_U64(Dqn_JsmnIterator const *it)
{
Dqn_u64 result = it->value && Dqn_Jsmn_TokenU64(*it->value, it->json);
return result;
}
@ -681,4 +935,236 @@ JSMN_API void jsmn_init(jsmn_parser *parser) {
#ifdef __cplusplus
}
#endif
/**
* @brief Takes an JSMN Array/Object and locates index for last item in collection
* @details Iterates over JSMN Array/Object until last item is found
*
* @param jsmn_tokens JSMN tokens
* @param jsmn_len JSMN token count
* @param parser_pos Current JSMN token
*
* @return < 0 - Error has occured, corresponds to one of JSMNITER_ERR_*
* >=0 - JSMN index for last item in Array/Object
*/
int jsmn_iterator_find_last( jsmntok_t *jsmn_tokens, unsigned int jsmn_len, unsigned int parser_pos ) {
int child_count;
unsigned int child_index;
int parent_end;
/* No tokens */
if (!jsmn_tokens)
return JSMNITER_ERR_PARAMETER;
/* pos outside tokens */
if (parser_pos >= jsmn_len)
return JSMNITER_ERR_PARAMETER;
/* Not an Array/Object */
if (jsmn_tokens[parser_pos].type != JSMN_ARRAY &&
jsmn_tokens[parser_pos].type != JSMN_OBJECT)
return JSMNITER_ERR_TYPE;
/* End position for Array/Object */
parent_end = jsmn_tokens[parser_pos].end;
/* First child item */
child_index = parser_pos + 1;
/* Count number of children we need to iterate */
child_count = jsmn_tokens[parser_pos].size * (jsmn_tokens[parser_pos].type == JSMN_OBJECT ? 2 : 1);
/* Loop until end of current Array/Object */
while(child_index < jsmn_len && jsmn_tokens[child_index].start <= parent_end && child_count >= 0) {
/* Add item count in sub Arrays/Objects that we need to skip */
if (jsmn_tokens[child_index].type == JSMN_ARRAY)
child_count += jsmn_tokens[child_index].size;
else if (jsmn_tokens[child_index].type == JSMN_OBJECT)
child_count += jsmn_tokens[child_index].size * 2;
child_count--;
child_index++;
}
/* Validate that we have visited correct number of children */
if (child_count != 0)
return JSMNITER_ERR_BROKEN;
/* Points to last index inside Array/Object */
return (int)child_index - 1;
}
/**
* @brief Initialize iterator
* @details Set initial value for iterator struct
*
* @param iterator Iterator struct
* @param jsmn_tokens JSMN tokens
* @param jsmn_len JSMN token count
* @param parser_pos Current JSMN token
*
* @return < 0 - Error has occured, corresponds to one of JSMNITER_ERR_*
* >=0 - Ok
*/
int jsmn_iterator_init(jsmn_iterator_t *iterator, jsmntok_t *jsmn_tokens, unsigned int jsmn_len, unsigned int parser_pos) {
/* No iterator */
if (!iterator)
return JSMNITER_ERR_PARAMETER;
/* No tokens */
if (!jsmn_tokens)
return JSMNITER_ERR_PARAMETER;
/* pos outside tokens */
if (parser_pos >= jsmn_len)
return JSMNITER_ERR_PARAMETER;
/* Not an Array/Object */
if (jsmn_tokens[parser_pos].type != JSMN_ARRAY &&
jsmn_tokens[parser_pos].type != JSMN_OBJECT)
return JSMNITER_ERR_TYPE;
/* Save jsmn pointer */
iterator->jsmn_tokens = jsmn_tokens;
iterator->jsmn_len = jsmn_len;
iterator->parent_pos = parser_pos;
iterator->parser_pos = parser_pos;
iterator->index = 0;
return 0;
}
/**
* @brief Get next item in JSMN Array/Object
* @details Gets JSMN position for next identifier and value in Array/Object
*
* @param iterator Iterator struct
* @param jsmn_identifier Return pointer for identifier, NULL for Array
* @param jsmn_value Return pointer for value
* @param next_value_index Possible to indicate where next value begins, allows determine end of sub
* Array/Object withouth manually searching for it
*
* @return < 0 - Error has occured, corresponds to one of JSMNITER_ERR_*
* 0 - No more values
* > 0 - Value (and identifier) has been returned
*/
int jsmn_iterator_next(jsmn_iterator_t *iterator, jsmntok_t **jsmn_identifier, jsmntok_t **jsmn_value,
unsigned int next_value_index) {
unsigned int is_object;
jsmntok_t *parent_item;
jsmntok_t *current_item;
jsmntok_t *jsmn_tokens;
unsigned int jsmn_len;
/* No iterator */
if (!iterator)
return JSMNITER_ERR_PARAMETER;
/* No value return pointer */
if (!jsmn_value)
return JSMNITER_ERR_PARAMETER;
/* Parser position is outside JSMN tokens array */
if (iterator->parser_pos > iterator->jsmn_len)
return JSMNITER_ERR_BROKEN;
jsmn_tokens = iterator->jsmn_tokens;
jsmn_len = iterator->jsmn_len;
parent_item = &jsmn_tokens[iterator->parent_pos];
/* parser_position is at the end of JSMN token array or points outside parent Array/Object */
if (jsmn_iterator_position(iterator) == iterator->jsmn_len || jsmn_tokens[jsmn_iterator_position(iterator)].start > parent_item->end) {
if (iterator->index != (unsigned int)parent_item->size)
return JSMNITER_ERR_BROKEN;
return 0;
}
current_item = &jsmn_tokens[jsmn_iterator_position(iterator)];
/* Are we in an Object */
is_object = (parent_item->type == JSMN_OBJECT ? 1 : 0);
/* Is it item we only need jump one to the next index */
if (jsmn_iterator_position(iterator) == iterator->parent_pos) {
next_value_index = jsmn_iterator_position(iterator) + 1;
}
/* For items that isn't Array/Object we only need to take the next value */
else if (current_item->type != JSMN_ARRAY &&
current_item->type != JSMN_OBJECT) {
next_value_index = jsmn_iterator_position(iterator) + 1;
}
/* Check if next_value_index is correct, else we need to calculate it ourself */
else if (next_value_index == 0 ||
next_value_index > jsmn_len ||
next_value_index <= jsmn_iterator_position(iterator) ||
current_item->end < jsmn_tokens[next_value_index - 1].end ||
(next_value_index < jsmn_len && current_item->end >= jsmn_tokens[next_value_index].start)) {
/* Find index for last item in the Array/Object manually */
int return_pos = jsmn_iterator_find_last(jsmn_tokens, jsmn_len, jsmn_iterator_position(iterator));
/* Error, bail out */
if (return_pos < 0)
return return_pos;
/* Update position to the next item token */
next_value_index = (unsigned int)(return_pos) + 1;
/* Outside valid array */
if (next_value_index > jsmn_len)
return JSMNITER_ERR_BROKEN;
/* Earlier than current value (not valid jsmn tree) */
if (next_value_index <= jsmn_iterator_position(iterator))
return JSMNITER_ERR_BROKEN;
/* Previus item is NOT inside current Array/Object */
if (jsmn_tokens[next_value_index - 1].end > current_item->end)
return JSMNITER_ERR_BROKEN;
/* Not last item and next item is NOT outside Array/Object */
if (next_value_index < jsmn_len && current_item->end > jsmn_tokens[next_value_index].start)
return JSMNITER_ERR_BROKEN;
}
/* Parser position is outside JSMN tokens array */
if (next_value_index > iterator->jsmn_len)
return JSMNITER_ERR_BROKEN;
/* parser_position is at the end of JSMN token array or points outside parent Array/Object */
if (next_value_index == (unsigned int)iterator->jsmn_len || jsmn_tokens[next_value_index].start > parent_item->end) {
if (iterator->index != (unsigned int)parent_item->size)
return JSMNITER_ERR_BROKEN;
/* Update parser position before exit */
iterator->parser_pos = next_value_index;
return 0;
}
/* Get current value/identifier */
current_item = &jsmn_tokens[next_value_index];
/* We have identifier, read it */
if (is_object) {
/* Must be string */
if (current_item->type != JSMN_STRING)
return JSMNITER_ERR_NOIDENT;
/* Ensure that we have next token */
if (next_value_index + 1 >= jsmn_len)
return JSMNITER_ERR_BROKEN;
/* Missing identifier pointer */
if (!jsmn_identifier)
return JSMNITER_ERR_PARAMETER;
/* Set identifier and update current pointer to value item */
*jsmn_identifier = current_item;
next_value_index++;
current_item = &jsmn_tokens[next_value_index];
}
/* Clear identifier if is set */
else if (jsmn_identifier) {
*jsmn_identifier = NULL;
}
/* Set value */
*jsmn_value = current_item;
/* Update parser position */
iterator->parser_pos = next_value_index;
/* Increase the index and return it as the positive value */
iterator->index++;
return (int)iterator->index;
}
#endif // DQN_JSMN_IMPLEMENTATION

View File

@ -18,9 +18,14 @@
#define DQN_WITH_MAP // Dqn_Map
#define DQN_WITH_MATH // Dqn_V2/3/4/Mat4 and friends ...
#define DQN_WITH_THREAD_CONTEXT // Dqn_ThreadContext and friends ...
#include "Dqn.h"
#include "dqn.h"
#define DQN_KECCAK_IMPLEMENTATION
#include "dqn_keccak.h"
#endif
#include "dqn_tests_helpers.cpp"
struct Dqn_TestState
{
int indent_level;
@ -59,7 +64,7 @@ static int g_dqn_test_total_tests;
#define DQN_TEST_ANSI_COLOR_RESET "\x1b[0m"
#endif
#define DQN_TEST_START_SCOPE(testing_state, test_name) \
#define DQN_TEST_START_SCOPE(testing_state, test_name, ...) \
DQN_DEFER \
{ \
if (testing_state.test.fail_expr.size == 0) testing_state.num_tests_ok_in_group++; \
@ -67,7 +72,7 @@ static int g_dqn_test_total_tests;
Dqn_ArenaAllocator_ResetUsage(&testing_state.arena, Dqn_ZeroMem::No); \
testing_state.test = {}; \
}; \
testing_state.test.name = DQN_STRING(test_name); \
testing_state.test.name = Dqn_String_Fmt(&testing_state.arena, test_name, ##__VA_ARGS__); \
testing_state.test.scope_started = true; \
testing_state.num_tests_in_group++
@ -1394,131 +1399,6 @@ void Dqn_Test_String()
}
}
void Dqn_Test_StringBuilder()
{
Dqn_TestingState testing_state = {};
DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "Dqn_StringBuilder");
Dqn_ArenaAllocator arena = {};
// NOTE: Dqn_StringBuilder_Append
{
{
DQN_TEST_START_SCOPE(testing_state, "Append variable size strings and build using heap arena");
Dqn_StringBuilder<> builder = {};
Dqn_StringBuilder_Append(&builder, "Abc", 1);
Dqn_StringBuilder_Append(&builder, "cd");
Dqn_isize size = 0;
char *result = Dqn_StringBuilder_Build(&builder, &arena, &size);
DQN_DEFER { Dqn_ArenaAllocator_Free(&arena); };
char const EXPECT_STR[] = "Acd";
DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size);
DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result);
}
{
DQN_TEST_START_SCOPE(testing_state, "Append empty string and build using heap arena");
Dqn_StringBuilder<> builder = {};
Dqn_StringBuilder_Append(&builder, "");
Dqn_StringBuilder_Append(&builder, "");
Dqn_isize size = 0;
char *result = Dqn_StringBuilder_Build(&builder, &arena, &size);
DQN_DEFER { Dqn_ArenaAllocator_Free(&arena); };
char const EXPECT_STR[] = "";
DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size);
DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result);
}
{
DQN_TEST_START_SCOPE(testing_state, "Append empty string onto string and build using heap arena");
Dqn_StringBuilder<> builder = {};
Dqn_StringBuilder_Append(&builder, "Acd");
Dqn_StringBuilder_Append(&builder, "");
Dqn_isize size = 0;
char *result = Dqn_StringBuilder_Build(&builder, &arena, &size);
DQN_DEFER { Dqn_ArenaAllocator_Free(&arena); };
char const EXPECT_STR[] = "Acd";
DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size);
DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result);
}
{
DQN_TEST_START_SCOPE(testing_state, "Append nullptr and build using heap arena");
Dqn_StringBuilder<> builder = {};
Dqn_StringBuilder_Append(&builder, nullptr, 5);
Dqn_isize size = 0;
char *result = Dqn_StringBuilder_Build(&builder, &arena, &size);
DQN_DEFER { Dqn_ArenaAllocator_Free(&arena); };
char const EXPECT_STR[] = "";
DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size);
DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result);
}
{
DQN_TEST_START_SCOPE(testing_state, "Append and require new linked buffer and build using heap arena");
Dqn_StringBuilder<2> builder = {};
Dqn_StringBuilder_InitWithArena(&builder, &arena);
Dqn_StringBuilder_Append(&builder, "A");
Dqn_StringBuilder_Append(&builder, "z"); // Should force a new memory block
Dqn_StringBuilder_Append(&builder, "tec");
Dqn_isize size = 0;
char *result = Dqn_StringBuilder_Build(&builder, &arena, &size);
DQN_DEFER { Dqn_ArenaAllocator_Free(&arena); };
char const EXPECT_STR[] = "Aztec";
DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size);
DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result);
}
}
// NOTE: Dqn_StringBuilder_AppendChar
{
DQN_TEST_START_SCOPE(testing_state, "Append char and build using heap arena");
Dqn_StringBuilder<> builder = {};
Dqn_StringBuilder_AppendChar(&builder, 'a');
Dqn_StringBuilder_AppendChar(&builder, 'b');
Dqn_isize size = 0;
char *result = Dqn_StringBuilder_Build(&builder, &arena, &size);
DQN_DEFER { Dqn_ArenaAllocator_Free(&arena); };
char const EXPECT_STR[] = "ab";
DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size);
DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result);
}
// NOTE: Dqn_StringBuilder_AppendFmt
{
{
DQN_TEST_START_SCOPE(testing_state, "Append format string and build using heap arena");
Dqn_StringBuilder<> builder = {};
Dqn_StringBuilder_AppendFmt(&builder, "Number: %d, String: %s, ", 4, "Hello Sailor");
Dqn_StringBuilder_AppendFmt(&builder, "Extra Stuff");
Dqn_isize size = 0;
char *result = Dqn_StringBuilder_Build(&builder, &arena, &size);
DQN_DEFER { Dqn_ArenaAllocator_Free(&arena); };
char const EXPECT_STR[] = "Number: 4, String: Hello Sailor, Extra Stuff";
DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size);
DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result);
}
{
DQN_TEST_START_SCOPE(testing_state, "Append nullptr format string and build using heap arena");
Dqn_StringBuilder<> builder = {};
Dqn_StringBuilder_AppendFmt(&builder, nullptr);
Dqn_isize size = 0;
char *result = Dqn_StringBuilder_Build(&builder, &arena, &size);
DQN_DEFER { Dqn_ArenaAllocator_Free(&arena); };
char const EXPECT_STR[] = "";
DQN_TEST_EXPECT_MSG(testing_state, size == Dqn_CharCountI(EXPECT_STR), "size: %zd", size);
DQN_TEST_EXPECT_MSG(testing_state, strncmp(result, EXPECT_STR, size) == 0, "result: %s", result);
}
}
}
void Dqn_Test_TicketMutex()
{
Dqn_TestingState testing_state = {};
@ -1609,6 +1489,221 @@ void Dqn_Test_Win()
#endif // DQN_OS_WIN32
}
#define DQN_TESTS_HASH_X_MACRO \
DQN_TESTS_HASH_X_ENTRY(SHA3_224, "SHA3-224") \
DQN_TESTS_HASH_X_ENTRY(SHA3_256, "SHA3-256") \
DQN_TESTS_HASH_X_ENTRY(SHA3_384, "SHA3-384") \
DQN_TESTS_HASH_X_ENTRY(SHA3_512, "SHA3-512") \
DQN_TESTS_HASH_X_ENTRY(Keccak_224, "Keccak-224") \
DQN_TESTS_HASH_X_ENTRY(Keccak_256, "Keccak-256") \
DQN_TESTS_HASH_X_ENTRY(Keccak_384, "Keccak-384") \
DQN_TESTS_HASH_X_ENTRY(Keccak_512, "Keccak-512") \
DQN_TESTS_HASH_X_ENTRY(Count, "Keccak-512")
enum Dqn_Tests__HashType
{
#define DQN_TESTS_HASH_X_ENTRY(enum_val, string) Hash_##enum_val,
DQN_TESTS_HASH_X_MACRO
#undef DQN_TESTS_HASH_X_ENTRY
};
Dqn_String const DQN_TESTS__HASH_STRING[] =
{
#define DQN_TESTS_HASH_X_ENTRY(enum_val, string) DQN_STRING(string),
DQN_TESTS_HASH_X_MACRO
#undef DQN_TESTS_HASH_X_ENTRY
};
void Dqn_Test__KeccakDispatch(Dqn_TestingState *testing_state, int hash_type, Dqn_String input)
{
#if defined(DQN_KECCAK_H)
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch();
Dqn_String input_hex = Dqn_Hex_BytesToHexStringArena(input.str, input.size, scratch.arena);
switch(hash_type)
{
case Hash_SHA3_224:
{
Dqn_KeccakBytes28 hash = Dqn_Keccak_SHA3_224_StringToBytes28(input);
Dqn_KeccakBytes28 expect;
FIPS202_SHA3_224(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data);
DQN_TEST_EXPECT_MSG((*testing_state),
Dqn_Keccak_Bytes28Equals(&hash, &expect),
"\ninput: %.*s"
"\nhash: %.*s"
"\nexpect: %.*s"
,
DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&hash).str),
DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&expect).str));
}
break;
case Hash_SHA3_256:
{
Dqn_KeccakBytes32 hash = Dqn_Keccak_SHA3_256_StringToBytes32(input);
Dqn_KeccakBytes32 expect;
FIPS202_SHA3_256(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data);
DQN_TEST_EXPECT_MSG((*testing_state),
Dqn_Keccak_Bytes32Equals(&hash, &expect),
"\ninput: %.*s"
"\nhash: %.*s"
"\nexpect: %.*s"
,
DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&hash).str),
DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&expect).str));
}
break;
case Hash_SHA3_384:
{
Dqn_KeccakBytes48 hash = Dqn_Keccak_SHA3_384_StringToBytes48(input);
Dqn_KeccakBytes48 expect;
FIPS202_SHA3_384(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data);
DQN_TEST_EXPECT_MSG((*testing_state),
Dqn_Keccak_Bytes48Equals(&hash, &expect),
"\ninput: %.*s"
"\nhash: %.*s"
"\nexpect: %.*s"
,
DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&hash).str),
DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&expect).str));
}
break;
case Hash_SHA3_512:
{
Dqn_KeccakBytes64 hash = Dqn_Keccak_SHA3_512_StringToBytes64(input);
Dqn_KeccakBytes64 expect;
FIPS202_SHA3_512(DQN_CAST(u8 *)input.str, input.size, (u8 *)expect.data);
DQN_TEST_EXPECT_MSG((*testing_state),
Dqn_Keccak_Bytes64Equals(&hash, &expect),
"\ninput: %.*s"
"\nhash: %.*s"
"\nexpect: %.*s"
,
DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&hash).str),
DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&expect).str));
}
break;
case Hash_Keccak_224:
{
Dqn_KeccakBytes28 hash = Dqn_Keccak_224_StringToBytes28(input);
Dqn_KeccakBytes28 expect;
Keccak(1152, 448, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect));
DQN_TEST_EXPECT_MSG((*testing_state),
Dqn_Keccak_Bytes28Equals(&hash, &expect),
"\ninput: %.*s"
"\nhash: %.*s"
"\nexpect: %.*s"
,
DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&hash).str),
DQN_KECCAK_STRING56_FMT(Dqn_Keccak_Bytes28ToHex(&expect).str));
}
break;
case Hash_Keccak_256:
{
Dqn_KeccakBytes32 hash = Dqn_Keccak_256_StringToBytes32(input);
Dqn_KeccakBytes32 expect;
Keccak(1088, 512, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect));
DQN_TEST_EXPECT_MSG((*testing_state),
Dqn_Keccak_Bytes32Equals(&hash, &expect),
"\ninput: %.*s"
"\nhash: %.*s"
"\nexpect: %.*s"
,
DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&hash).str),
DQN_KECCAK_STRING64_FMT(Dqn_Keccak_Bytes32ToHex(&expect).str));
}
break;
case Hash_Keccak_384:
{
Dqn_KeccakBytes48 hash = Dqn_Keccak_384_StringToBytes48(input);
Dqn_KeccakBytes48 expect;
Keccak(832, 768, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect));
DQN_TEST_EXPECT_MSG((*testing_state),
Dqn_Keccak_Bytes48Equals(&hash, &expect),
"\ninput: %.*s"
"\nhash: %.*s"
"\nexpect: %.*s"
,
DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&hash).str),
DQN_KECCAK_STRING96_FMT(Dqn_Keccak_Bytes48ToHex(&expect).str));
}
break;
case Hash_Keccak_512:
{
Dqn_KeccakBytes64 hash = Dqn_Keccak_512_StringToBytes64(input);
Dqn_KeccakBytes64 expect;
Keccak(576, 1024, DQN_CAST(u8 *)input.str, input.size, 0x01, (u8 *)expect.data, sizeof(expect));
DQN_TEST_EXPECT_MSG((*testing_state),
Dqn_Keccak_Bytes64Equals(&hash, &expect),
"\ninput: %.*s"
"\nhash: %.*s"
"\nexpect: %.*s"
,
DQN_STRING_FMT(input_hex),
DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&hash).str),
DQN_KECCAK_STRING128_FMT(Dqn_Keccak_Bytes64ToHex(&expect).str));
}
break;
}
#endif // DQN_KECCAK_H
}
void Dqn_Test_Keccak()
{
#if defined(DQN_KECCAK_H)
Dqn_TestingState testing_state = {};
Dqn_String const INPUTS[] = {
DQN_STRING("abc"),
DQN_STRING(""),
DQN_STRING("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"),
DQN_STRING("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmno"
"pqrstnopqrstu"),
};
DQN_TEST_DECLARE_GROUP_SCOPED(testing_state, "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);
for (Dqn_String input : INPUTS)
{
DQN_TEST_START_SCOPE(testing_state, "%.*s - Input: %.*s", DQN_STRING_FMT(DQN_TESTS__HASH_STRING[hash_type]), DQN_MIN(input.size, 54), input.str);
Dqn_Test__KeccakDispatch(&testing_state, hash_type, input);
}
DQN_TEST_START_SCOPE(testing_state, "%.*s - Deterministic random inputs", DQN_STRING_FMT(DQN_TESTS__HASH_STRING[hash_type]));
for (int index = 0; index < 128; index++)
{
char src[4096] = {};
Dqn_u32 src_size = pcg32_boundedrand_r(&rng, sizeof(src));
for (int src_index = 0; src_index < src_size; src_index++)
src[src_index] = pcg32_boundedrand_r(&rng, 255);
Dqn_String input = Dqn_String_Init(src, src_size);
Dqn_Test__KeccakDispatch(&testing_state, hash_type, input);
}
}
#endif // DQN_KECCAK_H
}
void Dqn_Test_RunSuite()
{
Dqn_Test_Array();
@ -1623,9 +1718,9 @@ void Dqn_Test_RunSuite()
Dqn_Test_Rect();
Dqn_Test_PerfCounter();
Dqn_Test_OS();
Dqn_Test_Keccak();
Dqn_Test_Str();
Dqn_Test_String();
Dqn_Test_StringBuilder();
Dqn_Test_TicketMutex();
Dqn_Test_Win();

1039
Dqn_U128.h

File diff suppressed because it is too large Load Diff

185
dqn_cpp_file.h Normal file
View File

@ -0,0 +1,185 @@
#if !defined(DQN_CPP_FILE_H)
#define DQN_CPP_FILE_H
// -----------------------------------------------------------------------------
// NOTE: Overview
// -----------------------------------------------------------------------------
// Utility functions for creating C++ files at run-time.
//
// -----------------------------------------------------------------------------
// NOTE: Macros
// -----------------------------------------------------------------------------
// #define DQN_CPP_FILE_IMPLEMENTATION
// Define this in one and only one C++ file to enable the implementation
// code of the header file.
//
// #define DQN_CPPF_ASSERT(expr)
// Define this macro to override the default assert used.
#include <stdio.h>
#include <stdarg.h>
// -----------------------------------------------------------------------------
// NOTE: Dqn_CppFile: Helper functions to generate formatted CPP files
// -----------------------------------------------------------------------------
#if !defined(DQN_CPPF_ASSERT)
#define DQN_CPPF_ASSERT(expr) do { if (!(expr)) { *((volatile int *)0) = 0; } } while (0)
#endif
struct Dqn_CppFile
{
FILE *file; // The file to output to.
int indent; // The current indent level
int space_per_indent; // The number of spaces applied per indent. If zero- the functions give a default value.
bool append_extra_new_line; // If true, when code blocks are terminated, an additional new line will be appended for whitespacing.
};
// return: The number of spaces per indent at this point of invocation. If
// spaces-per-indent in the CppFile is set to 0 the indent defaults to 4 spaces.
int Dqn_CppFile_SpacePerIndent(Dqn_CppFile const *cpp);
// Create a line in the C++ file. This is a piece-meal API where you can
// flexibly construct a line by calling {LineBegin, LineAdd, LineEnd}. Calling
// LineEnd terminates the line with a trailing new-line. Calling LineAdd is
// optional if the line can be constructed with just a LineAdd and LineEnd.
void Dqn_CppFile_LineBeginV (Dqn_CppFile *cpp, char const *fmt, va_list args);
void Dqn_CppFile_LineBegin (Dqn_CppFile *cpp, char const *fmt, ...);
void Dqn_CppFile_LineAdd (Dqn_CppFile *cpp, char const *fmt, ...);
void Dqn_CppFile_LineEnd (Dqn_CppFile *cpp, char const *fmt, ...);
// Create a line in the C++ file and terminate it with a new-line.
void Dqn_CppFile_LineV (Dqn_CppFile *cpp, char const *fmt, va_list args);
void Dqn_CppFile_Line (Dqn_CppFile *cpp, char const *fmt, ...);
void Dqn_CppFile_NewLine (Dqn_CppFile *cpp);
void Dqn_CppFile_Indent (Dqn_CppFile *cpp);
void Dqn_CppFile_Unindent (Dqn_CppFile *cpp);
// Begin a C++ code block which is any block that utilises a opening and
// closing brace.
// fmt: (Optional) The format string to print at the beginning of the block.
// When the fmt string is given, it will place a new-line at the end of the fmt
// string. When fmt is nullptr, no new line will be appended.
/*
Dqn_CppFile_Line(&cpp, "void MyFunction(int x, int y)");
Dqn_CppFile_BeginBlock(&cpp, nullptr);
Dqn_CppFile_Line(&cpp, "int result = x + y;");
Dqn_CppFile_Line(&cpp, "return result;");
Dqn_CppFile_EndFuncBlock(&cpp);
// Generates
//
// void MyFunction(int x, int y)
// {
// int result = x + y;
// return result;
// }
//
*/
void Dqn_CppFile_BeginBlock (Dqn_CppFile *cpp, char const *fmt, ...);
void Dqn_CppFile_EndBlock (Dqn_CppFile *cpp, bool trailing_semicolon, bool new_line_on_next_block);
#define Dqn_CppFile_EndEnumBlock(cpp) Dqn_CppFile_EndBlock(cpp, true /*trailing_semicolon*/, true /*new_line_on_next_block*/)
#define Dqn_CppFile_EndForBlock(cpp) Dqn_CppFile_EndBlock(cpp, false /*trailing_semicolon*/, false /*new_line_on_next_block*/)
#define Dqn_CppFile_EndIfBlock(cpp) Dqn_CppFile_EndBlock(cpp, false /*trailing_semicolon*/, false /*new_line_on_next_block*/)
#define Dqn_CppFile_EndFuncBlock(cpp) Dqn_CppFile_EndBlock(cpp, false /*trailing_semicolon*/, true /*new_line_on_next_block*/)
#define Dqn_CppFile_EndStructBlock(cpp) Dqn_CppFile_EndBlock(cpp, true /*trailing_semicolon*/, true /*new_line_on_next_block*/)
#endif // DQN_CPP_FILE_H
#if defined(DQN_CPP_FILE_IMPLEMENTATION)
// -----------------------------------------------------------------------------
// NOTE: Dqn_CppFile Implementation
// -----------------------------------------------------------------------------
int Dqn_CppFile_SpacePerIndent(Dqn_CppFile const *cpp)
{
int result = cpp->space_per_indent == 0 ? 4 : cpp->space_per_indent;
return result;
}
void Dqn_CppFile_LineBeginV(Dqn_CppFile *cpp, char const *fmt, va_list args)
{
int spaces = cpp->indent * Dqn_CppFile_SpacePerIndent(cpp);
fprintf(cpp->file, "%*s", spaces, "");
vfprintf(cpp->file, fmt, args);
}
void Dqn_CppFile_LineBegin(Dqn_CppFile *cpp, char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_CppFile_LineBeginV(cpp, fmt, args);
va_end(args);
}
void Dqn_CppFile_LineAdd(Dqn_CppFile *cpp, char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(cpp->file, fmt, args);
va_end(args);
}
void Dqn_CppFile_LineEnd(Dqn_CppFile *cpp, char const *fmt, ...)
{
if (fmt)
{
va_list args;
va_start(args, fmt);
vfprintf(cpp->file, fmt, args);
va_end(args);
}
fputc('\n', cpp->file);
}
void Dqn_CppFile_LineV(Dqn_CppFile *cpp, char const *fmt, va_list args)
{
Dqn_CppFile_LineBeginV(cpp, fmt, args);
Dqn_CppFile_LineEnd(cpp, nullptr);
}
void Dqn_CppFile_Line(Dqn_CppFile *cpp, char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
Dqn_CppFile_LineBeginV(cpp, fmt, args);
Dqn_CppFile_LineEnd(cpp, nullptr);
va_end(args);
}
void Dqn_CppFile_NewLine(Dqn_CppFile *cpp)
{
fputc('\n', cpp->file);
}
void Dqn_CppFile_Indent(Dqn_CppFile *cpp)
{
cpp->indent++;
}
void Dqn_CppFile_Unindent(Dqn_CppFile *cpp)
{
cpp->indent--;
DQN_CPPF_ASSERT(cpp->indent >= 0);
}
void Dqn_CppFile_BeginBlock(Dqn_CppFile *cpp, char const *fmt, ...)
{
if (fmt)
{
va_list args;
va_start(args, fmt);
Dqn_CppFile_LineV(cpp, fmt, args);
va_end(args);
}
Dqn_CppFile_Line(cpp, "{");
Dqn_CppFile_Indent(cpp);
}
void Dqn_CppFile_EndBlock(Dqn_CppFile *cpp, bool trailing_semicolon, bool new_line_on_next_block)
{
Dqn_CppFile_Unindent(cpp);
Dqn_CppFile_Line(cpp, trailing_semicolon ? "};" : "}");
if (new_line_on_next_block) fputc('\n', cpp->file);
}
#endif // DQN_CPP_FILE_IMPLEMENTATION

965
dqn_keccak.h Normal file
View File

@ -0,0 +1,965 @@
#if !defined(DQN_KECCAK_H)
#define DQN_KECCAK_H
// -----------------------------------------------------------------------------
// NOTE: Overview
// -----------------------------------------------------------------------------
// 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
// -----------------------------------------------------------------------------
// 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.
//
// -----------------------------------------------------------------------------
// NOTE: Macros
// -----------------------------------------------------------------------------
// #define DQN_KECCAK_IMPLEMENTATION
// Define this in one and only one C++ file to enable the implementation
// code of the header file.
//
// #define DQN_KECCAK_MEMCOPY
// Define this macro to override the memcpy implementation and avoid pulling
// in string.h.
//
// #define DQN_KECCAK_MEMCMP
// Define this macro to override the memcmp implementation and avoid pulling
// in string.h.
//
// #define DQN_NO_MALLOC_FUNCTIONS
// Define this macro to disable the non-essential helper functions that use
// malloc.
//
// #define DQN_KECCAK_MALLOC
// Define this macro to override the malloc implementation. This library
// provides helper functions that use malloc if DQN_NO_MALLOC_FUNCTIONS is
// not defined.
//
// #define DQN_KECCAK_ASSERT
// Define this macro to override the assert implementation.
#if !defined(DQN_KECCAK_MEMCOPY)
#include <string.h>
#define DQN_KECCAK_MEMCOPY(dest, src, count) memcpy(dest, src, count)
#endif
#if !defined(DQN_KECCAK_MEMCMP)
#include <string.h>
#define DQN_KECCAK_MEMCMP(dest, src, count) memcmp(dest, src, count)
#endif
#if !defined(DQN_NO_MALLOC_FUNCTIONS)
#if !defined(DQN_KECCAK_MALLOC)
#include <stdlib.h>
#define DQN_KECCAK_MALLOC(size) malloc(size)
#endif
#endif
#if !defined(DQN_KECCAK_ASSERT)
#if defined(NDEBUG)
#define DQN_KECCAK_ASSERT(expr)
#else
#define DQN_KECCAK_ASSERT(expr) \
do \
{ \
if (!(expr)) \
{ \
(*(volatile int *)0) = 0; \
} \
} while (0)
#endif
#endif
// Use this macro in a printf-like function,
/*
Dqn_KeccakString64 string = {};
printf("%.*s\n", DQN_KECCAK_STRING64_FMT(string));
*/
#define DQN_KECCAK_STRING56_FMT(string) 56, string
#define DQN_KECCAK_STRING64_FMT(string) 64, string
#define DQN_KECCAK_STRING96_FMT(string) 96, string
#define DQN_KECCAK_STRING128_FMT(string) 128, string
// -----------------------------------------------------------------------------
// NOTE: Data structures
// -----------------------------------------------------------------------------
struct Dqn_KeccakBytes28 { char data[28]; }; // 224 bit
struct Dqn_KeccakBytes32 { char data[32]; }; // 256 bit
struct Dqn_KeccakBytes48 { char data[48]; }; // 384 bit
struct Dqn_KeccakBytes64 { char data[64]; }; // 512 bit
struct Dqn_KeccakString56 { char str[(sizeof(Dqn_KeccakBytes28) * 2) + 1]; };
struct Dqn_KeccakString64 { char str[(sizeof(Dqn_KeccakBytes32) * 2) + 1]; };
struct Dqn_KeccakString96 { char str[(sizeof(Dqn_KeccakBytes48) * 2) + 1]; };
struct Dqn_KeccakString128 { char str[(sizeof(Dqn_KeccakBytes64) * 2) + 1]; };
// -----------------------------------------------------------------------------
// NOTE: API
// -----------------------------------------------------------------------------
// TODO(dqn): Write a streaming API
typedef unsigned char Dqn_Keccak_u8;
typedef unsigned short Dqn_Keccak_u16;
typedef unsigned int Dqn_Keccak_u32;
typedef unsigned int Dqn_Keccak_uint;
#ifdef _MSC_VER
typedef unsigned __int64 Dqn_Keccak_u64;
#else
typedef unsigned long long Dqn_Keccak_u64;
#endif
// -----------------------------------------------------------------------------
// NOTE: SHA3
// -----------------------------------------------------------------------------
// Applies the FIPS 202 SHA3 algorithm on the supplied buffer. The size of
// the returned hash is (bit-rate/8) bytes, i.e. the dest_size must be at least
// 32 bytes for SHA-256 and so forth.
// dest_size: The passed in destination buffer must be >= 28 (bytes), otherwise
// the function asserts or does no-op in release.
void Dqn_Keccak_SHA3_224(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size);
Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_ToBytes28(void *bytes, Dqn_Keccak_u64 bytes_size);
// dest_size: The passed in destination buffer must be >= 32 (bytes), otherwise
// the function asserts or does no-op in release.
void Dqn_Keccak_SHA3_256(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size);
Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_ToBytes32(void *bytes, Dqn_Keccak_u64 bytes_size);
// dest_size: The passed in destination buffer must be >= 48 (bytes), otherwise
// the function asserts or does no-op in release.
void Dqn_Keccak_SHA3_384(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size);
Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_ToBytes48(void *bytes, Dqn_Keccak_u64 bytes_size);
// dest_size: The passed in destination buffer must be >= 64 (bytes), otherwise
// the function asserts or does no-op in release.
void Dqn_Keccak_SHA3_512(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size);
Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_ToBytes64(void *bytes, Dqn_Keccak_u64 bytes_size);
#if defined(DQN_H)
// -----------------------------------------------------------------------------
// NOTE: SHA3 - Helpers for Dqn data structures
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_StringToBytes28(Dqn_String string);
Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_StringToBytes32(Dqn_String string);
Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_StringToBytes48(Dqn_String string);
Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_StringToBytes64(Dqn_String string);
Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_U8ArrayToBytes28(Dqn_Array<Dqn_Keccak_u8> array);
Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_U8ArrayToBytes32(Dqn_Array<Dqn_Keccak_u8> array);
Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_U8ArrayToBytes48(Dqn_Array<Dqn_Keccak_u8> array);
Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_U8ArrayToBytes64(Dqn_Array<Dqn_Keccak_u8> array);
#endif // DQN_H
// -----------------------------------------------------------------------------
// NOTE: Keccak
// -----------------------------------------------------------------------------
// Applies the non-finalized SHA3 algorithm (i.e. a delimited suffix of 0x1
// instead of 0x6 in SHA3). This is the version of the algorithm used by
// Ethereum and Monero as they adopted SHA3 before it was finalized.
// dest_size: The passed in destination buffer must be >= 28 (bytes), otherwise
// the function asserts or does no-op in release.
void Dqn_Keccak_224(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size);
Dqn_KeccakBytes28 Dqn_Keccak_224_ToBytes28(void *bytes, Dqn_Keccak_u64 bytes_size);
// dest_size: The passed in destination buffer must be >= 32 (bytes), otherwise
// the function asserts or does no-op in release.
void Dqn_Keccak_256(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size);
Dqn_KeccakBytes32 Dqn_Keccak_256_ToBytes32(void *bytes, Dqn_Keccak_u64 bytes_size);
// dest_size: The passed in destination buffer must be >= 48 (bytes), otherwise
// the function asserts or does no-op in release.
void Dqn_Keccak_384(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size);
Dqn_KeccakBytes48 Dqn_Keccak_384_ToBytes48(void *bytes, Dqn_Keccak_u64 bytes_size);
// dest_size: The passed in destination buffer must be >= 64 (bytes), otherwise
// the function asserts or does no-op in release.
void Dqn_Keccak_512(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size);
Dqn_KeccakBytes64 Dqn_Keccak_512_ToBytes64(void *bytes, Dqn_Keccak_u64 bytes_size);
#if defined(DQN_H)
// -----------------------------------------------------------------------------
// NOTE: Keccak - Helpers for Dqn data structures
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_Keccak_224_StringToBytes28(Dqn_String string);
Dqn_KeccakBytes32 Dqn_Keccak_256_StringToBytes32(Dqn_String string);
Dqn_KeccakBytes48 Dqn_Keccak_384_StringToBytes48(Dqn_String string);
Dqn_KeccakBytes64 Dqn_Keccak_512_StringToBytes64(Dqn_String string);
Dqn_KeccakBytes28 Dqn_Keccak_224_U8ArrayToBytes28(Dqn_Array<Dqn_Keccak_u8> array);
Dqn_KeccakBytes32 Dqn_Keccak_256_U8ArrayToBytes32(Dqn_Array<Dqn_Keccak_u8> array);
Dqn_KeccakBytes48 Dqn_Keccak_384_U8ArrayToBytes48(Dqn_Array<Dqn_Keccak_u8> array);
Dqn_KeccakBytes64 Dqn_Keccak_512_U8ArrayToBytes64(Dqn_Array<Dqn_Keccak_u8> array);
#endif // DQN_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.
// at least src_size * 2). This function does *not* null-terminate the buffer.
// The returned result does *not* include a leading 0x prefix.
void Dqn_Keccak_BytesToHex(void const *src, Dqn_Keccak_u64 src_size, char *dest, Dqn_Keccak_u64 dest_size);
#if defined(DQN_NO_MALLOC_FUNCTIONS)
// Convert the src bytes into a null-terminated c-string using malloc. Calls
// into Dqn_Keccak_BytesToHex under the hood.
// src: If src is nullptr, the function returns an empty null-terminated
// string, otherwise, the bytes will be converted and returned as hex.
// return: A null-terminated c-string. This string must be freed by the user
// using the CRT free(..). If malloc fails the returned string is nullptr.
char *Dqn_Keccak_BytesToHexCString(void const *src, Dqn_Keccak_u64 src_size);
#endif // DQN_NO_MALLOC_FUNCTIONS
// Converts a fixed amount of bytes into a hexadecimal string. Helper functions
// that call into Dqn_Keccak_BytesToHex.
// return: The hexadecimal string of the bytes, null-terminated.
Dqn_KeccakString56 Dqn_Keccak_Bytes28ToHex(Dqn_KeccakBytes28 const *bytes);
Dqn_KeccakString64 Dqn_Keccak_Bytes32ToHex(Dqn_KeccakBytes32 const *bytes);
Dqn_KeccakString96 Dqn_Keccak_Bytes48ToHex(Dqn_KeccakBytes48 const *bytes);
Dqn_KeccakString128 Dqn_Keccak_Bytes64ToHex(Dqn_KeccakBytes64 const *bytes);
// Compares byte data structures for byte equality (via memcmp).
// return: 1 if the contents are equal otherwise 0.
int Dqn_Keccak_Bytes28Equals(Dqn_KeccakBytes28 const *a, Dqn_KeccakBytes28 const *b);
int Dqn_Keccak_Bytes32Equals(Dqn_KeccakBytes32 const *a, Dqn_KeccakBytes32 const *b);
int Dqn_Keccak_Bytes48Equals(Dqn_KeccakBytes48 const *a, Dqn_KeccakBytes48 const *b);
int Dqn_Keccak_Bytes64Equals(Dqn_KeccakBytes64 const *a, Dqn_KeccakBytes64 const *b);
#if defined(DQN_H)
// -----------------------------------------------------------------------------
// NOTE: Other helper functions for Dqn data structures
// -----------------------------------------------------------------------------
// Converts a 64 character hex string into the 32 byte binary representation.
// Invalid hex characters in the string will be represented as 0.
// hex: Must be exactly a 64 character hex string.
Dqn_KeccakBytes32 Dqn_Keccak_Hex64StringToBytes(Dqn_String hex);
#endif // DQN_H
#endif // DQN_KECCAK_H
#if defined(DQN_KECCAK_IMPLEMENTATION)
Dqn_Keccak_u64 const DQN_KECCAK_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,
};
Dqn_Keccak_u64 const DQN_KECCAK_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 DQN_KECCAK_LANE_SIZE_U64 5
#define DQN_KECCAK_ROL64(val, rotate) (((val) << (rotate)) | (((val) >> (64 - (rotate)))))
static void Dqn_Keccak__ReferencePermute(Dqn_Keccak_u64 *A)
{
// TODO(dqn): Do some more tests and remove.
// NOTE: Reference permutation taken for cross-reference and back verifying
// against from digestpp by kerukuro
// https://github.com/kerukuro/digestpp/blob/master/algorithm/detail/sha3_provider.hpp
for (int round = 0; round < 24; round++)
{
Dqn_Keccak_u64 C[5], D[5];
C[0] = A[0 * 5 + 0] ^ A[1 * 5 + 0] ^ A[2 * 5 + 0] ^ A[3 * 5 + 0] ^ A[4 * 5 + 0];
C[1] = A[0 * 5 + 1] ^ A[1 * 5 + 1] ^ A[2 * 5 + 1] ^ A[3 * 5 + 1] ^ A[4 * 5 + 1];
C[2] = A[0 * 5 + 2] ^ A[1 * 5 + 2] ^ A[2 * 5 + 2] ^ A[3 * 5 + 2] ^ A[4 * 5 + 2];
C[3] = A[0 * 5 + 3] ^ A[1 * 5 + 3] ^ A[2 * 5 + 3] ^ A[3 * 5 + 3] ^ A[4 * 5 + 3];
C[4] = A[0 * 5 + 4] ^ A[1 * 5 + 4] ^ A[2 * 5 + 4] ^ A[3 * 5 + 4] ^ A[4 * 5 + 4];
D[0] = C[4] ^ DQN_KECCAK_ROL64(C[1], 1);
D[1] = C[0] ^ DQN_KECCAK_ROL64(C[2], 1);
D[2] = C[1] ^ DQN_KECCAK_ROL64(C[3], 1);
D[3] = C[2] ^ DQN_KECCAK_ROL64(C[4], 1);
D[4] = C[3] ^ DQN_KECCAK_ROL64(C[0], 1);
Dqn_Keccak_u64 B00 = A[0 * 5 + 0] ^ D[0];
Dqn_Keccak_u64 B10 = DQN_KECCAK_ROL64(A[0 * 5 + 1] ^ D[1], 1);
Dqn_Keccak_u64 B20 = DQN_KECCAK_ROL64(A[0 * 5 + 2] ^ D[2], 62);
Dqn_Keccak_u64 B5 = DQN_KECCAK_ROL64(A[0 * 5 + 3] ^ D[3], 28);
Dqn_Keccak_u64 B15 = DQN_KECCAK_ROL64(A[0 * 5 + 4] ^ D[4], 27);
Dqn_Keccak_u64 B16 = DQN_KECCAK_ROL64(A[1 * 5 + 0] ^ D[0], 36);
Dqn_Keccak_u64 B1 = DQN_KECCAK_ROL64(A[1 * 5 + 1] ^ D[1], 44);
Dqn_Keccak_u64 B11 = DQN_KECCAK_ROL64(A[1 * 5 + 2] ^ D[2], 6);
Dqn_Keccak_u64 B21 = DQN_KECCAK_ROL64(A[1 * 5 + 3] ^ D[3], 55);
Dqn_Keccak_u64 B6 = DQN_KECCAK_ROL64(A[1 * 5 + 4] ^ D[4], 20);
Dqn_Keccak_u64 B7 = DQN_KECCAK_ROL64(A[2 * 5 + 0] ^ D[0], 3);
Dqn_Keccak_u64 B17 = DQN_KECCAK_ROL64(A[2 * 5 + 1] ^ D[1], 10);
Dqn_Keccak_u64 B2 = DQN_KECCAK_ROL64(A[2 * 5 + 2] ^ D[2], 43);
Dqn_Keccak_u64 B12 = DQN_KECCAK_ROL64(A[2 * 5 + 3] ^ D[3], 25);
Dqn_Keccak_u64 B22 = DQN_KECCAK_ROL64(A[2 * 5 + 4] ^ D[4], 39);
Dqn_Keccak_u64 B23 = DQN_KECCAK_ROL64(A[3 * 5 + 0] ^ D[0], 41);
Dqn_Keccak_u64 B8 = DQN_KECCAK_ROL64(A[3 * 5 + 1] ^ D[1], 45);
Dqn_Keccak_u64 B18 = DQN_KECCAK_ROL64(A[3 * 5 + 2] ^ D[2], 15);
Dqn_Keccak_u64 B3 = DQN_KECCAK_ROL64(A[3 * 5 + 3] ^ D[3], 21);
Dqn_Keccak_u64 B13 = DQN_KECCAK_ROL64(A[3 * 5 + 4] ^ D[4], 8);
Dqn_Keccak_u64 B14 = DQN_KECCAK_ROL64(A[4 * 5 + 0] ^ D[0], 18);
Dqn_Keccak_u64 B24 = DQN_KECCAK_ROL64(A[4 * 5 + 1] ^ D[1], 2);
Dqn_Keccak_u64 B9 = DQN_KECCAK_ROL64(A[4 * 5 + 2] ^ D[2], 61);
Dqn_Keccak_u64 B19 = DQN_KECCAK_ROL64(A[4 * 5 + 3] ^ D[3], 56);
Dqn_Keccak_u64 B4 = DQN_KECCAK_ROL64(A[4 * 5 + 4] ^ D[4], 14);
#if 0
printf("B00: %024llu\n", A[0 * 5 + 0] ^ D[0]);
printf("B10: %024llu\n", DQN_KECCAK_ROL64(A[0 * 5 + 1] ^ D[1], 1));
printf("B20: %024llu\n", DQN_KECCAK_ROL64(A[0 * 5 + 2] ^ D[2], 62));
printf("B05: %024llu\n", DQN_KECCAK_ROL64(A[0 * 5 + 3] ^ D[3], 28));
printf("B15: %024llu\n\n", DQN_KECCAK_ROL64(A[0 * 5 + 4] ^ D[4], 27));
printf("B16: %024llu\n", DQN_KECCAK_ROL64(A[1 * 5 + 0] ^ D[0], 36));
printf("B01: %024llu\n", DQN_KECCAK_ROL64(A[1 * 5 + 1] ^ D[1], 44));
printf("B11: %024llu\n", DQN_KECCAK_ROL64(A[1 * 5 + 2] ^ D[2], 6));
printf("B21: %024llu\n", DQN_KECCAK_ROL64(A[1 * 5 + 3] ^ D[3], 55));
printf("B06: %024llu\n\n", DQN_KECCAK_ROL64(A[1 * 5 + 4] ^ D[4], 20));
printf("B07: %024llu\n", DQN_KECCAK_ROL64(A[2 * 5 + 0] ^ D[0], 3));
printf("B17: %024llu\n", DQN_KECCAK_ROL64(A[2 * 5 + 1] ^ D[1], 10));
printf("B02: %024llu\n", DQN_KECCAK_ROL64(A[2 * 5 + 2] ^ D[2], 43));
printf("B12: %024llu\n", DQN_KECCAK_ROL64(A[2 * 5 + 3] ^ D[3], 25));
printf("B22: %024llu\n\n", DQN_KECCAK_ROL64(A[2 * 5 + 4] ^ D[4], 39));
printf("B23: %024llu\n", DQN_KECCAK_ROL64(A[3 * 5 + 0] ^ D[0], 41));
printf("B08: %024llu\n", DQN_KECCAK_ROL64(A[3 * 5 + 1] ^ D[1], 45));
printf("B18: %024llu\n", DQN_KECCAK_ROL64(A[3 * 5 + 2] ^ D[2], 15));
printf("B03: %024llu\n", DQN_KECCAK_ROL64(A[3 * 5 + 3] ^ D[3], 21));
printf("B13: %024llu\n\n", DQN_KECCAK_ROL64(A[3 * 5 + 4] ^ D[4], 8));
printf("B14: %024llu\n", DQN_KECCAK_ROL64(A[4 * 5 + 0] ^ D[0], 18));
printf("B24: %024llu\n", DQN_KECCAK_ROL64(A[4 * 5 + 1] ^ D[1], 2));
printf("B09: %024llu\n", DQN_KECCAK_ROL64(A[4 * 5 + 2] ^ D[2], 61));
printf("B19: %024llu\n", DQN_KECCAK_ROL64(A[4 * 5 + 3] ^ D[3], 56));
printf("B04: %024llu\n\n", DQN_KECCAK_ROL64(A[4 * 5 + 4] ^ D[4], 14));
#endif
A[0 * 5 + 0] = B00 ^ ((~B1) & B2);
A[0 * 5 + 1] = B1 ^ ((~B2) & B3);
A[0 * 5 + 2] = B2 ^ ((~B3) & B4);
A[0 * 5 + 3] = B3 ^ ((~B4) & B00);
A[0 * 5 + 4] = B4 ^ ((~B00) & B1);
A[1 * 5 + 0] = B5 ^ ((~B6) & B7);
A[1 * 5 + 1] = B6 ^ ((~B7) & B8);
A[1 * 5 + 2] = B7 ^ ((~B8) & B9);
A[1 * 5 + 3] = B8 ^ ((~B9) & B5);
A[1 * 5 + 4] = B9 ^ ((~B5) & B6);
A[2 * 5 + 0] = B10 ^ ((~B11) & B12);
A[2 * 5 + 1] = B11 ^ ((~B12) & B13);
A[2 * 5 + 2] = B12 ^ ((~B13) & B14);
A[2 * 5 + 3] = B13 ^ ((~B14) & B10);
A[2 * 5 + 4] = B14 ^ ((~B10) & B11);
A[3 * 5 + 0] = B15 ^ ((~B16) & B17);
A[3 * 5 + 1] = B16 ^ ((~B17) & B18);
A[3 * 5 + 2] = B17 ^ ((~B18) & B19);
A[3 * 5 + 3] = B18 ^ ((~B19) & B15);
A[3 * 5 + 4] = B19 ^ ((~B15) & B16);
A[4 * 5 + 0] = B20 ^ ((~B21) & B22);
A[4 * 5 + 1] = B21 ^ ((~B22) & B23);
A[4 * 5 + 2] = B22 ^ ((~B23) & B24);
A[4 * 5 + 3] = B23 ^ ((~B24) & B20);
A[4 * 5 + 4] = B24 ^ ((~B20) & B21);
A[0] ^= DQN_KECCAK_ROUNDS[round];
#if 0
for (int y = 0; y < 5; y++)
{
for (int x = 0; x < 5; x++)
{
Dqn_Keccak_u64 lane = A[x + (y * 5)];
printf("[%d,%d] %024llu ", x, y, lane);
}
printf("\n");
}
#endif
}
}
void Dqn_Keccak__Permute(void *state)
{
// TODO(dqn): Do some profiling on unrolling and can we SIMD some part of
// this? Unroll loop, look at data dependencies and investigate.
#if 1
Dqn_Keccak_u64 *lanes_u64 = (Dqn_Keccak_u64 *)state;
for (int round_index = 0; round_index < 24; round_index++)
{
#define LANE_INDEX(x, y) ((x) + ((y) * DQN_KECCAK_LANE_SIZE_U64))
// ---------------------------------------------------------------------
// θ step
// ---------------------------------------------------------------------
#if 1
Dqn_Keccak_u64 c[DQN_KECCAK_LANE_SIZE_U64];
for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++)
{
c[x] = lanes_u64[LANE_INDEX(x, 0)] ^
lanes_u64[LANE_INDEX(x, 1)] ^
lanes_u64[LANE_INDEX(x, 2)] ^
lanes_u64[LANE_INDEX(x, 3)] ^
lanes_u64[LANE_INDEX(x, 4)];
}
Dqn_Keccak_u64 d[DQN_KECCAK_LANE_SIZE_U64];
for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++)
d[x] = c[(x + 4) % DQN_KECCAK_LANE_SIZE_U64] ^ DQN_KECCAK_ROL64(c[(x + 1) % DQN_KECCAK_LANE_SIZE_U64], 1);
for (int y = 0; y < DQN_KECCAK_LANE_SIZE_U64; y++)
for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++)
lanes_u64[LANE_INDEX(x, y)] ^= d[x];
#else
Dqn_Keccak_u64 c[5], d[5];
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] ^ DQN_KECCAK_ROL64(c[1], 1);
d[1] = c[0] ^ DQN_KECCAK_ROL64(c[2], 1);
d[2] = c[1] ^ DQN_KECCAK_ROL64(c[3], 1);
d[3] = c[2] ^ DQN_KECCAK_ROL64(c[4], 1);
d[4] = c[3] ^ DQN_KECCAK_ROL64(c[0], 1);
#endif
// ---------------------------------------------------------------------
// ρ and π steps
// ---------------------------------------------------------------------
Dqn_Keccak_u64 b[DQN_KECCAK_LANE_SIZE_U64 * DQN_KECCAK_LANE_SIZE_U64];
for (int y = 0; y < DQN_KECCAK_LANE_SIZE_U64; y++)
{
for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++)
{
Dqn_Keccak_u64 lane = lanes_u64[LANE_INDEX(x, y)];
Dqn_Keccak_u64 rotate_count = DQN_KECCAK_ROTATIONS[x][y];
b[LANE_INDEX(y, (2 * x + 3 * y) % 5)] = DQN_KECCAK_ROL64(lane, rotate_count);
#if 0
int index = LANE_INDEX(y, (2 * x + 3 * y) % 5);
printf("B%02d: %024llu\n", index, b[index]);
#endif
}
#if 0
printf("\n");
#endif
}
// ---------------------------------------------------------------------
// χ step
// ---------------------------------------------------------------------
for (int y = 0; y < DQN_KECCAK_LANE_SIZE_U64; y++)
{
for (int x = 0; x < DQN_KECCAK_LANE_SIZE_U64; x++)
{
Dqn_Keccak_u64 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)] ^= DQN_KECCAK_ROUNDS[round_index];
#if 0
for (int y = 0; y < 5; y++)
{
for (int x = 0; x < 5; x++)
{
Dqn_Keccak_u64 lane = lanes_u64[x + (y * 5)];
printf("[%d,%d] %024llu ", x, y, lane);
}
printf("\n");
}
#endif
#undef LANE_INDEX
#undef DQN_KECCAK_ROL64
}
#else
Dqn_Keccak__ReferencePermute((Dqn_Keccak_u64 *)state);
#endif
}
static void Dqn_Keccak__Construction(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size, int bitrate, char delimited_suffix)
{
int const ABSORB_SIZE = bitrate / 8;
int const ABSORB_SIZE_U64 = ABSORB_SIZE / 8;
Dqn_Keccak_u64 state_u64[DQN_KECCAK_LANE_SIZE_U64 * DQN_KECCAK_LANE_SIZE_U64] = {};
// ---------------------------------------------------------------------
// Sponge Step: Absorb all the bytes into the state
// ---------------------------------------------------------------------
Dqn_Keccak_u64 const *block = (Dqn_Keccak_u64 const *)src;
int const absorb_count = src_size / ABSORB_SIZE;
for (int absorb_index = 0; absorb_index < absorb_count; absorb_index++, block += ABSORB_SIZE_U64)
{
for (int index = 0; index < ABSORB_SIZE_U64; index++)
state_u64[index] ^= block[index];
Dqn_Keccak__Permute(state_u64);
}
// ---------------------------------------------------------------------
// Sponge Finalization Step: Remaining source bytes + padding
// ---------------------------------------------------------------------
// NOTE: Do the remainder bytes not divisible in the block, then, also,
// complete the sponge by adding the padding bits and delimited suffix.
{
Dqn_Keccak_u8 * state_u8 = (Dqn_Keccak_u8 *) state_u64;
Dqn_Keccak_u8 const *block_u8 = (Dqn_Keccak_u8 const *) block;
int const remainder = src_size % ABSORB_SIZE;
for (int index = 0; index < remainder; index++)
state_u8[index] ^= block_u8[index];
// NOTE: (remainder + 1) can never point out of array bounds, the
// remainder is guaranteed to be less than the ABSORB_SIZE, since we
// processed all the full blocks above.
int const delimited_suffix_index = remainder;
int const INDEX_OF_0X80_BYTE = ABSORB_SIZE - 1;
state_u8[delimited_suffix_index] ^= delimited_suffix;
// NOTE: In the reference implementation, it checks that if the
// delimited suffix is set to the padding bit (0x80), then we need to
// permute twice. Once for the delimited suffix, and a second time for
// the "padding" permute.
//
// However all standard algorithms either specify a 0x01, or 0x06, 0x04
// delimited suffix and so forth- so this case is never hit. We can omit
// this from the implementation here.
state_u8[INDEX_OF_0X80_BYTE] ^= 0x80;
Dqn_Keccak__Permute(state_u64);
}
// ---------------------------------------------------------------------
// Squeeze Step: Squeeze bytes from the state into our hash
// ---------------------------------------------------------------------
Dqn_Keccak_u8 * dest_u8 = (Dqn_Keccak_u8 *) dest;
int const squeeze_count = dest_size / ABSORB_SIZE;
int squeeze_index = 0;
for (; squeeze_index < squeeze_count; squeeze_index++)
{
if (squeeze_index) Dqn_Keccak__Permute(state_u64);
DQN_KECCAK_MEMCOPY(dest_u8, state_u64, ABSORB_SIZE);
dest_u8 += ABSORB_SIZE;
}
// ---------------------------------------------------------------------
// Squeeze Finalisation Step: Remainder bytes in hash
// ---------------------------------------------------------------------
{
int const remainder = dest_size % ABSORB_SIZE;
if (remainder)
{
if (squeeze_index) Dqn_Keccak__Permute(state_u64);
DQN_KECCAK_MEMCOPY(dest_u8, state_u64, remainder);
}
}
}
#define DQN_KECCAK_SHA3_DELIMITED_SUFFIX 0x06
#define DQN_KECCAK_DELIMITED_SUFFIX 0x01
// -----------------------------------------------------------------------------
// NOTE: SHA3-224
// -----------------------------------------------------------------------------
void Dqn_Keccak_SHA3_224(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size)
{
int const BITRATE = 1152;
DQN_KECCAK_ASSERT(dest_size >= 224 / 8);
Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_SHA3_DELIMITED_SUFFIX);
}
Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_ToBytes28(void *bytes, Dqn_Keccak_u64 bytes_size)
{
Dqn_KeccakBytes28 result;
Dqn_Keccak_SHA3_224(bytes, bytes_size, result.data, sizeof(result));
return result;
}
// -----------------------------------------------------------------------------
// NOTE: SHA3-256
// -----------------------------------------------------------------------------
void Dqn_Keccak_SHA3_256(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size)
{
int const BITRATE = 1088;
DQN_KECCAK_ASSERT(dest_size >= 256 / 8);
Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_SHA3_DELIMITED_SUFFIX);
}
Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_ToBytes32(void *bytes, Dqn_Keccak_u64 bytes_size)
{
Dqn_KeccakBytes32 result;
Dqn_Keccak_SHA3_256(bytes, bytes_size, result.data, sizeof(result));
return result;
}
// -----------------------------------------------------------------------------
// NOTE: SHA3-384
// -----------------------------------------------------------------------------
void Dqn_Keccak_SHA3_384(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size)
{
int const BITRATE = 832;
DQN_KECCAK_ASSERT(dest_size >= 384 / 8);
Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_SHA3_DELIMITED_SUFFIX);
}
Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_ToBytes48(void *bytes, Dqn_Keccak_u64 bytes_size)
{
Dqn_KeccakBytes48 result;
Dqn_Keccak_SHA3_384(bytes, bytes_size, result.data, sizeof(result));
return result;
}
// -----------------------------------------------------------------------------
// NOTE: SHA3-512
// -----------------------------------------------------------------------------
void Dqn_Keccak_SHA3_512(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size)
{
int const BITRATE = 576;
DQN_KECCAK_ASSERT(dest_size >= 512 / 8);
Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_SHA3_DELIMITED_SUFFIX);
}
Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_ToBytes64(void *bytes, Dqn_Keccak_u64 bytes_size)
{
Dqn_KeccakBytes64 result;
Dqn_Keccak_SHA3_512(bytes, bytes_size, result.data, sizeof(result));
return result;
}
#if defined(DQN_H)
// -----------------------------------------------------------------------------
// NOTE: SHA3 - Helpers for Dqn data structures
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_StringToBytes28(Dqn_String string)
{
Dqn_KeccakBytes28 result;
Dqn_Keccak_SHA3_224(string.str, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes28 Dqn_Keccak_SHA3_224_U8ArrayToBytes28(Dqn_Array<Dqn_Keccak_u8> array)
{
Dqn_KeccakBytes28 result;
Dqn_Keccak_SHA3_224(array.data, array.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_StringToBytes32(Dqn_String string)
{
Dqn_KeccakBytes32 result;
Dqn_Keccak_SHA3_256(string.str, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes32 Dqn_Keccak_SHA3_256_U8ArrayToBytes32(Dqn_Array<Dqn_Keccak_u8> array)
{
Dqn_KeccakBytes32 result;
Dqn_Keccak_SHA3_256(array.data, array.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_StringToBytes48(Dqn_String string)
{
Dqn_KeccakBytes48 result;
Dqn_Keccak_SHA3_384(string.str, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes48 Dqn_Keccak_SHA3_384_U8ArrayToBytes48(Dqn_Array<Dqn_Keccak_u8> array)
{
Dqn_KeccakBytes48 result;
Dqn_Keccak_SHA3_384(array.data, array.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_StringToBytes64(Dqn_String string)
{
Dqn_KeccakBytes64 result;
Dqn_Keccak_SHA3_512(string.str, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes64 Dqn_Keccak_SHA3_512_U8ArrayToBytes64(Dqn_Array<Dqn_Keccak_u8> array)
{
Dqn_KeccakBytes64 result;
Dqn_Keccak_SHA3_512(array.data, array.size, result.data, sizeof(result));
return result;
}
#endif // DQN_H
// -----------------------------------------------------------------------------
// NOTE: Keccak-224
// -----------------------------------------------------------------------------
void Dqn_Keccak_224(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size)
{
int const BITRATE = 1152;
DQN_KECCAK_ASSERT(dest_size >= 224 / 8);
Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_DELIMITED_SUFFIX);
}
Dqn_KeccakBytes28 Dqn_Keccak_224_ToBytes28(void *bytes, Dqn_Keccak_u64 bytes_size)
{
Dqn_KeccakBytes28 result;
Dqn_Keccak_224(bytes, bytes_size, result.data, sizeof(result));
return result;
}
// -----------------------------------------------------------------------------
// NOTE: Keccak-256
// -----------------------------------------------------------------------------
void Dqn_Keccak_256(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size)
{
int const BITRATE = 1088;
DQN_KECCAK_ASSERT(dest_size >= 256 / 8);
Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_DELIMITED_SUFFIX);
}
Dqn_KeccakBytes32 Dqn_Keccak_256_ToBytes32(void *bytes, Dqn_Keccak_u64 bytes_size)
{
Dqn_KeccakBytes32 result;
Dqn_Keccak_256(bytes, bytes_size, result.data, sizeof(result));
return result;
}
// -----------------------------------------------------------------------------
// NOTE: Keccak-384
// -----------------------------------------------------------------------------
void Dqn_Keccak_384(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size)
{
int const BITRATE = 832;
DQN_KECCAK_ASSERT(dest_size >= 384 / 8);
Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_DELIMITED_SUFFIX);
}
Dqn_KeccakBytes48 Dqn_Keccak_384_ToBytes48(void *bytes, Dqn_Keccak_u64 bytes_size)
{
Dqn_KeccakBytes48 result;
Dqn_Keccak_384(bytes, bytes_size, result.data, sizeof(result));
return result;
}
// -----------------------------------------------------------------------------
// NOTE: Keccak-512
// -----------------------------------------------------------------------------
void Dqn_Keccak_512(void const *src, Dqn_Keccak_u64 src_size, void *dest, int dest_size)
{
int const BITRATE = 576;
DQN_KECCAK_ASSERT(dest_size >= 512 / 8);
Dqn_Keccak__Construction(src, src_size, dest, dest_size, BITRATE, DQN_KECCAK_DELIMITED_SUFFIX);
}
Dqn_KeccakBytes64 Dqn_Keccak_512_ToBytes64(void *bytes, Dqn_Keccak_u64 bytes_size)
{
Dqn_KeccakBytes64 result;
Dqn_Keccak_512(bytes, bytes_size, result.data, sizeof(result));
return result;
}
#if defined(DQN_H)
// -----------------------------------------------------------------------------
// NOTE: Keccak - Helpers for Dqn data structures
// -----------------------------------------------------------------------------
Dqn_KeccakBytes28 Dqn_Keccak_224_StringToBytes28(Dqn_String string)
{
Dqn_KeccakBytes28 result;
Dqn_Keccak_224(string.str, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes28 Dqn_Keccak_224_U8ArrayToBytes28(Dqn_Array<Dqn_Keccak_u8> array)
{
Dqn_KeccakBytes28 result;
Dqn_Keccak_224(array.data, array.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes32 Dqn_Keccak_256_StringToBytes32(Dqn_String string)
{
Dqn_KeccakBytes32 result;
Dqn_Keccak_256(string.str, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes32 Dqn_Keccak_256_U8ArrayToBytes32(Dqn_Array<Dqn_Keccak_u8> array)
{
Dqn_KeccakBytes32 result;
Dqn_Keccak_256(array.data, array.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes48 Dqn_Keccak_384_StringToBytes48(Dqn_String string)
{
Dqn_KeccakBytes48 result;
Dqn_Keccak_384(string.str, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes48 Dqn_Keccak_384_U8ArrayToBytes48(Dqn_Array<Dqn_Keccak_u8> array)
{
Dqn_KeccakBytes48 result;
Dqn_Keccak_384(array.data, array.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes64 Dqn_Keccak_512_StringToBytes64(Dqn_String string)
{
Dqn_KeccakBytes64 result;
Dqn_Keccak_512(string.str, string.size, result.data, sizeof(result));
return result;
}
Dqn_KeccakBytes64 Dqn_Keccak_512_U8ArrayToBytes64(Dqn_Array<Dqn_Keccak_u8> array)
{
Dqn_KeccakBytes64 result;
Dqn_Keccak_512(array.data, array.size, result.data, sizeof(result));
return result;
}
#endif // DQN_H
// -----------------------------------------------------------------------------
// NOTE: Helper functions
// -----------------------------------------------------------------------------
void Dqn_Keccak_BytesToHex(void const *src, Dqn_Keccak_u64 src_size, char *dest, Dqn_Keccak_u64 dest_size)
{
(void)src_size; (void)dest_size;
DQN_KECCAK_ASSERT(dest_size >= src_size * 2);
unsigned char *src_u8 = (unsigned char *)src;
for (Dqn_Keccak_u64 src_index = 0, dest_index = 0;
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';
}
}
#if defined(DQN_NO_MALLOC_FUNCTIONS)
char *Dqn_Keccak_BytesToHexCString(void const *src, Dqn_Keccak_u64 src_size)
{
int result_size = (src_size * 2);
char *result = DQN_KECCAK_MALLOC(result_size + 1 /*null-terminator*/);
if (result)
{
Dqn_Keccak_BytesToHex(src, src_size, result, result_size);
result[result_size] = 0;
}
return result;
}
#endif // DQN_NO_MALLOC_FUNCTIONS
Dqn_KeccakString56 Dqn_Keccak_Bytes28ToHex(Dqn_KeccakBytes28 const *bytes)
{
Dqn_KeccakString56 result;
Dqn_Keccak_BytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str));
result.str[sizeof(result.str) - 1] = 0;
return result;
}
Dqn_KeccakString64 Dqn_Keccak_Bytes32ToHex(Dqn_KeccakBytes32 const *bytes)
{
Dqn_KeccakString64 result;
Dqn_Keccak_BytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str));
result.str[sizeof(result.str) - 1] = 0;
return result;
}
Dqn_KeccakString96 Dqn_Keccak_Bytes48ToHex(Dqn_KeccakBytes48 const *bytes)
{
Dqn_KeccakString96 result;
Dqn_Keccak_BytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str));
result.str[sizeof(result.str) - 1] = 0;
return result;
}
Dqn_KeccakString128 Dqn_Keccak_Bytes64ToHex(Dqn_KeccakBytes64 const *bytes)
{
Dqn_KeccakString128 result;
Dqn_Keccak_BytesToHex(bytes->data, sizeof(bytes->data), result.str, sizeof(result.str));
result.str[sizeof(result.str) - 1] = 0;
return result;
}
int Dqn_Keccak_Bytes28Equals(Dqn_KeccakBytes28 const *a, Dqn_KeccakBytes28 const *b)
{
int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
return result;
}
int Dqn_Keccak_Bytes32Equals(Dqn_KeccakBytes32 const *a, Dqn_KeccakBytes32 const *b)
{
int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
return result;
}
int Dqn_Keccak_Bytes48Equals(Dqn_KeccakBytes48 const *a, Dqn_KeccakBytes48 const *b)
{
int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
return result;
}
int Dqn_Keccak_Bytes64Equals(Dqn_KeccakBytes64 const *a, Dqn_KeccakBytes64 const *b)
{
int result = DQN_KECCAK_MEMCMP(a->data, b->data, sizeof(*a)) == 0;
return result;
}
#if defined(DQN_H)
// -----------------------------------------------------------------------------
// NOTE: Other helper functions for Dqn data structures
// -----------------------------------------------------------------------------
Dqn_KeccakBytes32 Dqn_Keccak_Hex64StringToBytes(Dqn_String hex)
{
DQN_KECCAK_ASSERT(hex.size == 64);
Dqn_KeccakBytes32 result;
Dqn_Hex_HexToBytes(hex.str, hex.size, result.data, sizeof(result));
return result;
}
#endif // DQN_H
#endif // DQN_KECCAK_IMPLEMENTATION

101
dqn_tests_helpers.cpp Normal file
View File

@ -0,0 +1,101 @@
#if defined(DQN_KECCAK_H)
// -----------------------------------------------------------------------------
// Dqn_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<n; ++i)
typedef unsigned char u8;
typedef unsigned long long int u64;
typedef unsigned int ui;
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen);
void FIPS202_SHAKE128(const u8 *in, u64 inLen, u8 *out, u64 outLen) { Keccak(1344, 256, in, inLen, 0x1F, out, outLen); }
void FIPS202_SHAKE256(const u8 *in, u64 inLen, u8 *out, u64 outLen) { Keccak(1088, 512, in, inLen, 0x1F, out, outLen); }
void FIPS202_SHA3_224(const u8 *in, u64 inLen, u8 *out) { Keccak(1152, 448, in, inLen, 0x06, out, 28); }
void FIPS202_SHA3_256(const u8 *in, u64 inLen, u8 *out) { Keccak(1088, 512, in, inLen, 0x06, out, 32); }
void FIPS202_SHA3_384(const u8 *in, u64 inLen, u8 *out) { Keccak(832, 768, in, inLen, 0x06, out, 48); }
void FIPS202_SHA3_512(const u8 *in, u64 inLen, u8 *out) { Keccak(576, 1024, in, inLen, 0x06, out, 64); }
int LFSR86540(u8 *R) { (*R)=((*R)<<1)^(((*R)&0x80)?0x71:0); return ((*R)&2)>>1; }
#define ROL(a,o) ((((u64)a)<<o)^(((u64)a)>>(64-o)))
static u64 load64(const u8 *x) { ui i; u64 u=0; FOR(i,8) { u<<=8; u|=x[7-i]; } return u; }
static void store64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]=u; u>>=8; } }
static void xor64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]^=u; u>>=8; } }
#define rL(x,y) load64((u8*)s+8*(x+5*y))
#define wL(x,y,l) store64((u8*)s+8*(x+5*y),l)
#define XL(x,y,l) xor64((u8*)s+8*(x+5*y),l)
void KeccakF1600(void *s)
{
ui r,x,y,i,j,Y; u8 R=0x01; u64 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 (LFSR86540(&R)) XL(0,0,(u64)1<<((1<<j)-1));
}
}
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen)
{
/*initialize*/ u8 s[200]; ui R=r/8; ui i,b=0; FOR(i,200) s[i]=0;
/*absorb*/ while(inLen>0) { b=(inLen<R)?inLen:R; FOR(i,b) s[i]^=in[i]; in+=b; inLen-=b; if (b==R) { KeccakF1600(s); b=0; } }
/*pad*/ s[b]^=sfx; if((sfx&0x80)&&(b==(R-1))) KeccakF1600(s); s[R-1]^=0x80; KeccakF1600(s);
/*squeeze*/ while(outLen>0) { b=(outLen<R)?outLen:R; FOR(i,b) out[i]=s[i]; out+=b; outLen-=b; if(outLen>0) KeccakF1600(s); }
}
// -----------------------------------------------------------------------------
// PCG32 Random Number Generator
// -----------------------------------------------------------------------------
// NOTE: https://github.com/imneme/pcg-c-basic
struct pcg_state_setseq_64
{ // Internals are *Private*.
Dqn_u64 state; // RNG state. All values are possible.
Dqn_u64 inc; // Controls which RNG sequence (stream) is
// selected. Must *always* be odd.
};
typedef struct pcg_state_setseq_64 pcg32_random_t;
// pcg32_random_r(rng)
// Generate a uniformly distributed 32-bit random number
Dqn_u32 pcg32_random_r(pcg32_random_t* rng)
{
Dqn_u64 oldstate = rng->state;
rng->state = oldstate * 6364136223846793005ULL + rng->inc;
Dqn_u32 xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
Dqn_u32 rot = oldstate >> 59u;
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
}
// pcg32_srandom_r(rng, initstate, initseq):
// Seed the rng. Specified in two parts, state initializer and a
// sequence selection constant (a.k.a. stream id)
void pcg32_srandom_r(pcg32_random_t* rng, Dqn_u64 initstate, Dqn_u64 initseq)
{
rng->state = 0U;
rng->inc = (initseq << 1u) | 1u;
pcg32_random_r(rng);
rng->state += initstate;
pcg32_random_r(rng);
}
// pcg32_boundedrand_r(rng, bound):
// Generate a uniformly distributed number, r, where 0 <= r < bound
Dqn_u32 pcg32_boundedrand_r(pcg32_random_t* rng, Dqn_u32 bound)
{
Dqn_u32 threshold = -bound % bound;
for (;;) {
Dqn_u32 r = pcg32_random_r(rng);
if (r >= threshold)
return r % bound;
}
}
#endif // DQN_KECCAK_H