MCPcopy Create free account
hub / github.com/ashvardanian/StringZilla / test_utf8_case_fold_fuzz

Function test_utf8_case_fold_fuzz

scripts/test_stringzilla.cpp:679–757  ·  view source on GitHub ↗

* @brief Exhaustive fuzz test for UTF-8 case folding using all Unicode codepoints. * * - First run: Tests all valid codepoints in order (0x0 to 0x10FFFF). * - Subsequent runs: Shuffles the codepoints to create random sequences. * - Checks for both length and content matches between serial and SIMD implementations. */

Source from the content-addressed store, hash-verified

677 * - Checks for both length and content matches between serial and SIMD implementations.
678 */
679void test_utf8_case_fold_fuzz(sz_utf8_case_fold_t fold_base, sz_utf8_case_fold_t fold_simd,
680 std::size_t iterations = scale_iterations(100)) {
681 std::printf(" - testing case folding fuzz (%zu iterations + ordered check)...\n", iterations);
682
683 // 1. Generate all valid codepoints (ordered initially)
684 std::vector<sz_rune_t> all_runes;
685 all_runes.reserve(0x10FFFF);
686 for (sz_rune_t cp = 0; cp <= 0x10FFFF; ++cp) {
687 if (cp >= 0xD800 && cp <= 0xDFFF) continue; // Skip surrogates
688 all_runes.push_back(cp);
689 }
690
691 // 2. Prepare buffers
692 // Max UTF-8 size is 4 bytes per rune.
693 std::vector<char> input_buffer(all_runes.size() * 4);
694 // Max expansion is handled by the folding functions, usually 3x is safe for worst cases (like µ)
695 std::vector<char> output_base(input_buffer.size() * 3 + 64);
696 std::vector<char> output_simd(input_buffer.size() * 3 + 64);
697
698 auto &rng = global_random_generator();
699
700 // Iterate: 0 = Ordered, 1..N = Shuffled
701 for (std::size_t it = 0; it <= iterations; ++it) {
702 if (it > 0) std::shuffle(all_runes.begin(), all_runes.end(), rng);
703
704 // Convert to UTF-8
705 char *data_ptr = input_buffer.data();
706 for (sz_rune_t cp : all_runes) data_ptr += sz_rune_export(cp, (sz_u8_t *)data_ptr);
707 sz_size_t input_len = data_ptr - input_buffer.data();
708
709 // Run tests
710 sz_size_t len_base = fold_base(input_buffer.data(), input_len, output_base.data());
711 sz_size_t len_simd = fold_simd(input_buffer.data(), input_len, output_simd.data());
712
713 // Validations
714 if (len_base != len_simd) {
715 std::fprintf(stderr, "Iteration %zu: Length mismatch base=%zu simd=%zu\n", it, len_base, len_simd);
716 assert(false);
717 }
718
719 // Optimize: Memcmp first, then diagnose
720 if (std::memcmp(output_base.data(), output_simd.data(), len_base) != 0) {
721 std::fprintf(stderr, "Iteration %zu: Content mismatch\n", it);
722 // Find first mismatch for debug
723 for (std::size_t i = 0; i < len_base; ++i) {
724 if (output_base[i] != output_simd[i]) {
725 std::fprintf(stderr, "Mismatch at byte %zu: 0x%02X vs 0x%02X\n", i, (unsigned char)output_base[i],
726 (unsigned char)output_simd[i]);
727
728 // Show context (16 bytes before/after)
729 sz_size_t start = (i > 16) ? i - 16 : 0;
730 sz_size_t end = (i + 16 < len_base) ? i + 16 : len_base;
731
732 std::fprintf(stderr, "Context (Base): ");
733 for (sz_size_t j = start; j < end; ++j)
734 std::fprintf(stderr, "%02X ", (unsigned char)output_base[j]);
735 std::fprintf(stderr, "\n");
736

Callers 1

test_equivalenceFunction · 0.85

Calls 7

scale_iterationsFunction · 0.85
reserveMethod · 0.80
push_backMethod · 0.80
sizeMethod · 0.45
beginMethod · 0.45
endMethod · 0.45
dataMethod · 0.45

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…