diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 0d6ae242ae6ec..ebe03d7b5b71b 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -434,6 +434,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/BitVector256.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/BitVector256.cs index 0f449d3044e91..56e68907c86d7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/BitVector256.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/BitVector256.cs @@ -35,6 +35,10 @@ public void Set(int c) public readonly bool Contains128(char c) => c < 128 && ContainsUnchecked(c); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Contains256(char c) => + c < 256 && ContainsUnchecked(c); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Contains(byte b) => ContainsUnchecked(b); diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Latin1CharSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Latin1CharSearchValues.cs new file mode 100644 index 0000000000000..3968825ccd4c7 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Latin1CharSearchValues.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Buffers +{ + internal sealed class Latin1CharSearchValues : SearchValues + { + private readonly BitVector256 _lookup; + + public Latin1CharSearchValues(ReadOnlySpan values) + { + foreach (char c in values) + { + if (c > 255) + { + // The values were modified concurrent with the call to SearchValues.Create + ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion(); + } + + _lookup.Set(c); + } + } + + internal override char[] GetValues() => _lookup.GetCharValues(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsCore(char value) => + _lookup.Contains256(value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int IndexOfAny(ReadOnlySpan span) => + IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int IndexOfAnyExcept(ReadOnlySpan span) => + IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAny(ReadOnlySpan span) => + LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => + LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length); + + private int IndexOfAny(ref char searchSpace, int searchSpaceLength) + where TNegator : struct, IndexOfAnyAsciiSearcher.INegator + { + ref char searchSpaceEnd = ref Unsafe.Add(ref searchSpace, searchSpaceLength); + ref char cur = ref searchSpace; + + while (!Unsafe.AreSame(ref cur, ref searchSpaceEnd)) + { + char c = cur; + if (TNegator.NegateIfNeeded(_lookup.Contains256(c))) + { + return (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref cur) / sizeof(char)); + } + + cur = ref Unsafe.Add(ref cur, 1); + } + + return -1; + } + + private int LastIndexOfAny(ref char searchSpace, int searchSpaceLength) + where TNegator : struct, IndexOfAnyAsciiSearcher.INegator + { + for (int i = searchSpaceLength - 1; i >= 0; i--) + { + char c = Unsafe.Add(ref searchSpace, i); + if (TNegator.NegateIfNeeded(_lookup.Contains256(c))) + { + return i; + } + } + + return -1; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs index 17c6bd6a69eab..13c6779a7d985 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.Wasm; using System.Runtime.Intrinsics.X86; @@ -154,6 +155,13 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(values)), : new ProbabilisticWithAsciiCharSearchValues(probabilisticValues); } + // We prefer using the ProbabilisticMap over Latin1CharSearchValues if the former is vectorized. + if (!(Sse41.IsSupported || AdvSimd.Arm64.IsSupported) && maxInclusive < 256) + { + // This will also match ASCII values when IndexOfAnyAsciiSearcher is not supported. + return new Latin1CharSearchValues(values); + } + return new ProbabilisticCharSearchValues(probabilisticValues); }