compileASCIIStringSetPrefixFilter builds a byte-oriented multi-prefix scanner for the narrow shape where it beats running strings.Index once per prefix: case-sensitive ASCII prefixes with at least two prefixes sharing a first byte. It indexes possible first bytes with strings.IndexAny, then verifies
(prefixes []string, ignoreCase bool, minRequiredLength int)
| 223 | // It indexes possible first bytes with strings.IndexAny, then verifies only the |
| 224 | // bucket for the byte found. Other shapes fall back to the old implementation. |
| 225 | func compileASCIIStringSetPrefixFilter(prefixes []string, ignoreCase bool, minRequiredLength int) (*asciiStringSetPrefixFilter, bool) { |
| 226 | if ignoreCase { |
| 227 | return nil, false |
| 228 | } |
| 229 | |
| 230 | filter := &asciiStringSetPrefixFilter{ |
| 231 | minRequiredBytes: minRequiredLength, |
| 232 | } |
| 233 | var firstChars [256]bool |
| 234 | var hasSharedFirst bool |
| 235 | for _, prefix := range prefixes { |
| 236 | if prefix == "" || !isASCIIString(prefix) { |
| 237 | return nil, false |
| 238 | } |
| 239 | |
| 240 | first := prefix[0] |
| 241 | filter.prefixesByFirst[first] = append(filter.prefixesByFirst[first], prefix) |
| 242 | if len(filter.prefixesByFirst[first]) > 1 { |
| 243 | hasSharedFirst = true |
| 244 | } |
| 245 | firstChars[first] = true |
| 246 | } |
| 247 | |
| 248 | if !hasSharedFirst { |
| 249 | return nil, false |
| 250 | } |
| 251 | |
| 252 | firstBytes := make([]byte, 0, len(prefixes)*2) |
| 253 | for i, ok := range firstChars { |
| 254 | if ok { |
| 255 | firstBytes = append(firstBytes, byte(i)) |
| 256 | } |
| 257 | } |
| 258 | if len(firstBytes) == 0 { |
| 259 | return nil, false |
| 260 | } |
| 261 | filter.firstChars = string(firstBytes) |
| 262 | return filter, true |
| 263 | } |
| 264 | |
| 265 | func (f *asciiStringSetPrefixFilter) index(input string, startAt int) (candidateByteIndex int, ok bool) { |
| 266 | if !hasMinRequiredBytes(input, startAt, f.minRequiredBytes) { |
no test coverage detected