* @brief Hashes a string and compares the output between a serial and hardware-specific SIMD backend. * * The test covers increasingly long and complex strings, starting with "abcabc..." repetitions and * progressing towards corner cases like empty strings, all-zero inputs, zero seeds, and so on. */
| 259 | * progressing towards corner cases like empty strings, all-zero inputs, zero seeds, and so on. |
| 260 | */ |
| 261 | void test_hash_equivalence( // |
| 262 | sz_hash_t hash_base, sz_hash_state_init_t init_base, // |
| 263 | sz_hash_state_update_t stream_base, sz_hash_state_digest_t fold_base, // |
| 264 | sz_hash_t hash_simd, sz_hash_state_init_t init_simd, // |
| 265 | sz_hash_state_update_t stream_simd, sz_hash_state_digest_t fold_simd) { |
| 266 | |
| 267 | auto test_on_seed = [&](std::string text, sz_u64_t seed) { |
| 268 | // Compute the entire hash at once, expecting the same output |
| 269 | sz_u64_t result_base = hash_base(text.data(), text.size(), seed); |
| 270 | sz_u64_t result_simd = hash_simd(text.data(), text.size(), seed); |
| 271 | assert(result_base == result_simd); |
| 272 | |
| 273 | // Compare incremental hashing across platforms |
| 274 | sz_hash_state_t state_base, state_simd; |
| 275 | init_base(&state_base, seed); |
| 276 | init_simd(&state_simd, seed); |
| 277 | assert(sz_hash_state_equal(&state_base, &state_base) == sz_true_k); // Self-equality |
| 278 | assert(sz_hash_state_equal(&state_simd, &state_simd) == sz_true_k); // Self-equality |
| 279 | assert(sz_hash_state_equal(&state_base, &state_simd) == sz_true_k); // Same across platforms |
| 280 | |
| 281 | // Let's also create an intentionally misaligned version of the state, |
| 282 | // assuming some of the SIMD instructions may require alignment. |
| 283 | sz_align_(64) char state_misaligned_buffer[sizeof(sz_hash_state_t) + 1]; |
| 284 | sz_hash_state_t &state_misaligned = *reinterpret_cast<sz_hash_state_t *>(state_misaligned_buffer + 1); |
| 285 | init_simd(&state_misaligned, seed); |
| 286 | assert(sz_hash_state_equal(&state_base, &state_misaligned) == sz_true_k); // Same across platforms |
| 287 | |
| 288 | // Try breaking those strings into arbitrary chunks, expecting the same output in the streaming mode. |
| 289 | // The length of each chunk and the number of chunks will be determined with a coin toss. |
| 290 | iterate_in_random_slices(text, [&](std::string slice) { |
| 291 | stream_base(&state_base, slice.data(), slice.size()); |
| 292 | stream_simd(&state_simd, slice.data(), slice.size()); |
| 293 | assert(sz_hash_state_equal(&state_base, &state_simd) == sz_true_k); // Same across platforms |
| 294 | |
| 295 | stream_simd(&state_misaligned, slice.data(), slice.size()); |
| 296 | assert(sz_hash_state_equal(&state_base, &state_misaligned) == sz_true_k); // Same across platforms |
| 297 | |
| 298 | result_base = fold_base(&state_base); |
| 299 | result_simd = fold_simd(&state_simd); |
| 300 | assert(result_base == result_simd); |
| 301 | sz_u64_t result_misaligned = fold_simd(&state_misaligned); |
| 302 | assert(result_base == result_misaligned); |
| 303 | }); |
| 304 | }; |
| 305 | |
| 306 | // Let's try different-length strings repeating a "abc" pattern: |
| 307 | std::vector<sz_u64_t> seeds = { |
| 308 | 0u, |
| 309 | 42u, // |
| 310 | std::numeric_limits<sz_u32_t>::max(), // |
| 311 | std::numeric_limits<sz_u64_t>::max(), // |
| 312 | }; |
| 313 | for (auto seed : seeds) |
| 314 | for (std::size_t copies = 1; copies != 100; ++copies) // |
| 315 | test_on_seed(repeat("abc", copies), seed); |
| 316 | |
| 317 | // Let's try truly random inputs of different lengths: |
| 318 | for (std::size_t length = 0; length != 200; ++length) { |
no test coverage detected
searching dependent graphs…