--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.SpanTests
+{
+ // Adapted from IndexOf.T.cs
+ public static partial class SpanTests // .Contains<T>
+ {
+ [Fact]
+ public static void ZeroLengthContains()
+ {
+ Span<int> span = new Span<int>(Array.Empty<int>());
+
+ bool found = span.Contains(0);
+ Assert.False(found);
+ }
+
+ [Fact]
+ public static void TestContains()
+ {
+ for (int length = 0; length < 32; length++)
+ {
+ int[] a = new int[length];
+ for (int i = 0; i < length; i++)
+ {
+ a[i] = 10 * (i + 1);
+ }
+ Span<int> span = new Span<int>(a);
+
+ for (int targetIndex = 0; targetIndex < length; targetIndex++)
+ {
+ int target = a[targetIndex];
+ bool found = span.Contains(target);
+ Assert.True(found);
+ }
+ }
+ }
+
+ [Fact]
+ public static void TestMultipleContains()
+ {
+ for (int length = 2; length < 32; length++)
+ {
+ int[] a = new int[length];
+ for (int i = 0; i < length; i++)
+ {
+ a[i] = 10 * (i + 1);
+ }
+
+ a[length - 1] = 5555;
+ a[length - 2] = 5555;
+
+ Span<int> span = new Span<int>(a);
+ bool found = span.Contains(5555);
+ Assert.True(found);
+ }
+ }
+
+ [Fact]
+ public static void OnNoMatchForContainsMakeSureEveryElementIsCompared()
+ {
+ for (int length = 0; length < 100; length++)
+ {
+ TIntLog log = new TIntLog();
+
+ TInt[] a = new TInt[length];
+ for (int i = 0; i < length; i++)
+ {
+ a[i] = new TInt(10 * (i + 1), log);
+ }
+ Span<TInt> span = new Span<TInt>(a);
+ bool found = span.Contains(new TInt(9999, log));
+ Assert.False(found);
+
+ // Since we asked for a non-existent value, make sure each element of the array was compared once.
+ // (Strictly speaking, it would not be illegal for IndexOf to compare an element more than once but
+ // that would be a non-optimal implementation and a red flag. So we'll stick with the stricter test.)
+ Assert.Equal(a.Length, log.Count);
+ foreach (TInt elem in a)
+ {
+ int numCompares = log.CountCompares(elem.Value, 9999);
+ Assert.True(numCompares == 1, $"Expected {numCompares} == 1 for element {elem.Value}.");
+ }
+ }
+ }
+
+ [Fact]
+ public static void MakeSureNoChecksForContainsGoOutOfRange()
+ {
+ const int GuardValue = 77777;
+ const int GuardLength = 50;
+
+ void checkForOutOfRangeAccess(int x, int y)
+ {
+ if (x == GuardValue || y == GuardValue)
+ throw new Exception("Detected out of range access in IndexOf()");
+ }
+
+ for (int length = 0; length < 100; length++)
+ {
+ TInt[] a = new TInt[GuardLength + length + GuardLength];
+ for (int i = 0; i < a.Length; i++)
+ {
+ a[i] = new TInt(GuardValue, checkForOutOfRangeAccess);
+ }
+
+ for (int i = 0; i < length; i++)
+ {
+ a[GuardLength + i] = new TInt(10 * (i + 1), checkForOutOfRangeAccess);
+ }
+
+ Span<TInt> span = new Span<TInt>(a, GuardLength, length);
+ bool found = span.Contains(new TInt(9999, checkForOutOfRangeAccess));
+ Assert.False(found);
+ }
+ }
+
+ [Fact]
+ public static void ZeroLengthContains_String()
+ {
+ Span<string> span = new Span<string>(Array.Empty<string>());
+ bool found = span.Contains("a");
+ Assert.False(found);
+ }
+
+ [Fact]
+ public static void TestMatchContains_String()
+ {
+ for (int length = 0; length < 32; length++)
+ {
+ string[] a = new string[length];
+ for (int i = 0; i < length; i++)
+ {
+ a[i] = (10 * (i + 1)).ToString();
+ }
+ Span<string> span = new Span<string>(a);
+
+ for (int targetIndex = 0; targetIndex < length; targetIndex++)
+ {
+ string target = a[targetIndex];
+ bool found = span.Contains(target);
+ Assert.True(found);
+ }
+ }
+ }
+
+ [Fact]
+ public static void TestNoMatchContains_String()
+ {
+ var rnd = new Random(42);
+ for (int length = 0; length <= byte.MaxValue; length++)
+ {
+ string[] a = new string[length];
+ string target = (rnd.Next(0, 256)).ToString();
+ for (int i = 0; i < length; i++)
+ {
+ string val = (i + 1).ToString();
+ a[i] = val == target ? (target + 1) : val;
+ }
+ Span<string> span = new Span<string>(a);
+
+ bool found = span.Contains(target);
+ Assert.False(found);
+ }
+ }
+
+ [Fact]
+ public static void TestMultipleMatchContains_String()
+ {
+ for (int length = 2; length < 32; length++)
+ {
+ string[] a = new string[length];
+ for (int i = 0; i < length; i++)
+ {
+ a[i] = (10 * (i + 1)).ToString();
+ }
+
+ a[length - 1] = "5555";
+ a[length - 2] = "5555";
+
+ Span<string> span = new Span<string>(a);
+ bool found = span.Contains("5555");
+ Assert.True(found);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(TestHelpers.ContainsNullData), MemberType = typeof(TestHelpers))]
+ public static void ContainsNull_String(string[] spanInput, bool expected)
+ {
+ Span<string> theStrings = spanInput;
+ Assert.Equal(expected, theStrings.Contains(null));
+ }
+ }
+}
public static ReadOnlyMemory<T> DangerousCreateReadOnlyMemory<T>(object obj, int offset, int length) =>
DangerousCreateMemory<T>(obj, offset, length);
+ public static TheoryData<string[], bool> ContainsNullData => new TheoryData<string[], bool>()
+ {
+ { new string[] { "1", null, "2" }, true},
+ { new string[] { "1", "3", "2" }, false},
+ { null, false},
+ { new string[] { "1", null, null }, true},
+ { new string[] { null, null, null }, true},
+ };
+
+ public static TheoryData<string[], string[], bool> SequenceEqualsNullData => new TheoryData<string[], string[], bool>()
+ {
+ { new string[] { "1", null, "2" }, new string[] { "1", null, "2" } , true},
+ { new string[] { "1", null, "2" }, new string[] { "1", "3", "2" } , false},
+ { new string[] { "1", null, "2" }, new string[] { null, "3", "2" } , false},
+ { new string[] { "1", null, "2" }, new string[] { null } , false},
+ { new string[] { "1", null, "2" }, null , false},
+
+ { new string[] { null, "2", "1" }, new string[] { null, "2" } , false},
+
+ { null, new string[] { null }, false},
+ { null, null , true},
+ { null, new string[] { "1", "3", "2" } , false},
+ { null, new string[] { "1", null, "2" } , false},
+
+ { new string[] { "1", null, null }, new string[] { "1", null, null }, true},
+ { new string[] { null, null, null }, new string[] { null, null, null }, true},
+ };
+
public static TheoryData<string[], int> IndexOfNullData => new TheoryData<string[], int>()
{
{ new string[] { "1", null, "2" }, 1},
{ new string[] { null, null, null }, new string[] { null, null }, 0},
};
+ public static TheoryData<string[], string[], int> IndexOfAnyNullSequenceData => new TheoryData<string[], string[], int>()
+ {
+ { new string[] { "1", null, "2" }, new string[] { "1", null, "2" }, 0},
+ { new string[] { "1", null, "2" }, new string[] { null, null }, 1},
+
+ { new string[] { "1", null, "2" }, new string[] { "3", null }, 1},
+ { new string[] { "1", null, "2" }, new string[] { "1", "2" }, 0},
+ { new string[] { "1", null, "2" }, new string[] { "3", "4" }, -1},
+
+ { new string[] { null, null, "2" }, new string[] { "3", null }, 0},
+ { new string[] { null, null, "2" }, new string[] { null, "1" }, 0},
+ { new string[] { null, null, "2" }, new string[] { null, "1" }, 0},
+
+ { new string[] { "1", "3", "2" }, new string[] { "1", null, "2" }, 0},
+ { new string[] { "1", "3", "2" }, new string[] { null, null }, -1},
+
+ { new string[] { "1", "3", "2" }, new string[] { null, "1" }, 0},
+
+ { null, new string[] { "1", null, "2" }, -1},
+
+ { new string[] { "1", null, null }, new string[] { null, null, "2" }, 1},
+ { new string[] { null, null, null }, new string[] { null, null }, 0},
+
+ { new string[] { "1", "3", "2" }, null, -1},
+ { new string[] { "1", null, "2" }, null, -1},
+ };
+
public static TheoryData<string[], int> LastIndexOfNullData => new TheoryData<string[], int>()
{
{ new string[] { "1", null, "2" }, 1},
{ new string[] { "1", null, null }, new string[] { null, null, "2" }, -1},
{ new string[] { null, null, null }, new string[] { null, null }, 1},
};
+
+ public static TheoryData<string[], string[], int> LastIndexOfAnyNullSequenceData => new TheoryData<string[], string[], int>()
+ {
+ { new string[] { "1", null, "2" }, new string[] { "1", null, "3" }, 1},
+ { new string[] { "1", null, "2" }, new string[] { null, null }, 1},
+ { new string[] { "1", null, "2" }, new string[] { "3", "4" }, -1},
+ { new string[] { "1", null, "2" }, new string[] { "3", null }, 1},
+ { new string[] { "1", null, "2" }, new string[] { "1", null }, 1},
+ { new string[] { "1", null, "2" }, new string[] { null, null }, 1},
+ { new string[] { "1", null, "2" }, new string[] { "1", "2" }, 2},
+ { null, new string[] { "1", null, "2" }, -1},
+
+ { new string[] { null, null, "2" }, new string[] { "3", null }, 1},
+ { new string[] { null, null, "2" }, new string[] { null, "1" }, 1},
+ { new string[] { null, null, "2" }, new string[] { null, "1" }, 1},
+
+ { new string[] { "1", "3", "2" }, new string[] { null, "1" }, 0},
+ { new string[] { "1", "3", "2" }, new string[] { "1", "2", null }, 2},
+ { new string[] { "1", "3", "2" }, new string[] { null, null }, -1},
+
+ { null, new string[] { null, "1" }, -1},
+
+ { new string[] { "1", null, null }, new string[] { null, null, "2" }, 2},
+ { new string[] { null, null, null }, new string[] { null, null }, 2},
+
+ { new string[] { "1", null, "2" }, null, -1},
+ { new string[] { "1", "3", "2" }, null, -1},
+ };
}
}