Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial version of {Last}IndexOfAnyExcept #67941

Merged
merged 2 commits into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/libraries/System.Memory/ref/System.Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ public static partial class MemoryExtensions
public static int IndexOfAny<T>(this System.Span<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAny<T>(this System.Span<T> span, T value0, T value1) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAny<T>(this System.Span<T> span, T value0, T value1, T value2) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAnyExcept<T>(this System.Span<T> span, T value) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAnyExcept<T>(this System.Span<T> span, T value0, T value1) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAnyExcept<T>(this System.Span<T> span, T value0, T value1, T value2) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAnyExcept<T>(this System.Span<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAnyExcept<T>(this System.ReadOnlySpan<T> span, T value) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAnyExcept<T>(this System.ReadOnlySpan<T> span, T value0, T value1) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAnyExcept<T>(this System.ReadOnlySpan<T> span, T value0, T value1, T value2) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAnyExcept<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T> { throw null; }
public static int IndexOf<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; }
public static int IndexOf<T>(this System.ReadOnlySpan<T> span, T value) where T : System.IEquatable<T> { throw null; }
public static int IndexOf<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; }
Expand All @@ -77,6 +85,14 @@ public static partial class MemoryExtensions
public static int LastIndexOfAny<T>(this System.Span<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOfAny<T>(this System.Span<T> span, T value0, T value1) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOfAny<T>(this System.Span<T> span, T value0, T value1, T value2) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOfAnyExcept<T>(this System.Span<T> span, T value) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOfAnyExcept<T>(this System.Span<T> span, T value0, T value1) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOfAnyExcept<T>(this System.Span<T> span, T value0, T value1, T value2) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOfAnyExcept<T>(this System.Span<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOfAnyExcept<T>(this System.ReadOnlySpan<T> span, T value) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOfAnyExcept<T>(this System.ReadOnlySpan<T> span, T value0, T value1) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOfAnyExcept<T>(this System.ReadOnlySpan<T> span, T value0, T value1, T value2) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOfAnyExcept<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOf<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOf<T>(this System.ReadOnlySpan<T> span, T value) where T : System.IEquatable<T> { throw null; }
public static int LastIndexOf<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; }
Expand Down
185 changes: 185 additions & 0 deletions src/libraries/System.Memory/tests/Span/IndexOfAnyExcept.T.cs
Original file line number Diff line number Diff line change
@@ -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<byte> { protected override byte Create(int value) => (byte)value; }
public class IndexOfAnyExceptTests_Char : IndexOfAnyExceptTests<char> { protected override char Create(int value) => (char)value; }
public class IndexOfAnyExceptTests_Int32 : IndexOfAnyExceptTests<int> { protected override int Create(int value) => value; }
public class IndexOfAnyExceptTests_Int64 : IndexOfAnyExceptTests<long> { protected override long Create(int value) => value; }
public class IndexOfAnyExceptTests_String : IndexOfAnyExceptTests<string> { protected override string Create(int value) => ((char)value).ToString(); }
public class IndexOfAnyExceptTests_Record : IndexOfAnyExceptTests<SimpleRecord> { protected override SimpleRecord Create(int value) => new SimpleRecord(value); }

public record SimpleRecord(int Value);

public abstract class IndexOfAnyExceptTests<T> where T : IEquatable<T>
{
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');
}

/// <summary>Validate that the methods return -1 when the source span is empty.</summary>
[Fact]
public void ZeroLengthSpan_ReturnNegative1()
{
Assert.Equal(-1, IndexOfAnyExcept(Span<T>.Empty));
Assert.Equal(-1, IndexOfAnyExcept(Span<T>.Empty, _a));
Assert.Equal(-1, IndexOfAnyExcept(Span<T>.Empty, _a, _b));
Assert.Equal(-1, IndexOfAnyExcept(Span<T>.Empty, _a, _b, _c));
Assert.Equal(-1, IndexOfAnyExcept(Span<T>.Empty, new[] { _a, _b, _c, _d }));

Assert.Equal(-1, LastIndexOfAnyExcept(Span<T>.Empty));
Assert.Equal(-1, LastIndexOfAnyExcept(Span<T>.Empty, _a));
Assert.Equal(-1, LastIndexOfAnyExcept(Span<T>.Empty, _a, _b));
Assert.Equal(-1, LastIndexOfAnyExcept(Span<T>.Empty, _a, _b, _c));
Assert.Equal(-1, LastIndexOfAnyExcept(Span<T>.Empty, new[] { _a, _b, _c, _d }));
}

public static IEnumerable<object[]> AllElementsMatch_ReturnsNegative1_MemberData()
{
foreach (int length in new[] { 1, 2, 4, 7, 15, 16, 17, 31, 32, 33, 100 })
{
yield return new object[] { length };
}
}

/// <summary>Validate that the methods return -1 when the source span contains only the values being excepted.</summary>
[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<object[]> 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 } };
}

/// <summary>Validate that the methods return the expected position when the source span contains some data other than the excepted.</summary>
[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<T> span, T value)
{
int result = MemoryExtensions.IndexOfAnyExcept(span, value);
Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan<T>)span, value));
Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((Span<T>)span, new[] { value }));
Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan<T>)span, new[] { value }));
return result;
}
private static int IndexOfAnyExcept(Span<T> span, T value0, T value1)
{
int result = MemoryExtensions.IndexOfAnyExcept(span, value0, value1);
Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan<T>)span, value0, value1));
Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((Span<T>)span, new[] { value0, value1 }));
Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan<T>)span, new[] { value0, value1 }));
return result;
}
private static int IndexOfAnyExcept(Span<T> span, T value0, T value1, T value2)
{
int result = MemoryExtensions.IndexOfAnyExcept(span, value0, value1, value2);
Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan<T>)span, value0, value1, value2));
Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((Span<T>)span, new[] { value0, value1, value2 }));
Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan<T>)span, new[] { value0, value1, value2 }));
return result;
}
private static int IndexOfAnyExcept(Span<T> span, params T[] values)
{
int result = MemoryExtensions.IndexOfAnyExcept(span, values);
Assert.Equal(result, MemoryExtensions.IndexOfAnyExcept((ReadOnlySpan<T>)span, values));
return result;
}
private static int LastIndexOfAnyExcept(Span<T> span, T value)
{
int result = MemoryExtensions.LastIndexOfAnyExcept(span, value);
Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan<T>)span, value));
Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((Span<T>)span, new[] { value }));
Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan<T>)span, new[] { value }));
return result;
}
private static int LastIndexOfAnyExcept(Span<T> span, T value0, T value1)
{
int result = MemoryExtensions.LastIndexOfAnyExcept(span, value0, value1);
Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan<T>)span, value0, value1));
Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((Span<T>)span, new[] { value0, value1 }));
Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan<T>)span, new[] { value0, value1 }));
return result;
}
private static int LastIndexOfAnyExcept(Span<T> span, T value0, T value1, T value2)
{
int result = MemoryExtensions.LastIndexOfAnyExcept(span, value0, value1, value2);
Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan<T>)span, value0, value1, value2));
Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((Span<T>)span, new[] { value0, value1, value2 }));
Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan<T>)span, new[] { value0, value1, value2 }));
return result;
}
private static int LastIndexOfAnyExcept(Span<T> span, params T[] values)
{
int result = MemoryExtensions.LastIndexOfAnyExcept(span, values);
Assert.Equal(result, MemoryExtensions.LastIndexOfAnyExcept((ReadOnlySpan<T>)span, values));
return result;
}
}
}
3 changes: 2 additions & 1 deletion src/libraries/System.Memory/tests/System.Memory.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TestRuntime>true</TestRuntime>
Expand Down Expand Up @@ -81,6 +81,7 @@
<Compile Include="Span\IndexOfAny.byte.cs" />
<Compile Include="Span\IndexOfAny.char.cs" />
<Compile Include="Span\IndexOfAny.T.cs" />
<Compile Include="Span\IndexOfAnyExcept.T.cs" />
<Compile Include="Span\IndexOfSequence.byte.cs" />
<Compile Include="Span\IndexOfSequence.char.cs" />
<Compile Include="Span\IndexOfSequence.T.cs" />
Expand Down
Loading