diff options
-rw-r--r-- | src/ext/equix/hashx/README.md | 37 | ||||
-rw-r--r-- | src/ext/equix/hashx/include/hashx.h | 72 | ||||
-rw-r--r-- | src/ext/equix/hashx/src/bench.c | 34 | ||||
-rw-r--r-- | src/ext/equix/hashx/src/compiler.c | 9 | ||||
-rw-r--r-- | src/ext/equix/hashx/src/compiler.h | 5 | ||||
-rw-r--r-- | src/ext/equix/hashx/src/context.c | 45 | ||||
-rw-r--r-- | src/ext/equix/hashx/src/context.h | 16 | ||||
-rw-r--r-- | src/ext/equix/hashx/src/hashx.c | 78 | ||||
-rw-r--r-- | src/ext/equix/hashx/src/tests.c | 116 | ||||
-rw-r--r-- | src/ext/equix/hashx/src/virtual_memory.c | 3 |
10 files changed, 269 insertions, 146 deletions
diff --git a/src/ext/equix/hashx/README.md b/src/ext/equix/hashx/README.md index 1d8ea47652..f6352475b0 100644 --- a/src/ext/equix/hashx/README.md +++ b/src/ext/equix/hashx/README.md @@ -13,7 +13,7 @@ and to ensure that each function takes exactly the same number of CPU cycles ## API -The API consists of 4 functions and is documented in the public header file +The API consists of 5 functions and is documented in the public header file [hashx.h](include/hashx.h). Example of usage: @@ -25,13 +25,15 @@ Example of usage: int main() { char seed[] = "this is a seed that will generate a hash function"; char hash[HASHX_SIZE]; - hashx_ctx* ctx = hashx_alloc(HASHX_COMPILED); - if (ctx == HASHX_NOTSUPP) - ctx = hashx_alloc(HASHX_INTERPRETED); + hashx_type func_type; + hashx_ctx* ctx = hashx_alloc(HASHX_TRY_COMPILE); if (ctx == NULL) return 1; - if (!hashx_make(ctx, seed, sizeof(seed))) /* generate a hash function */ + /* generate a hash function */ + if (hashx_make(ctx, seed, sizeof(seed)) != HASHX_OK) return 1; + if (hashx_query_type(ctx, &func_type) == HASHX_OK && func_type == HASHX_TYPE_COMPILED) + printf("Using the compiled implementation of HashX\n"); hashx_exec(ctx, 123456789, hash); /* calculate the hash of a nonce value */ hashx_free(ctx); for (unsigned i = 0; i < HASHX_SIZE; ++i) @@ -84,6 +86,31 @@ A benchmark executable is included: ./hashx-bench --seeds 500 ``` +## Error fallback + +The compiled implementation of HashX is much faster (very roughly 20x) so it +should be used whenever possible. It may be necessary to use the interpreter +for multiple reasons: either the platform is not supported at compile time, +or various runtime policies disallow the memory protection changes that are +necessary to do just-in-time compilation. Failures may be detected late, so +the library provides a built-in mechanism to fall back from the compiled +implementation to interpreted quickly without duplicating the whole context. + +The `hashx_query_type()` function is optional, provided for users of the +`HASHX_TRY_COMPILE` context who need to know which implementation was +ultimately used. + +The actual hash function, `hashx_exec()`, returns an error code for +completeness in reporting programming errors, but if a caller has invoked +`hashx_make()` successfully it can be considered infallible. + +It is always possible for `hashx_make()` to fail. In addition to the +OS-specific failures you may see when forcing HashX to use the compiled +implementation with `hashx_alloc(HASHX_TYPE_COMPILED)`, it's always possible +for `hashx_make` to fail unpredictably for a particular seed value. These +seeds should be discarded and a new one attempted by the caller when +`hashx_make` returns `HASHX_FAIL_SEED`. + ## Security HashX should provide strong preimage resistance. No other security guarantees are made. About diff --git a/src/ext/equix/hashx/include/hashx.h b/src/ext/equix/hashx/include/hashx.h index 0d5521177a..2910515d9a 100644 --- a/src/ext/equix/hashx/include/hashx.h +++ b/src/ext/equix/hashx/include/hashx.h @@ -15,14 +15,13 @@ int main() { char seed[] = "this is a seed that will generate a hash function"; char hash[HASHX_SIZE]; - hashx_ctx* ctx = hashx_alloc(HASHX_COMPILED); - if (ctx == HASHX_NOTSUPP) - ctx = hashx_alloc(HASHX_INTERPRETED); + hashx_ctx* ctx = hashx_alloc(HASHX_TRY_COMPILE); if (ctx == NULL) return 1; - if (!hashx_make(ctx, seed, sizeof(seed))) + if (hashx_make(ctx, seed, sizeof(seed)) != EQUIX_OK) + return 1; + if (hashx_exec(ctx, 123456789, hash) != EQUIX_OK) return 1; - hashx_exec(ctx, 123456789, hash); hashx_free(ctx); for (unsigned i = 0; i < HASHX_SIZE; ++i) printf("%02x", hash[i] & 0xff); @@ -58,14 +57,21 @@ /* Opaque struct representing a HashX instance */ typedef struct hashx_ctx hashx_ctx; -/* Type of hash function */ +/* Type of hash context / type of compiled function */ typedef enum hashx_type { - HASHX_INTERPRETED, - HASHX_COMPILED + HASHX_TYPE_INTERPRETED = 1, /* Only the interpreted implementation */ + HASHX_TYPE_COMPILED, /* Require the compiler, fail if unavailable */ + HASHX_TRY_COMPILE, /* (hashx_alloc) Try compiler, don't require */ } hashx_type; -/* Sentinel value used to indicate unsupported type */ -#define HASHX_NOTSUPP ((hashx_ctx*)-1) +/* Result code for hashx_make and hashx_exec */ +typedef enum hashx_result { + HASHX_OK = 0, + HASHX_FAIL_UNPREPARED, /* Trying to run an unmade hash funciton */ + HASHX_FAIL_UNDEFINED, /* Unrecognized hashx_type enum value */ + HASHX_FAIL_SEED, /* Can't construct a hash function from this seed */ + HASHX_FAIL_COMPILE, /* Can't compile, and no fallback is enabled. */ +} hashx_result; #if defined(_WIN32) || defined(__CYGWIN__) #define HASHX_WIN @@ -100,35 +106,65 @@ extern "C" { * @param type is the type of instance to be created. * * @return pointer to a new HashX instance. Returns NULL on memory allocation - * failure and HASHX_NOTSUPP if the requested type is not supported. -*/ + * failures only. Other failures are reported in hashx_make. + */ HASHX_API hashx_ctx* hashx_alloc(hashx_type type); /* - * Create a new HashX function from seed. + * Create a new HashX function from a variable-length seed value. + * + * The seed value will be hashed internally in order to initialize the state + * of the HashX program generator and create a new unique hash function. * * @param ctx is pointer to a HashX instance. * @param seed is a pointer to the seed value. * @param size is the size of the seed. * - * @return 1 on success, 0 on failure. + * @return HASHX_OK on success, HASHX_FAIL_SEED if the specific seed is + * not associated with a valid hash program, and HASHX_FAIL_COMPILE + * if the compiler failed for OS-specific reasons and the interpreter + * fallback was disabled by allocating the context with + * HASHX_TYPE_COMPILED rather than HASHX_TRY_COMPILE. + */ +HASHX_API hashx_result hashx_make(hashx_ctx* ctx, + const void* seed, size_t size); + +/* + * Asks the specific implementation of a function created with hashx_make. + * + * This will equal the parameter to hashx_alloc() if a specific type was + * chosen there, but a context allocated with HASHX_TRY_COMPILE will allow + * the implementation to vary dynamically during hashx_make. + * + * @param ctx is pointer to a HashX instance. + * @param type_out is a pointer to which, on success, we write + * a HASHX_TYPE_* value. + * + * @return HASHX_OK on success, or HASHX_FAIL_UNPREPARED if hashx_make has not + * been invoked successfully on this context. */ -HASHX_API int hashx_make(hashx_ctx* ctx, const void* seed, size_t size); +HASHX_API hashx_result hashx_query_type(hashx_ctx* ctx, hashx_type *type_out); /* * Execute the HashX function. * * @param ctx is pointer to a HashX instance. A HashX function must have - * been previously created by calling hashx_make. + * been previously created by invoking hashx_make successfully. * @param HASHX_INPUT is the input to be hashed (see definition above). * @param output is a pointer to the result buffer. HASHX_SIZE bytes will be * written. - s*/ -HASHX_API void hashx_exec(const hashx_ctx* ctx, HASHX_INPUT, void* output); + * + * @return HASHX_OK on success, or HASHX_FAIL_UNPREPARED if hashx_make has not + * been invoked successfully on this context. + */ +HASHX_API hashx_result hashx_exec(const hashx_ctx* ctx, + HASHX_INPUT, void* output); /* * Free a HashX instance. * + * Has no effect if ctx is NULL. + * * @param ctx is pointer to a HashX instance. */ HASHX_API void hashx_free(hashx_ctx* ctx); diff --git a/src/ext/equix/hashx/src/bench.c b/src/ext/equix/hashx/src/bench.c index fbcd41a064..a440825436 100644 --- a/src/ext/equix/hashx/src/bench.c +++ b/src/ext/equix/hashx/src/bench.c @@ -5,6 +5,7 @@ #include "hashx_thread.h" #include "hashx_endian.h" #include "hashx_time.h" +#include <assert.h> #include <limits.h> #include <inttypes.h> @@ -26,16 +27,31 @@ static hashx_thread_retval worker(void* args) { job->total_hashes = 0; job->best_hash = UINT64_MAX; for (int seed = job->start; seed < job->end; seed += job->step) { - if (!hashx_make(job->ctx, &seed, sizeof(seed))) { - continue; + { + hashx_result result = hashx_make(job->ctx, &seed, sizeof(seed)); + if (result == HASHX_FAIL_SEED) { + continue; + } + if (result == HASHX_FAIL_COMPILE) { + printf("Error: not supported. Try with --interpret\n"); + } + assert(result == HASHX_OK); + if (result != HASHX_OK) + break; } for (int nonce = 0; nonce < job->nonces; ++nonce) { uint8_t hash[HASHX_SIZE] = { 0 }; + { #ifndef HASHX_BLOCK_MODE - hashx_exec(job->ctx, nonce, hash); + hashx_result result = hashx_exec(job->ctx, nonce, hash); #else - hashx_exec(job->ctx, &nonce, sizeof(nonce), hash); + hashx_result result = hashx_exec(job->ctx, + &nonce, sizeof(nonce), hash); #endif + assert(result == HASHX_OK); + if (result != HASHX_OK) + break; + } uint64_t hashval = load64(hash); if (hashval < job->best_hash) { job->best_hash = hashval; @@ -70,9 +86,9 @@ int main(int argc, char** argv) { read_int_option("--nonces", argc, argv, &nonces, 65536); read_int_option("--threads", argc, argv, &threads, 1); read_option("--interpret", argc, argv, &interpret); - hashx_type flags = HASHX_INTERPRETED; + hashx_type ctx_type = HASHX_TYPE_INTERPRETED; if (!interpret) { - flags = HASHX_COMPILED; + ctx_type = HASHX_TYPE_COMPILED; } uint64_t best_hash = UINT64_MAX; uint64_t diff_ex = (uint64_t)diff * 1000ULL; @@ -88,15 +104,11 @@ int main(int argc, char** argv) { return 1; } for (int thd = 0; thd < threads; ++thd) { - jobs[thd].ctx = hashx_alloc(flags); + jobs[thd].ctx = hashx_alloc(ctx_type); if (jobs[thd].ctx == NULL) { printf("Error: memory allocation failure\n"); return 1; } - if (jobs[thd].ctx == HASHX_NOTSUPP) { - printf("Error: not supported. Try with --interpret\n"); - return 1; - } jobs[thd].id = thd; jobs[thd].start = start + thd; jobs[thd].step = threads; diff --git a/src/ext/equix/hashx/src/compiler.c b/src/ext/equix/hashx/src/compiler.c index f180bf2d25..870f3654b6 100644 --- a/src/ext/equix/hashx/src/compiler.c +++ b/src/ext/equix/hashx/src/compiler.c @@ -8,11 +8,12 @@ #include "program.h" #include "context.h" -bool hashx_compiler_init(hashx_ctx* ctx) { - ctx->code = hashx_vm_alloc(COMP_CODE_SIZE); - return ctx->code != NULL; +void hashx_compiler_init(hashx_ctx* ctx) { + /* This can fail, but it's uncommon. We report this up the call chain + * later, at the same time as an mprotect or similar failure. */ + ctx->compiler_mem = hashx_vm_alloc(COMP_CODE_SIZE); } void hashx_compiler_destroy(hashx_ctx* ctx) { - hashx_vm_free(ctx->code, COMP_CODE_SIZE); + hashx_vm_free(ctx->compiler_mem, COMP_CODE_SIZE); } diff --git a/src/ext/equix/hashx/src/compiler.h b/src/ext/equix/hashx/src/compiler.h index 140f797a58..f248e8c1ea 100644 --- a/src/ext/equix/hashx/src/compiler.h +++ b/src/ext/equix/hashx/src/compiler.h @@ -15,19 +15,16 @@ HASHX_PRIVATE bool hashx_compile_x86(const hashx_program* program, uint8_t* code HASHX_PRIVATE bool hashx_compile_a64(const hashx_program* program, uint8_t* code); #if defined(_M_X64) || defined(__x86_64__) -#define HASHX_COMPILER 1 #define HASHX_COMPILER_X86 #define hashx_compile(p,c) hashx_compile_x86(p,c) #elif defined(__aarch64__) -#define HASHX_COMPILER 1 #define HASHX_COMPILER_A64 #define hashx_compile(p,c) hashx_compile_a64(p,c) #else -#define HASHX_COMPILER 0 #define hashx_compile(p,c) (false) #endif -HASHX_PRIVATE bool hashx_compiler_init(hashx_ctx* compiler); +HASHX_PRIVATE void hashx_compiler_init(hashx_ctx* compiler); HASHX_PRIVATE void hashx_compiler_destroy(hashx_ctx* compiler); #define COMP_PAGE_SIZE 4096 diff --git a/src/ext/equix/hashx/src/context.c b/src/ext/equix/hashx/src/context.c index 8548fb7ffa..03a9de57fd 100644 --- a/src/ext/equix/hashx/src/context.c +++ b/src/ext/equix/hashx/src/context.c @@ -33,50 +33,25 @@ const blake2b_param hashx_blake2_params = { }; hashx_ctx* hashx_alloc(hashx_type type) { - if (!HASHX_COMPILER && (type & HASHX_COMPILED)) { - return HASHX_NOTSUPP; - } hashx_ctx* ctx = malloc(sizeof(hashx_ctx)); - if (ctx == NULL) { - goto failure; - } - ctx->code = NULL; - ctx->type = 0; - if (type & HASHX_COMPILED) { - if (!hashx_compiler_init(ctx)) { - goto failure; - } - ctx->type = HASHX_COMPILED; - } - else { - ctx->program = malloc(sizeof(hashx_program)); - if (ctx->program == NULL) { - goto failure; - } - ctx->type = HASHX_INTERPRETED; + if (ctx == NULL) + return NULL; + + memset(ctx, 0, sizeof *ctx); + ctx->ctx_type = type; + if (type == HASHX_TYPE_COMPILED || type == HASHX_TRY_COMPILE) { + hashx_compiler_init(ctx); } + #ifdef HASHX_BLOCK_MODE memcpy(&ctx->params, &hashx_blake2_params, 32); #endif -#ifndef NDEBUG - ctx->has_program = false; -#endif return ctx; -failure: - hashx_free(ctx); - return NULL; } void hashx_free(hashx_ctx* ctx) { - if (ctx != NULL && ctx != HASHX_NOTSUPP) { - if (ctx->code != NULL) { - if (ctx->type & HASHX_COMPILED) { - hashx_compiler_destroy(ctx); - } - else { - free(ctx->program); - } - } + if (ctx != NULL) { + hashx_compiler_destroy(ctx); free(ctx); } } diff --git a/src/ext/equix/hashx/src/context.h b/src/ext/equix/hashx/src/context.h index 40736397f8..ad434eb66c 100644 --- a/src/ext/equix/hashx/src/context.h +++ b/src/ext/equix/hashx/src/context.h @@ -9,8 +9,7 @@ #include "hashx.h" #include "blake2.h" #include "siphash.h" - -typedef void program_func(uint64_t r[8]); +#include "program.h" #ifdef __cplusplus extern "C" { @@ -26,20 +25,15 @@ typedef struct hashx_program hashx_program; /* HashX context. */ typedef struct hashx_ctx { - union { - uint8_t* code; - program_func* func; - hashx_program* program; - }; - hashx_type type; + uint8_t* compiler_mem; + hashx_type ctx_type; + hashx_type func_type; + hashx_program program; #ifndef HASHX_BLOCK_MODE siphash_state keys; #else blake2b_param params; #endif -#ifndef NDEBUG - bool has_program; -#endif } hashx_ctx; #endif diff --git a/src/ext/equix/hashx/src/hashx.c b/src/ext/equix/hashx/src/hashx.c index 372df09581..36a32fc298 100644 --- a/src/ext/equix/hashx/src/hashx.c +++ b/src/ext/equix/hashx/src/hashx.c @@ -22,25 +22,20 @@ #define HASHX_INPUT_ARGS input, size #endif -static int initialize_program(hashx_ctx* ctx, hashx_program* program, - siphash_state keys[2]) { - - if (!hashx_program_generate(&keys[0], program)) { - return 0; +static bool initialize_program(hashx_ctx* ctx, siphash_state keys[2]) { + if (!hashx_program_generate(&keys[0], &ctx->program)) { + return false; } #ifndef HASHX_BLOCK_MODE memcpy(&ctx->keys, &keys[1], 32); #else memcpy(&ctx->params.salt, &keys[1], 32); #endif -#ifndef NDEBUG - ctx->has_program = true; -#endif - return 1; + return true; } -int hashx_make(hashx_ctx* ctx, const void* seed, size_t size) { - assert(ctx != NULL && ctx != HASHX_NOTSUPP); +hashx_result hashx_make(hashx_ctx* ctx, const void* seed, size_t size) { + assert(ctx != NULL); assert(seed != NULL || size == 0); uint8_t keys_bytes[2 * sizeof(siphash_state)]; @@ -59,23 +54,48 @@ int hashx_make(hashx_ctx* ctx, const void* seed, size_t size) { keys[1].v2 = load64(keys_bytes + 6 * sizeof(uint64_t)); keys[1].v3 = load64(keys_bytes + 7 * sizeof(uint64_t)); - if (ctx->type & HASHX_COMPILED) { - hashx_program program; - if (!initialize_program(ctx, &program, keys)) { - return 0; + ctx->func_type = (hashx_type)0; + if (!initialize_program(ctx, keys)) { + return HASHX_FAIL_SEED; + } + + switch (ctx->ctx_type) { + case HASHX_TYPE_INTERPRETED: + ctx->func_type = HASHX_TYPE_INTERPRETED; + return HASHX_OK; + case HASHX_TYPE_COMPILED: + case HASHX_TRY_COMPILE: + if (ctx->compiler_mem != NULL && + hashx_compile(&ctx->program, ctx->compiler_mem)) { + ctx->func_type = HASHX_TYPE_COMPILED; + return HASHX_OK; } - if (!hashx_compile(&program, ctx->code)) { - return 0; + if (ctx->ctx_type == HASHX_TRY_COMPILE) { + ctx->func_type = HASHX_TYPE_INTERPRETED; + return HASHX_OK; + } else { + return HASHX_FAIL_COMPILE; } - return 1; + default: + return HASHX_FAIL_UNDEFINED; + } +} + +hashx_result hashx_query_type(hashx_ctx* ctx, hashx_type *type_out) { + assert(ctx != NULL); + assert(type_out != NULL); + + if (ctx->func_type == (hashx_type)0) { + return HASHX_FAIL_UNPREPARED; } - return initialize_program(ctx, ctx->program, keys); + *type_out = ctx->func_type; + return HASHX_OK; } -void hashx_exec(const hashx_ctx* ctx, HASHX_INPUT, void* output) { - assert(ctx != NULL && ctx != HASHX_NOTSUPP); +hashx_result hashx_exec(const hashx_ctx* ctx, HASHX_INPUT, void* output) { + assert(ctx != NULL); assert(output != NULL); - assert(ctx->has_program); + uint64_t r[8]; #ifndef HASHX_BLOCK_MODE hashx_siphash24_ctr_state512(&ctx->keys, input, r); @@ -83,11 +103,14 @@ void hashx_exec(const hashx_ctx* ctx, HASHX_INPUT, void* output) { hashx_blake2b_4r(&ctx->params, input, size, r); #endif - if (ctx->type & HASHX_COMPILED) { - ctx->func(r); - } - else { - hashx_program_execute(ctx->program, r); + if (ctx->func_type == HASHX_TYPE_COMPILED) { + typedef void program_func(uint64_t r[8]); + assert(ctx->compiler_mem != NULL); + ((program_func*)ctx->compiler_mem)(r); + } else if (ctx->func_type == HASHX_TYPE_INTERPRETED) { + hashx_program_execute(&ctx->program, r); + } else { + return HASHX_FAIL_UNPREPARED; } /* Hash finalization to remove bias toward 0 caused by multiplications */ @@ -145,4 +168,5 @@ void hashx_exec(const hashx_ctx* ctx, HASHX_INPUT, void* output) { memcpy(output, temp_out, HASHX_SIZE); #endif #endif + return HASHX_OK; } diff --git a/src/ext/equix/hashx/src/tests.c b/src/ext/equix/hashx/src/tests.c index e1569844ac..f0a4ebe713 100644 --- a/src/ext/equix/hashx/src/tests.c +++ b/src/ext/equix/hashx/src/tests.c @@ -14,6 +14,7 @@ static int test_no = 0; static hashx_ctx* ctx_int = NULL; static hashx_ctx* ctx_cmp = NULL; +static hashx_ctx* ctx_auto = NULL; static const char seed1[] = "This is a test"; static const char seed2[] = "Lorem ipsum dolor sit amet"; @@ -42,20 +43,21 @@ static void run_test(const char* name, test_func* func) { } static bool test_alloc() { - ctx_int = hashx_alloc(HASHX_INTERPRETED); - assert(ctx_int != NULL && ctx_int != HASHX_NOTSUPP); + ctx_int = hashx_alloc(HASHX_TYPE_INTERPRETED); + assert(ctx_int != NULL); return true; } static bool test_free() { hashx_free(ctx_int); hashx_free(ctx_cmp); + hashx_free(ctx_auto); return true; } static bool test_make1() { - int result = hashx_make(ctx_int, seed1, sizeof(seed1)); - assert(result == 1); + hashx_result result = hashx_make(ctx_int, seed1, sizeof(seed1)); + assert(result == HASHX_OK); return true; } @@ -65,7 +67,8 @@ static bool test_hash_ctr1() { #endif #ifndef HASHX_BLOCK_MODE char hash[HASHX_SIZE]; - hashx_exec(ctx_int, counter2, hash); + hashx_result result = hashx_exec(ctx_int, counter2, hash); + assert(result == HASHX_OK); /* printf("\n"); output_hex(hash, HASHX_SIZE); printf("\n"); */ @@ -82,7 +85,8 @@ static bool test_hash_ctr2() { #endif #ifndef HASHX_BLOCK_MODE char hash[HASHX_SIZE]; - hashx_exec(ctx_int, counter1, hash); + hashx_result result = hashx_exec(ctx_int, counter1, hash); + assert(result == HASHX_OK); assert(equals_hex(hash, "2b2f54567dcbea98fdb5d5e5ce9a65983c4a4e35ab1464b1efb61e83b7074bb2")); return true; #else @@ -91,8 +95,8 @@ static bool test_hash_ctr2() { } static bool test_make2() { - int result = hashx_make(ctx_int, seed2, sizeof(seed2)); - assert(result == 1); + hashx_result result = hashx_make(ctx_int, seed2, sizeof(seed2)); + assert(result == HASHX_OK); return true; } @@ -102,7 +106,8 @@ static bool test_hash_ctr3() { #endif #ifndef HASHX_BLOCK_MODE char hash[HASHX_SIZE]; - hashx_exec(ctx_int, counter2, hash); + hashx_result result = hashx_exec(ctx_int, counter2, hash); + assert(result == HASHX_OK); assert(equals_hex(hash, "ab3d155bf4bbb0aa3a71b7801089826186e44300e6932e6ffd287cf302bbb0ba")); return true; #else @@ -116,7 +121,8 @@ static bool test_hash_ctr4() { #endif #ifndef HASHX_BLOCK_MODE char hash[HASHX_SIZE]; - hashx_exec(ctx_int, counter3, hash); + hashx_result result = hashx_exec(ctx_int, counter3, hash); + assert(result == HASHX_OK); assert(equals_hex(hash, "8dfef0497c323274a60d1d93292b68d9a0496379ba407b4341cf868a14d30113")); return true; #else @@ -132,36 +138,40 @@ static bool test_hash_block1() { return false; #else char hash[HASHX_SIZE]; - hashx_exec(ctx_int, long_input, sizeof(long_input), hash); + hashx_result result = hashx_exec(ctx_int, long_input, sizeof(long_input), hash); + assert(result == HASHX_OK); assert(equals_hex(hash, "d0b232b832459501ca1ac9dc0429fd931414ead7624a457e375a43ea3e5e737a")); return true; #endif } static bool test_alloc_compiler() { - ctx_cmp = hashx_alloc(HASHX_COMPILED); + ctx_cmp = hashx_alloc(HASHX_TYPE_COMPILED); assert(ctx_cmp != NULL); - return ctx_cmp != HASHX_NOTSUPP; + return true; } static bool test_make3() { - if (ctx_cmp == HASHX_NOTSUPP) + hashx_result result = hashx_make(ctx_cmp, seed2, sizeof(seed2)); + if (result == HASHX_FAIL_COMPILE) { return false; - - int result = hashx_make(ctx_cmp, seed2, sizeof(seed2)); - assert(result == 1); + } + assert(result == HASHX_OK); return true; } static bool test_compiler_ctr1() { - if (ctx_cmp == HASHX_NOTSUPP) - return false; - #ifndef HASHX_BLOCK_MODE + hashx_result result; char hash1[HASHX_SIZE]; char hash2[HASHX_SIZE]; - hashx_exec(ctx_int, counter2, hash1); - hashx_exec(ctx_cmp, counter2, hash2); + result = hashx_exec(ctx_int, counter2, hash1); + assert(result == HASHX_OK); + result = hashx_exec(ctx_cmp, counter2, hash2); + if (result == HASHX_FAIL_UNPREPARED) { + return false; + } + assert(result == HASHX_OK); assert(hashes_equal(hash1, hash2)); return true; #else @@ -170,14 +180,17 @@ static bool test_compiler_ctr1() { } static bool test_compiler_ctr2() { - if (ctx_cmp == HASHX_NOTSUPP) - return false; - #ifndef HASHX_BLOCK_MODE + hashx_result result; char hash1[HASHX_SIZE]; char hash2[HASHX_SIZE]; - hashx_exec(ctx_int, counter1, hash1); - hashx_exec(ctx_cmp, counter1, hash2); + result = hashx_exec(ctx_int, counter1, hash1); + assert(result == HASHX_OK); + result = hashx_exec(ctx_cmp, counter1, hash2); + if (result == HASHX_FAIL_UNPREPARED) { + return false; + } + assert(result == HASHX_OK); assert(hashes_equal(hash1, hash2)); return true; #else @@ -186,20 +199,58 @@ static bool test_compiler_ctr2() { } static bool test_compiler_block1() { - if (ctx_cmp == HASHX_NOTSUPP) - return false; #ifndef HASHX_BLOCK_MODE return false; #else + hashx_result result; char hash1[HASHX_SIZE]; char hash2[HASHX_SIZE]; - hashx_exec(ctx_int, long_input, sizeof(long_input), hash1); - hashx_exec(ctx_cmp, long_input, sizeof(long_input), hash2); + result = hashx_exec(ctx_int, long_input, sizeof(long_input), hash1); + assert(result == HASHX_OK); + result = hashx_exec(ctx_cmp, long_input, sizeof(long_input), hash2); + if (result == HASHX_FAIL_UNPREPARED) { + return false; + } + assert(result == HASHX_OK); assert(hashes_equal(hash1, hash2)); return true; #endif } +static bool test_alloc_automatic() { + ctx_auto = hashx_alloc(HASHX_TRY_COMPILE); + assert(ctx_auto != NULL); + return true; +} + +static bool test_auto_fallback() { + hashx_result result = hashx_make(ctx_auto, seed2, sizeof(seed2)); + assert(result == HASHX_OK); + hashx_type actual_type = (hashx_type)-1; + result = hashx_query_type(ctx_auto, &actual_type); + assert(result == HASHX_OK); + assert(actual_type == HASHX_TYPE_INTERPRETED || + actual_type == HASHX_TYPE_COMPILED); + return actual_type == HASHX_TYPE_INTERPRETED; +} + +static bool test_bad_seeds() { +#ifdef HASHX_SALT + return false; +#else + hashx_result result; + result = hashx_make(ctx_auto, "\xf8\x05\x00\x00", 4); + assert(result == HASHX_OK); + result = hashx_make(ctx_auto, "\xf9\x05\x00\x00", 4); + assert(result == HASHX_FAIL_SEED); + result = hashx_make(ctx_auto, "\x5d\x93\x02\x00", 4); + assert(result == HASHX_FAIL_SEED); + result = hashx_make(ctx_auto, "\x5e\x93\x02\x00", 4); + assert(result == HASHX_OK); + return true; +#endif +} + int main() { RUN_TEST(test_alloc); RUN_TEST(test_make1); @@ -214,6 +265,9 @@ int main() { RUN_TEST(test_compiler_ctr2); RUN_TEST(test_hash_block1); RUN_TEST(test_compiler_block1); + RUN_TEST(test_alloc_automatic); + RUN_TEST(test_auto_fallback); + RUN_TEST(test_bad_seeds); RUN_TEST(test_free); printf("\nAll tests were successful\n"); diff --git a/src/ext/equix/hashx/src/virtual_memory.c b/src/ext/equix/hashx/src/virtual_memory.c index 8d574740c5..e9df825c9f 100644 --- a/src/ext/equix/hashx/src/virtual_memory.c +++ b/src/ext/equix/hashx/src/virtual_memory.c @@ -120,6 +120,9 @@ void* hashx_vm_alloc_huge(size_t bytes) { } void hashx_vm_free(void* ptr, size_t bytes) { + if (!ptr) { + return; + } #ifdef HASHX_WIN (void)bytes; VirtualFree(ptr, 0, MEM_RELEASE); |