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);
}