// 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 being invariant where T : IEquatable #nullable restore { if (Unsafe.SizeOf() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable()) 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) #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.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) #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.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) #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.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) #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.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) #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.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) #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). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SequenceCompareTo(this ReadOnlySpan 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); } /// /// Determines whether the specified sequence appears at the start of the span. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool StartsWith(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 { int valueLength = value.Length; if (RuntimeHelpers.IsBitwiseEquatable()) { nuint size = (nuint)Unsafe.SizeOf(); 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) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { int valueLength = value.Length; if (RuntimeHelpers.IsBitwiseEquatable()) { nuint size = (nuint)Unsafe.SizeOf(); 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) #nullable disable // to enable use with both T and T? for reference types due to IEquatable being invariant where T : IEquatable #nullable restore { int spanLength = span.Length; int valueLength = value.Length; if (RuntimeHelpers.IsBitwiseEquatable()) { nuint size = (nuint)Unsafe.SizeOf(); 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); } /// /// Determines whether the specified sequence appears at the end of the span. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EndsWith(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 { int spanLength = span.Length; int valueLength = value.Length; if (RuntimeHelpers.IsBitwiseEquatable()) { nuint size = (nuint)Unsafe.SizeOf(); 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); } /// /// Reverses the sequence of the elements in the entire span. /// public static void Reverse(this Span span) { if (span.Length <= 1) { return; } ref T first = ref MemoryMarshal.GetReference(span); ref T last = ref Unsafe.Add(ref Unsafe.Add(ref first, span.Length), -1); do { T temp = first; first = last; last = temp; first = ref Unsafe.Add(ref first, 1); last = ref Unsafe.Add(ref last, -1); } while (Unsafe.IsAddressLessThan(ref first, ref last)); } /// /// Creates a new span over the target array. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this T[]? array) { return new Span(array); } /// /// Creates a new Span over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). /// /// The target array. /// The index at which to begin the Span. /// The number of items in the Span. /// Returns default when is null. /// Thrown when is covariant and array's type is not exactly T[]. /// /// Thrown when the specified or end index is not in the range (<0 or >Length). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this T[]? array, int start, int length) { return new Span(array, start, length); } /// /// Creates a new span over the portion of the target array segment. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this ArraySegment segment) { return new Span(segment.Array, segment.Offset, segment.Count); } /// /// Creates a new Span over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). /// /// The target array. /// The index at which to begin the Span. /// Returns default when is null. /// Thrown when is covariant and array's type is not exactly T[]. /// /// Thrown when the specified or end index is not in the range (<0 or >segment.Count). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this ArraySegment segment, int start) { if (((uint)start) > (uint)segment.Count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); return new Span(segment.Array, segment.Offset + start, segment.Count - start); } /// /// Creates a new Span over the portion of the target array beginning /// at 'startIndex' and ending at the end of the segment. /// /// The target array. /// The index at which to begin the Span. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this ArraySegment segment, Index startIndex) { int actualIndex = startIndex.GetOffset(segment.Count); return AsSpan(segment, actualIndex); } /// /// Creates a new Span over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). /// /// The target array. /// The index at which to begin the Span. /// The number of items in the Span. /// Returns default when is null. /// Thrown when is covariant and array's type is not exactly T[]. /// /// Thrown when the specified or end index is not in the range (<0 or >segment.Count). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this ArraySegment segment, int start, int length) { if (((uint)start) > (uint)segment.Count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); if (((uint)length) > (uint)(segment.Count - start)) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); return new Span(segment.Array, segment.Offset + start, length); } /// /// Creates a new Span over the portion of the target array using the range start and end indexes /// /// The target array. /// The range which has start and end indexes to use for slicing the array. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsSpan(this ArraySegment segment, Range range) { (int start, int length) = range.GetOffsetAndLength(segment.Count); return new Span(segment.Array, segment.Offset + start, length); } /// /// Creates a new memory over the target array. /// public static Memory AsMemory(this T[]? array) => new Memory(array); /// /// Creates a new memory over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). /// /// The target array. /// The index at which to begin the memory. /// Returns default when is null. /// Thrown when is covariant and array's type is not exactly T[]. /// /// Thrown when the specified or end index is not in the range (<0 or >array.Length). /// public static Memory AsMemory(this T[]? array, int start) => new Memory(array, start); /// /// Creates a new memory over the portion of the target array starting from /// 'startIndex' to the end of the array. /// public static Memory AsMemory(this T[]? array, Index startIndex) { if (array == null) { if (!startIndex.Equals(Index.Start)) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); return default; } int actualIndex = startIndex.GetOffset(array.Length); return new Memory(array, actualIndex); } /// /// Creates a new memory over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). /// /// The target array. /// The index at which to begin the memory. /// The number of items in the memory. /// Returns default when is null. /// Thrown when is covariant and array's type is not exactly T[]. /// /// Thrown when the specified or end index is not in the range (<0 or >Length). /// public static Memory AsMemory(this T[]? array, int start, int length) => new Memory(array, start, length); /// /// Creates a new memory over the portion of the target array beginning at inclusive start index of the range /// and ending at the exclusive end index of the range. /// public static Memory AsMemory(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; } (int start, int length) = range.GetOffsetAndLength(array.Length); return new Memory(array, start, length); } /// /// Creates a new memory over the portion of the target array. /// public static Memory AsMemory(this ArraySegment segment) => new Memory(segment.Array, segment.Offset, segment.Count); /// /// Creates a new memory over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). /// /// The target array. /// The index at which to begin the memory. /// Returns default when is null. /// Thrown when is covariant and array's type is not exactly T[]. /// /// Thrown when the specified or end index is not in the range (<0 or >segment.Count). /// public static Memory AsMemory(this ArraySegment segment, int start) { if (((uint)start) > (uint)segment.Count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); return new Memory(segment.Array, segment.Offset + start, segment.Count - start); } /// /// Creates a new memory over the portion of the target array beginning /// at 'start' index and ending at 'end' index (exclusive). /// /// The target array. /// The index at which to begin the memory. /// The number of items in the memory. /// Returns default when is null. /// Thrown when is covariant and array's type is not exactly T[]. /// /// Thrown when the specified or end index is not in the range (<0 or >segment.Count). /// public static Memory AsMemory(this ArraySegment segment, int start, int length) { if (((uint)start) > (uint)segment.Count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); if (((uint)length) > (uint)(segment.Count - start)) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); return new Memory(segment.Array, segment.Offset + start, length); } /// /// Copies the contents of the array into the span. If the source /// and destinations overlap, this method behaves as if the original values in /// a temporary location before the destination is overwritten. /// ///The array to copy items from. /// The span to copy items into. /// /// Thrown when the destination Span is shorter than the source array. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CopyTo(this T[]? source, Span destination) { new ReadOnlySpan(source).CopyTo(destination); } /// /// Copies the contents of the array into the memory. If the source /// and destinations overlap, this method behaves as if the original values are in /// a temporary location before the destination is overwritten. /// ///The array to copy items from. /// The memory to copy items into. /// /// Thrown when the destination is shorter than the source array. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CopyTo(this T[]? source, Memory destination) { source.CopyTo(destination.Span); } // // Overlaps // ======== // // The following methods can be used to determine if two sequences // overlap in memory. // // Two sequences overlap if they have positions in common and neither // is empty. Empty sequences do not overlap with any other sequence. // // If two sequences overlap, the element offset is the number of // elements by which the second sequence is offset from the first // sequence (i.e., second minus first). An exception is thrown if the // number is not a whole number, which can happen when a sequence of a // smaller type is cast to a sequence of a larger type with unsafe code // or NonPortableCast. If the sequences do not overlap, the offset is // meaningless and arbitrarily set to zero. // // Implementation // -------------- // // Implementing this correctly is quite tricky due of two problems: // // * If the sequences refer to two different objects on the managed // heap, the garbage collector can move them freely around or change // their relative order in memory. // // * The distance between two sequences can be greater than // int.MaxValue (on a 32-bit system) or long.MaxValue (on a 64-bit // system). // // (For simplicity, the following text assumes a 32-bit system, but // everything also applies to a 64-bit system if every 32 is replaced a // 64.) // // The first problem is solved by calculating the distance with exactly // one atomic operation. If the garbage collector happens to move the // sequences afterwards and the sequences overlapped before, they will // still overlap after the move and their distance hasn't changed. If // the sequences did not overlap, the distance can change but the // sequences still won't overlap. // // The second problem is solved by making all addresses relative to the // start of the first sequence and performing all operations in // unsigned integer arithmetic modulo 2^32. // // Example // ------- // // Let's say there are two sequences, x and y. Let // // ref T xRef = MemoryMarshal.GetReference(x) // uint xLength = x.Length * Unsafe.SizeOf() // ref T yRef = MemoryMarshal.GetReference(y) // uint yLength = y.Length * Unsafe.SizeOf() // // Visually, the two sequences are located somewhere in the 32-bit // address space as follows: // // [----------------------------------------------) normal address space // 0 2^32 // [------------------) first sequence // xRef xRef + xLength // [--------------------------) . second sequence // yRef . yRef + yLength // : . . . // : . . . // . . . // . . . // . . . // [----------------------------------------------) relative address space // 0 . . 2^32 // [------------------) : first sequence // x1 . x2 : // -------------) [------------- second sequence // y2 y1 // // The idea is to make all addresses relative to xRef: Let x1 be the // start address of x in this relative address space, x2 the end // address of x, y1 the start address of y, and y2 the end address of // y: // // nuint x1 = 0 // nuint x2 = xLength // nuint y1 = (nuint)Unsafe.ByteOffset(xRef, yRef) // nuint y2 = y1 + yLength // // xRef relative to xRef is 0. // // x2 is simply x1 + xLength. This cannot overflow. // // yRef relative to xRef is (yRef - xRef). If (yRef - xRef) is // negative, casting it to an unsigned 32-bit integer turns it into // (yRef - xRef + 2^32). So, in the example above, y1 moves to the right // of x2. // // y2 is simply y1 + yLength. Note that this can overflow, as in the // example above, which must be avoided. // // The two sequences do *not* overlap if y is entirely in the space // right of x in the relative address space. (It can't be left of it!) // // (y1 >= x2) && (y2 <= 2^32) // // Inversely, they do overlap if // // (y1 < x2) || (y2 > 2^32) // // After substituting x2 and y2 with their respective definition: // // == (y1 < xLength) || (y1 + yLength > 2^32) // // Since yLength can't be greater than the size of the address space, // the overflow can be avoided as follows: // // == (y1 < xLength) || (y1 > 2^32 - yLength) // // However, 2^32 cannot be stored in an unsigned 32-bit integer, so one // more change is needed to keep doing everything with unsigned 32-bit // integers: // // == (y1 < xLength) || (y1 > -yLength) // // Due to modulo arithmetic, this gives exactly same result *except* if // yLength is zero, since 2^32 - 0 is 0 and not 2^32. So the case // y.IsEmpty must be handled separately first. // /// /// Determines whether two sequences overlap in memory. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Overlaps(this Span span, ReadOnlySpan other) { return Overlaps((ReadOnlySpan)span, other); } /// /// Determines whether two sequences overlap in memory and outputs the element offset. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Overlaps(this Span span, ReadOnlySpan other, out int elementOffset) { return Overlaps((ReadOnlySpan)span, other, out elementOffset); } /// /// Determines whether two sequences overlap in memory. /// public static bool Overlaps(this ReadOnlySpan span, ReadOnlySpan other) { if (span.IsEmpty || other.IsEmpty) { return false; } IntPtr byteOffset = Unsafe.ByteOffset( ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other)); if (Unsafe.SizeOf() == sizeof(int)) { return (uint)byteOffset < (uint)(span.Length * Unsafe.SizeOf()) || (uint)byteOffset > (uint)-(other.Length * Unsafe.SizeOf()); } else { return (ulong)byteOffset < (ulong)((long)span.Length * Unsafe.SizeOf()) || (ulong)byteOffset > (ulong)-((long)other.Length * Unsafe.SizeOf()); } } /// /// Determines whether two sequences overlap in memory and outputs the element offset. /// public static bool Overlaps(this ReadOnlySpan span, ReadOnlySpan other, out int elementOffset) { if (span.IsEmpty || other.IsEmpty) { elementOffset = 0; return false; } IntPtr byteOffset = Unsafe.ByteOffset( ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other)); if (Unsafe.SizeOf() == sizeof(int)) { if ((uint)byteOffset < (uint)(span.Length * Unsafe.SizeOf()) || (uint)byteOffset > (uint)-(other.Length * Unsafe.SizeOf())) { if ((int)byteOffset % Unsafe.SizeOf() != 0) ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch(); elementOffset = (int)byteOffset / Unsafe.SizeOf(); return true; } else { elementOffset = 0; return false; } } else { if ((ulong)byteOffset < (ulong)((long)span.Length * Unsafe.SizeOf()) || (ulong)byteOffset > (ulong)-((long)other.Length * Unsafe.SizeOf())) { if ((long)byteOffset % Unsafe.SizeOf() != 0) ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch(); elementOffset = (int)((long)byteOffset / Unsafe.SizeOf()); return true; } else { elementOffset = 0; return false; } } } /// /// Searches an entire sorted for a value /// using the specified generic interface. /// /// The element type of the span. /// The sorted to search. /// The to use when comparing. /// /// The zero-based index of in the sorted , /// if is found; otherwise, a negative number that is the bitwise complement /// of the index of the next element that is larger than or, if there is /// no larger element, the bitwise complement of . /// /// /// is . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch( this Span span, IComparable comparable) { return BinarySearch>(span, comparable); } /// /// Searches an entire sorted for a value /// using the specified generic type. /// /// The element type of the span. /// The specific type of . /// The sorted to search. /// The to use when comparing. /// /// The zero-based index of in the sorted , /// if is found; otherwise, a negative number that is the bitwise complement /// of the index of the next element that is larger than or, if there is /// no larger element, the bitwise complement of . /// /// /// is . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch( this Span span, TComparable comparable) where TComparable : IComparable { return BinarySearch((ReadOnlySpan)span, comparable); } /// /// Searches an entire sorted for the specified /// using the specified generic type. /// /// The element type of the span. /// The specific type of . /// The sorted to search. /// The object to locate. The value can be null for reference types. /// The to use when comparing. /// /// /// The zero-based index of in the sorted , /// if is found; otherwise, a negative number that is the bitwise complement /// of the index of the next element that is larger than or, if there is /// no larger element, the bitwise complement of . /// /// /// is . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch( this Span span, T value, TComparer comparer) where TComparer : IComparer { return BinarySearch((ReadOnlySpan)span, value, comparer); } /// /// Searches an entire sorted for a value /// using the specified generic interface. /// /// The element type of the span. /// The sorted to search. /// The to use when comparing. /// /// The zero-based index of in the sorted , /// if is found; otherwise, a negative number that is the bitwise complement /// of the index of the next element that is larger than or, if there is /// no larger element, the bitwise complement of . /// /// /// is . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch( this ReadOnlySpan span, IComparable comparable) { return BinarySearch>(span, comparable); } /// /// Searches an entire sorted for a value /// using the specified generic type. /// /// The element type of the span. /// The specific type of . /// The sorted to search. /// The to use when comparing. /// /// The zero-based index of in the sorted , /// if is found; otherwise, a negative number that is the bitwise complement /// of the index of the next element that is larger than or, if there is /// no larger element, the bitwise complement of . /// /// /// is . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch( this ReadOnlySpan span, TComparable comparable) where TComparable : IComparable { return SpanHelpers.BinarySearch(span, comparable); } /// /// Searches an entire sorted for the specified /// using the specified generic type. /// /// The element type of the span. /// The specific type of . /// The sorted to search. /// The object to locate. The value can be null for reference types. /// The to use when comparing. /// /// /// The zero-based index of in the sorted , /// if is found; otherwise, a negative number that is the bitwise complement /// of the index of the next element that is larger than or, if there is /// no larger element, the bitwise complement of . /// /// /// is . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch( this ReadOnlySpan span, T value, TComparer comparer) where TComparer : IComparer { if (comparer == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer); var comparable = new SpanHelpers.ComparerComparable( value, comparer); return BinarySearch(span, comparable); } /// /// Sorts the elements in the entire using the implementation /// of each element of the /// /// The type of the elements of the span. /// The to sort. /// /// One or more elements in do not implement the interface. /// public static void Sort(this Span span) => Sort(span, (IComparer?)null); /// /// Sorts the elements in the entire using the . /// /// The type of the elements of the span. /// The type of the comparer to use to compare elements. /// The to sort. /// /// The implementation to use when comparing elements, or null to /// use the interface implementation of each element. /// /// /// is null, and one or more elements in do not /// implement the interface. /// /// /// The implementation of caused an error during the sort. /// public static void Sort(this Span span, TComparer comparer) where TComparer : IComparer? { if (span.Length > 1) { ArraySortHelper.Default.Sort(span, comparer); // value-type comparer will be boxed } } /// /// Sorts the elements in the entire using the specified . /// /// The type of the elements of the span. /// The to sort. /// The to use when comparing elements. /// is null. public static void Sort(this Span span, Comparison comparison) { if (comparison == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison); if (span.Length > 1) { ArraySortHelper.Sort(span, comparison); } } /// /// Sorts a pair of spans (one containing the keys and the other containing the corresponding items) /// based on the keys in the first using the /// implementation of each key. /// /// The type of the elements of the key span. /// The type of the elements of the items span. /// The span that contains the keys to sort. /// The span that contains the items that correspond to the keys in . /// /// The length of isn't equal to the length of . /// /// /// One or more elements in do not implement the interface. /// public static void Sort(this Span keys, Span items) => Sort(keys, items, (IComparer?)null); /// /// Sorts a pair of spans (one containing the keys and the other containing the corresponding items) /// based on the keys in the first using the specified comparer. /// /// The type of the elements of the key span. /// The type of the elements of the items span. /// The type of the comparer to use to compare elements. /// The span that contains the keys to sort. /// The span that contains the items that correspond to the keys in . /// /// The implementation to use when comparing elements, or null to /// use the interface implementation of each element. /// /// /// The length of isn't equal to the length of . /// /// /// is null, and one or more elements in do not /// implement the interface. /// public static void Sort(this Span keys, Span items, TComparer comparer) where TComparer : IComparer? { if (keys.Length != items.Length) ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_SpansMustHaveSameLength); if (keys.Length > 1) { ArraySortHelper.Default.Sort(keys, items, comparer); // value-type comparer will be boxed } } /// /// Sorts a pair of spans (one containing the keys and the other containing the corresponding items) /// based on the keys in the first using the specified comparison. /// /// The type of the elements of the key span. /// The type of the elements of the items span. /// The span that contains the keys to sort. /// The span that contains the items that correspond to the keys in . /// The to use when comparing elements. /// is null. /// /// The length of isn't equal to the length of . /// public static void Sort(this Span keys, Span items, Comparison comparison) { if (comparison == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison); if (keys.Length != items.Length) ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_SpansMustHaveSameLength); if (keys.Length > 1) { ArraySortHelper.Default.Sort(keys, items, new ComparisonComparer(comparison)); } } } }