// 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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Internal.Runtime.CompilerServices;
#if BIT64
using nuint = System.UInt64;
#else
using nuint = System.UInt32;
#endif // BIT64
namespace System
{
///
/// Extension methods for Span{T}, Memory{T}, and friends.
///
public static partial class MemoryExtensions
{
///
/// Removes all leading and trailing white-space characters from the span.
///
public static ReadOnlySpan Trim(this ReadOnlySpan span)
{
int start = 0;
for (; start < span.Length; start++)
{
if (!char.IsWhiteSpace(span[start]))
break;
}
int end = span.Length - 1;
for (; end >= start; end--)
{
if (!char.IsWhiteSpace(span[end]))
break;
}
return span.Slice(start, end - start + 1);
}
///
/// Removes all leading white-space characters from the span.
///
public static ReadOnlySpan TrimStart(this ReadOnlySpan span)
{
int start = 0;
for (; start < span.Length; start++)
{
if (!char.IsWhiteSpace(span[start]))
break;
}
return span.Slice(start);
}
///
/// Removes all trailing white-space characters from the span.
///
public static ReadOnlySpan TrimEnd(this ReadOnlySpan span)
{
int end = span.Length - 1;
for (; end >= 0; end--)
{
if (!char.IsWhiteSpace(span[end]))
break;
}
return span.Slice(0, end + 1);
}
///
/// Removes all leading and trailing occurrences of a specified character.
///
/// The source span from which the character is removed.
/// The specified character to look for and remove.
public static ReadOnlySpan Trim(this ReadOnlySpan span, char trimChar)
{
int start = 0;
for (; start < span.Length; start++)
{
if (span[start] != trimChar)
break;
}
int end = span.Length - 1;
for (; end >= start; end--)
{
if (span[end] != trimChar)
break;
}
return span.Slice(start, end - start + 1);
}
///
/// Removes all leading occurrences of a specified character.
///
/// The source span from which the character is removed.
/// The specified character to look for and remove.
public static ReadOnlySpan TrimStart(this ReadOnlySpan span, char trimChar)
{
int start = 0;
for (; start < span.Length; start++)
{
if (span[start] != trimChar)
break;
}
return span.Slice(start);
}
///
/// Removes all trailing occurrences of a specified character.
///
/// The source span from which the character is removed.
/// The specified character to look for and remove.
public static ReadOnlySpan TrimEnd(this ReadOnlySpan span, char trimChar)
{
int end = span.Length - 1;
for (; end >= 0; end--)
{
if (span[end] != trimChar)
break;
}
return span.Slice(0, end + 1);
}
///
/// Removes all leading and trailing occurrences of a set of characters specified
/// in a readonly span from the span.
///
/// The source span from which the characters are removed.
/// The span which contains the set of characters to remove.
/// If is empty, white-space characters are removed instead.
public static ReadOnlySpan Trim(this ReadOnlySpan span, ReadOnlySpan trimChars)
{
return span.TrimStart(trimChars).TrimEnd(trimChars);
}
///
/// Removes all leading occurrences of a set of characters specified
/// in a readonly span from the span.
///
/// The source span from which the characters are removed.
/// The span which contains the set of characters to remove.
/// If is empty, white-space characters are removed instead.
public static ReadOnlySpan TrimStart(this ReadOnlySpan span, ReadOnlySpan trimChars)
{
if (trimChars.IsEmpty)
{
return span.TrimStart();
}
int start = 0;
for (; start < span.Length; start++)
{
for (int i = 0; i < trimChars.Length; i++)
{
if (span[start] == trimChars[i])
goto Next;
}
break;
Next:
;
}
return span.Slice(start);
}
///
/// Removes all trailing occurrences of a set of characters specified
/// in a readonly span from the span.
///
/// The source span from which the characters are removed.
/// The span which contains the set of characters to remove.
/// If is empty, white-space characters are removed instead.
public static ReadOnlySpan TrimEnd(this ReadOnlySpan span, ReadOnlySpan trimChars)
{
if (trimChars.IsEmpty)
{
return span.TrimEnd();
}
int end = span.Length - 1;
for (; end >= 0; end--)
{
for (int i = 0; i < trimChars.Length; i++)
{
if (span[end] == trimChars[i])
goto Next;
}
break;
Next:
;
}
return span.Slice(0, end + 1);
}
///
/// Indicates whether the specified span contains only white-space characters.
///
public static bool IsWhiteSpace(this ReadOnlySpan span)
{
for (int i = 0; i < span.Length; i++)
{
if (!char.IsWhiteSpace(span[i]))
return false;
}
return true;
}
///
/// Searches for the specified value and returns true if found. If not found, returns false. Values are compared using IEquatable{T}.Equals(T).
///
///
/// The span to search.
/// The value to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Contains(this Span span, T value)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.Contains(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.Contains(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length);
}
///
/// Searches for the specified value and returns true if found. If not found, returns false. Values are compared using IEquatable{T}.Equals(T).
///
///
/// The span to search.
/// The value to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Contains(this ReadOnlySpan span, T value)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.Contains(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.Contains(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length);
}
///
/// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
///
/// The span to search.
/// The value to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf(this Span span, T value)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length);
}
///
/// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
///
/// The span to search.
/// The sequence to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf(this Span span, ReadOnlySpan value)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(value)),
value.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(value)),
value.Length);
return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
}
///
/// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
///
/// The span to search.
/// The value to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOf(this Span span, T value)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.LastIndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), value, span.Length);
}
///
/// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
///
/// The span to search.
/// The sequence to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOf(this Span span, ReadOnlySpan value)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(value)),
value.Length);
return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
}
///
/// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool SequenceEqual(this Span span, ReadOnlySpan other)
where T : IEquatable
{
int length = span.Length;
if (default(T) != null && IsTypeComparableAsBytes(out nuint size))
return length == other.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As(ref MemoryMarshal.GetReference(other)),
((nuint)length) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
return length == other.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length);
}
///
/// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T).
///
public static int SequenceCompareTo(this Span span, ReadOnlySpan other)
where T : IComparable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.SequenceCompareTo(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(other)),
other.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.SequenceCompareTo(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(other)),
other.Length);
return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length);
}
///
/// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
///
/// The span to search.
/// The value to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf(this ReadOnlySpan span, T value)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length);
}
///
/// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
///
/// The span to search.
/// The sequence to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(value)),
value.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(value)),
value.Length);
return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
}
///
/// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
///
/// The span to search.
/// The value to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOf(this ReadOnlySpan span, T value)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.LastIndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), value, span.Length);
}
///
/// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T).
///
/// The span to search.
/// The sequence to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOf(this ReadOnlySpan span, ReadOnlySpan value)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(value)),
value.Length);
return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
}
///
/// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
///
/// The span to search.
/// One of the values to search for.
/// One of the values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfAny(this Span span, T value0, T value1)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
span.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
span.Length);
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
}
///
/// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
///
/// The span to search.
/// One of the values to search for.
/// One of the values to search for.
/// One of the values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfAny(this Span span, T value0, T value1, T value2)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
Unsafe.As(ref value2),
span.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
Unsafe.As(ref value2),
span.Length);
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
}
///
/// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
///
/// The span to search.
/// The set of values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfAny(this Span span, ReadOnlySpan values)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
{
ref byte valueRef = ref Unsafe.As(ref MemoryMarshal.GetReference(values));
if (values.Length == 2)
{
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
span.Length);
}
else if (values.Length == 3)
{
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
Unsafe.Add(ref valueRef, 2),
span.Length);
}
else
{
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref valueRef,
values.Length);
}
}
if (typeof(T) == typeof(char))
{
ref var valueRef = ref Unsafe.As(ref MemoryMarshal.GetReference(values));
if (values.Length == 5)
{
// Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
Unsafe.Add(ref valueRef, 2),
Unsafe.Add(ref valueRef, 3),
Unsafe.Add(ref valueRef, 4),
span.Length);
}
else if (values.Length == 2)
{
// Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
span.Length);
}
else if (values.Length == 4)
{
// Length 4 before 3 as 3 has an explicit overload
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
Unsafe.Add(ref valueRef, 2),
Unsafe.Add(ref valueRef, 3),
span.Length);
}
else if (values.Length == 3)
{
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
Unsafe.Add(ref valueRef, 2),
span.Length);
}
else if (values.Length == 1)
{
// Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value
// is already throwing away a bunch of performance vs just calling IndexOf
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
span.Length);
}
}
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
}
///
/// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
///
/// The span to search.
/// One of the values to search for.
/// One of the values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfAny(this ReadOnlySpan span, T value0, T value1)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
span.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
span.Length);
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
}
///
/// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
///
/// The span to search.
/// One of the values to search for.
/// One of the values to search for.
/// One of the values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfAny(this ReadOnlySpan span, T value0, T value1, T value2)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
Unsafe.As(ref value2),
span.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
Unsafe.As(ref value2),
span.Length);
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
}
///
/// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1.
///
/// The span to search.
/// The set of values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfAny(this ReadOnlySpan span, ReadOnlySpan values)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
{
ref byte valueRef = ref Unsafe.As(ref MemoryMarshal.GetReference(values));
if (values.Length == 2)
{
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
span.Length);
}
else if (values.Length == 3)
{
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
Unsafe.Add(ref valueRef, 2),
span.Length);
}
else
{
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref valueRef,
values.Length);
}
}
if (typeof(T) == typeof(char))
{
ref var valueRef = ref Unsafe.As(ref MemoryMarshal.GetReference(values));
if (values.Length == 5)
{
// Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
Unsafe.Add(ref valueRef, 2),
Unsafe.Add(ref valueRef, 3),
Unsafe.Add(ref valueRef, 4),
span.Length);
}
else if (values.Length == 2)
{
// Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
span.Length);
}
else if (values.Length == 4)
{
// Length 4 before 3 as 3 has an explicit overload
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
Unsafe.Add(ref valueRef, 2),
Unsafe.Add(ref valueRef, 3),
span.Length);
}
else if (values.Length == 3)
{
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
Unsafe.Add(ref valueRef, 1),
Unsafe.Add(ref valueRef, 2),
span.Length);
}
else if (values.Length == 1)
{
// Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value
// is already throwing away a bunch of performance vs just calling IndexOf
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
valueRef,
span.Length);
}
}
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
}
///
/// 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.
///
/// The span to search.
/// One of the values to search for.
/// One of the values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny(this Span span, T value0, T value1)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
span.Length);
return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
}
///
/// 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.
///
/// The span to search.
/// One of the values to search for.
/// One of the values to search for.
/// One of the values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny(this Span span, T value0, T value1, T value2)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
Unsafe.As(ref value2),
span.Length);
return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
}
///
/// 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.
///
/// The span to search.
/// The set of values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny(this Span span, ReadOnlySpan values)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(values)),
values.Length);
return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
}
///
/// 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.
///
/// The span to search.
/// One of the values to search for.
/// One of the values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny(this ReadOnlySpan span, T value0, T value1)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
span.Length);
return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
}
///
/// 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.
///
/// The span to search.
/// One of the values to search for.
/// One of the values to search for.
/// One of the values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny(this ReadOnlySpan span, T value0, T value1, T value2)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
Unsafe.As(ref value2),
span.Length);
return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
}
///
/// 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.
///
/// The span to search.
/// The set of values to search for.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int LastIndexOfAny(this ReadOnlySpan span, ReadOnlySpan values)
where T : IEquatable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(values)),
values.Length);
return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length);
}
///
/// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T).
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool SequenceEqual(this ReadOnlySpan span, ReadOnlySpan other)
where T : IEquatable
{
int length = span.Length;
if (default(T) != null && IsTypeComparableAsBytes(out nuint size))
return length == other.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As(ref MemoryMarshal.GetReference(other)),
((nuint)length) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
return length == other.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length);
}
///
/// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T).
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int SequenceCompareTo(this ReadOnlySpan span, ReadOnlySpan other)
where T : IComparable
{
if (typeof(T) == typeof(byte))
return SpanHelpers.SequenceCompareTo(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(other)),
other.Length);
if (typeof(T) == typeof(char))
return SpanHelpers.SequenceCompareTo(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(other)),
other.Length);
return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length);
}
///
/// Determines whether the specified sequence appears at the start of the span.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool StartsWith(this Span span, ReadOnlySpan value)
where T : IEquatable
{
int valueLength = value.Length;
if (default(T) != null && IsTypeComparableAsBytes(out nuint size))
return valueLength <= span.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As(ref MemoryMarshal.GetReference(value)),
((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength);
}
///
/// Determines whether the specified sequence appears at the start of the span.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool StartsWith(this ReadOnlySpan span, ReadOnlySpan value)
where T : IEquatable
{
int valueLength = value.Length;
if (default(T) != null && IsTypeComparableAsBytes(out nuint size))
return valueLength <= span.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As(ref MemoryMarshal.GetReference(value)),
((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength);
}
///
/// Determines whether the specified sequence appears at the end of the span.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool EndsWith(this Span span, ReadOnlySpan value)
where T : IEquatable
{
int spanLength = span.Length;
int valueLength = value.Length;
if (default(T) != null && IsTypeComparableAsBytes(out nuint size))
return valueLength <= spanLength &&
SpanHelpers.SequenceEqual(
ref Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)),
ref Unsafe.As(ref MemoryMarshal.GetReference(value)),
((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
return valueLength <= spanLength &&
SpanHelpers.SequenceEqual(
ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength),
ref MemoryMarshal.GetReference(value),
valueLength);
}
///