diff options
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/SpanHelpers.Char.cs')
-rw-r--r-- | src/System.Private.CoreLib/shared/System/SpanHelpers.Char.cs | 113 |
1 files changed, 96 insertions, 17 deletions
diff --git a/src/System.Private.CoreLib/shared/System/SpanHelpers.Char.cs b/src/System.Private.CoreLib/shared/System/SpanHelpers.Char.cs index 51ace58a3..02df69feb 100644 --- a/src/System.Private.CoreLib/shared/System/SpanHelpers.Char.cs +++ b/src/System.Private.CoreLib/shared/System/SpanHelpers.Char.cs @@ -4,18 +4,15 @@ using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Numerics; #if !netstandard using Internal.Runtime.CompilerServices; #endif -#if !netstandard11 -using System.Numerics; -#endif - namespace System { - internal static partial class SpanHelpers + internal static partial class SpanHelpers // .Char { public static unsafe int SequenceCompareTo(ref char first, int firstLength, ref char second, int secondLength) { @@ -32,7 +29,6 @@ namespace System if ((byte*)minLength >= (byte*)(sizeof(UIntPtr) / sizeof(char))) { -#if !netstandard11 if (Vector.IsHardwareAccelerated && (byte*)minLength >= (byte*)Vector<ushort>.Count) { IntPtr nLength = minLength - Vector<ushort>.Count; @@ -47,7 +43,6 @@ namespace System } while ((byte*)nLength >= (byte*)i); } -#endif while ((byte*)minLength >= (byte*)(i + sizeof(UIntPtr) / sizeof(char))) { @@ -81,6 +76,94 @@ namespace System return lengthDelta; } + // Adapted from IndexOf(...) + public static unsafe bool Contains(ref char searchSpace, char value, int length) + { + Debug.Assert(length >= 0); + + fixed (char* pChars = &searchSpace) + { + char* pCh = pChars; + char* pEndCh = pCh + length; + + if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2) + { + // Figure out how many characters to read sequentially until we are vector aligned + // This is equivalent to: + // unaligned = ((int)pCh % Unsafe.SizeOf<Vector<ushort>>()) / elementsPerByte + // length = (Vector<ushort>.Count - unaligned) % Vector<ushort>.Count + const int elementsPerByte = sizeof(ushort) / sizeof(byte); + int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte; + length = (Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1); + } + + SequentialScan: + while (length >= 4) + { + length -= 4; + + if (value == *pCh || + value == *(pCh + 1) || + value == *(pCh + 2) || + value == *(pCh + 3)) + { + goto Found; + } + + pCh += 4; + } + + while (length > 0) + { + length -= 1; + + if (value == *pCh) + goto Found; + + pCh += 1; + } + + // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow + // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated. + if (Vector.IsHardwareAccelerated && pCh < pEndCh) + { + // Get the highest multiple of Vector<ushort>.Count that is within the search space. + // That will be how many times we iterate in the loop below. + // This is equivalent to: length = Vector<ushort>.Count * ((int)(pEndCh - pCh) / Vector<ushort>.Count) + length = (int)((pEndCh - pCh) & ~(Vector<ushort>.Count - 1)); + + // Get comparison Vector + Vector<ushort> vComparison = new Vector<ushort>(value); + + while (length > 0) + { + // Using Unsafe.Read instead of ReadUnaligned since the search space is pinned and pCh is always vector aligned + Debug.Assert(((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) == 0); + Vector<ushort> vMatches = Vector.Equals(vComparison, Unsafe.Read<Vector<ushort>>(pCh)); + if (Vector<ushort>.Zero.Equals(vMatches)) + { + pCh += Vector<ushort>.Count; + length -= Vector<ushort>.Count; + continue; + } + + goto Found; + } + + if (pCh < pEndCh) + { + length = (int)(pEndCh - pCh); + goto SequentialScan; + } + } + + return false; + + Found: + return true; + } + } + public static unsafe int IndexOf(ref char searchSpace, char value, int length) { Debug.Assert(length >= 0); @@ -90,7 +173,6 @@ namespace System char* pCh = pChars; char* pEndCh = pCh + length; -#if !netstandard11 if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2) { // Figure out how many characters to read sequentially until we are vector aligned @@ -101,8 +183,8 @@ namespace System int unaligned = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte; length = (Vector<ushort>.Count - unaligned) & (Vector<ushort>.Count - 1); } + SequentialScan: -#endif while (length >= 4) { length -= 4; @@ -128,7 +210,7 @@ namespace System pCh += 1; } -#if !netstandard11 + // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated. if (Vector.IsHardwareAccelerated && pCh < pEndCh) @@ -162,7 +244,7 @@ namespace System goto SequentialScan; } } -#endif + return -1; Found3: pCh++; @@ -184,7 +266,6 @@ namespace System char* pCh = pChars + length; char* pEndCh = pChars; -#if !netstandard11 if (Vector.IsHardwareAccelerated && length >= Vector<ushort>.Count * 2) { // Figure out how many characters to read sequentially from the end until we are vector aligned @@ -192,8 +273,8 @@ namespace System const int elementsPerByte = sizeof(ushort) / sizeof(byte); length = ((int)pCh & (Unsafe.SizeOf<Vector<ushort>>() - 1)) / elementsPerByte; } + SequentialScan: -#endif while (length >= 4) { length -= 4; @@ -217,7 +298,7 @@ namespace System if (*pCh == value) goto Found; } -#if !netstandard11 + // We get past SequentialScan only if IsHardwareAccelerated is true. However, we still have the redundant check to allow // the JIT to see that the code is unreachable and eliminate it when the platform does not have hardware accelerated. if (Vector.IsHardwareAccelerated && pCh > pEndCh) @@ -252,7 +333,7 @@ namespace System goto SequentialScan; } } -#endif + return -1; Found: return (int)(pCh - pEndCh); @@ -265,7 +346,6 @@ namespace System } } -#if !netstandard11 // Vector sub-search adapted from https://github.com/aspnet/KestrelHttpServer/pull/1138 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LocateFirstFoundChar(Vector<ushort> match) @@ -336,6 +416,5 @@ namespace System } return index; } -#endif } } |