Skip to content

Commit

Permalink
Remove IValueLength
Browse files Browse the repository at this point in the history
  • Loading branch information
MihaZupan committed Aug 4, 2023
1 parent 822f5c6 commit 2e70415
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 159 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Text;

namespace System.Buffers
{
Expand Down Expand Up @@ -38,38 +37,13 @@ public static bool StartsWith<TCaseSensitivity>(ref char matchStart, int lengthR
return TCaseSensitivity.Equals(ref matchStart, candidate);
}

public interface IValueLength
{
static abstract bool AtLeast4Chars { get; }
static abstract bool AtLeast8Chars { get; }
}

public readonly struct ValueLengthLessThan4 : IValueLength
{
public static bool AtLeast4Chars => false;
public static bool AtLeast8Chars => false;
}

public readonly struct ValueLength4To7 : IValueLength
{
public static bool AtLeast4Chars => true;
public static bool AtLeast8Chars => false;
}

public readonly struct ValueLength8OrLonger : IValueLength
{
public static bool AtLeast4Chars => true;
public static bool AtLeast8Chars => true;
}

public interface ICaseSensitivity
{
static abstract char TransformInput(char input);
static abstract Vector128<byte> TransformInput(Vector128<byte> input);
static abstract Vector256<byte> TransformInput(Vector256<byte> input);
static abstract Vector512<byte> TransformInput(Vector512<byte> input);
static abstract bool Equals(ref char matchStart, string candidate);
static abstract bool Equals<TValueLength>(ref char matchStart, string candidate) where TValueLength : struct, IValueLength;
}

public readonly struct CaseSensitive : ICaseSensitivity
Expand Down Expand Up @@ -106,37 +80,6 @@ public static bool Equals(ref char matchStart, string candidate)

return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Equals<TValueLength>(ref char matchStart, string candidate)
where TValueLength : struct, IValueLength
{
Debug.Assert(candidate.Length > 1);
Debug.Assert(matchStart == candidate[0], "This should only be called after the first character has been checked");

ref byte first = ref Unsafe.As<char, byte>(ref matchStart);
ref byte second = ref Unsafe.As<char, byte>(ref candidate.GetRawStringData());
nuint byteLength = (nuint)(uint)candidate.Length * 2;

if (TValueLength.AtLeast8Chars)
{
return SpanHelpers.SequenceEqual(ref first, ref second, byteLength);
}
else if (TValueLength.AtLeast4Chars)
{
nuint offset = byteLength - sizeof(ulong);
ulong differentBits = Unsafe.ReadUnaligned<ulong>(ref first) - Unsafe.ReadUnaligned<ulong>(ref second);
differentBits |= Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref first, offset)) - Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref second, offset));
return differentBits == 0;
}
else
{
nuint offset = byteLength - sizeof(uint);

return Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref first, offset))
== Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref second, offset));
}
}
}

public readonly struct CaseInsensitiveAsciiLetters : ICaseSensitivity
Expand Down Expand Up @@ -166,38 +109,6 @@ public static bool Equals(ref char matchStart, string candidate)

return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Equals<TValueLength>(ref char matchStart, string candidate)
where TValueLength : struct, IValueLength
{
Debug.Assert(candidate.Length > 1);
Debug.Assert(candidate.ToUpperInvariant() == candidate);

if (TValueLength.AtLeast8Chars)
{
return Ascii.EqualsIgnoreCase(ref matchStart, ref candidate.GetRawStringData(), (uint)candidate.Length);
}

ref byte first = ref Unsafe.As<char, byte>(ref matchStart);
ref byte second = ref Unsafe.As<char, byte>(ref candidate.GetRawStringData());
nuint byteLength = (nuint)(uint)candidate.Length * 2;

if (TValueLength.AtLeast4Chars)
{
nuint offset = byteLength - sizeof(ulong);
ulong differentBits = (Unsafe.ReadUnaligned<ulong>(ref first) & ~0x20002000200020u) - Unsafe.ReadUnaligned<ulong>(ref second);
differentBits |= (Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref first, offset)) & ~0x20002000200020u) - Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref second, offset));
return differentBits == 0;
}
else
{
nuint offset = byteLength - sizeof(uint);
uint differentBits = (Unsafe.ReadUnaligned<uint>(ref first) & ~0x200020u) - Unsafe.ReadUnaligned<uint>(ref second);
differentBits |= (Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref first, offset)) & ~0x200020u) - Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref second, offset));
return differentBits == 0;
}
}
}

public readonly struct CaseInsensitiveAscii : ICaseSensitivity
Expand Down Expand Up @@ -251,18 +162,6 @@ public static bool Equals(ref char matchStart, string candidate)

return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Equals<TValueLength>(ref char matchStart, string candidate)
where TValueLength : struct, IValueLength
{
if (Vector128.IsHardwareAccelerated && TValueLength.AtLeast8Chars)
{
return Ascii.EqualsIgnoreCase(ref matchStart, ref candidate.GetRawStringData(), (uint)candidate.Length);
}

return Equals(ref matchStart, candidate);
}
}

public readonly struct CaseInsensitiveUnicode : ICaseSensitivity
Expand All @@ -277,18 +176,6 @@ public static bool Equals(ref char matchStart, string candidate)
{
return Ordinal.EqualsIgnoreCase(ref matchStart, ref candidate.GetRawStringData(), candidate.Length);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Equals<TValueLength>(ref char matchStart, string candidate)
where TValueLength : struct, IValueLength
{
if (Vector128.IsHardwareAccelerated && TValueLength.AtLeast8Chars)
{
return Ordinal.EqualsIgnoreCase_Vector128(ref matchStart, ref candidate.GetRawStringData(), candidate.Length);
}

return Ordinal.EqualsIgnoreCase_Scalar(ref matchStart, ref candidate.GetRawStringData(), candidate.Length);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ namespace System.Buffers
{
// Based on SpanHelpers.IndexOf(ref char, int, ref char, int)
// This implementation uses 3 precomputed anchor points when searching.
internal sealed class SingleStringSearchValuesThreeChars<TValueLength, TCaseSensitivity> : StringSearchValuesBase
where TValueLength : struct, IValueLength
internal sealed class SingleStringSearchValuesThreeChars<TCaseSensitivity> : StringSearchValuesBase
where TCaseSensitivity : struct, ICaseSensitivity
{
private const ushort CaseConversionMask = unchecked((ushort)~0x20);
Expand Down Expand Up @@ -203,7 +202,7 @@ private int IndexOf(ref char searchSpace, int searchSpaceLength)
ref char cur = ref Unsafe.Add(ref searchSpace, i);

if ((typeof(TCaseSensitivity) == typeof(CaseInsensitiveUnicode) || TCaseSensitivity.TransformInput(cur) == valueHead) &&
TCaseSensitivity.Equals<TValueLength>(ref cur, value))
TCaseSensitivity.Equals(ref cur, value))
{
return (int)i;
}
Expand Down Expand Up @@ -285,8 +284,7 @@ private bool TryMatch(ref char searchSpaceStart, ref char searchSpace, uint mask

ref char matchRef = ref Unsafe.AddByteOffset(ref searchSpace, bitPos);

if ((typeof(TCaseSensitivity) == typeof(CaseSensitive) && !TValueLength.AtLeast4Chars) ||
TCaseSensitivity.Equals<TValueLength>(ref matchRef, _value))
if (TCaseSensitivity.Equals(ref matchRef, _value))
{
offsetFromStart = (int)((nuint)Unsafe.ByteOffset(ref searchSpaceStart, ref matchRef) / 2);
return true;
Expand All @@ -310,8 +308,7 @@ private bool TryMatch(ref char searchSpaceStart, ref char searchSpace, ulong mas

ref char matchRef = ref Unsafe.AddByteOffset(ref searchSpace, bitPos);

if ((typeof(TCaseSensitivity) == typeof(CaseSensitive) && !TValueLength.AtLeast4Chars) ||
TCaseSensitivity.Equals<TValueLength>(ref matchRef, _value))
if (TCaseSensitivity.Equals(ref matchRef, _value))
{
offsetFromStart = (int)((nuint)Unsafe.ByteOffset(ref searchSpaceStart, ref matchRef) / 2);
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,16 +294,25 @@ private static SearchValues<string> CreateForSingleValue(

if (Vector128.IsHardwareAccelerated && value.Length > 1 && value.Length <= maxLength)
{
SearchValues<string>? searchValues = value.Length switch
if (!ignoreCase)
{
< 4 => TryCreateSingleValuesThreeChars<ValueLengthLessThan4>(value, uniqueValues, ignoreCase, allAscii, asciiLettersOnly),
< 8 => TryCreateSingleValuesThreeChars<ValueLength4To7>(value, uniqueValues, ignoreCase, allAscii, asciiLettersOnly),
_ => TryCreateSingleValuesThreeChars<ValueLength8OrLonger>(value, uniqueValues, ignoreCase, allAscii, asciiLettersOnly),
};
return new SingleStringSearchValuesThreeChars<CaseSensitive>(value, uniqueValues);
}

if (asciiLettersOnly)
{
return new SingleStringSearchValuesThreeChars<CaseInsensitiveAsciiLetters>(value, uniqueValues);
}

if (searchValues is not null)
if (allAscii)
{
return searchValues;
return new SingleStringSearchValuesThreeChars<CaseInsensitiveAscii>(value, uniqueValues);
}

// When ignoring casing, all anchor chars we search for must be ASCII.
if (char.IsAscii(value[0]) && value.AsSpan().LastIndexOfAnyInRange((char)0, (char)127) > 0)
{
return new SingleStringSearchValuesThreeChars<CaseInsensitiveUnicode>(value, uniqueValues);
}
}

Expand All @@ -312,38 +321,6 @@ private static SearchValues<string> CreateForSingleValue(
: new SingleStringSearchValuesFallback<SearchValues.FalseConst>(value, uniqueValues);
}

private static SearchValues<string>? TryCreateSingleValuesThreeChars<TValueLength>(
string value,
HashSet<string> uniqueValues,
bool ignoreCase,
bool allAscii,
bool asciiLettersOnly)
where TValueLength : struct, IValueLength
{
if (!ignoreCase)
{
return new SingleStringSearchValuesThreeChars<TValueLength, CaseSensitive>(value, uniqueValues);
}

if (asciiLettersOnly)
{
return new SingleStringSearchValuesThreeChars<TValueLength, CaseInsensitiveAsciiLetters>(value, uniqueValues);
}

if (allAscii)
{
return new SingleStringSearchValuesThreeChars<TValueLength, CaseInsensitiveAscii>(value, uniqueValues);
}

// When ignoring casing, all anchor chars we search for must be ASCII.
if (char.IsAscii(value[0]) && value.AsSpan().LastIndexOfAnyInRange((char)0, (char)127) > 0)
{
return new SingleStringSearchValuesThreeChars<TValueLength, CaseInsensitiveUnicode>(value, uniqueValues);
}

return null;
}

private static void AnalyzeValues(
ReadOnlySpan<string> values,
ref bool ignoreCase,
Expand Down

0 comments on commit 2e70415

Please sign in to comment.