A codepoint is case-agnostic, or caseless, if all three conditions are true:
- Self-folding: case folding produces exactly the original codepoint.
- Not bicameral: it does not belong to any script with case distinctions.
- Not an expansion target: it appears in no multi-rune case fold expansion.
The third condition is subtle but critical. Consider 'ʾ' (U+02BE, CA BE):
- It has no case variant and folds to itself.
- However, 'ẚ' (U+1E9A, E1 BA 9A) → "aʾ" (U+0061 U+02BE, 61 CA BE).
- A needle containing 'ʾ' must match at position 1 of the folded expansion of 'ẚ'.
- Binary search cannot handle this, so 'ʾ' must not be case-agnostic.
Case-agnostic scripts include: CJK ideographs, Hangul, digits, punctuation, most symbols, Hebrew, Arabic, Thai, Hindi (Devanagari), and many other scripts without case distinctions.
The function is conservative: it reports any codepoint that participates in case folding, even if the specific instance wouldn't change. For example, lowercase 'a' is reported because it's a case-folding target.
Use Case
This function enables an important optimization: if both haystack and needle are fully case-agnostic, then sz_find_best can be used directly instead of the slower sz_utf8_uncased_search_best. This is particularly valuable for:
- CJK text (Chinese, Japanese, Korean) - always caseless
- Numeric data and punctuation-heavy content
- Middle Eastern scripts (Arabic, Hebrew, Persian)
- South/Southeast Asian scripts (Thai, Hindi, Vietnamese without Latin)
The optimization pattern:
sz_cptr_t haystack = "价格:¥1234"; // Chinese + punctuation + digits
sz_cptr_t needle = "¥1234";
sz_cptr_t haystack_cased, needle_cased, match;
sz_utf8_find_cased_best(haystack, haystack_length, &haystack_cased, capabilities, STRINGZILLA_NULL);
sz_utf8_find_cased_best(needle, needle_length, &needle_cased, capabilities, STRINGZILLA_NULL);
if (!haystack_cased && !needle_cased) {
// Fast path: both strings are fully caseless, so use binary search
sz_find_best(haystack, haystack_length, needle, needle_length, &match, capabilities, STRINGZILLA_NULL);
} else {
// Slow path: full uncased search
sz_utf8_uncased_needle_t prepared;
sz_size_t match_length;
sz_utf8_uncased_needle_init_best(needle, needle_length, &prepared, capabilities, STRINGZILLA_NULL);
sz_utf8_uncased_search_best(haystack, haystack_length, &prepared, &match, &match_length, capabilities,
STRINGZILLA_NULL);
}