fn find_case_insensitive(haystack: &str, needle_lower: &str) -> Option<usize>Expand description
Case-insensitive substring search returning a byte offset into the original haystack, not into a lowercased copy of it.
The naive version of this (haystack.to_lowercase().find(needle), then
slice haystack at the result) is wrong, and wrong in a way that can
panic: lowercasing is not length-preserving. Turkish İ (U+0130, 2 bytes)
lowercases to i̇ (3 bytes), so every such character before the match
shifts the two strings out of alignment — slicing the original at an offset
derived from the lowercased copy can land mid-character and panic, or
silently return a misaligned snippet. Caught by reagentx P1 on PR #3321.
So the lowercase mapping is built explicitly, recording which original byte each lowercased byte came from.