@@ -31,6 +31,14 @@ internal sealed class SingleStringSearchValuesThreeChars<TValueLength, TCaseSens
3131
3232 private static bool IgnoreCase => typeof ( TCaseSensitivity ) != typeof ( CaseSensitive ) ;
3333
34+ // If the value is short (!TValueLength.AtLeast4Chars => 2 or 3 characters), the anchors already represent the whole value.
35+ // With case-sensitive comparisons, we've therefore already confirmed the match.
36+ // With case-insensitive comparisons, we've applied the CaseConversionMask to the input, so while the anchors likely matched, we can't be sure.
37+ // An exception to that is if we know the value is composed of only ASCII letters, in which case masking the input can't produce false positives.
38+ private static bool CanSkipAnchorMatchVerification =>
39+ ! TValueLength . AtLeast4Chars &&
40+ ( typeof ( TCaseSensitivity ) == typeof ( CaseSensitive ) || typeof ( TCaseSensitivity ) == typeof ( CaseInsensitiveAsciiLetters ) ) ;
41+
3442 public SingleStringSearchValuesThreeChars ( HashSet < string > ? uniqueValues , string value ) : base ( uniqueValues )
3543 {
3644 // We could have more than one entry in 'uniqueValues' if this value is an exact prefix of all the others.
@@ -327,11 +335,7 @@ private bool TryMatch(ref char searchSpaceStart, int searchSpaceLength, ref char
327335
328336 ValidateReadPosition ( ref searchSpaceStart , searchSpaceLength , ref matchRef , _value . Length ) ;
329337
330- // If the value is short (!TValueLength.AtLeast4Chars => 2 or 3 characters), the anchors already represent the whole value.
331- // With case-sensitive comparisons, we've therefore already confirmed the match, so we can skip doing so here.
332- // With case-insensitive comparisons, we applied a mask to the input, so while the anchors likely matched, we can't be sure.
333- if ( ( typeof ( TCaseSensitivity ) == typeof ( CaseSensitive ) && ! TValueLength . AtLeast4Chars ) ||
334- TCaseSensitivity . Equals < TValueLength > ( ref matchRef , _value ) )
338+ if ( CanSkipAnchorMatchVerification || TCaseSensitivity . Equals < TValueLength > ( ref matchRef , _value ) )
335339 {
336340 offsetFromStart = ( int ) ( ( nuint ) Unsafe . ByteOffset ( ref searchSpaceStart , ref matchRef ) / 2 ) ;
337341 return true ;
@@ -359,11 +363,7 @@ private bool TryMatch(ref char searchSpaceStart, int searchSpaceLength, ref char
359363
360364 ValidateReadPosition ( ref searchSpaceStart , searchSpaceLength , ref matchRef , _value . Length ) ;
361365
362- // If the value is short (!TValueLength.AtLeast4Chars => 2 or 3 characters), the anchors already represent the whole value.
363- // With case-sensitive comparisons, we've therefore already confirmed the match, so we can skip doing so here.
364- // With case-insensitive comparisons, we applied a mask to the input, so while the anchors likely matched, we can't be sure.
365- if ( ( typeof ( TCaseSensitivity ) == typeof ( CaseSensitive ) && ! TValueLength . AtLeast4Chars ) ||
366- TCaseSensitivity . Equals < TValueLength > ( ref matchRef , _value ) )
366+ if ( CanSkipAnchorMatchVerification || TCaseSensitivity . Equals < TValueLength > ( ref matchRef , _value ) )
367367 {
368368 offsetFromStart = ( int ) ( ( nuint ) Unsafe . ByteOffset ( ref searchSpaceStart , ref matchRef ) / 2 ) ;
369369 return true ;
0 commit comments