summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ext/equix/hashx/README.md37
-rw-r--r--src/ext/equix/hashx/include/hashx.h72
-rw-r--r--src/ext/equix/hashx/src/bench.c34
-rw-r--r--src/ext/equix/hashx/src/compiler.c9
-rw-r--r--src/ext/equix/hashx/src/compiler.h5
-rw-r--r--src/ext/equix/hashx/src/context.c45
-rw-r--r--src/ext/equix/hashx/src/context.h16
-rw-r--r--src/ext/equix/hashx/src/hashx.c78
-rw-r--r--src/ext/equix/hashx/src/tests.c116
-rw-r--r--src/ext/equix/hashx/src/virtual_memory.c3
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);