Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
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
10 changes: 9 additions & 1 deletion src/System.Memory/ref/System.Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ public static class MemoryExtensions
public static int LastIndexOf<T>(this Span<T> span, T value) where T : IEquatable<T> { throw null; }
public static int LastIndexOf<T>(this Span<T> span, ReadOnlySpan<T> value) where T : IEquatable<T> { throw null; }

public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1) where T : IEquatable<T> { throw null; }
public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1, T value2) where T : IEquatable<T> { throw null; }
public static int LastIndexOfAny<T>(this Span<T> span, ReadOnlySpan<T> values) where T : IEquatable<T> { throw null; }

public static bool SequenceEqual<T>(this Span<T> first, ReadOnlySpan<T> second) where T : IEquatable<T> { throw null; }

public static bool StartsWith<T>(this Span<T> span, ReadOnlySpan<T> value) where T : IEquatable<T> { throw null; }
Expand Down Expand Up @@ -134,6 +138,10 @@ public static class MemoryExtensions
public static int LastIndexOf<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T> { throw null; }
public static int LastIndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value) where T : IEquatable<T> { throw null; }

public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1) where T : IEquatable<T> { throw null; }
public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2) where T : IEquatable<T> { throw null; }
public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values) where T : IEquatable<T> { throw null; }

public static bool SequenceEqual<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second) where T : IEquatable<T> { throw null; }

public static bool StartsWith<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value) where T : IEquatable<T> { throw null; }
Expand Down Expand Up @@ -354,7 +362,7 @@ namespace System.Buffers
{
public const byte MaxPrecision = 99;
public const byte NoPrecision = 255;
public StandardFormat(char symbol, byte precision= 255) => throw null;
public StandardFormat(char symbol, byte precision = 255) => throw null;
public bool HasPrecision => throw null;
public bool IsDefault => throw null;
public byte Precision => throw null;
Expand Down
116 changes: 116 additions & 0 deletions src/System.Memory/src/System/MemoryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,122 @@ public static int IndexOfAny(this ReadOnlySpan<byte> span, ReadOnlySpan<byte> va
return SpanHelpers.IndexOfAny(ref span.DangerousGetPinnableReference(), span.Length, ref values.DangerousGetPinnableReference(), values.Length);
}

/// <summary>
/// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value0">One of the values to search for.</param>
/// <param name="value1">One of the values to search for.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1)
where T : IEquatable<T>
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference()),
Unsafe.As<T, byte>(ref value0),
Unsafe.As<T, byte>(ref value1),
span.Length);
return SpanHelpers.LastIndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, span.Length);
}

/// <summary>
/// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value0">One of the values to search for.</param>
/// <param name="value1">One of the values to search for.</param>
/// <param name="value2">One of the values to search for.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1, T value2)
where T : IEquatable<T>
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference()),
Unsafe.As<T, byte>(ref value0),
Unsafe.As<T, byte>(ref value1),
Unsafe.As<T, byte>(ref value2),
span.Length);
return SpanHelpers.LastIndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length);
}

/// <summary>
/// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="values">The set of values to search for.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny<T>(this Span<T> span, ReadOnlySpan<T> values)
where T : IEquatable<T>
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference()),
span.Length,
ref Unsafe.As<T, byte>(ref values.DangerousGetPinnableReference()),
values.Length);
return SpanHelpers.LastIndexOfAny(ref span.DangerousGetPinnableReference(), span.Length, ref values.DangerousGetPinnableReference(), values.Length);
}

/// <summary>
/// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value0">One of the values to search for.</param>
/// <param name="value1">One of the values to search for.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1)
where T : IEquatable<T>
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference()),
Unsafe.As<T, byte>(ref value0),
Unsafe.As<T, byte>(ref value1),
span.Length);
return SpanHelpers.LastIndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, span.Length);
}

/// <summary>
/// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="value0">One of the values to search for.</param>
/// <param name="value1">One of the values to search for.</param>
/// <param name="value2">One of the values to search for.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2)
where T : IEquatable<T>
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference()),
Unsafe.As<T, byte>(ref value0),
Unsafe.As<T, byte>(ref value1),
Unsafe.As<T, byte>(ref value2),
span.Length);
return SpanHelpers.LastIndexOfAny(ref span.DangerousGetPinnableReference(), value0, value1, value2, span.Length);
}

/// <summary>
/// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1.
/// </summary>
/// <param name="span">The span to search.</param>
/// <param name="values">The set of values to search for.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values)
where T : IEquatable<T>
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference()),
span.Length,
ref Unsafe.As<T, byte>(ref values.DangerousGetPinnableReference()),
values.Length);
return SpanHelpers.LastIndexOfAny<T>(ref span.DangerousGetPinnableReference(), span.Length, ref values.DangerousGetPinnableReference(), values.Length);
}

/// <summary>
/// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
/// </summary>
Expand Down
185 changes: 185 additions & 0 deletions src/System.Memory/src/System/SpanHelpers.T.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ public static int IndexOf<T>(ref T searchSpace, int searchSpaceLength, ref T val
return -1;
}

public static int LastIndexOfAny<T>(ref T searchSpace, int searchSpaceLength, ref T value, int valueLength)
where T : IEquatable<T>
{
Debug.Assert(searchSpaceLength >= 0);
Debug.Assert(valueLength >= 0);

if (valueLength == 0)
return 0; // A zero-length sequence is always treated as "found" at the start of the search space.

int index = -1;
for (int i = 0; i < valueLength; i++)
{
var tempIndex = LastIndexOf(ref searchSpace, Unsafe.Add(ref value, i), searchSpaceLength);
if (tempIndex != -1)
{
index = (index == -1 || index < tempIndex) ? tempIndex : index;
}
}
return index;
}

public static unsafe int IndexOf<T>(ref T searchSpace, T value, int length)
where T : IEquatable<T>
{
Expand Down Expand Up @@ -222,6 +243,170 @@ public static unsafe int LastIndexOf<T>(ref T searchSpace, T value, int length)
return length + 7;
}

public static unsafe int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, int length)
where T : IEquatable<T>
{
Debug.Assert(length >= 0);

T lookUp;
while (length >= 8)
{
length -= 8;

lookUp = Unsafe.Add(ref searchSpace, length + 7);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found7;
lookUp = Unsafe.Add(ref searchSpace, length + 6);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found6;
lookUp = Unsafe.Add(ref searchSpace, length + 5);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found5;
lookUp = Unsafe.Add(ref searchSpace, length + 4);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found4;
lookUp = Unsafe.Add(ref searchSpace, length + 3);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found3;
lookUp = Unsafe.Add(ref searchSpace, length + 2);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found2;
lookUp = Unsafe.Add(ref searchSpace, length + 1);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found1;
lookUp = Unsafe.Add(ref searchSpace, length);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found;
}

if (length >= 4)
{
length -= 4;

lookUp = Unsafe.Add(ref searchSpace, length + 3);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found3;
lookUp = Unsafe.Add(ref searchSpace, length + 2);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found2;
lookUp = Unsafe.Add(ref searchSpace, length + 1);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found1;
lookUp = Unsafe.Add(ref searchSpace, length);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found;
}

while (length > 0)
{
length--;

lookUp = Unsafe.Add(ref searchSpace, length);
if (value0.Equals(lookUp) || value1.Equals(lookUp))
goto Found;
}
return -1;

Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
return length;
Found1:
return length + 1;
Found2:
return length + 2;
Found3:
return length + 3;
Found4:
return length + 4;
Found5:
return length + 5;
Found6:
return length + 6;
Found7:
return length + 7;
}

public static unsafe int LastIndexOfAny<T>(ref T searchSpace, T value0, T value1, T value2, int length)
where T : IEquatable<T>
{
Debug.Assert(length >= 0);

T lookUp;
while (length >= 8)
{
length -= 8;

lookUp = Unsafe.Add(ref searchSpace, length + 7);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found7;
lookUp = Unsafe.Add(ref searchSpace, length + 6);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found6;
lookUp = Unsafe.Add(ref searchSpace, length + 5);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found5;
lookUp = Unsafe.Add(ref searchSpace, length + 4);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found4;
lookUp = Unsafe.Add(ref searchSpace, length + 3);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found3;
lookUp = Unsafe.Add(ref searchSpace, length + 2);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found2;
lookUp = Unsafe.Add(ref searchSpace, length + 1);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found1;
lookUp = Unsafe.Add(ref searchSpace, length);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found;
}

if (length >= 4)
{
length -= 4;

lookUp = Unsafe.Add(ref searchSpace, length + 3);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found3;
lookUp = Unsafe.Add(ref searchSpace, length + 2);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found2;
lookUp = Unsafe.Add(ref searchSpace, length + 1);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found1;
lookUp = Unsafe.Add(ref searchSpace, length);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found;
}

while (length > 0)
{
length--;

lookUp = Unsafe.Add(ref searchSpace, length);
if (value0.Equals(lookUp) || value1.Equals(lookUp) || value2.Equals(lookUp))
goto Found;
}
return -1;

Found: // Workaround for https://github.com/dotnet/coreclr/issues/13549
return length;
Found1:
return length + 1;
Found2:
return length + 2;
Found3:
return length + 3;
Found4:
return length + 4;
Found5:
return length + 5;
Found6:
return length + 6;
Found7:
return length + 7;
}

public static bool SequenceEqual<T>(ref T first, ref T second, int length)
where T : IEquatable<T>
{
Expand Down
Loading