// 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 Internal.Runtime.CompilerServices;
#pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
#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
{
///
/// Creates a new span over the portion of the target array.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span AsSpan(this T[]? array, int start)
{
if (array == null)
{
if (start != 0)
ThrowHelper.ThrowArgumentOutOfRangeException();
return default;
}
if (default(T)! == null && array.GetType() != typeof(T[])) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
ThrowHelper.ThrowArrayTypeMismatchException();
if ((uint)start > (uint)array.Length)
ThrowHelper.ThrowArgumentOutOfRangeException();
return new Span(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start), array.Length - start);
}
///
/// Creates a new span over the portion of the target array.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span AsSpan(this T[]? array, Index startIndex)
{
if (array == null)
{
if (!startIndex.Equals(Index.Start))
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
return default;
}
if (default(T)! == null && array.GetType() != typeof(T[])) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
ThrowHelper.ThrowArrayTypeMismatchException();
int actualIndex = startIndex.GetOffset(array.Length);
if ((uint)actualIndex > (uint)array.Length)
ThrowHelper.ThrowArgumentOutOfRangeException();
return new Span(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), actualIndex), array.Length - actualIndex);
}
///
/// Creates a new span over the portion of the target array.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span AsSpan(this T[]? array, Range range)
{
if (array == null)
{
Index startIndex = range.Start;
Index endIndex = range.End;
if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
return default;
}
if (default(T)! == null && array.GetType() != typeof(T[])) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
ThrowHelper.ThrowArrayTypeMismatchException();
(int start, int length) = range.GetOffsetAndLength(array.Length);
return new Span(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start), length);
}
///
/// Creates a new readonly span over the portion of the target string.
///
/// The target string.
/// Returns default when is null.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan AsSpan(this string? text)
{
if (text == null)
return default;
return new ReadOnlySpan(ref text.GetRawStringData(), text.Length);
}
///
/// Creates a new readonly span over the portion of the target string.
///
/// The target string.
/// The index at which to begin this slice.
/// Thrown when is null.
///
/// Thrown when the specified index is not in range (<0 or >text.Length).
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan AsSpan(this string? text, int start)
{
if (text == null)
{
if (start != 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
return default;
}
if ((uint)start > (uint)text.Length)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
return new ReadOnlySpan(ref Unsafe.Add(ref text.GetRawStringData(), start), text.Length - start);
}
///
/// Creates a new readonly span over the portion of the target string.
///
/// The target string.
/// The index at which to begin this slice.
/// The desired length for the slice (exclusive).
/// Returns default when is null.
///
/// Thrown when the specified index or is not in range.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan AsSpan(this string? text, int start, int length)
{
if (text == null)
{
if (start != 0 || length != 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
return default;
}
#if BIT64
// See comment in Span.Slice for how this works.
if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
#else
if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
#endif
return new ReadOnlySpan(ref Unsafe.Add(ref text.GetRawStringData(), start), length);
}
/// Creates a new over the portion of the target string.
/// The target string.
/// Returns default when is null.
public static ReadOnlyMemory AsMemory(this string? text)
{
if (text == null)
return default;
return new ReadOnlyMemory(text, 0, text.Length);
}
/// Creates a new over the portion of the target string.
/// The target string.
/// The index at which to begin this slice.
/// Returns default when is null.
///
/// Thrown when the specified index is not in range (<0 or >text.Length).
///
public static ReadOnlyMemory AsMemory(this string? text, int start)
{
if (text == null)
{
if (start != 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
return default;
}
if ((uint)start > (uint)text.Length)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
return new ReadOnlyMemory(text, start, text.Length - start);
}
/// Creates a new over the portion of the target string.
/// The target string.
/// The index at which to begin this slice.
public static ReadOnlyMemory AsMemory(this string? text, Index startIndex)
{
if (text == null)
{
if (!startIndex.Equals(Index.Start))
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
return default;
}
int actualIndex = startIndex.GetOffset(text.Length);
if ((uint)actualIndex > (uint)text.Length)
ThrowHelper.ThrowArgumentOutOfRangeException();
return new ReadOnlyMemory(text, actualIndex, text.Length - actualIndex);
}
/// Creates a new over the portion of the target string.
/// The target string.
/// The index at which to begin this slice.
/// The desired length for the slice (exclusive).
/// Returns default when is null.
///
/// Thrown when the specified index or is not in range.
///
public static ReadOnlyMemory AsMemory(this string? text, int start, int length)
{
if (text == null)
{
if (start != 0 || length != 0)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
return default;
}
#if BIT64
// See comment in Span.Slice for how this works.
if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
#else
if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
#endif
return new ReadOnlyMemory(text, start, length);
}
/// Creates a new over the portion of the target string.
/// The target string.
/// The range used to indicate the start and length of the sliced string.
public static ReadOnlyMemory AsMemory(this string? text, Range range)
{
if (text == null)
{
Index startIndex = range.Start;
Index endIndex = range.End;
if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
return default;
}
(int start, int length) = range.GetOffsetAndLength(text.Length);
return new ReadOnlyMemory(text, start, 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 Span span, T value)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(byte))
return SpanHelpers.Contains(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(byte))
return SpanHelpers.Contains(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(byte))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(byte))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(value)),
value.Length);
if (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(byte))
return SpanHelpers.LastIndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (Unsafe.SizeOf() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable())
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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
int length = span.Length;
if (RuntimeHelpers.IsBitwiseEquatable())
{
nuint size = (nuint)Unsafe.SizeOf();
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
{
// Can't use IsBitwiseEquatable() below because that only tells us about
// equality checks, not about CompareTo checks.
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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(byte))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(byte))
return SpanHelpers.IndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
span.Length,
ref Unsafe.As(ref MemoryMarshal.GetReference(value)),
value.Length);
if (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(byte))
return SpanHelpers.LastIndexOf(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value),
span.Length);
if (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (Unsafe.SizeOf() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable())
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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(byte))
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
span.Length);
if (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(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 (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(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 (Unsafe.SizeOf() == sizeof(char))
{
ref char 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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(byte))
return SpanHelpers.IndexOfAny(
ref Unsafe.As(ref MemoryMarshal.GetReference(span)),
Unsafe.As(ref value0),
Unsafe.As(ref value1),
span.Length);
if (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(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 (Unsafe.SizeOf() == sizeof(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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant
where T : IEquatable
#nullable restore
{
if (RuntimeHelpers.IsBitwiseEquatable())
{
if (Unsafe.SizeOf() == sizeof(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 (Unsafe.SizeOf() == sizeof(char))
{
ref char 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)
#nullable disable // to enable use with both T and T? for reference types due to IEquatable