From 7bccebe8e7bf62a94402756739cc9c47c4dad8e0 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Wed, 10 Jan 2024 21:19:38 +0100 Subject: [PATCH] Add ContainsAny{Except} path to SearchValues --- .../src/System/MemoryExtensions.cs | 22 +- .../SearchValues/AnyByteSearchValues.cs | 24 +- .../SearchValues/AsciiByteSearchValues.cs | 24 +- .../SearchValues/AsciiCharSearchValues.cs | 24 +- .../SearchValues/IndexOfAnyAsciiSearcher.cs | 278 ++++++++++++------ .../ProbabilisticWithAsciiCharSearchValues.cs | 12 +- .../src/System/SearchValues/SearchValues.T.cs | 3 + .../Strings/Helpers/AhoCorasick.cs | 4 +- 8 files changed, 271 insertions(+), 120 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 4d8ff44fd202e..bffab304afb0c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -492,8 +492,15 @@ public static bool ContainsAny(this ReadOnlySpan span, ReadOnlySpan val /// The span to search. /// The set of values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ContainsAny(this ReadOnlySpan span, SearchValues values) where T : IEquatable? => - IndexOfAny(span, values) >= 0; + public static bool ContainsAny(this ReadOnlySpan span, SearchValues values) where T : IEquatable? + { + if (values is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + + return values.ContainsAny(span); + } /// /// Searches for any occurrence of any of the specified substring and returns true if found. If not found, returns false. @@ -569,8 +576,15 @@ public static bool ContainsAnyExcept(this ReadOnlySpan span, ReadOnlySpan< /// If all of the values are in , returns false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ContainsAnyExcept(this ReadOnlySpan span, SearchValues values) where T : IEquatable? => - IndexOfAnyExcept(span, values) >= 0; + public static bool ContainsAnyExcept(this ReadOnlySpan span, SearchValues values) where T : IEquatable? + { + if (values is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + + return values.ContainsAnyExcept(span); + } /// /// Searches for any value in the range between and , inclusive, and returns true if found. If not found, returns false. diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AnyByteSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AnyByteSearchValues.cs index d95d557bd89a4..7d2524371edc7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AnyByteSearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AnyByteSearchValues.cs @@ -28,7 +28,7 @@ internal override bool ContainsCore(byte value) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorizedAnyByte( + IndexOfAnyAsciiSearcher.IndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -36,7 +36,7 @@ internal override int IndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorizedAnyByte( + IndexOfAnyAsciiSearcher.IndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -44,7 +44,7 @@ internal override int IndexOfAnyExcept(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorizedAnyByte( + IndexOfAnyAsciiSearcher.LastIndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -52,7 +52,23 @@ internal override int LastIndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorizedAnyByte( + IndexOfAnyAsciiSearcher.LastIndexOfAny( + ref MemoryMarshal.GetReference(span), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAny(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( + ref MemoryMarshal.GetReference(span), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAnyExcept(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiByteSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiByteSearchValues.cs index 3fc67d6086a19..57b755b2ce8a4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiByteSearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiByteSearchValues.cs @@ -28,7 +28,7 @@ internal override bool ContainsCore(byte value) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.IndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -36,7 +36,7 @@ internal override int IndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.IndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -44,7 +44,7 @@ internal override int IndexOfAnyExcept(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.LastIndexOfAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -52,7 +52,23 @@ internal override int LastIndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.LastIndexOfAny( + ref MemoryMarshal.GetReference(span), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAny(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( + ref MemoryMarshal.GetReference(span), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAnyExcept(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( ref MemoryMarshal.GetReference(span), span.Length, ref _state); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiCharSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiCharSearchValues.cs index 4df9f55252f19..111a3ad313b9d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiCharSearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/AsciiCharSearchValues.cs @@ -29,7 +29,7 @@ internal override bool ContainsCore(char value) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -37,7 +37,7 @@ internal override int IndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int IndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -45,7 +45,7 @@ internal override int IndexOfAnyExcept(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAny(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); [CompExactlyDependsOn(typeof(Ssse3))] @@ -53,7 +53,23 @@ internal override int LastIndexOfAny(ReadOnlySpan span) => [CompExactlyDependsOn(typeof(PackedSimd))] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + IndexOfAnyAsciiSearcher.LastIndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAny(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsAnyExcept(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.ContainsAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs index 4f3ff556492ff..bc8b3fd0c6c8f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs @@ -150,8 +150,8 @@ private static unsafe bool TryIndexOfAny(ref short searchSpace, int se state.Bitmap = Vector256.Create(state.Bitmap._lower, state.Bitmap._lower); index = (Ssse3.IsSupported || PackedSimd.IsSupported) && needleContainsZero - ? IndexOfAnyVectorized(ref searchSpace, searchSpaceLength, ref state) - : IndexOfAnyVectorized(ref searchSpace, searchSpaceLength, ref state); + ? IndexOfAny(ref searchSpace, searchSpaceLength, ref state) + : IndexOfAny(ref searchSpace, searchSpaceLength, ref state); return true; } } @@ -177,8 +177,8 @@ private static unsafe bool TryLastIndexOfAny(ref short searchSpace, in state.Bitmap = Vector256.Create(state.Bitmap._lower, state.Bitmap._lower); index = (Ssse3.IsSupported || PackedSimd.IsSupported) && needleContainsZero - ? LastIndexOfAnyVectorized(ref searchSpace, searchSpaceLength, ref state) - : LastIndexOfAnyVectorized(ref searchSpace, searchSpaceLength, ref state); + ? LastIndexOfAny(ref searchSpace, searchSpaceLength, ref state) + : LastIndexOfAny(ref searchSpace, searchSpaceLength, ref state); return true; } } @@ -187,12 +187,32 @@ private static unsafe bool TryLastIndexOfAny(ref short searchSpace, in return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int IndexOfAnyVectorized(ref short searchSpace, int searchSpaceLength, ref AsciiState state) + public static bool ContainsAny(ref short searchSpace, int searchSpaceLength, ref AsciiState state) + where TNegator : struct, INegator + where TOptimizations : struct, IOptimizations => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + public static int IndexOfAny(ref short searchSpace, int searchSpaceLength, ref AsciiState state) + where TNegator : struct, INegator + where TOptimizations : struct, IOptimizations => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + private static TResult IndexOfAnyCore(ref short searchSpace, int searchSpaceLength, ref AsciiState state) + where TResult : struct where TNegator : struct, INegator where TOptimizations : struct, IOptimizations + where TResultMapper : struct, IResultMapper { ref short currentSearchSpace = ref searchSpace; @@ -205,13 +225,13 @@ internal static int IndexOfAnyVectorized(ref short sea char c = (char)currentSearchSpace; if (TNegator.NegateIfNeeded(state.Lookup.Contains128(c))) { - return (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref currentSearchSpace) / sizeof(char)); + return TResultMapper.ScalarResult(ref searchSpace, ref currentSearchSpace); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 1); } - return -1; + return TResultMapper.NotFound; } #pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false @@ -238,7 +258,7 @@ internal static int IndexOfAnyVectorized(ref short sea Vector256 result = IndexOfAnyLookup(source0, source1, bitmap256); if (result != Vector256.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector256.Count); @@ -262,11 +282,11 @@ internal static int IndexOfAnyVectorized(ref short sea Vector256 result = IndexOfAnyLookup(source0, source1, bitmap256); if (result != Vector256.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } Vector128 bitmap = state.Bitmap._lower; @@ -291,7 +311,7 @@ internal static int IndexOfAnyVectorized(ref short sea Vector128 result = IndexOfAnyLookup(source0, source1, bitmap); if (result != Vector128.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 2 * Vector128.Count); @@ -315,17 +335,17 @@ internal static int IndexOfAnyVectorized(ref short sea Vector128 result = IndexOfAnyLookup(source0, source1, bitmap); if (result != Vector128.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref oneVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int LastIndexOfAnyVectorized(ref short searchSpace, int searchSpaceLength, ref AsciiState state) + public static int LastIndexOfAny(ref short searchSpace, int searchSpaceLength, ref AsciiState state) where TNegator : struct, INegator where TOptimizations : struct, IOptimizations { @@ -451,11 +471,29 @@ internal static int LastIndexOfAnyVectorized(ref short return -1; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int IndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) + public static bool ContainsAny(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) + where TNegator : struct, INegator => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + public static int IndexOfAny(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) + where TNegator : struct, INegator => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + private static TResult IndexOfAnyCore(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) + where TResult : struct where TNegator : struct, INegator + where TResultMapper : struct, IResultMapper { ref byte currentSearchSpace = ref searchSpace; @@ -468,13 +506,13 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea byte b = currentSearchSpace; if (TNegator.NegateIfNeeded(state.Lookup.Contains(b))) { - return (int)Unsafe.ByteOffset(ref searchSpace, ref currentSearchSpace); + return TResultMapper.ScalarResult(ref searchSpace, ref currentSearchSpace); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 1); } - return -1; + return TResultMapper.NotFound; } #pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false @@ -498,7 +536,7 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea Vector256 result = TNegator.NegateIfNeeded(IndexOfAnyLookupCore(source, bitmap256)); if (result != Vector256.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256.Count); @@ -523,11 +561,11 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea Vector256 result = TNegator.NegateIfNeeded(IndexOfAnyLookupCore(source, bitmap256)); if (result != Vector256.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } Vector128 bitmap = state.Bitmap._lower; @@ -547,7 +585,7 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea Vector128 result = TNegator.NegateIfNeeded(IndexOfAnyLookupCore(source, bitmap)); if (result != Vector128.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128.Count); @@ -572,17 +610,17 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea Vector128 result = TNegator.NegateIfNeeded(IndexOfAnyLookupCore(source, bitmap)); if (result != Vector128.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) + public static int LastIndexOfAny(ref byte searchSpace, int searchSpaceLength, ref AsciiState state) where TNegator : struct, INegator { if (searchSpaceLength < sizeof(ulong)) @@ -703,11 +741,29 @@ internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int return -1; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) + public static bool ContainsAny(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) + where TNegator : struct, INegator => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + public static int IndexOfAny(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) + where TNegator : struct, INegator => + IndexOfAnyCore>(ref searchSpace, searchSpaceLength, ref state); + + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + private static TResult IndexOfAnyCore(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) + where TResult : struct where TNegator : struct, INegator + where TResultMapper : struct, IResultMapper { ref byte currentSearchSpace = ref searchSpace; @@ -720,13 +776,13 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, byte b = currentSearchSpace; if (TNegator.NegateIfNeeded(state.Lookup.Contains(b))) { - return (int)Unsafe.ByteOffset(ref searchSpace, ref currentSearchSpace); + return TResultMapper.ScalarResult(ref searchSpace, ref currentSearchSpace); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, 1); } - return -1; + return TResultMapper.NotFound; } #pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false @@ -751,7 +807,7 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, Vector256 result = IndexOfAnyLookup(source, bitmap256_0, bitmap256_1); if (result != Vector256.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector256.Count); @@ -776,11 +832,11 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, Vector256 result = IndexOfAnyLookup(source, bitmap256_0, bitmap256_1); if (result != Vector256.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } Vector128 bitmap0 = state.Bitmap0._lower; @@ -803,7 +859,7 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, Vector128 result = IndexOfAnyLookup(source, bitmap0, bitmap1); if (result != Vector128.Zero) { - return ComputeFirstIndex(ref searchSpace, ref currentSearchSpace, result); + return TResultMapper.FirstIndex(ref searchSpace, ref currentSearchSpace, result); } currentSearchSpace = ref Unsafe.Add(ref currentSearchSpace, Vector128.Count); @@ -828,17 +884,17 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, Vector128 result = IndexOfAnyLookup(source, bitmap0, bitmap1); if (result != Vector128.Zero) { - return ComputeFirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); + return TResultMapper.FirstIndexOverlapped(ref searchSpace, ref firstVector, ref halfVectorAwayFromEnd, result); } } - return -1; + return TResultMapper.NotFound; } [CompExactlyDependsOn(typeof(Ssse3))] [CompExactlyDependsOn(typeof(AdvSimd))] [CompExactlyDependsOn(typeof(PackedSimd))] - internal static int LastIndexOfAnyVectorizedAnyByte(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) + public static int LastIndexOfAny(ref byte searchSpace, int searchSpaceLength, ref AnyByteState state) where TNegator : struct, INegator { if (!IsVectorizationSupported || searchSpaceLength < sizeof(ulong)) @@ -1081,30 +1137,6 @@ private static Vector256 IndexOfAnyLookup(Vector256 source return TNegator.NegateIfNeeded(result); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe int ComputeFirstIndex(ref T searchSpace, ref T current, Vector128 result) - where TNegator : struct, INegator - { - uint mask = TNegator.ExtractMask(result); - int offsetInVector = BitOperations.TrailingZeroCount(mask); - return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe int ComputeFirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector128 result) - where TNegator : struct, INegator - { - uint mask = TNegator.ExtractMask(result); - int offsetInVector = BitOperations.TrailingZeroCount(mask); - if (offsetInVector >= Vector128.Count) - { - // We matched within the second vector - current0 = ref current1; - offsetInVector -= Vector128.Count; - } - return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / (nuint)sizeof(T)); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe int ComputeLastIndex(ref T searchSpace, ref T current, Vector128 result) where TNegator : struct, INegator @@ -1129,44 +1161,6 @@ private static unsafe int ComputeLastIndexOverlapped(ref T searchSp return offsetInVector - Vector128.Count + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref secondVector) / (nuint)sizeof(T)); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CompExactlyDependsOn(typeof(Avx2))] - private static unsafe int ComputeFirstIndex(ref T searchSpace, ref T current, Vector256 result) - where TNegator : struct, INegator - { - if (typeof(T) == typeof(short)) - { - result = PackedSpanHelpers.FixUpPackedVector256Result(result); - } - - uint mask = TNegator.ExtractMask(result); - - int offsetInVector = BitOperations.TrailingZeroCount(mask); - return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CompExactlyDependsOn(typeof(Avx2))] - private static unsafe int ComputeFirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector256 result) - where TNegator : struct, INegator - { - if (typeof(T) == typeof(short)) - { - result = PackedSpanHelpers.FixUpPackedVector256Result(result); - } - - uint mask = TNegator.ExtractMask(result); - - int offsetInVector = BitOperations.TrailingZeroCount(mask); - if (offsetInVector >= Vector256.Count) - { - // We matched within the second vector - current0 = ref current1; - offsetInVector -= Vector256.Count; - } - return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / (nuint)sizeof(T)); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Avx2))] private static unsafe int ComputeLastIndex(ref T searchSpace, ref T current, Vector256 result) @@ -1294,5 +1288,97 @@ public static Vector256 PackSources(Vector256 lower, Vector256 + where TResult : struct + { + static abstract TResult NotFound { get; } + + static abstract TResult ScalarResult(ref T searchSpace, ref T current); + static abstract TResult FirstIndex(ref T searchSpace, ref T current, Vector128 result) where TNegator : struct, INegator; + static abstract TResult FirstIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator; + static abstract TResult FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector128 result) where TNegator : struct, INegator; + static abstract TResult FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector256 result) where TNegator : struct, INegator; + } + + private readonly struct ContainsAnyResultMapper : IResultMapper + { + public static bool NotFound => false; + + public static bool ScalarResult(ref T searchSpace, ref T current) => true; + public static bool FirstIndex(ref T searchSpace, ref T current, Vector128 result) where TNegator : struct, INegator => true; + public static bool FirstIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator => true; + public static bool FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector128 result) where TNegator : struct, INegator => true; + public static bool FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector256 result) where TNegator : struct, INegator => true; + } + + private readonly unsafe struct IndexOfAnyResultMapper : IResultMapper + { + public static int NotFound => -1; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ScalarResult(ref T searchSpace, ref T current) + { + return (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FirstIndex(ref T searchSpace, ref T current, Vector128 result) where TNegator : struct, INegator + { + uint mask = TNegator.ExtractMask(result); + int offsetInVector = BitOperations.TrailingZeroCount(mask); + return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Avx2))] + public static int FirstIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator + { + if (typeof(T) == typeof(short)) + { + result = PackedSpanHelpers.FixUpPackedVector256Result(result); + } + + uint mask = TNegator.ExtractMask(result); + + int offsetInVector = BitOperations.TrailingZeroCount(mask); + return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector128 result) where TNegator : struct, INegator + { + uint mask = TNegator.ExtractMask(result); + int offsetInVector = BitOperations.TrailingZeroCount(mask); + if (offsetInVector >= Vector128.Count) + { + // We matched within the second vector + current0 = ref current1; + offsetInVector -= Vector128.Count; + } + return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / (nuint)sizeof(T)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Avx2))] + public static int FirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector256 result) where TNegator : struct, INegator + { + if (typeof(T) == typeof(short)) + { + result = PackedSpanHelpers.FixUpPackedVector256Result(result); + } + + uint mask = TNegator.ExtractMask(result); + + int offsetInVector = BitOperations.TrailingZeroCount(mask); + if (offsetInVector >= Vector256.Count) + { + // We matched within the second vector + current0 = ref current1; + offsetInVector -= Vector256.Count; + } + return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / (nuint)sizeof(T)); + } + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/ProbabilisticWithAsciiCharSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/ProbabilisticWithAsciiCharSearchValues.cs index 7ef2b3c23a203..b9b9227aa3ff6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/ProbabilisticWithAsciiCharSearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/ProbabilisticWithAsciiCharSearchValues.cs @@ -57,7 +57,7 @@ internal override int IndexOfAny(ReadOnlySpan span) { Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0."); - offset = IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + offset = IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _inverseAsciiState); @@ -67,7 +67,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle."); - offset = IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + offset = IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _inverseAsciiState); @@ -107,7 +107,7 @@ internal override int IndexOfAnyExcept(ReadOnlySpan span) if (IndexOfAnyAsciiSearcher.IsVectorizationSupported && span.Length >= Vector128.Count && char.IsAscii(span[0])) { // Do a regular IndexOfAnyExcept for the ASCII characters. The search will stop if we encounter a non-ASCII char. - offset = IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + offset = IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _asciiState); @@ -157,7 +157,7 @@ internal override int LastIndexOfAny(ReadOnlySpan span) { Debug.Assert(_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap did not contain a 0."); - offset = IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + offset = IndexOfAnyAsciiSearcher.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _inverseAsciiState); @@ -167,7 +167,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Debug.Assert(!(Ssse3.IsSupported || PackedSimd.IsSupported) || !_inverseAsciiState.Lookup.Contains(0), "The inverse bitmap contained a 0, but we're not using Ssse3AndWasmHandleZeroInNeedle."); - offset = IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + offset = IndexOfAnyAsciiSearcher.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _inverseAsciiState); @@ -197,7 +197,7 @@ internal override int LastIndexOfAnyExcept(ReadOnlySpan span) if (IndexOfAnyAsciiSearcher.IsVectorizationSupported && span.Length >= Vector128.Count && char.IsAscii(span[^1])) { // Do a regular LastIndexOfAnyExcept for the ASCII characters. The search will stop if we encounter a non-ASCII char. - int offset = IndexOfAnyAsciiSearcher.LastIndexOfAnyVectorized( + int offset = IndexOfAnyAsciiSearcher.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _asciiState); diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.T.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.T.cs index 62736734ed1bd..65a54082f93b9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.T.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.T.cs @@ -39,6 +39,9 @@ private protected SearchValues() { } internal virtual int LastIndexOfAny(ReadOnlySpan span) => throw new UnreachableException(); internal virtual int LastIndexOfAnyExcept(ReadOnlySpan span) => throw new UnreachableException(); + internal virtual bool ContainsAny(ReadOnlySpan span) => IndexOfAny(span) >= 0; + internal virtual bool ContainsAnyExcept(ReadOnlySpan span) => IndexOfAnyExcept(span) >= 0; + // This is only implemented and used by SearchValues. internal virtual int IndexOfAnyMultiString(ReadOnlySpan span) => throw new UnreachableException(); diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/Helpers/AhoCorasick.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/Helpers/AhoCorasick.cs index bcd7693d80ecc..ebc94616ae642 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/Helpers/AhoCorasick.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/Helpers/AhoCorasick.cs @@ -96,7 +96,7 @@ private readonly int IndexOfAnyCore(ReadOnly // If '\0' is one of the starting chars and we're running on Ssse3 hardware, this may return false-positives. // False-positives here are okay, we'll just rule them out below. While we could flow the Ssse3AndWasmHandleZeroInNeedle // generic through, we expect such values to be rare enough that introducing more code is not worth it. - int offset = IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + int offset = IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), i)), remainingLength, ref Unsafe.AsRef(in _startingAsciiChars)); @@ -205,7 +205,7 @@ private readonly int IndexOfAnyCaseInsensitiveUnicode(ReadOnly if (remainingLength >= Vector128.Count) { - int offset = IndexOfAnyAsciiSearcher.IndexOfAnyVectorized( + int offset = IndexOfAnyAsciiSearcher.IndexOfAny( ref Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), i)), remainingLength, ref Unsafe.AsRef(in _startingAsciiChars));