Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corefx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/System.Memory/System.Memory.sln1
-rw-r--r--src/System.Memory/ref/System.Memory.cs4
-rw-r--r--src/System.Memory/src/System/MemoryExtensions.Portable.cs52
-rw-r--r--src/System.Memory/tests/ReadOnlySpan/CompareTo.cs279
-rw-r--r--src/System.Memory/tests/ReadOnlySpan/Contains.cs181
-rw-r--r--src/System.Memory/tests/ReadOnlySpan/Equals.cs262
-rw-r--r--src/System.Memory/tests/ReadOnlySpan/IndexOf.charSpan.cs409
-rw-r--r--src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.char.cs12
-rw-r--r--src/System.Memory/tests/System.Memory.Tests.csproj4
9 files changed, 1204 insertions, 0 deletions
diff --git a/src/System.Memory/System.Memory.sln b/src/System.Memory/System.Memory.sln
index 7c68229780..00692c230f 100644
--- a/src/System.Memory/System.Memory.sln
+++ b/src/System.Memory/System.Memory.sln
@@ -27,6 +27,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2ED
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3F794AE4-319F-4498-A9DA-C49A4EB11D9E}"
ProjectSection(SolutionItems) = preProject
+ ..\Common\src\CoreLib\System\Runtime\InteropServices\MemoryMarshal.Fast.cs = ..\Common\src\CoreLib\System\Runtime\InteropServices\MemoryMarshal.Fast.cs
..\Common\src\CoreLib\System\ReadOnlySpan.Fast.cs = ..\Common\src\CoreLib\System\ReadOnlySpan.Fast.cs
..\Common\src\CoreLib\System\Span.Fast.cs = ..\Common\src\CoreLib\System\Span.Fast.cs
..\Common\src\CoreLib\System\Span.NonGeneric.cs = ..\Common\src\CoreLib\System\Span.NonGeneric.cs
diff --git a/src/System.Memory/ref/System.Memory.cs b/src/System.Memory/ref/System.Memory.cs
index e8f33c577d..5c630a8e74 100644
--- a/src/System.Memory/ref/System.Memory.cs
+++ b/src/System.Memory/ref/System.Memory.cs
@@ -29,11 +29,15 @@ namespace System
public static int BinarySearch<T, TComparable>(this System.ReadOnlySpan<T> span, TComparable comparable) where TComparable : System.IComparable<T> { throw null; }
public static int BinarySearch<T, TComparer>(this System.Span<T> span, T value, TComparer comparer) where TComparer : System.Collections.Generic.IComparer<T> { throw null; }
public static int BinarySearch<T, TComparable>(this System.Span<T> span, TComparable comparable) where TComparable : System.IComparable<T> { throw null; }
+ public static int CompareTo(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; }
+ public static bool Contains(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; }
public static void CopyTo<T>(this T[] array, System.Memory<T> destination) { }
public static void CopyTo<T>(this T[] array, System.Span<T> destination) { }
public static bool EndsWith(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; }
public static bool EndsWith<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; }
public static bool EndsWith<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T> { throw null; }
+ public static bool Equals(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; }
+ public static int IndexOf(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; }
public static int IndexOfAny<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> values) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAny<T>(this System.ReadOnlySpan<T> span, T value0, T value1) where T : System.IEquatable<T> { throw null; }
public static int IndexOfAny<T>(this System.ReadOnlySpan<T> span, T value0, T value1, T value2) where T : System.IEquatable<T> { throw null; }
diff --git a/src/System.Memory/src/System/MemoryExtensions.Portable.cs b/src/System.Memory/src/System/MemoryExtensions.Portable.cs
index 0cfbff8f37..d5c36d3968 100644
--- a/src/System.Memory/src/System/MemoryExtensions.Portable.cs
+++ b/src/System.Memory/src/System/MemoryExtensions.Portable.cs
@@ -14,6 +14,58 @@ namespace System
public static partial class MemoryExtensions
{
/// <summary>
+ /// Returns a value indicating whether the specified <paramref name="value"/> occurs within the <paramref name="span"/>.
+ /// <param name="span">The source span.</param>
+ /// <param name="value">The value to seek within the source span.</param>
+ /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
+ /// </summary>
+ public static bool Contains(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
+ => (IndexOf(span, value, comparisonType) >= 0);
+
+ /// <summary>
+ /// Determines whether this <paramref name="span"/> and the specified <paramref name="value"/> span have the same characters
+ /// when compared using the specified <paramref name="comparisonType"/> option.
+ /// <param name="span">The source span.</param>
+ /// <param name="value">The value to compare with the source span.</param>
+ /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
+ /// </summary>
+ public static bool Equals(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
+ {
+ if (comparisonType == StringComparison.Ordinal)
+ {
+ return span.SequenceEqual<char>(value);
+ }
+
+ return span.ToString().Equals(value.ToString(), comparisonType);
+ }
+
+ /// <summary>
+ /// Compares the specified <paramref name="span"/> and <paramref name="value"/> using the specified <paramref name="comparisonType"/>,
+ /// and returns an integer that indicates their relative position in the sort order.
+ /// <param name="span">The source span.</param>
+ /// <param name="value">The value to compare with the source span.</param>
+ /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
+ /// </summary>
+ public static int CompareTo(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
+ => string.Compare(span.ToString(), value.ToString(), comparisonType);
+
+ /// <summary>
+ /// Reports the zero-based index of the first occurrence of the specified <paramref name="value"/> in the current <paramref name="span"/>.
+ /// <param name="span">The source span.</param>
+ /// <param name="value">The value to seek within the source span.</param>
+ /// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
+ /// </summary>
+ public static int IndexOf(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
+ {
+ if (comparisonType == StringComparison.Ordinal)
+ {
+ return span.IndexOf<char>(value);
+ }
+
+ return span.ToString().IndexOf(value.ToString(), comparisonType);
+ }
+
+ /// <summary>
/// Copies the characters from the source span into the destination, converting each character to lowercase,
/// using the casing rules of the specified culture.
/// </summary>
diff --git a/src/System.Memory/tests/ReadOnlySpan/CompareTo.cs b/src/System.Memory/tests/ReadOnlySpan/CompareTo.cs
new file mode 100644
index 0000000000..ab1f294fe1
--- /dev/null
+++ b/src/System.Memory/tests/ReadOnlySpan/CompareTo.cs
@@ -0,0 +1,279 @@
+// 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 System.Globalization;
+using System.Threading;
+using Xunit;
+
+namespace System.SpanTests
+{
+ public static partial class ReadOnlySpanTests
+ {
+ [Fact]
+ public static void ZeroLengthCompareTo_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+
+ var span = new ReadOnlySpan<char>(a);
+ var slice = new ReadOnlySpan<char>(a, 2, 0);
+ Assert.True(0 < span.CompareTo(slice, StringComparison.Ordinal));
+
+ Assert.True(0 < span.CompareTo(slice, StringComparison.CurrentCulture));
+ Assert.True(0 < span.CompareTo(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(0 < span.CompareTo(slice, StringComparison.InvariantCulture));
+ Assert.True(0 < span.CompareTo(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(0 < span.CompareTo(slice, StringComparison.OrdinalIgnoreCase));
+
+ span = new ReadOnlySpan<char>(a, 1, 0);
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.Ordinal));
+
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCulture));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCulture));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void SameSpanCompareTo_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a);
+ Assert.Equal(0, span.CompareTo(span, StringComparison.Ordinal));
+
+ Assert.Equal(0, span.CompareTo(span, StringComparison.CurrentCulture));
+ Assert.Equal(0, span.CompareTo(span, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(0, span.CompareTo(span, StringComparison.InvariantCulture));
+ Assert.Equal(0, span.CompareTo(span, StringComparison.InvariantCultureIgnoreCase));
+ Assert.Equal(0, span.CompareTo(span, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void LengthMismatchCompareTo_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a, 0, 2);
+ var slice = new ReadOnlySpan<char>(a, 0, 3);
+ Assert.True(0 > span.CompareTo(slice, StringComparison.Ordinal));
+
+ Assert.True(0 > span.CompareTo(slice, StringComparison.CurrentCulture));
+ Assert.True(0 > span.CompareTo(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(0 > span.CompareTo(slice, StringComparison.InvariantCulture));
+ Assert.True(0 > span.CompareTo(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(0 > span.CompareTo(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void CompareToOverlappingMatch_StringComparison()
+ {
+ char[] a = { '4', '5', '6', '5', '6', '5' };
+ var span = new ReadOnlySpan<char>(a, 1, 3);
+ var slice = new ReadOnlySpan<char>(a, 3, 3);
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.Ordinal));
+
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCulture));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCulture));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void CompareToMatchDifferentSpans_StringComparison()
+ {
+ char[] a = { '4', '5', '6', '7' };
+ char[] b = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a, 0, 3);
+ var slice = new ReadOnlySpan<char>(b, 0, 3);
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.Ordinal));
+
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCulture));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCulture));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.Equal(0, span.CompareTo(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void CompareToNoMatch_StringComparison()
+ {
+ for (int length = 1; length < 150; length++)
+ {
+ for (int mismatchIndex = 0; mismatchIndex < length; mismatchIndex++)
+ {
+ var first = new char[length];
+ var second = new char[length];
+ for (int i = 0; i < length; i++)
+ {
+ first[i] = second[i] = (char)(i + 1);
+ }
+
+ second[mismatchIndex] = (char)(second[mismatchIndex] + 1);
+
+ var firstSpan = new ReadOnlySpan<char>(first);
+ var secondSpan = new ReadOnlySpan<char>(second);
+ Assert.True(0 > firstSpan.CompareTo(secondSpan, StringComparison.Ordinal));
+
+ Assert.Equal(
+ string.Compare(firstSpan.ToString(), secondSpan.ToString(), StringComparison.OrdinalIgnoreCase),
+ firstSpan.CompareTo(secondSpan, StringComparison.OrdinalIgnoreCase));
+ Assert.Equal(
+ string.Compare(firstSpan.ToString(), secondSpan.ToString(), StringComparison.CurrentCulture),
+ firstSpan.CompareTo(secondSpan, StringComparison.CurrentCulture));
+ Assert.Equal(
+ string.Compare(firstSpan.ToString(), secondSpan.ToString(), StringComparison.CurrentCultureIgnoreCase),
+ firstSpan.CompareTo(secondSpan, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(
+ string.Compare(firstSpan.ToString(), secondSpan.ToString(), StringComparison.InvariantCulture),
+ firstSpan.CompareTo(secondSpan, StringComparison.InvariantCulture));
+ Assert.Equal(
+ string.Compare(firstSpan.ToString(), secondSpan.ToString(), StringComparison.InvariantCultureIgnoreCase),
+ firstSpan.CompareTo(secondSpan, StringComparison.InvariantCultureIgnoreCase));
+ }
+ }
+ }
+
+ [Fact]
+ public static void MakeSureNoCompareToChecksGoOutOfRange_StringComparison()
+ {
+ for (int length = 0; length < 100; length++)
+ {
+ var first = new char[length + 2];
+ first[0] = (char)99;
+ first[length + 1] = (char)99;
+ var second = new char[length + 2];
+ second[0] = (char)100;
+ second[length + 1] = (char)100;
+ var span1 = new ReadOnlySpan<char>(first, 1, length);
+ var span2 = new ReadOnlySpan<char>(second, 1, length);
+ Assert.Equal(0, span1.CompareTo(span2, StringComparison.Ordinal));
+
+ Assert.Equal(0, span1.CompareTo(span2, StringComparison.CurrentCulture));
+ Assert.Equal(0, span1.CompareTo(span2, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(0, span1.CompareTo(span2, StringComparison.InvariantCulture));
+ Assert.Equal(0, span1.CompareTo(span2, StringComparison.InvariantCultureIgnoreCase));
+ Assert.Equal(0, span1.CompareTo(span2, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+
+ [Fact]
+ public static void CompareToUnknownComparisonType_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a);
+ TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.CompareTo(_span, StringComparison.CurrentCulture - 1));
+ TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.CompareTo(_span, StringComparison.OrdinalIgnoreCase + 1));
+ TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.CompareTo(_span, (StringComparison)6));
+ }
+
+ [Theory]
+ // CurrentCulture
+ [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.CurrentCulture, 0)]
+ [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.CurrentCulture, 1)]
+ [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.CurrentCulture, -1)]
+ [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.CurrentCulture, 1)]
+ [InlineData("hello", 2, "HELLO", 2, 3, StringComparison.CurrentCulture, -1)]
+ [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.CurrentCulture, 0)]
+ [InlineData("Hello", 2, "Goodbye", 2, 3, StringComparison.CurrentCulture, -1)]
+ [InlineData("A", 0, "B", 0, 1, StringComparison.CurrentCulture, -1)]
+ [InlineData("B", 0, "A", 0, 1, StringComparison.CurrentCulture, 1)]
+ // CurrentCultureIgnoreCase
+ [InlineData("HELLO", 0, "hello", 0, 5, StringComparison.CurrentCultureIgnoreCase, 0)]
+ [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.CurrentCultureIgnoreCase, 0)]
+ [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.CurrentCultureIgnoreCase, 0)]
+ [InlineData("Hello", 2, "Yellow", 2, 3, StringComparison.CurrentCultureIgnoreCase, 0)]
+ [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.CurrentCultureIgnoreCase, 1)]
+ [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.CurrentCultureIgnoreCase, -1)]
+ [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.CurrentCultureIgnoreCase, 0)]
+ [InlineData("Hello", 2, "Goodbye", 2, 3, StringComparison.CurrentCultureIgnoreCase, -1)]
+ // InvariantCulture
+ [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.InvariantCulture, 0)]
+ [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.InvariantCulture, 1)]
+ [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.InvariantCulture, -1)]
+ [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.InvariantCulture, 1)]
+ [InlineData("hello", 2, "HELLO", 2, 3, StringComparison.InvariantCulture, -1)]
+ // InvariantCultureIgnoreCase
+ [InlineData("HELLO", 0, "hello", 0, 5, StringComparison.InvariantCultureIgnoreCase, 0)]
+ [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.InvariantCultureIgnoreCase, 0)]
+ [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.InvariantCultureIgnoreCase, 0)]
+ [InlineData("Hello", 2, "Yellow", 2, 3, StringComparison.InvariantCultureIgnoreCase, 0)]
+ [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.InvariantCultureIgnoreCase, 1)]
+ [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.InvariantCultureIgnoreCase, -1)]
+ [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.InvariantCultureIgnoreCase, 0)]
+ [InlineData("Hello", 2, "Goodbye", 2, 3, StringComparison.InvariantCultureIgnoreCase, -1)]
+ // Ordinal
+ [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.Ordinal, 0)]
+ [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.Ordinal, 1)]
+ [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.Ordinal, -1)]
+ [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.Ordinal, 0)]
+ [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.Ordinal, -1)]
+ [InlineData("Hello", 2, "Goodbye", 2, 3, StringComparison.Ordinal, -1)]
+ [InlineData("Hello", 0, "Hello", 0, 0, StringComparison.Ordinal, 0)]
+ [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.Ordinal, 0)]
+ [InlineData("Hello", 0, "Hello", 0, 3, StringComparison.Ordinal, 0)]
+ [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.Ordinal, 0)]
+ [InlineData("Hello", 0, "He" + SoftHyphen + "llo", 0, 5, StringComparison.Ordinal, -1)]
+ [InlineData("Hello", 0, "-=<Hello>=-", 3, 5, StringComparison.Ordinal, 0)]
+ [InlineData("\uD83D\uDD53Hello\uD83D\uDD50", 1, "\uD83D\uDD53Hello\uD83D\uDD54", 1, 7, StringComparison.Ordinal, 0)] // Surrogate split
+ [InlineData("Hello", 0, "Hello123", 0, int.MaxValue, StringComparison.Ordinal, -1)] // Recalculated length, second string longer
+ [InlineData("Hello123", 0, "Hello", 0, int.MaxValue, StringComparison.Ordinal, 1)] // Recalculated length, first string longer
+ [InlineData("---aaaaaaaaaaa", 3, "+++aaaaaaaaaaa", 3, 100, StringComparison.Ordinal, 0)] // Equal long alignment 2, equal compare
+ [InlineData("aaaaaaaaaaaaaa", 3, "aaaxaaaaaaaaaa", 3, 100, StringComparison.Ordinal, -1)] // Equal long alignment 2, different compare at n=1
+ [InlineData("-aaaaaaaaaaaaa", 1, "+aaaaaaaaaaaaa", 1, 100, StringComparison.Ordinal, 0)] // Equal long alignment 6, equal compare
+ [InlineData("aaaaaaaaaaaaaa", 1, "axaaaaaaaaaaaa", 1, 100, StringComparison.Ordinal, -1)] // Equal long alignment 6, different compare at n=1
+ [InlineData("aaaaaaaaaaaaaa", 0, "aaaaaaaaaaaaaa", 0, 100, StringComparison.Ordinal, 0)] // Equal long alignment 4, equal compare
+ [InlineData("aaaaaaaaaaaaaa", 0, "xaaaaaaaaaaaaa", 0, 100, StringComparison.Ordinal, -1)] // Equal long alignment 4, different compare at n=1
+ [InlineData("aaaaaaaaaaaaaa", 0, "axaaaaaaaaaaaa", 0, 100, StringComparison.Ordinal, -1)] // Equal long alignment 4, different compare at n=2
+ [InlineData("--aaaaaaaaaaaa", 2, "++aaaaaaaaaaaa", 2, 100, StringComparison.Ordinal, 0)] // Equal long alignment 0, equal compare
+ [InlineData("aaaaaaaaaaaaaa", 2, "aaxaaaaaaaaaaa", 2, 100, StringComparison.Ordinal, -1)] // Equal long alignment 0, different compare at n=1
+ [InlineData("aaaaaaaaaaaaaa", 2, "aaaxaaaaaaaaaa", 2, 100, StringComparison.Ordinal, -1)] // Equal long alignment 0, different compare at n=2
+ [InlineData("aaaaaaaaaaaaaa", 2, "aaaaxaaaaaaaaa", 2, 100, StringComparison.Ordinal, -1)] // Equal long alignment 0, different compare at n=3
+ [InlineData("aaaaaaaaaaaaaa", 2, "aaaaaxaaaaaaaa", 2, 100, StringComparison.Ordinal, -1)] // Equal long alignment 0, different compare at n=4
+ [InlineData("aaaaaaaaaaaaaa", 2, "aaaaaaxaaaaaaa", 2, 100, StringComparison.Ordinal, -1)] // Equal long alignment 0, different compare at n=5
+ [InlineData("aaaaaaaaaaaaaa", 0, "+aaaaaaaaaaaaa", 1, 13, StringComparison.Ordinal, 0)] // Different int alignment, equal compare
+ [InlineData("aaaaaaaaaaaaaa", 0, "aaaaaaaaaaaaax", 1, 100, StringComparison.Ordinal, -1)] // Different int alignment
+ [InlineData("aaaaaaaaaaaaaa", 1, "aaaxaaaaaaaaaa", 3, 100, StringComparison.Ordinal, -1)] // Different long alignment, abs of 4, one of them is 2, different at n=1
+ [InlineData("-aaaaaaaaaaaaa", 1, "++++aaaaaaaaaa", 4, 10, StringComparison.Ordinal, 0)] // Different long alignment, equal compare
+ [InlineData("aaaaaaaaaaaaaa", 1, "aaaaaaaaaaaaax", 4, 100, StringComparison.Ordinal, -1)] // Different long alignment
+ [InlineData("\0", 0, "", 0, 1, StringComparison.Ordinal, 1)] // Same memory layout, except for m_stringLength (m_firstChars are both 0)
+ [InlineData("\0\0", 0, "", 0, 2, StringComparison.Ordinal, 1)] // Same as above, except m_stringLength for one is 2
+ [InlineData("", 0, "\0b", 0, 2, StringComparison.Ordinal, -1)] // strA's second char != strB's second char codepath
+ [InlineData("", 0, "b", 0, 1, StringComparison.Ordinal, -1)] // Should hit strA.m_firstChar != strB.m_firstChar codepath
+ [InlineData("abcxxxxxxxxxxxxxxxxxxxxxx", 0, "abdxxxxxxxxxxxxxxx", 0, int.MaxValue, StringComparison.Ordinal, -1)] // 64-bit: first long compare is different
+ [InlineData("abcdefgxxxxxxxxxxxxxxxxxx", 0, "abcdefhxxxxxxxxxxx", 0, int.MaxValue, StringComparison.Ordinal, -1)] // 64-bit: second long compare is different
+ [InlineData("abcdefghijkxxxxxxxxxxxxxx", 0, "abcdefghijlxxxxxxx", 0, int.MaxValue, StringComparison.Ordinal, -1)] // 64-bit: third long compare is different
+ [InlineData("abcdexxxxxxxxxxxxxxxxxxxx", 0, "abcdfxxxxxxxxxxxxx", 0, int.MaxValue, StringComparison.Ordinal, -1)] // 32-bit: second int compare is different
+ [InlineData("abcdefghixxxxxxxxxxxxxxxx", 0, "abcdefghjxxxxxxxxx", 0, int.MaxValue, StringComparison.Ordinal, -1)] // 32-bit: fourth int compare is different
+ // OrdinalIgnoreCase
+ [InlineData("HELLO", 0, "hello", 0, 5, StringComparison.OrdinalIgnoreCase, 0)]
+ [InlineData("Hello", 0, "Hello", 0, 5, StringComparison.OrdinalIgnoreCase, 0)]
+ [InlineData("Hello", 2, "Hello", 2, 3, StringComparison.OrdinalIgnoreCase, 0)]
+ [InlineData("Hello", 2, "Yellow", 2, 3, StringComparison.OrdinalIgnoreCase, 0)]
+ [InlineData("Hello", 0, "Goodbye", 0, 5, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("Goodbye", 0, "Hello", 0, 5, StringComparison.OrdinalIgnoreCase, -1)]
+ [InlineData("HELLO", 2, "hello", 2, 3, StringComparison.OrdinalIgnoreCase, 0)]
+ [InlineData("Hello", 2, "Goodbye", 2, 3, StringComparison.OrdinalIgnoreCase, -1)]
+ [InlineData("A", 0, "x", 0, 1, StringComparison.OrdinalIgnoreCase, -1)]
+ [InlineData("a", 0, "X", 0, 1, StringComparison.OrdinalIgnoreCase, -1)]
+ [InlineData("[", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("[", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("\\", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("\\", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("]", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("]", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("^", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("^", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("_", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("_", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("`", 0, "A", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ [InlineData("`", 0, "a", 0, 1, StringComparison.OrdinalIgnoreCase, 1)]
+ public static void Compare(string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType, int expected)
+ {
+ ReadOnlySpan<char> span = length <= (strA.Length - indexA) ? strA.AsSpan(indexA, length) : strA.AsSpan(indexA);
+ ReadOnlySpan<char> value = length <= (strB.Length - indexB) ? strB.AsSpan(indexB, length) : strB.AsSpan(indexB);
+ //Assert.True(expected == Math.Sign(span.CompareTo(value, comparisonType)), span.ToString() + "|" + value.ToString() + "|" + length);
+ Assert.Equal(expected, Math.Sign(span.CompareTo(value, comparisonType)));
+ }
+ }
+}
diff --git a/src/System.Memory/tests/ReadOnlySpan/Contains.cs b/src/System.Memory/tests/ReadOnlySpan/Contains.cs
new file mode 100644
index 0000000000..a38f904966
--- /dev/null
+++ b/src/System.Memory/tests/ReadOnlySpan/Contains.cs
@@ -0,0 +1,181 @@
+// 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
+{
+ public static partial class ReadOnlySpanTests
+ {
+ [Fact]
+ public static void ZeroLengthContains_StringComparison()
+ {
+ var a = new char[3];
+
+ var span = new ReadOnlySpan<char>(a);
+ var slice = new ReadOnlySpan<char>(a, 2, 0);
+ Assert.True(span.Contains(slice, StringComparison.Ordinal));
+
+ Assert.True(span.Contains(slice, StringComparison.CurrentCulture));
+ Assert.True(span.Contains(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(span.Contains(slice, StringComparison.InvariantCulture));
+ Assert.True(span.Contains(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(span.Contains(slice, StringComparison.OrdinalIgnoreCase));
+
+ span = ReadOnlySpan<char>.Empty;
+ Assert.True(span.Contains(slice, StringComparison.Ordinal));
+
+ Assert.True(span.Contains(slice, StringComparison.CurrentCulture));
+ Assert.True(span.Contains(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(span.Contains(slice, StringComparison.InvariantCulture));
+ Assert.True(span.Contains(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(span.Contains(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void SameSpanContains_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a);
+ Assert.True(span.Contains(span, StringComparison.Ordinal));
+
+ Assert.True(span.Contains(span, StringComparison.CurrentCulture));
+ Assert.True(span.Contains(span, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(span.Contains(span, StringComparison.InvariantCulture));
+ Assert.True(span.Contains(span, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(span.Contains(span, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void LengthMismatchContains_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a, 0, 2);
+ var slice = new ReadOnlySpan<char>(a, 0, 3);
+ Assert.False(span.Contains(slice, StringComparison.Ordinal));
+
+ Assert.False(span.Contains(slice, StringComparison.CurrentCulture));
+ Assert.False(span.Contains(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.False(span.Contains(slice, StringComparison.InvariantCulture));
+ Assert.False(span.Contains(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.False(span.Contains(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void ContainsMatch_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a, 0, 3);
+ var slice = new ReadOnlySpan<char>(a, 0, 2);
+ Assert.True(span.Contains(slice, StringComparison.Ordinal));
+
+ Assert.True(span.Contains(slice, StringComparison.CurrentCulture));
+ Assert.True(span.Contains(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(span.Contains(slice, StringComparison.InvariantCulture));
+ Assert.True(span.Contains(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(span.Contains(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void ContainsMatchDifferentSpans_StringComparison()
+ {
+ char[] a = { '4', '5', '6', '7' };
+ char[] b = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a, 0, 3);
+ var slice = new ReadOnlySpan<char>(b, 0, 3);
+ Assert.True(span.Contains(slice, StringComparison.Ordinal));
+
+ Assert.True(span.Contains(slice, StringComparison.CurrentCulture));
+ Assert.True(span.Contains(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(span.Contains(slice, StringComparison.InvariantCulture));
+ Assert.True(span.Contains(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(span.Contains(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void ContainsNoMatch_StringComparison()
+ {
+ for (int length = 1; length < 150; length++)
+ {
+ for (int mismatchIndex = 0; mismatchIndex < length; mismatchIndex++)
+ {
+ var first = new char[length];
+ var second = new char[length];
+ for (int i = 0; i < length; i++)
+ {
+ first[i] = second[i] = (char)(i + 1);
+ }
+
+ second[mismatchIndex] = (char)(second[mismatchIndex] + 1);
+
+ var firstSpan = new ReadOnlySpan<char>(first);
+ var secondSpan = new ReadOnlySpan<char>(second);
+ Assert.False(firstSpan.Contains(secondSpan, StringComparison.Ordinal));
+
+ Assert.False(firstSpan.Contains(secondSpan, StringComparison.OrdinalIgnoreCase));
+
+ // Different behavior depending on OS
+ Assert.Equal(
+ firstSpan.ToString().StartsWith(secondSpan.ToString(), StringComparison.CurrentCulture),
+ firstSpan.Contains(secondSpan, StringComparison.CurrentCulture));
+ Assert.Equal(
+ firstSpan.ToString().StartsWith(secondSpan.ToString(), StringComparison.CurrentCulture),
+ firstSpan.Contains(secondSpan, StringComparison.CurrentCulture));
+ Assert.Equal(
+ firstSpan.ToString().StartsWith(secondSpan.ToString(), StringComparison.InvariantCulture),
+ firstSpan.Contains(secondSpan, StringComparison.InvariantCulture));
+ Assert.Equal(
+ firstSpan.ToString().StartsWith(secondSpan.ToString(), StringComparison.InvariantCultureIgnoreCase),
+ firstSpan.Contains(secondSpan, StringComparison.InvariantCultureIgnoreCase));
+ }
+ }
+ }
+
+ [Fact]
+ public static void MakeSureNoContainsChecksGoOutOfRange_StringComparison()
+ {
+ for (int length = 0; length < 100; length++)
+ {
+ var first = new char[length + 2];
+ first[0] = (char)99;
+ first[length + 1] = (char)99;
+ var second = new char[length + 2];
+ second[0] = (char)100;
+ second[length + 1] = (char)100;
+ var span1 = new ReadOnlySpan<char>(first, 1, length);
+ var span2 = new ReadOnlySpan<char>(second, 1, length);
+ Assert.True(span1.Contains(span2, StringComparison.Ordinal));
+
+ Assert.True(span1.Contains(span2, StringComparison.CurrentCulture));
+ Assert.True(span1.Contains(span2, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(span1.Contains(span2, StringComparison.InvariantCulture));
+ Assert.True(span1.Contains(span2, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(span1.Contains(span2, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+
+ [Fact]
+ public static void ContainsUnknownComparisonType_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a);
+ TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Contains(_span, StringComparison.CurrentCulture - 1));
+ TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Contains(_span, StringComparison.OrdinalIgnoreCase + 1));
+ TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Contains(_span, (StringComparison)6));
+ }
+
+ [Theory]
+ [InlineData("Hello", "ello", true)]
+ [InlineData("Hello", "ELL", false)]
+ [InlineData("Hello", "Larger Hello", false)]
+ [InlineData("Hello", "Goodbye", false)]
+ [InlineData("", "", true)]
+ [InlineData("", "hello", false)]
+ [InlineData("Hello", "", true)]
+ public static void Contains(string s, string value, bool expected)
+ {
+ Assert.Equal(expected, s.AsSpan().Contains(value.AsSpan(), StringComparison.Ordinal));
+ }
+ }
+}
diff --git a/src/System.Memory/tests/ReadOnlySpan/Equals.cs b/src/System.Memory/tests/ReadOnlySpan/Equals.cs
new file mode 100644
index 0000000000..b2462d5a00
--- /dev/null
+++ b/src/System.Memory/tests/ReadOnlySpan/Equals.cs
@@ -0,0 +1,262 @@
+// 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 System.Collections.Generic;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Xunit;
+
+namespace System.SpanTests
+{
+ public static partial class ReadOnlySpanTests
+ {
+ [Fact]
+ public static void ZeroLengthEquals_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a);
+ var slice = new ReadOnlySpan<char>(a, 2, 0);
+ Assert.False(span.Equals(slice, StringComparison.Ordinal));
+
+ Assert.False(span.Equals(slice, StringComparison.CurrentCulture));
+ Assert.False(span.Equals(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.False(span.Equals(slice, StringComparison.InvariantCulture));
+ Assert.False(span.Equals(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.False(span.Equals(slice, StringComparison.OrdinalIgnoreCase));
+
+ span = new ReadOnlySpan<char>(a, 1, 0);
+ Assert.True(span.Equals(slice, StringComparison.Ordinal));
+
+ Assert.True(span.Equals(slice, StringComparison.CurrentCulture));
+ Assert.True(span.Equals(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(span.Equals(slice, StringComparison.InvariantCulture));
+ Assert.True(span.Equals(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(span.Equals(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void SameSpanEquals_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a);
+ Assert.True(span.Equals(span, StringComparison.Ordinal));
+
+ Assert.True(span.Equals(span, StringComparison.CurrentCulture));
+ Assert.True(span.Equals(span, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(span.Equals(span, StringComparison.InvariantCulture));
+ Assert.True(span.Equals(span, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(span.Equals(span, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void LengthMismatchEquals_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a, 0, 2);
+ var slice = new ReadOnlySpan<char>(a, 0, 3);
+ Assert.False(span.Equals(slice, StringComparison.Ordinal));
+
+ Assert.False(span.Equals(slice, StringComparison.CurrentCulture));
+ Assert.False(span.Equals(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.False(span.Equals(slice, StringComparison.InvariantCulture));
+ Assert.False(span.Equals(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.False(span.Equals(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void EqualsOverlappingMatch_StringComparison()
+ {
+ char[] a = { '4', '5', '6', '5', '6', '5' };
+ var span = new ReadOnlySpan<char>(a, 1, 3);
+ var slice = new ReadOnlySpan<char>(a, 3, 3);
+ Assert.True(span.Equals(slice, StringComparison.Ordinal));
+
+ Assert.True(span.Equals(slice, StringComparison.CurrentCulture));
+ Assert.True(span.Equals(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(span.Equals(slice, StringComparison.InvariantCulture));
+ Assert.True(span.Equals(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(span.Equals(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void EqualsMatchDifferentSpans_StringComparison()
+ {
+ char[] a = { '4', '5', '6', '7' };
+ char[] b = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a, 0, 3);
+ var slice = new ReadOnlySpan<char>(b, 0, 3);
+ Assert.True(span.Equals(slice, StringComparison.Ordinal));
+
+ Assert.True(span.Equals(slice, StringComparison.CurrentCulture));
+ Assert.True(span.Equals(slice, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(span.Equals(slice, StringComparison.InvariantCulture));
+ Assert.True(span.Equals(slice, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(span.Equals(slice, StringComparison.OrdinalIgnoreCase));
+ }
+
+ [Fact]
+ public static void EqualsNoMatch_StringComparison()
+ {
+ for (int length = 1; length < 150; length++)
+ {
+ for (int mismatchIndex = 0; mismatchIndex < length; mismatchIndex++)
+ {
+ var first = new char[length];
+ var second = new char[length];
+ for (int i = 0; i < length; i++)
+ {
+ first[i] = second[i] = (char)(i + 1);
+ }
+
+ second[mismatchIndex] = (char)(second[mismatchIndex] + 1);
+
+ var firstSpan = new ReadOnlySpan<char>(first);
+ var secondSpan = new ReadOnlySpan<char>(second);
+ Assert.False(firstSpan.Equals(secondSpan, StringComparison.Ordinal));
+
+ Assert.False(firstSpan.Equals(secondSpan, StringComparison.OrdinalIgnoreCase));
+
+ // Different behavior depending on OS
+ Assert.Equal(
+ firstSpan.ToString().Equals(secondSpan.ToString(), StringComparison.CurrentCulture),
+ firstSpan.Equals(secondSpan, StringComparison.CurrentCulture));
+ Assert.Equal(
+ firstSpan.ToString().Equals(secondSpan.ToString(), StringComparison.CurrentCultureIgnoreCase),
+ firstSpan.Equals(secondSpan, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(
+ firstSpan.ToString().Equals(secondSpan.ToString(), StringComparison.InvariantCulture),
+ firstSpan.Equals(secondSpan, StringComparison.InvariantCulture));
+ Assert.Equal(
+ firstSpan.ToString().Equals(secondSpan.ToString(), StringComparison.InvariantCultureIgnoreCase),
+ firstSpan.Equals(secondSpan, StringComparison.InvariantCultureIgnoreCase));
+ }
+ }
+ }
+
+ [Fact]
+ public static void MakeSureNoEqualsChecksGoOutOfRange_StringComparison()
+ {
+ for (int length = 0; length < 100; length++)
+ {
+ var first = new char[length + 2];
+ first[0] = (char)99;
+ first[length + 1] = (char)99;
+ var second = new char[length + 2];
+ second[0] = (char)100;
+ second[length + 1] = (char)100;
+ var span1 = new ReadOnlySpan<char>(first, 1, length);
+ var span2 = new ReadOnlySpan<char>(second, 1, length);
+ Assert.True(span1.Equals(span2, StringComparison.Ordinal));
+
+ Assert.True(span1.Equals(span2, StringComparison.CurrentCulture));
+ Assert.True(span1.Equals(span2, StringComparison.CurrentCultureIgnoreCase));
+ Assert.True(span1.Equals(span2, StringComparison.InvariantCulture));
+ Assert.True(span1.Equals(span2, StringComparison.InvariantCultureIgnoreCase));
+ Assert.True(span1.Equals(span2, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+
+ [Fact]
+ public static void EqualsUnknownComparisonType_StringComparison()
+ {
+ char[] a = { '4', '5', '6' };
+ var span = new ReadOnlySpan<char>(a);
+ TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Equals(_span, StringComparison.CurrentCulture - 1));
+ TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Equals(_span, StringComparison.OrdinalIgnoreCase + 1));
+ TestHelpers.AssertThrows<ArgumentException, char>(span, (_span) => _span.Equals(_span, (StringComparison)6));
+ }
+
+ [Theory]
+ // CurrentCulture
+ [InlineData("Hello", "Hello", StringComparison.CurrentCulture, true)]
+ [InlineData("Hello", "hello", StringComparison.CurrentCulture, false)]
+ [InlineData("Hello", "Helloo", StringComparison.CurrentCulture, false)]
+ [InlineData("Hello", "Hell", StringComparison.CurrentCulture, false)]
+ [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.CurrentCulture, true)]
+ [InlineData("Hello", "", StringComparison.CurrentCulture, false)]
+ [InlineData("", "Hello", StringComparison.CurrentCulture, false)]
+ [InlineData("", "", StringComparison.CurrentCulture, true)]
+ // CurrentCultureIgnoreCase
+ [InlineData("Hello", "Hello", StringComparison.CurrentCultureIgnoreCase, true)]
+ [InlineData("Hello", "hello", StringComparison.CurrentCultureIgnoreCase, true)]
+ [InlineData("Hello", "helloo", StringComparison.CurrentCultureIgnoreCase, false)]
+ [InlineData("Hello", "hell", StringComparison.CurrentCultureIgnoreCase, false)]
+ [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.CurrentCulture, true)]
+ [InlineData("Hello", "", StringComparison.CurrentCultureIgnoreCase, false)]
+ [InlineData("", "Hello", StringComparison.CurrentCultureIgnoreCase, false)]
+ [InlineData("", "", StringComparison.CurrentCultureIgnoreCase, true)]
+ // InvariantCulture
+ [InlineData("Hello", "Hello", StringComparison.InvariantCulture, true)]
+ [InlineData("Hello", "hello", StringComparison.InvariantCulture, false)]
+ [InlineData("Hello", "Helloo", StringComparison.InvariantCulture, false)]
+ [InlineData("Hello", "Hell", StringComparison.InvariantCulture, false)]
+ [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.CurrentCulture, true)]
+ [InlineData("Hello", "", StringComparison.InvariantCulture, false)]
+ [InlineData("", "Hello", StringComparison.InvariantCulture, false)]
+ [InlineData("", "", StringComparison.InvariantCulture, true)]
+ // InvariantCultureIgnoreCase
+ [InlineData("Hello", "Hello", StringComparison.InvariantCultureIgnoreCase, true)]
+ [InlineData("Hello", "hello", StringComparison.InvariantCultureIgnoreCase, true)]
+ [InlineData("Hello", "Helloo", StringComparison.InvariantCultureIgnoreCase, false)]
+ [InlineData("Hello", "Hell", StringComparison.InvariantCultureIgnoreCase, false)]
+ [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.CurrentCulture, true)]
+ [InlineData("Hello", "", StringComparison.InvariantCultureIgnoreCase, false)]
+ [InlineData("", "Hello", StringComparison.InvariantCultureIgnoreCase, false)]
+ [InlineData("", "", StringComparison.InvariantCultureIgnoreCase, true)]
+ // Ordinal
+ [InlineData("Hello", "Hello", StringComparison.Ordinal, true)]
+ [InlineData("Hello", "hello", StringComparison.Ordinal, false)]
+ [InlineData("Hello", "Helloo", StringComparison.Ordinal, false)]
+ [InlineData("Hello", "Hell", StringComparison.Ordinal, false)]
+ [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.Ordinal, false)]
+ [InlineData("Hello", "", StringComparison.Ordinal, false)]
+ [InlineData("", "Hello", StringComparison.Ordinal, false)]
+ [InlineData("", "", StringComparison.Ordinal, true)]
+ // OridinalIgnoreCase
+ [InlineData("Hello", "Hello", StringComparison.OrdinalIgnoreCase, true)]
+ [InlineData("HELLO", "hello", StringComparison.OrdinalIgnoreCase, true)]
+ [InlineData("Hello", "Helloo", StringComparison.OrdinalIgnoreCase, false)]
+ [InlineData("Hello", "Hell", StringComparison.OrdinalIgnoreCase, false)]
+ [InlineData("Hello", SoftHyphen + "Hello" + SoftHyphen, StringComparison.OrdinalIgnoreCase, false)]
+ [InlineData("\u1234\u5678", "\u1234\u5678", StringComparison.OrdinalIgnoreCase, true)]
+ [InlineData("\u1234\u5678", "\u1234\u5679", StringComparison.OrdinalIgnoreCase, false)]
+ [InlineData("\u1234\u5678", "\u1235\u5678", StringComparison.OrdinalIgnoreCase, false)]
+ [InlineData("\u1234\u5678", "\u1234", StringComparison.OrdinalIgnoreCase, false)]
+ [InlineData("\u1234\u5678", "\u1234\u56789\u1234", StringComparison.OrdinalIgnoreCase, false)]
+ [InlineData("Hello", "", StringComparison.OrdinalIgnoreCase, false)]
+ [InlineData("", "Hello", StringComparison.OrdinalIgnoreCase, false)]
+ [InlineData("", "", StringComparison.OrdinalIgnoreCase, true)]
+ public static void Equals(string s1, string s2, StringComparison comparisonType, bool expected)
+ {
+ Assert.Equal(expected, s1.AsSpan().Equals(s2.AsSpan(), comparisonType));
+ }
+
+ public static IEnumerable<object[]> Equals_EncyclopaediaData()
+ {
+ yield return new object[] { StringComparison.CurrentCulture, false };
+ yield return new object[] { StringComparison.CurrentCultureIgnoreCase, false };
+ yield return new object[] { StringComparison.Ordinal, false };
+ yield return new object[] { StringComparison.OrdinalIgnoreCase, false };
+
+ // Windows and ICU disagree about how these strings compare in the default locale.
+ yield return new object[] { StringComparison.InvariantCulture, PlatformDetection.IsWindows };
+ yield return new object[] { StringComparison.InvariantCultureIgnoreCase, PlatformDetection.IsWindows };
+ }
+
+ [Theory]
+ [MemberData(nameof(Equals_EncyclopaediaData))]
+ public static void Equals_Encyclopaedia_ReturnsExpected(StringComparison comparison, bool expected)
+ {
+ string source = "encyclop\u00e6dia";
+ string target = "encyclopaedia";
+ CultureInfo backupCulture = CultureInfo.CurrentCulture;
+
+ Thread.CurrentThread.CurrentCulture = new CultureInfo("se-SE");
+ Assert.Equal(expected, source.AsSpan().Equals(target.AsSpan(), comparison));
+
+ Thread.CurrentThread.CurrentCulture = backupCulture;
+ }
+ }
+}
diff --git a/src/System.Memory/tests/ReadOnlySpan/IndexOf.charSpan.cs b/src/System.Memory/tests/ReadOnlySpan/IndexOf.charSpan.cs
new file mode 100644
index 0000000000..b505deaa87
--- /dev/null
+++ b/src/System.Memory/tests/ReadOnlySpan/IndexOf.charSpan.cs
@@ -0,0 +1,409 @@
+// 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 System.Collections.Generic;
+using System.Globalization;
+using System.Threading;
+using Xunit;
+
+namespace System.SpanTests
+{
+ public static partial class ReadOnlySpanTests
+ {
+ [Theory]
+ [InlineData("Hello", 'l', 0, 5, 2)]
+ [InlineData("Hello", 'x', 0, 5, -1)]
+ [InlineData("Hello", 'l', 1, 4, 2)]
+ [InlineData("Hello", 'l', 3, 2, 3)]
+ [InlineData("Hello", 'l', 4, 1, -1)]
+ [InlineData("Hello", 'x', 1, 4, -1)]
+ [InlineData("Hello", 'l', 3, 0, -1)]
+ [InlineData("Hello", 'l', 0, 2, -1)]
+ [InlineData("Hello", 'l', 0, 3, 2)]
+ [InlineData("Hello", 'l', 4, 1, -1)]
+ [InlineData("Hello", 'x', 1, 4, -1)]
+ [InlineData("Hello", 'o', 5, 0, -1)]
+ [InlineData("H" + SoftHyphen + "ello", 'e', 0, 3, 2)]
+ [InlineData("\ud800\udfff", '\ud800', 0, 1, 0)] // Surrogate characters
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'A', 0, 26, 0)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'B', 1, 25, 1)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'C', 2, 24, 2)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'D', 3, 23, 3)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'G', 2, 24, 6)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'K', 2, 24, 10)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'O', 2, 24, 14)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'P', 2, 24, 15)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'Q', 2, 24, 16)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'R', 2, 24, 17)]
+ [InlineData("________\u8080\u8080\u8080________", '\u0080', 0, 19, -1)]
+ [InlineData("________\u8000\u8000\u8000________", '\u0080', 0, 19, -1)]
+ [InlineData("__\u8080\u8000\u0080______________", '\u0080', 0, 19, 4)]
+ [InlineData("__\u8080\u8000__\u0080____________", '\u0080', 0, 19, 6)]
+ [InlineData("__________________________________", '\ufffd', 0, 34, -1)]
+ [InlineData("____________________________\ufffd", '\ufffd', 0, 29, 28)]
+ [InlineData("ABCDEFGHIJKLM", 'M', 0, 13, 12)]
+ [InlineData("ABCDEFGHIJKLMN", 'N', 0, 14, 13)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", '@', 0, 26, -1)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXY", '@', 0, 25, -1)]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ#", '@', 0, 27, -1)]
+ [InlineData("_____________\u807f", '\u007f', 0, 14, -1)]
+ [InlineData("_____________\u807f__", '\u007f', 0, 16, -1)]
+ [InlineData("_____________\u807f\u007f_", '\u007f', 0, 16, 14)]
+ [InlineData("__\u807f_______________", '\u007f', 0, 18, -1)]
+ [InlineData("__\u807f___\u007f___________", '\u007f', 0, 18, 6)]
+ [InlineData("ABCDEFGHIJKLMN", 'N', 2, 11, -1)]
+ [InlineData("!@#$%^&", '%', 0, 7, 4)]
+ [InlineData("!@#$", '!', 0, 4, 0)]
+ [InlineData("!@#$", '@', 0, 4, 1)]
+ [InlineData("!@#$", '#', 0, 4, 2)]
+ [InlineData("!@#$", '$', 0, 4, 3)]
+ [InlineData("!@#$%^&*", '%', 0, 8, 4)]
+ public static void IndexOf_SingleLetter(string s, char target, int startIndex, int count, int expected)
+ {
+ bool safeForCurrentCulture =
+ IsSafeForCurrentCultureComparisons(s)
+ && IsSafeForCurrentCultureComparisons(target.ToString());
+
+ ReadOnlySpan<char> span = s.AsSpan();
+ var charArray = new char[1];
+ charArray[0] = target;
+ ReadOnlySpan<char> targetSpan = charArray;
+
+ int expectedFromSpan = expected == -1 ? expected : expected - startIndex;
+
+ if (count + startIndex == s.Length)
+ {
+ if (startIndex == 0)
+ {
+ Assert.Equal(expectedFromSpan, span.IndexOf(targetSpan, StringComparison.Ordinal));
+ Assert.Equal(expectedFromSpan, span.IndexOf(targetSpan, StringComparison.OrdinalIgnoreCase));
+
+ // To be safe we only want to run CurrentCulture comparisons if
+ // we know the results will not vary depending on location
+ if (safeForCurrentCulture)
+ {
+ Assert.Equal(expectedFromSpan, span.IndexOf(targetSpan, StringComparison.CurrentCulture));
+ }
+ }
+
+ Assert.Equal(expectedFromSpan, span.Slice(startIndex).IndexOf(targetSpan, StringComparison.Ordinal));
+ Assert.Equal(expectedFromSpan, span.Slice(startIndex).IndexOf(targetSpan, StringComparison.OrdinalIgnoreCase));
+
+ if (safeForCurrentCulture)
+ {
+ Assert.Equal(expectedFromSpan, span.Slice(startIndex).IndexOf(targetSpan, StringComparison.CurrentCulture));
+ }
+ }
+ Assert.Equal(expectedFromSpan, span.Slice(startIndex, count).IndexOf(targetSpan, StringComparison.Ordinal));
+ Assert.Equal(expectedFromSpan, span.Slice(startIndex, count).IndexOf(targetSpan, StringComparison.OrdinalIgnoreCase));
+
+ if (safeForCurrentCulture)
+ {
+ Assert.Equal(expectedFromSpan, span.Slice(startIndex, count).IndexOf(targetSpan, StringComparison.CurrentCulture));
+ }
+ }
+
+ private static bool IsSafeForCurrentCultureComparisons(string str)
+ {
+ for (int i = 0; i < str.Length; i++)
+ {
+ char c = str[i];
+ // We only want ASCII chars that you can see
+ // No controls, no delete, nothing >= 0x80
+ if (c < 0x20 || c == 0x7f || c >= 0x80)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ // NOTE: This is by design. Unix ignores the null characters (i.e. null characters have no weights for the string comparison).
+ // For desired behavior, use ordinal comparison instead of linguistic comparison.
+ // This is a known difference between Windows and Unix (https://github.com/dotnet/coreclr/issues/2051).
+ [Theory]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ [InlineData("He\0lo", "He\0lo", 0)]
+ [InlineData("He\0lo", "He\0", 0)]
+ [InlineData("He\0lo", "\0", 2)]
+ [InlineData("He\0lo", "\0lo", 2)]
+ [InlineData("He\0lo", "lo", 3)]
+ [InlineData("Hello", "lo\0", -1)]
+ [InlineData("Hello", "\0lo", -1)]
+ [InlineData("Hello", "l\0o", -1)]
+ public static void IndexOf_NullInStrings(string s, string value, int expected)
+ {
+ Assert.Equal(expected, s.AsSpan().IndexOf(value.AsSpan(), StringComparison.Ordinal));
+ }
+
+ [Theory]
+ [MemberData(nameof(AllSubstringsAndComparisons), new object[] { "abcde" })]
+ public static void IndexOf_AllSubstrings(string s, string value, int startIndex, StringComparison comparison)
+ {
+ bool ignoringCase = comparison == StringComparison.OrdinalIgnoreCase || comparison == StringComparison.CurrentCultureIgnoreCase;
+
+ // First find the substring. We should be able to with all comparison types.
+ Assert.Equal(startIndex, s.AsSpan().IndexOf(value.AsSpan(), comparison)); // in the whole string
+ Assert.Equal(0, s.AsSpan(startIndex).IndexOf(value.AsSpan(), comparison)); // starting at substring
+ if (startIndex > 0)
+ {
+ Assert.Equal(1, s.AsSpan(startIndex - 1).IndexOf(value.AsSpan(), comparison)); // starting just before substring
+ }
+ Assert.Equal(-1, s.AsSpan(startIndex + 1).IndexOf(value.AsSpan(), comparison)); // starting just after start of substring
+
+ // Shouldn't be able to find the substring if the count is less than substring's length
+ Assert.Equal(-1, s.AsSpan(0, value.Length - 1).IndexOf(value.AsSpan(), comparison));
+
+ // Now double the source. Make sure we find the first copy of the substring.
+ int halfLen = s.Length;
+ s += s;
+ Assert.Equal(startIndex, s.AsSpan().IndexOf(value.AsSpan(), comparison));
+
+ // Now change the case of a letter.
+ s = s.ToUpperInvariant();
+ Assert.Equal(ignoringCase ? startIndex : -1, s.AsSpan().IndexOf(value.AsSpan(), comparison));
+ }
+
+ public static IEnumerable<object[]> AllSubstringsAndComparisons(string source)
+ {
+ var comparisons = new StringComparison[]
+ {
+ StringComparison.CurrentCulture,
+ StringComparison.CurrentCultureIgnoreCase,
+ StringComparison.Ordinal,
+ StringComparison.OrdinalIgnoreCase
+ };
+
+ foreach (StringComparison comparison in comparisons)
+ {
+ for (int i = 0; i <= source.Length; i++)
+ {
+ for (int subLen = source.Length - i; subLen > 0; subLen--)
+ {
+ yield return new object[] { source, source.Substring(i, subLen), i, comparison };
+ }
+ }
+ }
+ }
+
+ [Fact]
+ public static void IndexOf_TurkishI_TurkishCulture()
+ {
+ CultureInfo backupCulture = CultureInfo.CurrentCulture;
+
+ Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
+
+ string str = "Turkish I \u0131s TROUBL\u0130NG!";
+ string valueString = "\u0130";
+ ReadOnlySpan<char> s = str.AsSpan();
+ ReadOnlySpan<char> value = valueString.AsSpan();
+ Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(4, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(19, s.IndexOf(value, StringComparison.Ordinal));
+ Assert.Equal(19, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));
+
+ valueString = "\u0131";
+ value = valueString.AsSpan();
+ Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(10, s.IndexOf(value, StringComparison.Ordinal));
+ Assert.Equal(10, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));
+
+ Thread.CurrentThread.CurrentCulture = backupCulture;
+ }
+
+ [Fact]
+ public static void IndexOf_TurkishI_InvariantCulture()
+ {
+ CultureInfo backupCulture = CultureInfo.CurrentCulture;
+
+ Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
+
+ string str = "Turkish I \u0131s TROUBL\u0130NG!";
+ string valueString = "\u0130";
+ ReadOnlySpan<char> s = str.AsSpan();
+ ReadOnlySpan<char> value = valueString.AsSpan();
+
+ Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+
+ valueString = "\u0131";
+ value = valueString.AsSpan();
+ Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+
+ Thread.CurrentThread.CurrentCulture = backupCulture;
+ }
+
+ [Fact]
+ public static void IndexOf_TurkishI_EnglishUSCulture()
+ {
+ CultureInfo backupCulture = CultureInfo.CurrentCulture;
+
+ Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
+
+ string str = "Turkish I \u0131s TROUBL\u0130NG!";
+ string valueString = "\u0130";
+ ReadOnlySpan<char> s = str.AsSpan();
+ ReadOnlySpan<char> value = valueString.AsSpan();
+
+ Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(19, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+
+ valueString = "\u0131";
+ value = valueString.AsSpan();
+ Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+
+ Thread.CurrentThread.CurrentCulture = backupCulture;
+ }
+
+ [Fact]
+ public static void IndexOf_HungarianDoubleCompression_HungarianCulture()
+ {
+ string str = "dzsdzs";
+ string valueString = "ddzs";
+
+ ReadOnlySpan<char> s = str.AsSpan();
+ ReadOnlySpan<char> value = valueString.AsSpan();
+
+ CultureInfo backupCulture = CultureInfo.CurrentCulture;
+
+ Thread.CurrentThread.CurrentCulture = new CultureInfo("hu-HU");
+ /*
+ There are differences between Windows and ICU regarding contractions.
+ Windows has equal contraction collation weights, including case (target="Ddzs" same behavior as "ddzs").
+ ICU has different contraction collation weights, depending on locale collation rules.
+ If CurrentCultureIgnoreCase is specified, ICU will use 'secondary' collation rules
+ which ignore the contraction collation weights (defined as 'tertiary' rules)
+ */
+ Assert.Equal(PlatformDetection.IsWindows ? 0 : -1, s.IndexOf(value, StringComparison.CurrentCulture));
+
+ Assert.Equal(0, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(-1, s.IndexOf(value, StringComparison.Ordinal));
+ Assert.Equal(-1, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));
+
+ Thread.CurrentThread.CurrentCulture = backupCulture;
+ }
+
+ [Fact]
+ public static void IndexOf_HungarianDoubleCompression_InvariantCulture()
+ {
+ string str = "dzsdzs";
+ string valueString = "ddzs";
+
+ ReadOnlySpan<char> s = str.AsSpan();
+ ReadOnlySpan<char> value = valueString.AsSpan();
+
+ CultureInfo backupCulture = CultureInfo.CurrentCulture;
+
+ Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
+ Assert.Equal(-1, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(-1, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+
+ Thread.CurrentThread.CurrentCulture = backupCulture;
+ }
+
+ [Fact]
+ public static void IndexOf_EquivalentDiacritics_EnglishUSCulture()
+ {
+ string str = "Exhibit a\u0300\u00C0";
+ string valueString = "\u00C0";
+
+ ReadOnlySpan<char> s = str.AsSpan();
+ ReadOnlySpan<char> value = valueString.AsSpan();
+
+ CultureInfo backupCulture = CultureInfo.CurrentCulture;
+
+ Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
+ Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(10, s.IndexOf(value, StringComparison.Ordinal));
+ Assert.Equal(10, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));
+
+ valueString = "a\u0300"; // this diacritic combines with preceding character
+ value = valueString.AsSpan();
+ Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(8, s.IndexOf(value, StringComparison.Ordinal));
+ Assert.Equal(8, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));
+
+ Thread.CurrentThread.CurrentCulture = backupCulture;
+ }
+
+ [Fact]
+ public static void IndexOf_EquivalentDiacritics_InvariantCulture()
+ {
+ string str = "Exhibit a\u0300\u00C0";
+ string valueString = "\u00C0";
+
+ ReadOnlySpan<char> s = str.AsSpan();
+ ReadOnlySpan<char> value = valueString.AsSpan();
+
+ CultureInfo backupCulture = CultureInfo.CurrentCulture;
+
+ Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
+ Assert.Equal(10, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+
+ valueString = "a\u0300"; // this diacritic combines with preceding character
+ value = valueString.AsSpan();
+ Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(8, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+
+ Thread.CurrentThread.CurrentCulture = backupCulture;
+ }
+
+ [Fact]
+ public static void IndexOf_CyrillicE_EnglishUSCulture()
+ {
+ string str = "Foo\u0400Bar";
+ string valueString = "\u0400";
+
+ ReadOnlySpan<char> s = str.AsSpan();
+ ReadOnlySpan<char> value = valueString.AsSpan();
+
+ CultureInfo backupCulture = CultureInfo.CurrentCulture;
+
+ Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
+ Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(3, s.IndexOf(value, StringComparison.Ordinal));
+ Assert.Equal(3, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));
+
+ valueString = "bar";
+ value = valueString.AsSpan();
+ Assert.Equal(-1, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(4, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+ Assert.Equal(-1, s.IndexOf(value, StringComparison.Ordinal));
+ Assert.Equal(4, s.IndexOf(value, StringComparison.OrdinalIgnoreCase));
+
+ Thread.CurrentThread.CurrentCulture = backupCulture;
+ }
+
+ [Fact]
+ public static void IndexOf_CyrillicE_InvariantCulture()
+ {
+ string str = "Foo\u0400Bar";
+ string valueString = "\u0400";
+
+ ReadOnlySpan<char> s = str.AsSpan();
+ ReadOnlySpan<char> value = valueString.AsSpan();
+
+ CultureInfo backupCulture = CultureInfo.CurrentCulture;
+
+ Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
+ Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(3, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+
+ valueString = "bar";
+ value = valueString.AsSpan();
+ Assert.Equal(-1, s.IndexOf(value, StringComparison.CurrentCulture));
+ Assert.Equal(4, s.IndexOf(value, StringComparison.CurrentCultureIgnoreCase));
+
+ Thread.CurrentThread.CurrentCulture = backupCulture;
+ }
+ }
+}
diff --git a/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.char.cs b/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.char.cs
index eedc14ac0c..9841b76fa1 100644
--- a/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.char.cs
+++ b/src/System.Memory/tests/ReadOnlySpan/IndexOfSequence.char.cs
@@ -15,6 +15,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '5', '1', '7' });
int index = span.IndexOf(value);
Assert.Equal(0, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
[Fact]
@@ -24,6 +25,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '2', '3' });
int index = span.IndexOf(value);
Assert.Equal(1, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
[Fact]
@@ -33,6 +35,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '7', '7', '8' });
int index = span.IndexOf(value);
Assert.Equal(10, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
[Fact]
@@ -42,6 +45,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '7', '7', '8', 'X' });
int index = span.IndexOf(value);
Assert.Equal(-1, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
[Fact]
@@ -51,6 +55,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { 'X', '7', '8', '9' });
int index = span.IndexOf(value);
Assert.Equal(-1, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
[Fact]
@@ -60,6 +65,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '3', '4', '5' });
int index = span.IndexOf(value);
Assert.Equal(3, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
[Fact]
@@ -69,6 +75,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '3', '4', '5' });
int index = span.IndexOf(value);
Assert.Equal(-1, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
[Fact]
@@ -79,6 +86,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(Array.Empty<char>());
int index = span.IndexOf(value);
Assert.Equal(0, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
[Fact]
@@ -88,6 +96,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '1', '2', '3' });
int index = span.IndexOf(value);
Assert.Equal(-1, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
[Fact]
@@ -98,6 +107,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '2' });
int index = span.IndexOf(value);
Assert.Equal(2, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
[Fact]
@@ -108,6 +118,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '5' });
int index = span.IndexOf(value);
Assert.Equal(5, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
[Fact]
@@ -118,6 +129,7 @@ namespace System.SpanTests
ReadOnlySpan<char> value = new ReadOnlySpan<char>(new char[] { '5' });
int index = span.IndexOf(value);
Assert.Equal(-1, index);
+ Assert.Equal(index, span.IndexOf(value, StringComparison.Ordinal));
}
}
}
diff --git a/src/System.Memory/tests/System.Memory.Tests.csproj b/src/System.Memory/tests/System.Memory.Tests.csproj
index 7035a2af6e..c777b5bd86 100644
--- a/src/System.Memory/tests/System.Memory.Tests.csproj
+++ b/src/System.Memory/tests/System.Memory.Tests.csproj
@@ -82,6 +82,8 @@
<Compile Include="ReadOnlySpan\AsBytes.cs" />
<Compile Include="ReadOnlySpan\AsReadOnlySpan.cs" />
<Compile Include="ReadOnlySpan\BinarySearch.cs" />
+ <Compile Include="ReadOnlySpan\CompareTo.cs" />
+ <Compile Include="ReadOnlySpan\Contains.cs" />
<Compile Include="ReadOnlySpan\CopyTo.cs" />
<Compile Include="ReadOnlySpan\CtorArray.cs" />
<Compile Include="ReadOnlySpan\CtorArrayIntInt.cs" />
@@ -91,12 +93,14 @@
<Compile Include="ReadOnlySpan\EndsWith.char.cs" />
<Compile Include="ReadOnlySpan\EndsWith.T.cs" />
<Compile Include="ReadOnlySpan\Equality.cs" />
+ <Compile Include="ReadOnlySpan\Equals.cs" />
<Compile Include="ReadOnlySpan\GetEnumerator.cs" />
<Compile Include="ReadOnlySpan\GetHashCode.cs" />
<Compile Include="ReadOnlySpan\ImplicitConversion.cs" />
<Compile Include="ReadOnlySpan\IndexOf.T.cs" />
<Compile Include="ReadOnlySpan\IndexOf.byte.cs" />
<Compile Include="ReadOnlySpan\IndexOf.char.cs" />
+ <Compile Include="ReadOnlySpan\IndexOf.charSpan.cs" />
<Compile Include="ReadOnlySpan\IndexOfAny.T.cs" />
<Compile Include="ReadOnlySpan\IndexOfAny.byte.cs" />
<Compile Include="ReadOnlySpan\IndexOfSequence.T.cs" />