#region Private Data Members
private readonly SafeTypeNameParserHandle m_NativeParser;
- private const string SpecialChars = ",[]&*+\\"; // see typeparse.h
- private static readonly IndexOfAnyValues<char> s_specialChars = IndexOfAnyValues.Create(SpecialChars);
+ private static readonly IndexOfAnyValues<char> s_specialChars = IndexOfAnyValues.Create(",[]&*+\\"); // see typeparse.h
#endregion
#region Constructor and Disposer
foreach (char c in name.AsSpan(specialCharIndex))
{
- if (SpecialChars.Contains(c))
+ if (s_specialChars.Contains(c))
sb.Append('\\');
sb.Append(c);
}
}
- [Fact]
- [OuterLoop("Takes about a second to execute")]
- public static void TestIndexOfAny_RandomInputs_Byte()
- {
- IndexOfAnyCharTestHelper.TestRandomInputs(
- expected: IndexOfAnyReferenceImpl,
- indexOfAny: (searchSpace, values) => searchSpace.IndexOfAny(values),
- indexOfAnyValues: (searchSpace, values) => searchSpace.IndexOfAny(values));
-
- static int IndexOfAnyReferenceImpl(ReadOnlySpan<byte> searchSpace, ReadOnlySpan<byte> values)
- {
- for (int i = 0; i < searchSpace.Length; i++)
- {
- if (values.Contains(searchSpace[i]))
- {
- return i;
- }
- }
-
- return -1;
- }
- }
-
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public static void AsciiNeedle_ProperlyHandlesEdgeCases_Byte(bool needleContainsZero)
- {
- // There is some special handling we have to do for ASCII needles to properly filter out non-ASCII results
- ReadOnlySpan<byte> needleValues = needleContainsZero ? "AEIOU\0"u8 : "AEIOU!"u8;
- IndexOfAnyValues<byte> needle = IndexOfAnyValues.Create(needleValues);
-
- ReadOnlySpan<byte> repeatingHaystack = "AaAaAaAaAaAa"u8;
- Assert.Equal(0, repeatingHaystack.IndexOfAny(needle));
- Assert.Equal(1, repeatingHaystack.IndexOfAnyExcept(needle));
- Assert.Equal(10, repeatingHaystack.LastIndexOfAny(needle));
- Assert.Equal(11, repeatingHaystack.LastIndexOfAnyExcept(needle));
-
- ReadOnlySpan<byte> haystackWithZeroes = "Aa\0Aa\0Aa\0"u8;
- Assert.Equal(0, haystackWithZeroes.IndexOfAny(needle));
- Assert.Equal(1, haystackWithZeroes.IndexOfAnyExcept(needle));
- Assert.Equal(needleContainsZero ? 8 : 6, haystackWithZeroes.LastIndexOfAny(needle));
- Assert.Equal(needleContainsZero ? 7 : 8, haystackWithZeroes.LastIndexOfAnyExcept(needle));
-
- Span<byte> haystackWithOffsetNeedle = new byte[100];
- for (int i = 0; i < haystackWithOffsetNeedle.Length; i++)
- {
- haystackWithOffsetNeedle[i] = (byte)(128 + needleValues[i % needleValues.Length]);
- }
-
- Assert.Equal(-1, haystackWithOffsetNeedle.IndexOfAny(needle));
- Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
- Assert.Equal(-1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
- Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
-
- // Mix matching characters back in
- for (int i = 0; i < haystackWithOffsetNeedle.Length; i += 3)
- {
- haystackWithOffsetNeedle[i] = needleValues[i % needleValues.Length];
- }
-
- Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAny(needle));
- Assert.Equal(1, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
- Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
- Assert.Equal(haystackWithOffsetNeedle.Length - 2, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
- }
-
private static int IndexOf(Span<byte> span, byte value)
{
int index = span.IndexOf(value);
using System.Buffers;
using System.Linq;
using System.Numerics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Text;
using Xunit;
namespace System.SpanTests
}
}
- [Fact]
- [OuterLoop("Takes about a second to execute")]
- public static void TestIndexOfAny_RandomInputs_Char()
- {
- IndexOfAnyCharTestHelper.TestRandomInputs(
- expected: IndexOfAnyReferenceImpl,
- indexOfAny: (searchSpace, values) => searchSpace.IndexOfAny(values),
- indexOfAnyValues: (searchSpace, values) => searchSpace.IndexOfAny(values));
-
- static int IndexOfAnyReferenceImpl(ReadOnlySpan<char> searchSpace, ReadOnlySpan<char> values)
- {
- for (int i = 0; i < searchSpace.Length; i++)
- {
- if (values.Contains(searchSpace[i]))
- {
- return i;
- }
- }
-
- return -1;
- }
- }
-
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public static void AsciiNeedle_ProperlyHandlesEdgeCases_Char(bool needleContainsZero)
- {
- // There is some special handling we have to do for ASCII needles to properly filter out non-ASCII results
- ReadOnlySpan<char> needleValues = needleContainsZero ? "AEIOU\0" : "AEIOU!";
- IndexOfAnyValues<char> needle = IndexOfAnyValues.Create(needleValues);
-
- ReadOnlySpan<char> repeatingHaystack = "AaAaAaAaAaAa";
- Assert.Equal(0, repeatingHaystack.IndexOfAny(needle));
- Assert.Equal(1, repeatingHaystack.IndexOfAnyExcept(needle));
- Assert.Equal(10, repeatingHaystack.LastIndexOfAny(needle));
- Assert.Equal(11, repeatingHaystack.LastIndexOfAnyExcept(needle));
-
- ReadOnlySpan<char> haystackWithZeroes = "Aa\0Aa\0Aa\0";
- Assert.Equal(0, haystackWithZeroes.IndexOfAny(needle));
- Assert.Equal(1, haystackWithZeroes.IndexOfAnyExcept(needle));
- Assert.Equal(needleContainsZero ? 8 : 6, haystackWithZeroes.LastIndexOfAny(needle));
- Assert.Equal(needleContainsZero ? 7 : 8, haystackWithZeroes.LastIndexOfAnyExcept(needle));
-
- Span<char> haystackWithOffsetNeedle = new char[100];
- for (int i = 0; i < haystackWithOffsetNeedle.Length; i++)
- {
- haystackWithOffsetNeedle[i] = (char)(128 + needleValues[i % needleValues.Length]);
- }
-
- Assert.Equal(-1, haystackWithOffsetNeedle.IndexOfAny(needle));
- Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
- Assert.Equal(-1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
- Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
-
- // Mix matching characters back in
- for (int i = 0; i < haystackWithOffsetNeedle.Length; i += 3)
- {
- haystackWithOffsetNeedle[i] = needleValues[i % needleValues.Length];
- }
-
- Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAny(needle));
- Assert.Equal(1, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
- Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
- Assert.Equal(haystackWithOffsetNeedle.Length - 2, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
-
- // With chars, the lower byte could be matching, but we have to check that the higher byte is also 0
- for (int i = 0; i < haystackWithOffsetNeedle.Length; i++)
- {
- haystackWithOffsetNeedle[i] = (char)(((i + 1) * 256) + needleValues[i % needleValues.Length]);
- }
-
- Assert.Equal(-1, haystackWithOffsetNeedle.IndexOfAny(needle));
- Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
- Assert.Equal(-1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
- Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
-
- // Mix matching characters back in
- for (int i = 0; i < haystackWithOffsetNeedle.Length; i += 3)
- {
- haystackWithOffsetNeedle[i] = needleValues[i % needleValues.Length];
- }
-
- Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAny(needle));
- Assert.Equal(1, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
- Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
- Assert.Equal(haystackWithOffsetNeedle.Length - 2, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
- }
-
private static int IndexOf(Span<char> span, char value)
{
int index = span.IndexOf(value);
return index;
}
}
-
- public static class IndexOfAnyCharTestHelper
- {
- private const int MaxNeedleLength = 10;
- private const int MaxHaystackLength = 40;
-
- private static readonly char[] s_randomAsciiChars;
- private static readonly char[] s_randomLatin1Chars;
- private static readonly char[] s_randomChars;
- private static readonly byte[] s_randomAsciiBytes;
- private static readonly byte[] s_randomBytes;
-
- static IndexOfAnyCharTestHelper()
- {
- s_randomAsciiChars = new char[100 * 1024];
- s_randomLatin1Chars = new char[100 * 1024];
- s_randomChars = new char[1024 * 1024];
- s_randomBytes = new byte[100 * 1024];
-
- var rng = new Random(42);
-
- for (int i = 0; i < s_randomAsciiChars.Length; i++)
- {
- s_randomAsciiChars[i] = (char)rng.Next(0, 128);
- }
-
- for (int i = 0; i < s_randomLatin1Chars.Length; i++)
- {
- s_randomLatin1Chars[i] = (char)rng.Next(0, 256);
- }
-
- rng.NextBytes(MemoryMarshal.Cast<char, byte>(s_randomChars));
-
- s_randomAsciiBytes = Encoding.ASCII.GetBytes(s_randomAsciiChars);
-
- rng.NextBytes(s_randomBytes);
- }
-
- public delegate int IndexOfAnySearchDelegate<T>(ReadOnlySpan<T> searchSpace, ReadOnlySpan<T> values) where T : IEquatable<T>?;
-
- public delegate int IndexOfAnyValuesSearchDelegate<T>(ReadOnlySpan<T> searchSpace, IndexOfAnyValues<T> values) where T : IEquatable<T>?;
-
- public static void TestRandomInputs(IndexOfAnySearchDelegate<byte> expected, IndexOfAnySearchDelegate<byte> indexOfAny, IndexOfAnyValuesSearchDelegate<byte> indexOfAnyValues)
- {
- var rng = new Random(42);
-
- for (int iterations = 0; iterations < 1_000_000; iterations++)
- {
- // There are more interesting corner cases with ASCII needles, test those more.
- Test(rng, s_randomBytes, s_randomAsciiBytes, expected, indexOfAny, indexOfAnyValues);
-
- Test(rng, s_randomBytes, s_randomBytes, expected, indexOfAny, indexOfAnyValues);
- }
- }
-
- public static void TestRandomInputs(IndexOfAnySearchDelegate<char> expected, IndexOfAnySearchDelegate<char> indexOfAny, IndexOfAnyValuesSearchDelegate<char> indexOfAnyValues)
- {
- var rng = new Random(42);
-
- for (int iterations = 0; iterations < 1_000_000; iterations++)
- {
- // There are more interesting corner cases with ASCII needles, test those more.
- Test(rng, s_randomChars, s_randomAsciiChars, expected, indexOfAny, indexOfAnyValues);
-
- Test(rng, s_randomChars, s_randomLatin1Chars, expected, indexOfAny, indexOfAnyValues);
-
- Test(rng, s_randomChars, s_randomChars, expected, indexOfAny, indexOfAnyValues);
- }
- }
-
- private static void Test<T>(Random rng, ReadOnlySpan<T> haystackRandom, ReadOnlySpan<T> needleRandom,
- IndexOfAnySearchDelegate<T> expected, IndexOfAnySearchDelegate<T> indexOfAny, IndexOfAnyValuesSearchDelegate<T> indexOfAnyValues)
- where T : INumber<T>
- {
- ReadOnlySpan<T> haystack = GetRandomSlice(rng, haystackRandom, MaxHaystackLength);
- ReadOnlySpan<T> needle = GetRandomSlice(rng, needleRandom, MaxNeedleLength);
-
- IndexOfAnyValues<T> indexOfAnyValuesInstance = (IndexOfAnyValues<T>)(object)(typeof(T) == typeof(byte)
- ? IndexOfAnyValues.Create(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(needle)), needle.Length))
- : IndexOfAnyValues.Create(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(needle)), needle.Length)));
-
- int expectedIndex = expected(haystack, needle);
- int indexOfAnyIndex = indexOfAny(haystack, needle);
- int indexOfAnyValuesIndex = indexOfAnyValues(haystack, indexOfAnyValuesInstance);
-
- if (expectedIndex != indexOfAnyIndex)
- {
- AssertionFailed(haystack, needle, expectedIndex, indexOfAnyIndex, nameof(indexOfAny));
- }
-
- if (expectedIndex != indexOfAnyValuesIndex)
- {
- AssertionFailed(haystack, needle, expectedIndex, indexOfAnyValuesIndex, nameof(indexOfAnyValues));
- }
- }
-
- private static ReadOnlySpan<T> GetRandomSlice<T>(Random rng, ReadOnlySpan<T> span, int maxLength)
- {
- ReadOnlySpan<T> slice = span.Slice(rng.Next(span.Length + 1));
- return slice.Slice(0, Math.Min(slice.Length, rng.Next(maxLength + 1)));
- }
-
- private static void AssertionFailed<T>(ReadOnlySpan<T> haystack, ReadOnlySpan<T> needle, int expected, int actual, string approach)
- where T : INumber<T>
- {
- string readableHaystack = string.Join(", ", haystack.ToString().Select(c => int.CreateChecked(c)));
- string readableNeedle = string.Join(", ", needle.ToString().Select(c => int.CreateChecked(c)));
-
- Assert.True(false, $"Expected {expected}, got {approach}={actual} for needle='{readableNeedle}', haystack='{readableHaystack}'");
- }
- }
}
break;
}
}
-
- [Fact]
- [OuterLoop("Takes about a second to execute")]
- public static void TestIndexOfAnyExcept_RandomInputs_Byte()
- {
- IndexOfAnyCharTestHelper.TestRandomInputs(
- expected: IndexOfAnyExceptReferenceImpl,
- indexOfAny: (searchSpace, values) => searchSpace.IndexOfAnyExcept(values),
- indexOfAnyValues: (searchSpace, values) => searchSpace.IndexOfAnyExcept(values));
-
- static int IndexOfAnyExceptReferenceImpl(ReadOnlySpan<byte> searchSpace, ReadOnlySpan<byte> values)
- {
- for (int i = 0; i < searchSpace.Length; i++)
- {
- if (!values.Contains(searchSpace[i]))
- {
- return i;
- }
- }
-
- return -1;
- }
- }
-
- [Fact]
- [OuterLoop("Takes about a second to execute")]
- public static void TestLastIndexOfAnyExcept_RandomInputs_Byte()
- {
- IndexOfAnyCharTestHelper.TestRandomInputs(
- expected: LastIndexOfAnyExceptReferenceImpl,
- indexOfAny: (searchSpace, values) => searchSpace.LastIndexOfAnyExcept(values),
- indexOfAnyValues: (searchSpace, values) => searchSpace.LastIndexOfAnyExcept(values));
-
- static int LastIndexOfAnyExceptReferenceImpl(ReadOnlySpan<byte> searchSpace, ReadOnlySpan<byte> values)
- {
- for (int i = searchSpace.Length - 1; i >= 0; i--)
- {
- if (!values.Contains(searchSpace[i]))
- {
- return i;
- }
- }
-
- return -1;
- }
- }
-
- [Fact]
- [OuterLoop("Takes about a second to execute")]
- public static void TestIndexOfAnyExcept_RandomInputs_Char()
- {
- IndexOfAnyCharTestHelper.TestRandomInputs(
- expected: IndexOfAnyExceptReferenceImpl,
- indexOfAny: (searchSpace, values) => searchSpace.IndexOfAnyExcept(values),
- indexOfAnyValues: (searchSpace, values) => searchSpace.IndexOfAnyExcept(values));
-
- static int IndexOfAnyExceptReferenceImpl(ReadOnlySpan<char> searchSpace, ReadOnlySpan<char> values)
- {
- for (int i = 0; i < searchSpace.Length; i++)
- {
- if (!values.Contains(searchSpace[i]))
- {
- return i;
- }
- }
-
- return -1;
- }
- }
-
- [Fact]
- [OuterLoop("Takes about a second to execute")]
- public static void TestLastIndexOfAnyExcept_RandomInputs_Char()
- {
- IndexOfAnyCharTestHelper.TestRandomInputs(
- expected: LastIndexOfAnyExceptReferenceImpl,
- indexOfAny: (searchSpace, values) => searchSpace.LastIndexOfAnyExcept(values),
- indexOfAnyValues: (searchSpace, values) => searchSpace.LastIndexOfAnyExcept(values));
-
- static int LastIndexOfAnyExceptReferenceImpl(ReadOnlySpan<char> searchSpace, ReadOnlySpan<char> values)
- {
- for (int i = searchSpace.Length - 1; i >= 0; i--)
- {
- if (!values.Contains(searchSpace[i]))
- {
- return i;
- }
- }
-
- return -1;
- }
- }
}
public record SimpleRecord(int Value);
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Buffers;
+using System.Linq;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using Xunit;
+
+namespace System.SpanTests
+{
+ public static partial class SpanTests
+ {
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public static void AsciiNeedle_ProperlyHandlesEdgeCases_Char(bool needleContainsZero)
+ {
+ // There is some special handling we have to do for ASCII needles to properly filter out non-ASCII results
+ ReadOnlySpan<char> needleValues = needleContainsZero ? "AEIOU\0" : "AEIOU!";
+ IndexOfAnyValues<char> needle = IndexOfAnyValues.Create(needleValues);
+
+ ReadOnlySpan<char> repeatingHaystack = "AaAaAaAaAaAa";
+ Assert.Equal(0, repeatingHaystack.IndexOfAny(needle));
+ Assert.Equal(1, repeatingHaystack.IndexOfAnyExcept(needle));
+ Assert.Equal(10, repeatingHaystack.LastIndexOfAny(needle));
+ Assert.Equal(11, repeatingHaystack.LastIndexOfAnyExcept(needle));
+
+ ReadOnlySpan<char> haystackWithZeroes = "Aa\0Aa\0Aa\0";
+ Assert.Equal(0, haystackWithZeroes.IndexOfAny(needle));
+ Assert.Equal(1, haystackWithZeroes.IndexOfAnyExcept(needle));
+ Assert.Equal(needleContainsZero ? 8 : 6, haystackWithZeroes.LastIndexOfAny(needle));
+ Assert.Equal(needleContainsZero ? 7 : 8, haystackWithZeroes.LastIndexOfAnyExcept(needle));
+
+ Span<char> haystackWithOffsetNeedle = new char[100];
+ for (int i = 0; i < haystackWithOffsetNeedle.Length; i++)
+ {
+ haystackWithOffsetNeedle[i] = (char)(128 + needleValues[i % needleValues.Length]);
+ }
+
+ Assert.Equal(-1, haystackWithOffsetNeedle.IndexOfAny(needle));
+ Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
+ Assert.Equal(-1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
+ Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
+
+ // Mix matching characters back in
+ for (int i = 0; i < haystackWithOffsetNeedle.Length; i += 3)
+ {
+ haystackWithOffsetNeedle[i] = needleValues[i % needleValues.Length];
+ }
+
+ Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAny(needle));
+ Assert.Equal(1, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
+ Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
+ Assert.Equal(haystackWithOffsetNeedle.Length - 2, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
+
+ // With chars, the lower byte could be matching, but we have to check that the higher byte is also 0
+ for (int i = 0; i < haystackWithOffsetNeedle.Length; i++)
+ {
+ haystackWithOffsetNeedle[i] = (char)(((i + 1) * 256) + needleValues[i % needleValues.Length]);
+ }
+
+ Assert.Equal(-1, haystackWithOffsetNeedle.IndexOfAny(needle));
+ Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
+ Assert.Equal(-1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
+ Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
+
+ // Mix matching characters back in
+ for (int i = 0; i < haystackWithOffsetNeedle.Length; i += 3)
+ {
+ haystackWithOffsetNeedle[i] = needleValues[i % needleValues.Length];
+ }
+
+ Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAny(needle));
+ Assert.Equal(1, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
+ Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
+ Assert.Equal(haystackWithOffsetNeedle.Length - 2, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public static void AsciiNeedle_ProperlyHandlesEdgeCases_Byte(bool needleContainsZero)
+ {
+ // There is some special handling we have to do for ASCII needles to properly filter out non-ASCII results
+ ReadOnlySpan<byte> needleValues = needleContainsZero ? "AEIOU\0"u8 : "AEIOU!"u8;
+ IndexOfAnyValues<byte> needle = IndexOfAnyValues.Create(needleValues);
+
+ ReadOnlySpan<byte> repeatingHaystack = "AaAaAaAaAaAa"u8;
+ Assert.Equal(0, repeatingHaystack.IndexOfAny(needle));
+ Assert.Equal(1, repeatingHaystack.IndexOfAnyExcept(needle));
+ Assert.Equal(10, repeatingHaystack.LastIndexOfAny(needle));
+ Assert.Equal(11, repeatingHaystack.LastIndexOfAnyExcept(needle));
+
+ ReadOnlySpan<byte> haystackWithZeroes = "Aa\0Aa\0Aa\0"u8;
+ Assert.Equal(0, haystackWithZeroes.IndexOfAny(needle));
+ Assert.Equal(1, haystackWithZeroes.IndexOfAnyExcept(needle));
+ Assert.Equal(needleContainsZero ? 8 : 6, haystackWithZeroes.LastIndexOfAny(needle));
+ Assert.Equal(needleContainsZero ? 7 : 8, haystackWithZeroes.LastIndexOfAnyExcept(needle));
+
+ Span<byte> haystackWithOffsetNeedle = new byte[100];
+ for (int i = 0; i < haystackWithOffsetNeedle.Length; i++)
+ {
+ haystackWithOffsetNeedle[i] = (byte)(128 + needleValues[i % needleValues.Length]);
+ }
+
+ Assert.Equal(-1, haystackWithOffsetNeedle.IndexOfAny(needle));
+ Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
+ Assert.Equal(-1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
+ Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
+
+ // Mix matching characters back in
+ for (int i = 0; i < haystackWithOffsetNeedle.Length; i += 3)
+ {
+ haystackWithOffsetNeedle[i] = needleValues[i % needleValues.Length];
+ }
+
+ Assert.Equal(0, haystackWithOffsetNeedle.IndexOfAny(needle));
+ Assert.Equal(1, haystackWithOffsetNeedle.IndexOfAnyExcept(needle));
+ Assert.Equal(haystackWithOffsetNeedle.Length - 1, haystackWithOffsetNeedle.LastIndexOfAny(needle));
+ Assert.Equal(haystackWithOffsetNeedle.Length - 2, haystackWithOffsetNeedle.LastIndexOfAnyExcept(needle));
+ }
+
+ [Theory]
+ [InlineData("\0")]
+ [InlineData("a")]
+ [InlineData("ab")]
+ [InlineData("ac")]
+ [InlineData("abc")]
+ [InlineData("aei")]
+ [InlineData("abcd")]
+ [InlineData("aeio")]
+ [InlineData("aeiou")]
+ [InlineData("abceiou")]
+ [InlineData("123456789")]
+ [InlineData("123456789123")]
+ [InlineData("abcdefgh")]
+ [InlineData("abcdefghIJK")]
+ [InlineData("aa")]
+ [InlineData("aaa")]
+ [InlineData("aaaa")]
+ [InlineData("aaaaa")]
+ [InlineData("\u0000\u0001\u0002\u0003\u0004\u0005")]
+ [InlineData("\u0001\u0002\u0003\u0004\u0005\u0006")]
+ [InlineData("\u0001\u0002\u0003\u0004\u0005\u0007")]
+ [InlineData("\u007E\u007F\u0080\u0081\u0082\u0083")]
+ [InlineData("\u007E\u007F\u0080\u0081\u0082\u0084")]
+ [InlineData("\u007E\u007F\u0080\u0081\u0082")]
+ [InlineData("\u007E\u007F\u0080\u0081\u0083")]
+ [InlineData("\u00FE\u00FF\u0100\u0101\u0102\u0103")]
+ [InlineData("\u00FE\u00FF\u0100\u0101\u0102\u0104")]
+ [InlineData("\u00FE\u00FF\u0100\u0101\u0102")]
+ [InlineData("\u00FE\u00FF\u0100\u0101\u0103")]
+ [InlineData("\uFFFF\uFFFE\uFFFD\uFFFC\uFFFB\uFFFA")]
+ [InlineData("\uFFFF\uFFFE\uFFFD\uFFFC\uFFFB\uFFFB")]
+ [InlineData("\uFFFF\uFFFE\uFFFD\uFFFC\uFFFB\uFFF9")]
+ public static void IndexOfAnyValues_Contains(string needle)
+ {
+ Test(needle, IndexOfAnyValues.Create(needle));
+
+ byte[] needleBytes = Encoding.Latin1.GetBytes(needle);
+ Test(needleBytes, IndexOfAnyValues.Create(needleBytes));
+
+ static void Test<T>(ReadOnlySpan<T> needle, IndexOfAnyValues<T> values) where T : struct, INumber<T>, IMinMaxValue<T>
+ {
+ for (int i = int.CreateChecked(T.MaxValue); i >= 0; i--)
+ {
+ T t = T.CreateChecked(i);
+ Assert.Equal(needle.Contains(t), values.Contains(t));
+ }
+ }
+ }
+
+ [Fact]
+ [OuterLoop("Takes about a second to execute")]
+ public static void TestIndexOfAny_RandomInputs_Byte()
+ {
+ IndexOfAnyValuesTestHelper.TestRandomInputs(
+ expected: IndexOfAnyReferenceImpl,
+ indexOfAny: (searchSpace, values) => searchSpace.IndexOfAny(values),
+ indexOfAnyValues: (searchSpace, values) => searchSpace.IndexOfAny(values));
+
+ static int IndexOfAnyReferenceImpl(ReadOnlySpan<byte> searchSpace, ReadOnlySpan<byte> values)
+ {
+ for (int i = 0; i < searchSpace.Length; i++)
+ {
+ if (values.Contains(searchSpace[i]))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+ }
+
+ [Fact]
+ [OuterLoop("Takes about a second to execute")]
+ public static void TestIndexOfAny_RandomInputs_Char()
+ {
+ IndexOfAnyValuesTestHelper.TestRandomInputs(
+ expected: IndexOfAnyReferenceImpl,
+ indexOfAny: (searchSpace, values) => searchSpace.IndexOfAny(values),
+ indexOfAnyValues: (searchSpace, values) => searchSpace.IndexOfAny(values));
+
+ static int IndexOfAnyReferenceImpl(ReadOnlySpan<char> searchSpace, ReadOnlySpan<char> values)
+ {
+ for (int i = 0; i < searchSpace.Length; i++)
+ {
+ if (values.Contains(searchSpace[i]))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+ }
+
+ [Fact]
+ [OuterLoop("Takes about a second to execute")]
+ public static void TestLastIndexOfAny_RandomInputs_Byte()
+ {
+ IndexOfAnyValuesTestHelper.TestRandomInputs(
+ expected: LastIndexOfAnyReferenceImpl,
+ indexOfAny: (searchSpace, values) => searchSpace.LastIndexOfAny(values),
+ indexOfAnyValues: (searchSpace, values) => searchSpace.LastIndexOfAny(values));
+
+ static int LastIndexOfAnyReferenceImpl(ReadOnlySpan<byte> searchSpace, ReadOnlySpan<byte> values)
+ {
+ for (int i = searchSpace.Length - 1; i >= 0; i--)
+ {
+ if (values.Contains(searchSpace[i]))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+ }
+
+ [Fact]
+ [OuterLoop("Takes about a second to execute")]
+ public static void TestLastIndexOfAny_RandomInputs_Char()
+ {
+ IndexOfAnyValuesTestHelper.TestRandomInputs(
+ expected: LastIndexOfAnyReferenceImpl,
+ indexOfAny: (searchSpace, values) => searchSpace.LastIndexOfAny(values),
+ indexOfAnyValues: (searchSpace, values) => searchSpace.LastIndexOfAny(values));
+
+ static int LastIndexOfAnyReferenceImpl(ReadOnlySpan<char> searchSpace, ReadOnlySpan<char> values)
+ {
+ for (int i = searchSpace.Length - 1; i >= 0; i--)
+ {
+ if (values.Contains(searchSpace[i]))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+ }
+
+ [Fact]
+ [OuterLoop("Takes about a second to execute")]
+ public static void TestIndexOfAnyExcept_RandomInputs_Byte()
+ {
+ IndexOfAnyValuesTestHelper.TestRandomInputs(
+ expected: IndexOfAnyExceptReferenceImpl,
+ indexOfAny: (searchSpace, values) => searchSpace.IndexOfAnyExcept(values),
+ indexOfAnyValues: (searchSpace, values) => searchSpace.IndexOfAnyExcept(values));
+
+ static int IndexOfAnyExceptReferenceImpl(ReadOnlySpan<byte> searchSpace, ReadOnlySpan<byte> values)
+ {
+ for (int i = 0; i < searchSpace.Length; i++)
+ {
+ if (!values.Contains(searchSpace[i]))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+ }
+
+ [Fact]
+ [OuterLoop("Takes about a second to execute")]
+ public static void TestIndexOfAnyExcept_RandomInputs_Char()
+ {
+ IndexOfAnyValuesTestHelper.TestRandomInputs(
+ expected: IndexOfAnyExceptReferenceImpl,
+ indexOfAny: (searchSpace, values) => searchSpace.IndexOfAnyExcept(values),
+ indexOfAnyValues: (searchSpace, values) => searchSpace.IndexOfAnyExcept(values));
+
+ static int IndexOfAnyExceptReferenceImpl(ReadOnlySpan<char> searchSpace, ReadOnlySpan<char> values)
+ {
+ for (int i = 0; i < searchSpace.Length; i++)
+ {
+ if (!values.Contains(searchSpace[i]))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+ }
+
+ [Fact]
+ [OuterLoop("Takes about a second to execute")]
+ public static void TestLastIndexOfAnyExcept_RandomInputs_Byte()
+ {
+ IndexOfAnyValuesTestHelper.TestRandomInputs(
+ expected: LastIndexOfAnyExceptReferenceImpl,
+ indexOfAny: (searchSpace, values) => searchSpace.LastIndexOfAnyExcept(values),
+ indexOfAnyValues: (searchSpace, values) => searchSpace.LastIndexOfAnyExcept(values));
+
+ static int LastIndexOfAnyExceptReferenceImpl(ReadOnlySpan<byte> searchSpace, ReadOnlySpan<byte> values)
+ {
+ for (int i = searchSpace.Length - 1; i >= 0; i--)
+ {
+ if (!values.Contains(searchSpace[i]))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+ }
+
+ [Fact]
+ [OuterLoop("Takes about a second to execute")]
+ public static void TestLastIndexOfAnyExcept_RandomInputs_Char()
+ {
+ IndexOfAnyValuesTestHelper.TestRandomInputs(
+ expected: LastIndexOfAnyExceptReferenceImpl,
+ indexOfAny: (searchSpace, values) => searchSpace.LastIndexOfAnyExcept(values),
+ indexOfAnyValues: (searchSpace, values) => searchSpace.LastIndexOfAnyExcept(values));
+
+ static int LastIndexOfAnyExceptReferenceImpl(ReadOnlySpan<char> searchSpace, ReadOnlySpan<char> values)
+ {
+ for (int i = searchSpace.Length - 1; i >= 0; i--)
+ {
+ if (!values.Contains(searchSpace[i]))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+ }
+
+ private static class IndexOfAnyValuesTestHelper
+ {
+ private const int MaxNeedleLength = 10;
+ private const int MaxHaystackLength = 40;
+
+ private static readonly char[] s_randomAsciiChars;
+ private static readonly char[] s_randomLatin1Chars;
+ private static readonly char[] s_randomChars;
+ private static readonly byte[] s_randomAsciiBytes;
+ private static readonly byte[] s_randomBytes;
+
+ static IndexOfAnyValuesTestHelper()
+ {
+ s_randomAsciiChars = new char[100 * 1024];
+ s_randomLatin1Chars = new char[100 * 1024];
+ s_randomChars = new char[1024 * 1024];
+ s_randomBytes = new byte[100 * 1024];
+
+ var rng = new Random(42);
+
+ for (int i = 0; i < s_randomAsciiChars.Length; i++)
+ {
+ s_randomAsciiChars[i] = (char)rng.Next(0, 128);
+ }
+
+ for (int i = 0; i < s_randomLatin1Chars.Length; i++)
+ {
+ s_randomLatin1Chars[i] = (char)rng.Next(0, 256);
+ }
+
+ rng.NextBytes(MemoryMarshal.Cast<char, byte>(s_randomChars));
+
+ s_randomAsciiBytes = Encoding.ASCII.GetBytes(s_randomAsciiChars);
+
+ rng.NextBytes(s_randomBytes);
+ }
+
+ public delegate int IndexOfAnySearchDelegate<T>(ReadOnlySpan<T> searchSpace, ReadOnlySpan<T> values) where T : IEquatable<T>?;
+
+ public delegate int IndexOfAnyValuesSearchDelegate<T>(ReadOnlySpan<T> searchSpace, IndexOfAnyValues<T> values) where T : IEquatable<T>?;
+
+ public static void TestRandomInputs(IndexOfAnySearchDelegate<byte> expected, IndexOfAnySearchDelegate<byte> indexOfAny, IndexOfAnyValuesSearchDelegate<byte> indexOfAnyValues)
+ {
+ var rng = new Random(42);
+
+ for (int iterations = 0; iterations < 1_000_000; iterations++)
+ {
+ // There are more interesting corner cases with ASCII needles, test those more.
+ Test(rng, s_randomBytes, s_randomAsciiBytes, expected, indexOfAny, indexOfAnyValues);
+
+ Test(rng, s_randomBytes, s_randomBytes, expected, indexOfAny, indexOfAnyValues);
+ }
+ }
+
+ public static void TestRandomInputs(IndexOfAnySearchDelegate<char> expected, IndexOfAnySearchDelegate<char> indexOfAny, IndexOfAnyValuesSearchDelegate<char> indexOfAnyValues)
+ {
+ var rng = new Random(42);
+
+ for (int iterations = 0; iterations < 1_000_000; iterations++)
+ {
+ // There are more interesting corner cases with ASCII needles, test those more.
+ Test(rng, s_randomChars, s_randomAsciiChars, expected, indexOfAny, indexOfAnyValues);
+
+ Test(rng, s_randomChars, s_randomLatin1Chars, expected, indexOfAny, indexOfAnyValues);
+
+ Test(rng, s_randomChars, s_randomChars, expected, indexOfAny, indexOfAnyValues);
+ }
+ }
+
+ private static void Test<T>(Random rng, ReadOnlySpan<T> haystackRandom, ReadOnlySpan<T> needleRandom,
+ IndexOfAnySearchDelegate<T> expected, IndexOfAnySearchDelegate<T> indexOfAny, IndexOfAnyValuesSearchDelegate<T> indexOfAnyValues)
+ where T : struct, INumber<T>, IMinMaxValue<T>
+ {
+ ReadOnlySpan<T> haystack = GetRandomSlice(rng, haystackRandom, MaxHaystackLength);
+ ReadOnlySpan<T> needle = GetRandomSlice(rng, needleRandom, MaxNeedleLength);
+
+ IndexOfAnyValues<T> indexOfAnyValuesInstance = (IndexOfAnyValues<T>)(object)(typeof(T) == typeof(byte)
+ ? IndexOfAnyValues.Create(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(needle)), needle.Length))
+ : IndexOfAnyValues.Create(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(needle)), needle.Length)));
+
+ int expectedIndex = expected(haystack, needle);
+ int indexOfAnyIndex = indexOfAny(haystack, needle);
+ int indexOfAnyValuesIndex = indexOfAnyValues(haystack, indexOfAnyValuesInstance);
+
+ if (expectedIndex != indexOfAnyIndex)
+ {
+ AssertionFailed(haystack, needle, expectedIndex, indexOfAnyIndex, nameof(indexOfAny));
+ }
+
+ if (expectedIndex != indexOfAnyValuesIndex)
+ {
+ AssertionFailed(haystack, needle, expectedIndex, indexOfAnyValuesIndex, nameof(indexOfAnyValues));
+ }
+ }
+
+ private static ReadOnlySpan<T> GetRandomSlice<T>(Random rng, ReadOnlySpan<T> span, int maxLength)
+ {
+ ReadOnlySpan<T> slice = span.Slice(rng.Next(span.Length + 1));
+ return slice.Slice(0, Math.Min(slice.Length, rng.Next(maxLength + 1)));
+ }
+
+ private static void AssertionFailed<T>(ReadOnlySpan<T> haystack, ReadOnlySpan<T> needle, int expected, int actual, string approach)
+ where T : INumber<T>
+ {
+ string readableHaystack = string.Join(", ", haystack.ToString().Select(c => int.CreateChecked(c)));
+ string readableNeedle = string.Join(", ", needle.ToString().Select(c => int.CreateChecked(c)));
+
+ Assert.True(false, $"Expected {expected}, got {approach}={actual} for needle='{readableNeedle}', haystack='{readableHaystack}'");
+ }
+ }
+ }
+}
}
}
}
-
- [Fact]
- [OuterLoop("Takes about a second to execute")]
- public static void TestLastIndexOfAny_RandomInputs_Byte()
- {
- IndexOfAnyCharTestHelper.TestRandomInputs(
- expected: LastIndexOfAnyReferenceImpl,
- indexOfAny: (searchSpace, values) => searchSpace.LastIndexOfAny(values),
- indexOfAnyValues: (searchSpace, values) => searchSpace.LastIndexOfAny(values));
-
- static int LastIndexOfAnyReferenceImpl(ReadOnlySpan<byte> searchSpace, ReadOnlySpan<byte> values)
- {
- for (int i = searchSpace.Length - 1; i >= 0; i--)
- {
- if (values.Contains(searchSpace[i]))
- {
- return i;
- }
- }
-
- return -1;
- }
- }
-
- [Fact]
- [OuterLoop("Takes about a second to execute")]
- public static void TestLastIndexOfAny_RandomInputs_Char()
- {
- IndexOfAnyCharTestHelper.TestRandomInputs(
- expected: LastIndexOfAnyReferenceImpl,
- indexOfAny: (searchSpace, values) => searchSpace.LastIndexOfAny(values),
- indexOfAnyValues: (searchSpace, values) => searchSpace.LastIndexOfAny(values));
-
- static int LastIndexOfAnyReferenceImpl(ReadOnlySpan<char> searchSpace, ReadOnlySpan<char> values)
- {
- for (int i = searchSpace.Length - 1; i >= 0; i--)
- {
- if (values.Contains(searchSpace[i]))
- {
- return i;
- }
- }
-
- return -1;
- }
- }
}
}
<Compile Include="Span\IndexOfAny.T.cs" />
<Compile Include="Span\IndexOfAnyExcept.T.cs" />
<Compile Include="Span\IndexOfAnyInRange.cs" />
+ <Compile Include="Span\IndexOfAnyValues.cs" />
<Compile Include="Span\IndexOfSequence.byte.cs" />
<Compile Include="Span\IndexOfSequence.char.cs" />
<Compile Include="Span\IndexOfSequence.T.cs" />
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
+using System.Numerics;
using System.Runtime.CompilerServices;
namespace System.Buffers
{
internal sealed class IndexOfAny1Value<T> : IndexOfAnyValues<T>
- where T : struct, IEquatable<T>
+ where T : struct, INumber<T>
{
private readonly T _e0;
internal override T[] GetValues() => new[] { _e0 };
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal override bool ContainsCore(T value) =>
+ value == _e0;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
span.IndexOf(_e0);
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
+using System.Numerics;
using System.Runtime.CompilerServices;
namespace System.Buffers
{
internal sealed class IndexOfAny2Values<T> : IndexOfAnyValues<T>
- where T : struct, IEquatable<T>
+ where T : struct, INumber<T>
{
private readonly T _e0, _e1;
internal override T[] GetValues() => new[] { _e0, _e1 };
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal override bool ContainsCore(T value) =>
+ value == _e0 || value == _e1;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
span.IndexOfAny(_e0, _e1);
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
+using System.Numerics;
using System.Runtime.CompilerServices;
namespace System.Buffers
{
internal sealed class IndexOfAny3Values<T> : IndexOfAnyValues<T>
- where T : struct, IEquatable<T>
+ where T : struct, INumber<T>
{
private readonly T _e0, _e1, _e2;
internal override T[] GetValues() => new[] { _e0, _e1, _e2 };
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal override bool ContainsCore(T value) =>
+ value == _e0 || value == _e1 || value == _e2;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
span.IndexOfAny(_e0, _e1, _e2);
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+#pragma warning disable 8500 // address of managed types
+
namespace System.Buffers
{
internal sealed class IndexOfAny4Values<T, TImpl> : IndexOfAnyValues<T>
(_e0, _e1, _e2, _e3) = (values[0], values[1], values[2], values[3]);
}
- internal override T[] GetValues()
+ internal override unsafe T[] GetValues()
{
TImpl e0 = _e0, e1 = _e1, e2 = _e2, e3 = _e3;
- return new[] { Unsafe.As<TImpl, T>(ref e0), Unsafe.As<TImpl, T>(ref e1), Unsafe.As<TImpl, T>(ref e2), Unsafe.As<TImpl, T>(ref e3) };
+ return new[] { *(T*)&e0, *(T*)&e1, *(T*)&e2, *(T*)&e3 };
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal override unsafe bool ContainsCore(T value) =>
+ *(TImpl*)&value == _e0 ||
+ *(TImpl*)&value == _e1 ||
+ *(TImpl*)&value == _e2 ||
+ *(TImpl*)&value == _e3;
+
#if MONO // Revert this once https://github.com/dotnet/runtime/pull/78015 is merged
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
span.IndexOfAny(GetValues());
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+#pragma warning disable 8500 // address of managed types
+
namespace System.Buffers
{
internal sealed class IndexOfAny5Values<T, TImpl> : IndexOfAnyValues<T>
(_e0, _e1, _e2, _e3, _e4) = (values[0], values[1], values[2], values[3], values[4]);
}
- internal override T[] GetValues()
+ internal override unsafe T[] GetValues()
{
TImpl e0 = _e0, e1 = _e1, e2 = _e2, e3 = _e3, e4 = _e4;
- return new[] { Unsafe.As<TImpl, T>(ref e0), Unsafe.As<TImpl, T>(ref e1), Unsafe.As<TImpl, T>(ref e2), Unsafe.As<TImpl, T>(ref e3), Unsafe.As<TImpl, T>(ref e4) };
+ return new[] { *(T*)&e0, *(T*)&e1, *(T*)&e2, *(T*)&e3, *(T*)&e4 };
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal override unsafe bool ContainsCore(T value) =>
+ *(TImpl*)&value == _e0 ||
+ *(TImpl*)&value == _e1 ||
+ *(TImpl*)&value == _e2 ||
+ *(TImpl*)&value == _e3 ||
+ *(TImpl*)&value == _e4;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
SpanHelpers.IndexOfAnyValueType(ref Unsafe.As<T, TImpl>(ref MemoryMarshal.GetReference(span)), _e0, _e1, _e2, _e3, _e4, span.Length);
internal override byte[] GetValues() => _lookup.GetByteValues();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal override bool ContainsCore(byte value) =>
+ _lookup.Contains(value);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<byte> span) =>
IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(ref MemoryMarshal.GetReference(span), span.Length);
internal override char[] GetValues() => _lookup.GetCharValues();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal override bool ContainsCore(char value) =>
+ _lookup.Contains128(value);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<char> span) =>
IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(ref MemoryMarshal.GetReference(span), span.Length);
internal override byte[] GetValues() => _lookup.GetByteValues();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal override bool ContainsCore(byte value) =>
+ _lookup.Contains(value);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<byte> span) =>
IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(ref MemoryMarshal.GetReference(span), span.Length);
internal override char[] GetValues() => _values.ToCharArray();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal override bool ContainsCore(char value) =>
+ ProbabilisticMap.Contains(ref Unsafe.As<ProbabilisticMap, uint>(ref _map), _values, value);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<char> span) =>
IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(ref MemoryMarshal.GetReference(span), span.Length);
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<char> span) =>
IndexOfAny<IndexOfAnyAsciiSearcher.DontNegate>(ref MemoryMarshal.GetReference(span), span.Length);
/// <summary>Used by <see cref="IndexOfAnyValuesDebugView{T}"/>.</summary>
internal virtual T[] GetValues() => throw new UnreachableException();
+ /// <summary>
+ /// Searches for the specified value and returns true if found. If not found, returns false.
+ /// </summary>
+ /// <param name="value">The value to search for.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Contains(T value) => ContainsCore(value);
+
+ internal virtual bool ContainsCore(T value) => throw new UnreachableException();
+
internal virtual int IndexOfAny(ReadOnlySpan<T> span) => throw new UnreachableException();
internal virtual int IndexOfAnyExcept(ReadOnlySpan<T> span) => throw new UnreachableException();
internal virtual int LastIndexOfAny(ReadOnlySpan<T> span) => throw new UnreachableException();
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
where T : struct, INumber<T>
{
private readonly T _lowInclusive, _highInclusive;
+ private readonly uint _lowUint, _highMinusLow;
- public IndexOfAnyValuesInRange(T lowInclusive, T highInclusive) =>
+ public IndexOfAnyValuesInRange(T lowInclusive, T highInclusive)
+ {
+ Debug.Assert(lowInclusive is byte or char);
(_lowInclusive, _highInclusive) = (lowInclusive, highInclusive);
+ _lowUint = uint.CreateChecked(lowInclusive);
+ _highMinusLow = uint.CreateChecked(highInclusive - lowInclusive);
+ }
internal override T[] GetValues()
{
- T[] values = new T[int.CreateChecked(_highInclusive - _lowInclusive)];
+ T[] values = new T[_highMinusLow + 1];
T element = _lowInclusive;
for (int i = 0; i < values.Length; i++)
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal override bool ContainsCore(T value) =>
+ uint.CreateChecked(value) - _lowUint <= _highMinusLow;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
span.IndexOfAnyInRange(_lowInclusive, _highInclusive);
internal sealed class IndexOfEmptyValues<T> : IndexOfAnyValues<T>
where T : IEquatable<T>?
{
- internal override T[] GetValues() => Array.Empty<T>();
+ internal override T[] GetValues() =>
+ Array.Empty<T>();
+
+ internal override bool ContainsCore(T value) =>
+ false;
internal override int IndexOfAny(ReadOnlySpan<T> span) =>
-1;
public class IndexOfAnyValues<T> where T : System.IEquatable<T>?
{
internal IndexOfAnyValues() { }
+ public bool Contains(T value) { throw null; }
}
public static class IndexOfAnyValues
{