Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal static unsafe void InternalCopy(string src, IntPtr dest, int len)
{
if (len != 0)
{
SpanHelpers.Memmove(ref *(byte*)dest, ref Unsafe.As<char, byte>(ref src.GetRawStringData()), (nuint)len);
SpanHelpers.Memmove(ref *(byte*)dest, ref src.GetRawStringDataAsUInt8(), (nuint)len);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ private static void Test(ReadOnlySpan<char> haystack, ReadOnlySpan<char> haystac
SearchValues<string> searchValues = SearchValues.Create(needles, comparisonType);

int index = haystack.IndexOfAny(searchValues);
Assert.Equal(index, haystackCopy.IndexOfAny(searchValues));
Assert.Equal(index, IndexOfAnyReferenceImpl(haystack, needles, comparisonType));
AssertEqual(index, haystackCopy.IndexOfAny(searchValues), searchValues);
AssertEqual(index, IndexOfAnyReferenceImpl(haystack, needles, comparisonType), searchValues);
}

private static int IndexOfAnyReferenceImpl(ReadOnlySpan<char> haystack, string[] needles, StringComparison comparisonType)
Expand All @@ -55,4 +55,15 @@ private static int IndexOfAnyReferenceImpl(ReadOnlySpan<char> haystack, string[]

return minIndex == int.MaxValue ? -1 : minIndex;
}

private static void AssertEqual(int expected, int actual, SearchValues<string> searchValues)
{
if (expected != actual)
{
Type implType = searchValues.GetType();
string impl = $"{implType.Name} [{string.Join(", ", implType.GenericTypeArguments.Select(t => t.Name))}]";

throw new Exception($"Expected {expected}, got {actual} for impl='{impl}'");
}
}
}
45 changes: 43 additions & 2 deletions src/libraries/System.Memory/tests/Span/StringSearchValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.DotNet.RemoteExecutor;
Expand Down Expand Up @@ -313,6 +314,7 @@ public static void IndexOfAny_InvalidUtf16()
IndexOfAny(StringComparison.OrdinalIgnoreCase, -1, " foO\uD801bar", "oo\uD800baR, bar\uD800foo");

// Low surrogate without the high surrogate.
IndexOfAny(StringComparison.OrdinalIgnoreCase, 1, "\uD801\uDCD8\uD8FB\uDCD8", "\uDCD8");
IndexOfAny(StringComparison.OrdinalIgnoreCase, 1, "\uD801\uDCD8\uD8FB\uDCD8", "foo, \uDCD8");
}

Expand All @@ -337,6 +339,15 @@ public static void IndexOfAny_InvalidUtf16()
[InlineData("abcd!")]
[InlineData("abcdefgh")]
[InlineData("abcdefghi")]
[InlineData("123456789")]
[InlineData("123456789a")]
[InlineData("123456789ab")]
[InlineData("123456789abc")]
[InlineData("123456789abcd")]
[InlineData("123456789abcde")]
[InlineData("123456789abcdef")]
[InlineData("123456789abcdefg")]
[InlineData("123456789abcdefgh")]
// Multiple values, but they all share the same prefix
[InlineData("abc", "ab", "abcd")]
// These should hit the Aho-Corasick implementation
Expand Down Expand Up @@ -406,9 +417,25 @@ static void TestCore(string[] valuesArray)
Values_ImplementsSearchValuesBase(StringComparison.OrdinalIgnoreCase, valuesArray);

string values = string.Join(", ", valuesArray);
string text = valuesArray[0];

IndexOfAny(StringComparison.Ordinal, 0, valuesArray[0], values);
IndexOfAny(StringComparison.OrdinalIgnoreCase, 0, valuesArray[0], values);
IndexOfAny(StringComparison.Ordinal, 0, text, values);
IndexOfAny(StringComparison.OrdinalIgnoreCase, 0, text, values);

// Replace every position in the text with a different character.
foreach (StringComparison comparisonType in new[] { StringComparison.Ordinal, StringComparison.OrdinalIgnoreCase })
{
SearchValues<string> stringValues = SearchValues.Create(valuesArray, comparisonType);

for (int i = 0; i < text.Length - 1; i++)
{
foreach (char replacement in "AaBb _!\u00F6")
{
string newText = $"{text.AsSpan(0, i)}{replacement}{text.AsSpan(i + 1)}";
Assert.Equal(IndexOfAnyReferenceImpl(newText, valuesArray, comparisonType), newText.IndexOfAny(stringValues));
}
}
}
}
}

Expand Down Expand Up @@ -499,6 +526,20 @@ public static void TestIndexOfAny_RandomInputs_Stress()
{
RunStress();

if (RemoteExecutor.IsSupported && Avx512F.IsSupported)
{
var psi = new ProcessStartInfo();
psi.Environment.Add("DOTNET_EnableAVX512F", "0");
RemoteExecutor.Invoke(RunStress, new RemoteInvokeOptions { StartInfo = psi, TimeOut = 10 * 60 * 1000 }).Dispose();
}

if (RemoteExecutor.IsSupported && Avx2.IsSupported)
{
var psi = new ProcessStartInfo();
psi.Environment.Add("DOTNET_EnableAVX2", "0");
RemoteExecutor.Invoke(RunStress, new RemoteInvokeOptions { StartInfo = psi, TimeOut = 10 * 60 * 1000 }).Dispose();
}

if (CanTestInvariantCulture)
{
RunUsingInvariantCulture(static () => RunStress());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public override int GetHashCode(string? obj)
// The Ordinal version of Marvin32 operates over bytes.
// The multiplication from # chars -> # bytes will never integer overflow.
return Marvin.ComputeHash32(
ref Unsafe.As<char, byte>(ref obj.GetRawStringData()),
ref obj.GetRawStringDataAsUInt8(),
(uint)obj.Length * 2,
_seed.p0, _seed.p1);
}
Expand Down
Loading
Loading