From 19841f4cafa988d34cf3515e79c3b73c1384b9c0 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 12 Apr 2022 21:57:28 -0400 Subject: [PATCH 1/2] Add initial version of {Last}IndexOfAnyExcept These are functional but not vectorized. At least some of these should be vectorized for at least some data types subsequently, but that's a more intensive change. Once that's in, we can update a few places to use these, e.g. Regex should end up using any of the overloads that are vectorized. --- .../System.Memory/ref/System.Memory.cs | 16 + .../tests/Span/IndexOfAnyExcept.T.cs | 185 +++++++++++ .../tests/System.Memory.Tests.csproj | 3 +- .../src/System/MemoryExtensions.cs | 306 ++++++++++++++++++ 4 files changed, 509 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.Memory/tests/Span/IndexOfAnyExcept.T.cs diff --git a/src/libraries/System.Memory/ref/System.Memory.cs b/src/libraries/System.Memory/ref/System.Memory.cs index 12d9021699747..088efa1c8eb81 100644 --- a/src/libraries/System.Memory/ref/System.Memory.cs +++ b/src/libraries/System.Memory/ref/System.Memory.cs @@ -65,6 +65,14 @@ public static void CopyTo(this T[]? source, System.Span destination) { } public static int IndexOfAny(this System.Span span, System.ReadOnlySpan values) where T : System.IEquatable { throw null; } public static int IndexOfAny(this System.Span span, T value0, T value1) where T : System.IEquatable { throw null; } public static int IndexOfAny(this System.Span span, T value0, T value1, T value2) where T : System.IEquatable { throw null; } + public static int IndexOfAnyExcept(this System.Span span, T value) where T : System.IEquatable { throw null; } + public static int IndexOfAnyExcept(this System.Span span, T value0, T value1) where T : System.IEquatable { throw null; } + public static int IndexOfAnyExcept(this System.Span span, T value0, T value1, T value2) where T : System.IEquatable { throw null; } + public static int IndexOfAnyExcept(this System.Span span, System.ReadOnlySpan values) where T : System.IEquatable { throw null; } + public static int IndexOfAnyExcept(this System.ReadOnlySpan span, T value) where T : System.IEquatable { throw null; } + public static int IndexOfAnyExcept(this System.ReadOnlySpan span, T value0, T value1) where T : System.IEquatable { throw null; } + public static int IndexOfAnyExcept(this System.ReadOnlySpan span, T value0, T value1, T value2) where T : System.IEquatable { throw null; } + public static int IndexOfAnyExcept(this System.ReadOnlySpan span, System.ReadOnlySpan values) where T : System.IEquatable { throw null; } public static int IndexOf(this System.ReadOnlySpan span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } public static int IndexOf(this System.ReadOnlySpan span, T value) where T : System.IEquatable { throw null; } public static int IndexOf(this System.Span span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } @@ -77,6 +85,14 @@ public static void CopyTo(this T[]? source, System.Span destination) { } public static int LastIndexOfAny(this System.Span span, System.ReadOnlySpan values) where T : System.IEquatable { throw null; } public static int LastIndexOfAny(this System.Span span, T value0, T value1) where T : System.IEquatable { throw null; } public static int LastIndexOfAny(this System.Span span, T value0, T value1, T value2) where T : System.IEquatable { throw null; } + public static int LastIndexOfAnyExcept(this System.Span span, T value) where T : System.IEquatable { throw null; } + public static int LastIndexOfAnyExcept(this System.Span span, T value0, T value1) where T : System.IEquatable { throw null; } + public static int LastIndexOfAnyExcept(this System.Span span, T value0, T value1, T value2) where T : System.IEquatable { throw null; } + public static int LastIndexOfAnyExcept(this System.Span span, System.ReadOnlySpan values) where T : System.IEquatable { throw null; } + public static int LastIndexOfAnyExcept(this System.ReadOnlySpan span, T value) where T : System.IEquatable { throw null; } + public static int LastIndexOfAnyExcept(this System.ReadOnlySpan span, T value0, T value1) where T : System.IEquatable { throw null; } + public static int LastIndexOfAnyExcept(this System.ReadOnlySpan span, T value0, T value1, T value2) where T : System.IEquatable { throw null; } + public static int LastIndexOfAnyExcept(this System.ReadOnlySpan span, System.ReadOnlySpan values) where T : System.IEquatable { throw null; } public static int LastIndexOf(this System.ReadOnlySpan span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } public static int LastIndexOf(this System.ReadOnlySpan span, T value) where T : System.IEquatable { throw null; } public static int LastIndexOf(this System.Span span, System.ReadOnlySpan value) where T : System.IEquatable { throw null; } diff --git a/src/libraries/System.Memory/tests/Span/IndexOfAnyExcept.T.cs b/src/libraries/System.Memory/tests/Span/IndexOfAnyExcept.T.cs new file mode 100644 index 0000000000000..37d24ee93c237 --- /dev/null +++ b/src/libraries/System.Memory/tests/Span/IndexOfAnyExcept.T.cs @@ -0,0 +1,185 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Generic; +using Xunit; + +namespace System.SpanTests +{ + public class IndexOfAnyExceptTests_Byte : IndexOfAnyExceptTests { protected override byte Create(int value) => (byte)value; } + public class IndexOfAnyExceptTests_Char : IndexOfAnyExceptTests { protected override char Create(int value) => (char)value; } + public class IndexOfAnyExceptTests_Int32 : IndexOfAnyExceptTests { protected override int Create(int value) => value; } + public class IndexOfAnyExceptTests_Int64 : IndexOfAnyExceptTests { protected override long Create(int value) => value; } + public class IndexOfAnyExceptTests_String : IndexOfAnyExceptTests { protected override string Create(int value) => ((char)value).ToString(); } + public class IndexOfAnyExceptTests_Record : IndexOfAnyExceptTests { protected override SimpleRecord Create(int value) => new SimpleRecord(value); } + + public record SimpleRecord(int Value); + + public abstract class IndexOfAnyExceptTests where T : IEquatable + { + private readonly T _a, _b, _c, _d, _e; + + public IndexOfAnyExceptTests() + { + _a = Create('a'); + _b = Create('b'); + _c = Create('c'); + _d = Create('d'); + _e = Create('e'); + } + + /// Validate that the methods return -1 when the source span is empty. + [Fact] + public void ZeroLengthSpan_ReturnNegative1() + { + Assert.Equal(-1, IndexOfAnyExcept(Span.Empty)); + Assert.Equal(-1, IndexOfAnyExcept(Span.Empty, _a)); + Assert.Equal(-1, IndexOfAnyExcept(Span.Empty, _a, _b)); + Assert.Equal(-1, IndexOfAnyExcept(Span.Empty, _a, _b, _c)); + Assert.Equal(-1, IndexOfAnyExcept(Span.Empty, new[] { _a, _b, _c, _d })); + + Assert.Equal(-1, LastIndexOfAnyExcept(Span.Empty)); + Assert.Equal(-1, LastIndexOfAnyExcept(Span.Empty, _a)); + Assert.Equal(-1, LastIndexOfAnyExcept(Span.Empty, _a, _b)); + Assert.Equal(-1, LastIndexOfAnyExcept(Span.Empty, _a, _b, _c)); + Assert.Equal(-1, LastIndexOfAnyExcept(Span.Empty, new[] { _a, _b, _c, _d })); + } + + public static IEnumerable AllElementsMatch_ReturnsNegative1_MemberData() + { + foreach (int length in new[] { 1, 2, 4, 7, 15, 16, 17, 31, 32, 33, 100 }) + { + yield return new object[] { length }; + } + } + + /// Validate that the methods return -1 when the source span contains only the values being excepted. + [Theory] + [MemberData(nameof(AllElementsMatch_ReturnsNegative1_MemberData))] + public void AllElementsMatch_ReturnsNegative1(int length) + { + Assert.Equal(-1, IndexOfAnyExcept(CreateArray(length, _a), _a)); + Assert.Equal(-1, IndexOfAnyExcept(CreateArray(length, _a, _b), _a, _b)); + Assert.Equal(-1, IndexOfAnyExcept(CreateArray(length, _a, _b, _c), _a, _b, _c)); + Assert.Equal(-1, IndexOfAnyExcept(CreateArray(length, _a, _b, _c, _d), _a, _b, _c, _d)); + + Assert.Equal(-1, LastIndexOfAnyExcept(CreateArray(length, _a), _a)); + Assert.Equal(-1, LastIndexOfAnyExcept(CreateArray(length, _a, _b), _a, _b)); + Assert.Equal(-1, LastIndexOfAnyExcept(CreateArray(length, _a, _b, _c), _a, _b, _c)); + Assert.Equal(-1, LastIndexOfAnyExcept(CreateArray(length, _a, _b, _c, _d), _a, _b, _c, _d)); + } + + public static IEnumerable SomeElementsDontMatch_ReturnsOffset_MemberData() + { + yield return new object[] { 1, new[] { 0 } }; + yield return new object[] { 2, new[] { 0, 1 } }; + yield return new object[] { 4, new[] { 2 } }; + yield return new object[] { 5, new[] { 2 } }; + yield return new object[] { 31, new[] { 30 } }; + yield return new object[] { 10, new[] { 1, 7 } }; + yield return new object[] { 100, new[] { 19, 20, 21, 80 } }; + } + + /// Validate that the methods return the expected position when the source span contains some data other than the excepted. + [Theory] + [MemberData(nameof(SomeElementsDontMatch_ReturnsOffset_MemberData))] + public void SomeElementsDontMatch_ReturnsOffset(int length, int[] matchPositions) + { + Assert.Equal(matchPositions[0], IndexOfAnyExcept(Set(CreateArray(length, _a), _e, matchPositions), _a)); + Assert.Equal(matchPositions[0], IndexOfAnyExcept(Set(CreateArray(length, _a, _b), _e, matchPositions), _a, _b)); + Assert.Equal(matchPositions[0], IndexOfAnyExcept(Set(CreateArray(length, _a, _b, _c), _e, matchPositions), _a, _b, _c)); + Assert.Equal(matchPositions[0], IndexOfAnyExcept(Set(CreateArray(length, _a, _b, _c, _d), _e, matchPositions), _a, _b, _c, _d)); + + Assert.Equal(matchPositions[^1], LastIndexOfAnyExcept(Set(CreateArray(length, _a), _e, matchPositions), _a)); + Assert.Equal(matchPositions[^1], LastIndexOfAnyExcept(Set(CreateArray(length, _a, _b), _e, matchPositions), _a, _b)); + Assert.Equal(matchPositions[^1], LastIndexOfAnyExcept(Set(CreateArray(length, _a, _b, _c), _e, matchPositions), _a, _b, _c)); + Assert.Equal(matchPositions[^1], LastIndexOfAnyExcept(Set(CreateArray(length, _a, _b, _c, _d), _e, matchPositions), _a, _b, _c, _d)); + } + + protected abstract T Create(int value); + + private T[] CreateArray(int length, params T[] values) + { + var arr = new T[length]; + for (int i = 0; i < arr.Length; i++) + { + arr[i] = values[i % values.Length]; + } + return arr; + } + + private T[] Set(T[] arr, T value, params int[] valuePositions) + { + foreach (int pos in valuePositions) + { + arr[pos] = value; + } + return arr; + } + + // Wrappers for {Last}IndexOfAnyExcept that invoke both the Span and ReadOnlySpan overloads, + // as well as the values overloads, ensuring they all produce the same result, and returning that result. + // This avoids needing to code the same call sites twice in all the above tests. + private static int IndexOfAnyExcept(Span span, T value) + { + int result = MemoryExtensions.IndexOfAnyExcept(span, value); + Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan)span, value)); + Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((Span)span, new[] { value })); + Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan)span, new[] { value })); + return result; + } + private static int IndexOfAnyExcept(Span span, T value0, T value1) + { + int result = MemoryExtensions.IndexOfAnyExcept(span, value0, value1); + Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan)span, value0, value1)); + Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((Span)span, new[] { value0, value1 })); + Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan)span, new[] { value0, value1 })); + return result; + } + private static int IndexOfAnyExcept(Span span, T value0, T value1, T value2) + { + int result = MemoryExtensions.IndexOfAnyExcept(span, value0, value1, value2); + Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan)span, value0, value1, value2)); + Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((Span)span, new[] { value0, value1, value2 })); + Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan)span, new[] { value0, value1, value2 })); + return result; + } + private static int IndexOfAnyExcept(Span span, params T[] values) + { + int result = MemoryExtensions.IndexOfAnyExcept(span, values); + Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan)span, values)); + return result; + } + private static int LastIndexOfAnyExcept(Span span, T value) + { + int result = MemoryExtensions.LastIndexOfAnyExcept(span, value); + Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan)span, value)); + Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((Span)span, new[] { value })); + Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan)span, new[] { value })); + return result; + } + private static int LastIndexOfAnyExcept(Span span, T value0, T value1) + { + int result = MemoryExtensions.LastIndexOfAnyExcept(span, value0, value1); + Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan)span, value0, value1)); + Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((Span)span, new[] { value0, value1 })); + Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan)span, new[] { value0, value1 })); + return result; + } + private static int LastIndexOfAnyExcept(Span span, T value0, T value1, T value2) + { + int result = MemoryExtensions.LastIndexOfAnyExcept(span, value0, value1, value2); + Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan)span, value0, value1, value2)); + Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((Span)span, new[] { value0, value1, value2 })); + Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan)span, new[] { value0, value1, value2 })); + return result; + } + private static int LastIndexOfAnyExcept(Span span, params T[] values) + { + int result = MemoryExtensions.LastIndexOfAnyExcept(span, values); + Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan)span, values)); + return result; + } + } +} diff --git a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj index 80fbcdcd64af1..3f7b83c38af19 100644 --- a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj @@ -1,4 +1,4 @@ - + true true @@ -81,6 +81,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 560925d532fdf..3872f15f731be 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -443,6 +443,312 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(value)), return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } + /// Searches for the first index of any value other than the specified . + /// The type of the span and values. + /// The span to search. + /// The value for which to search. + /// + /// The index in the span of the first occurrence of any value other than . + /// If all of the values are , returns -1. + /// + public static int IndexOfAnyExcept(this Span span, T value) where T : IEquatable => + IndexOfAnyExcept((ReadOnlySpan)span, value); + + /// Searches for the first index of any value other than the specified or . + /// The type of the span and values. + /// The span to search. + /// A value to avoid. + /// A value to avoid + /// + /// The index in the span of the first occurrence of any value other than and . + /// If all of the values are or , returns -1. + /// + public static int IndexOfAnyExcept(this Span span, T value0, T value1) where T : IEquatable => + IndexOfAnyExcept((ReadOnlySpan)span, value0, value1); + + /// Searches for the first index of any value other than the specified , , or . + /// The type of the span and values. + /// The span to search. + /// A value to avoid. + /// A value to avoid + /// A value to avoid + /// + /// The index in the span of the first occurrence of any value other than , , and . + /// If all of the values are , , and , returns -1. + /// + public static int IndexOfAnyExcept(this Span span, T value0, T value1, T value2) where T : IEquatable => + IndexOfAnyExcept((ReadOnlySpan)span, value0, value1, value2); + + /// Searches for the first index of any value other than the specified . + /// The type of the span and values. + /// The span to search. + /// The values to avoid. + /// + /// The index in the span of the first occurrence of any value other than those in . + /// If all of the values are in , returns -1. + /// + public static int IndexOfAnyExcept(this Span span, ReadOnlySpan values) where T : IEquatable => + IndexOfAnyExcept((ReadOnlySpan)span, values); + + /// Searches for the first index of any value other than the specified . + /// The type of the span and values. + /// The span to search. + /// The value for which to search. + /// + /// The index in the span of the first occurrence of any value other than . + /// If all of the values are , returns -1. + /// + public static int IndexOfAnyExcept(this ReadOnlySpan span, T value) where T : IEquatable + { + for (int i = 0; i < span.Length; i++) + { + if (!EqualityComparer.Default.Equals(span[i], value)) + { + return i; + } + } + + return -1; + } + + /// Searches for the first index of any value other than the specified or . + /// The type of the span and values. + /// The span to search. + /// A value to avoid. + /// A value to avoid + /// + /// The index in the span of the first occurrence of any value other than and . + /// If all of the values are or , returns -1. + /// + public static int IndexOfAnyExcept(this ReadOnlySpan span, T value0, T value1) where T : IEquatable + { + for (int i = 0; i < span.Length; i++) + { + if (!EqualityComparer.Default.Equals(span[i], value0) && + !EqualityComparer.Default.Equals(span[i], value1)) + { + return i; + } + } + + return -1; + } + + /// Searches for the first index of any value other than the specified , , or . + /// The type of the span and values. + /// The span to search. + /// A value to avoid. + /// A value to avoid + /// A value to avoid + /// + /// The index in the span of the first occurrence of any value other than , , and . + /// If all of the values are , , and , returns -1. + /// + public static int IndexOfAnyExcept(this ReadOnlySpan span, T value0, T value1, T value2) where T : IEquatable + { + for (int i = 0; i < span.Length; i++) + { + if (!EqualityComparer.Default.Equals(span[i], value0) && + !EqualityComparer.Default.Equals(span[i], value1) && + !EqualityComparer.Default.Equals(span[i], value2)) + { + return i; + } + } + + return -1; + } + + /// Searches for the first index of any value other than the specified . + /// The type of the span and values. + /// The span to search. + /// The values to avoid. + /// + /// The index in the span of the first occurrence of any value other than those in . + /// If all of the values are in , returns -1. + /// + public static int IndexOfAnyExcept(this ReadOnlySpan span, ReadOnlySpan values) where T : IEquatable + { + switch (values.Length) + { + case 0: + return -1; + + case 1: + return IndexOfAnyExcept(span, values[0]); + + case 2: + return IndexOfAnyExcept(span, values[0], values[1]); + + case 3: + return IndexOfAnyExcept(span, values[0], values[1], values[2]); + + default: + for (int i = 0; i < span.Length; i++) + { + if (!values.Contains(span[i])) + { + return i; + } + } + + return -1; + } + } + + /// Searches for the last index of any value other than the specified . + /// The type of the span and values. + /// The span to search. + /// The value for which to search. + /// + /// The index in the span of the last occurrence of any value other than . + /// If all of the values are , returns -1. + /// + public static int LastIndexOfAnyExcept(this Span span, T value) where T : IEquatable => + LastIndexOfAnyExcept((ReadOnlySpan)span, value); + + /// Searches for the last index of any value other than the specified or . + /// The type of the span and values. + /// The span to search. + /// A value to avoid. + /// A value to avoid + /// + /// The index in the span of the last occurrence of any value other than and . + /// If all of the values are or , returns -1. + /// + public static int LastIndexOfAnyExcept(this Span span, T value0, T value1) where T : IEquatable => + LastIndexOfAnyExcept((ReadOnlySpan)span, value0, value1); + + /// Searches for the last index of any value other than the specified , , or . + /// The type of the span and values. + /// The span to search. + /// A value to avoid. + /// A value to avoid + /// A value to avoid + /// + /// The index in the span of the last occurrence of any value other than , , and . + /// If all of the values are , , and , returns -1. + /// + public static int LastIndexOfAnyExcept(this Span span, T value0, T value1, T value2) where T : IEquatable => + LastIndexOfAnyExcept((ReadOnlySpan)span, value0, value1, value2); + + /// Searches for the last index of any value other than the specified . + /// The type of the span and values. + /// The span to search. + /// The values to avoid. + /// + /// The index in the span of the last occurrence of any value other than those in . + /// If all of the values are in , returns -1. + /// + public static int LastIndexOfAnyExcept(this Span span, ReadOnlySpan values) where T : IEquatable => + LastIndexOfAnyExcept((ReadOnlySpan)span, values); + + /// Searches for the last index of any value other than the specified . + /// The type of the span and values. + /// The span to search. + /// The value for which to search. + /// + /// The index in the span of the last occurrence of any value other than . + /// If all of the values are , returns -1. + /// + public static int LastIndexOfAnyExcept(this ReadOnlySpan span, T value) where T : IEquatable + { + for (int i = span.Length - 1; i >= 0; i--) + { + if (!EqualityComparer.Default.Equals(span[i], value)) + { + return i; + } + } + + return -1; + } + + /// Searches for the last index of any value other than the specified or . + /// The type of the span and values. + /// The span to search. + /// A value to avoid. + /// A value to avoid + /// + /// The index in the span of the last occurrence of any value other than and . + /// If all of the values are or , returns -1. + /// + public static int LastIndexOfAnyExcept(this ReadOnlySpan span, T value0, T value1) where T : IEquatable + { + for (int i = span.Length - 1; i >= 0; i--) + { + if (!EqualityComparer.Default.Equals(span[i], value0) && + !EqualityComparer.Default.Equals(span[i], value1)) + { + return i; + } + } + + return -1; + } + + /// Searches for the last index of any value other than the specified , , or . + /// The type of the span and values. + /// The span to search. + /// A value to avoid. + /// A value to avoid + /// A value to avoid + /// + /// The index in the span of the last occurrence of any value other than , , and . + /// If all of the values are , , and , returns -1. + /// + public static int LastIndexOfAnyExcept(this ReadOnlySpan span, T value0, T value1, T value2) where T : IEquatable + { + for (int i = span.Length - 1; i >= 0; i--) + { + if (!EqualityComparer.Default.Equals(span[i], value0) && + !EqualityComparer.Default.Equals(span[i], value1) && + !EqualityComparer.Default.Equals(span[i], value2)) + { + return i; + } + } + + return -1; + } + + /// Searches for the last index of any value other than the specified . + /// The type of the span and values. + /// The span to search. + /// The values to avoid. + /// + /// The index in the span of the first occurrence of any value other than those in . + /// If all of the values are in , returns -1. + /// + public static int LastIndexOfAnyExcept(this ReadOnlySpan span, ReadOnlySpan values) where T : IEquatable + { + switch (values.Length) + { + case 0: + return -1; + + case 1: + return LastIndexOfAnyExcept(span, values[0]); + + case 2: + return LastIndexOfAnyExcept(span, values[0], values[1]); + + case 3: + return LastIndexOfAnyExcept(span, values[0], values[1], values[2]); + + default: + for (int i = span.Length - 1; i >= 0; i--) + { + if (!values.Contains(span[i])) + { + return i; + } + } + + return -1; + } + } + /// /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T). /// From 9c2ab3bd5f7092fc0783545a36b574be9f4e0e61 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 19 Apr 2022 17:10:46 -0400 Subject: [PATCH 2/2] Fix comments --- .../System.Private.CoreLib/src/System/MemoryExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 3872f15f731be..27ef33931b325 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -446,7 +446,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(value)), /// Searches for the first index of any value other than the specified . /// The type of the span and values. /// The span to search. - /// The value for which to search. + /// A value to avoid. /// /// The index in the span of the first occurrence of any value other than . /// If all of the values are , returns -1. @@ -493,7 +493,7 @@ public static int IndexOfAnyExcept(this Span span, ReadOnlySpan values) /// Searches for the first index of any value other than the specified . /// The type of the span and values. /// The span to search. - /// The value for which to search. + /// A value to avoid. /// /// The index in the span of the first occurrence of any value other than . /// If all of the values are , returns -1. @@ -599,7 +599,7 @@ public static int IndexOfAnyExcept(this ReadOnlySpan span, ReadOnlySpan /// Searches for the last index of any value other than the specified . /// The type of the span and values. /// The span to search. - /// The value for which to search. + /// A value to avoid. /// /// The index in the span of the last occurrence of any value other than . /// If all of the values are , returns -1. @@ -646,7 +646,7 @@ public static int LastIndexOfAnyExcept(this Span span, ReadOnlySpan val /// Searches for the last index of any value other than the specified . /// The type of the span and values. /// The span to search. - /// The value for which to search. + /// A value to avoid. /// /// The index in the span of the last occurrence of any value other than . /// If all of the values are , returns -1.