I had a case where like half of the time was spent in case-insensitive text comparison. But it was doing a case-insensitive Pos. For just 10 kilobytes of text, it would call the text comparison 10000 times. It became much faster when I changed it to compare the first character before calling the full comparison function
Could you not have lowercased the entire string once, perform the search/pos and take the results on the mixed case string?
Of course that does not work for Unicode where a lowercase char may have a different byte-len than its upper char. But since the "dec(c1, 32);" only works for English anyway...
Maybe even more clever (again leaving out stuff like normalization), get the "lookup char" (for wich you want the pos in the long string) in both: upper and lower (that can still be done via true upper/lower handling all languages). Then compare each pos in the string against the 2 lookup chars. That should save almost all upper/lower conversions.