* @brief Fuzz tests case-insensitive UTF-8 substring search with controlled haystack sizes. * * Uses two verification modes: * - Exhaustive (max_needles_per_haystack == 0): Tests ALL N*(N+1)/2 substrings of each folded haystack * - Sampled (max_needles_per_haystack > 0): Tests up to that many random substrings per haystack * * Algorithm: * 1. Generate random haystack of ~haystack_len
| 775 | * @param total_queries Total needle searches to perform across all haystacks |
| 776 | */ |
| 777 | void test_utf8_ci_find_fuzz(sz_utf8_case_insensitive_find_t find_serial, sz_utf8_case_insensitive_find_t find_simd, |
| 778 | sz_utf8_case_fold_t case_fold, sz_utf8_find_nth_t utf8_find_nth, sz_utf8_count_t utf8_count, |
| 779 | std::size_t haystack_length, std::size_t max_needles_per_haystack, |
| 780 | std::size_t total_queries) { |
| 781 | |
| 782 | char const *mode = max_needles_per_haystack == 0 ? "exhaustive" : "sampled"; |
| 783 | std::printf(" - fuzz testing (%s, haystack_len=%zu, queries=%zu)...\n", mode, haystack_length, total_queries); |
| 784 | |
| 785 | auto &rng = global_random_generator(); |
| 786 | |
| 787 | // Character pool with normal + weird Unicode characters from safety profiles |
| 788 | char const *char_pool[] = { |
| 789 | // Normal ASCII (individual characters for mixing) |
| 790 | // clang-format off |
| 791 | "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", |
| 792 | "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", |
| 793 | "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", |
| 794 | "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", |
| 795 | "0", "1", "2", "3", " ", ".", ",", "!", "?", |
| 796 | // clang-format on |
| 797 | // ASCII words for realistic text patterns |
| 798 | "Hello", |
| 799 | "World", |
| 800 | "the", |
| 801 | "quick", |
| 802 | "brown", |
| 803 | "fox", |
| 804 | "jumps", |
| 805 | |
| 806 | // Folded representations of multi-byte danger chars (ASCII equivalents) |
| 807 | // These are what the complex characters fold TO - essential for testing both directions |
| 808 | "ss", // ß (U+00DF, C3 9F) and ẞ (U+1E9E, E1 BA 9E) fold to this |
| 809 | "fi", // fi (U+FB01, EF AC 81) folds to this |
| 810 | "fl", // fl (U+FB02, EF AC 82) folds to this |
| 811 | "ff", // ff (U+FB00, EF AC 80) folds to this |
| 812 | "ffi", // ffi (U+FB03, EF AC 83) folds to this |
| 813 | "ffl", // ffl (U+FB04, EF AC 84) folds to this |
| 814 | "st", // ſt (U+FB05), st (U+FB06) fold to this |
| 815 | "k", // K (U+212A, E2 84 AA) Kelvin folds to this |
| 816 | "s", // ſ (U+017F, C5 BF) Long S folds to this |
| 817 | |
| 818 | // Latin-1/Extended (Western European) |
| 819 | "\xC3\x9F", // 'ß' (U+00DF, C3 9F) - Latin Small Letter Sharp S (folds to ss) |
| 820 | "\xC3\xB6", // 'ö' (U+00F6, C3 B6) - Latin Small Letter O with Diaeresis |
| 821 | "\xC3\x96", // 'Ö' (U+00D6, C3 96) - Latin Capital Letter O with Diaeresis |
| 822 | "\xC3\xBC", // 'ü' (U+00FC, C3 BC) - Latin Small Letter U with Diaeresis |
| 823 | "\xC3\x9C", // 'Ü' (U+00DC, C3 9C) - Latin Capital Letter U with Diaeresis |
| 824 | "\xC3\xA4", // 'ä' (U+00E4, C3 A4) - Latin Small Letter A with Diaeresis |
| 825 | "\xC3\x84", // 'Ä' (U+00C4, C3 84) - Latin Capital Letter A with Diaeresis |
| 826 | "\xC3\xA9", // 'é' (U+00E9, C3 A9) - Latin Small Letter E with Acute |
| 827 | "\xC3\x89", // 'É' (U+00C9, C3 89) - Latin Capital Letter E with Acute |
| 828 | "\xC3\xA0", // 'à' (U+00E0, C3 A0) - Latin Small Letter A with Grave |
| 829 | "\xC3\x80", // 'À' (U+00C0, C3 80) - Latin Capital Letter A with Grave |
| 830 | "\xC3\xB1", // 'ñ' (U+00F1, C3 B1) - Latin Small Letter N with Tilde |
| 831 | "\xC3\x91", // 'Ñ' (U+00D1, C3 91) - Latin Capital Letter N with Tilde |
| 832 | "\xC2\xAA", // 'ª' (U+00AA, C2 AA) - Feminine Ordinal Indicator (caseless) |
| 833 | "\xC2\xBA", // 'º' (U+00BA, C2 BA) - Masculine Ordinal Indicator (caseless) |
| 834 | "\xC2\xB5", // 'µ' (U+00B5, C2 B5) - Micro Sign (folds to Greek mu) |