// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using Internal.Runtime.CompilerServices; #if BIT64 using nuint = System.UInt64; #else using nuint = System.UInt32; #endif // BIT64 namespace System { /// /// Extension methods for Span{T}, Memory{T}, and friends. /// public static partial class MemoryExtensions { /// /// Removes all leading and trailing white-space characters from the span. /// public static ReadOnlySpan Trim(this ReadOnlySpan span) { int start = 0; for (; start < span.Length; start++) { if (!char.IsWhiteSpace(span[start])) break; } int end = span.Length - 1; for (; end >= start; end--) { if (!char.IsWhiteSpace(span[end])) break; } return span.Slice(start, end - start + 1); } /// /// Removes all leading white-space characters from the span. /// public static ReadOnlySpan TrimStart(this ReadOnlySpan span) { int start = 0; for (; start < span.Length; start++) { if (!char.IsWhiteSpace(span[start])) break; } return span.Slice(start); } /// /// Removes all trailing white-space characters from the span. /// public static ReadOnlySpan TrimEnd(this ReadOnlySpan span) { int end = span.Length - 1; for (; end >= 0; end--) { if (!char.IsWhiteSpace(span[end])) break; } return span.Slice(0, end + 1); } /// /// Removes all leading and trailing occurrences of a specified character. /// /// The source span from which the character is removed. /// The specified character to look for and remove. public static ReadOnlySpan Trim(this ReadOnlySpan span, char trimChar) { int start = 0; for (; start < span.Length; start++) { if (span[start] != trimChar) break; } int end = span.Length - 1; for (; end >= start; end--) { if (span[end] != trimChar) break; } return span.Slice(start, end - start + 1); } /// /// Removes all leading occurrences of a specified character. /// /// The source span from which the character is removed. /// The specified character to look for and remove. public static ReadOnlySpan TrimStart(this ReadOnlySpan span, char trimChar) { int start = 0; for (; start < span.Length; start++) { if (span[start] != trimChar) break; } return span.Slice(start); } /// /// Removes all trailing occurrences of a specified character. /// /// The source span from which the character is removed. /// The specified character to look for and remove. public static ReadOnlySpan TrimEnd(this ReadOnlySpan span, char trimChar) { int end = span.Length - 1; for (; end >= 0; end--) { if (span[end] != trimChar) break; } return span.Slice(0, end + 1); } /// /// Removes all leading and trailing occurrences of a set of characters specified /// in a readonly span from the span. /// /// The source span from which the characters are removed. /// The span which contains the set of characters to remove. /// If is empty, white-space characters are removed instead. public static ReadOnlySpan Trim(this ReadOnlySpan span, ReadOnlySpan trimChars) { return span.TrimStart(trimChars).TrimEnd(trimChars); } /// /// Removes all leading occurrences of a set of characters specified /// in a readonly span from the span. /// /// The source span from which the characters are removed. /// The span which contains the set of characters to remove. /// If is empty, white-space characters are removed instead. public static ReadOnlySpan TrimStart(this ReadOnlySpan span, ReadOnlySpan trimChars) { if (trimChars.IsEmpty) { return span.TrimStart(); } int start = 0; for (; start < span.Length; start++) { for (int i = 0; i < trimChars.Length; i++) { if (span[start] == trimChars[i]) goto Next; } break; Next: ; } return span.Slice(start); } /// /// Removes all trailing occurrences of a set of characters specified /// in a readonly span from the span. /// /// The source span from which the characters are removed. /// The span which contains the set of characters to remove. /// If is empty, white-space characters are removed instead. public static ReadOnlySpan TrimEnd(this ReadOnlySpan span, ReadOnlySpan trimChars) { if (trimChars.IsEmpty) { return span.TrimEnd(); } int end = span.Length - 1; for (; end >= 0; end--) { for (int i = 0; i < trimChars.Length; i++) { if (span[end] == trimChars[i]) goto Next; } break; Next: ; } return span.Slice(0, end + 1); } /// /// Indicates whether the specified span contains only white-space characters. /// public static bool IsWhiteSpace(this ReadOnlySpan span) { for (int i = 0; i < span.Length; i++) { if (!char.IsWhiteSpace(span[i])) return false; } return true; } /// /// Searches for the specified value and returns true if found. If not found, returns false. Values are compared using IEquatable{T}.Equals(T). /// /// /// The span to search. /// The value to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Contains(this Span span, T value) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.Contains( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); if (typeof(T) == typeof(char)) return SpanHelpers.Contains( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length); } /// /// Searches for the specified value and returns true if found. If not found, returns false. Values are compared using IEquatable{T}.Equals(T). /// /// /// The span to search. /// The value to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Contains(this ReadOnlySpan span, T value) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.Contains( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); if (typeof(T) == typeof(char)) return SpanHelpers.Contains( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length); } /// /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). /// /// The span to search. /// The value to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOf(this Span span, T value) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.IndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); if (typeof(T) == typeof(char)) return SpanHelpers.IndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } /// /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). /// /// The span to search. /// The sequence to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOf(this Span span, ReadOnlySpan value) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.IndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); if (typeof(T) == typeof(char)) return SpanHelpers.IndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } /// /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). /// /// The span to search. /// The value to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOf(this Span span, T value) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.LastIndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); if (typeof(T) == typeof(char)) return SpanHelpers.LastIndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } /// /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). /// /// The span to search. /// The sequence to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOf(this Span span, ReadOnlySpan value) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.LastIndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } /// /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool SequenceEqual(this Span span, ReadOnlySpan other) where T : IEquatable { int length = span.Length; if (default(T) != null && IsTypeComparableAsBytes(out nuint size)) return length == other.Length && SpanHelpers.SequenceEqual( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref Unsafe.As(ref MemoryMarshal.GetReference(other)), ((nuint)length) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking. return length == other.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length); } /// /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T). /// public static int SequenceCompareTo(this Span span, ReadOnlySpan other) where T : IComparable { if (typeof(T) == typeof(byte)) return SpanHelpers.SequenceCompareTo( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(other)), other.Length); if (typeof(T) == typeof(char)) return SpanHelpers.SequenceCompareTo( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(other)), other.Length); return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length); } /// /// Searches for the specified value and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). /// /// The span to search. /// The value to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOf(this ReadOnlySpan span, T value) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.IndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); if (typeof(T) == typeof(char)) return SpanHelpers.IndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } /// /// Searches for the specified sequence and returns the index of its first occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). /// /// The span to search. /// The sequence to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOf(this ReadOnlySpan span, ReadOnlySpan value) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.IndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); if (typeof(T) == typeof(char)) return SpanHelpers.IndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } /// /// Searches for the specified value and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). /// /// The span to search. /// The value to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOf(this ReadOnlySpan span, T value) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.LastIndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); if (typeof(T) == typeof(char)) return SpanHelpers.LastIndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value), span.Length); return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } /// /// Searches for the specified sequence and returns the index of its last occurrence. If not found, returns -1. Values are compared using IEquatable{T}.Equals(T). /// /// The span to search. /// The sequence to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOf(this ReadOnlySpan span, ReadOnlySpan value) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.LastIndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(value)), value.Length); return SpanHelpers.LastIndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } /// /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// One of the values to search for. /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this Span span, T value0, T value1) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), span.Length); if (typeof(T) == typeof(char)) return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), span.Length); return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); } /// /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// One of the values to search for. /// One of the values to search for. /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this Span span, T value0, T value1, T value2) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), Unsafe.As(ref value2), span.Length); if (typeof(T) == typeof(char)) return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), Unsafe.As(ref value2), span.Length); return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); } /// /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// The set of values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this Span span, ReadOnlySpan values) where T : IEquatable { if (typeof(T) == typeof(byte)) { ref byte valueRef = ref Unsafe.As(ref MemoryMarshal.GetReference(values)); if (values.Length == 2) { return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), span.Length); } else if (values.Length == 3) { return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), Unsafe.Add(ref valueRef, 2), span.Length); } else { return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref valueRef, values.Length); } } if (typeof(T) == typeof(char)) { ref var valueRef = ref Unsafe.As(ref MemoryMarshal.GetReference(values)); if (values.Length == 5) { // Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), Unsafe.Add(ref valueRef, 2), Unsafe.Add(ref valueRef, 3), Unsafe.Add(ref valueRef, 4), span.Length); } else if (values.Length == 2) { // Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), span.Length); } else if (values.Length == 4) { // Length 4 before 3 as 3 has an explicit overload return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), Unsafe.Add(ref valueRef, 2), Unsafe.Add(ref valueRef, 3), span.Length); } else if (values.Length == 3) { return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), Unsafe.Add(ref valueRef, 2), span.Length); } else if (values.Length == 1) { // Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value // is already throwing away a bunch of performance vs just calling IndexOf return SpanHelpers.IndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, span.Length); } } return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); } /// /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// One of the values to search for. /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this ReadOnlySpan span, T value0, T value1) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), span.Length); if (typeof(T) == typeof(char)) return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), span.Length); return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); } /// /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// One of the values to search for. /// One of the values to search for. /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this ReadOnlySpan span, T value0, T value1, T value2) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), Unsafe.As(ref value2), span.Length); if (typeof(T) == typeof(char)) return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), Unsafe.As(ref value2), span.Length); return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); } /// /// Searches for the first index of any of the specified values similar to calling IndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// The set of values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int IndexOfAny(this ReadOnlySpan span, ReadOnlySpan values) where T : IEquatable { if (typeof(T) == typeof(byte)) { ref byte valueRef = ref Unsafe.As(ref MemoryMarshal.GetReference(values)); if (values.Length == 2) { return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), span.Length); } else if (values.Length == 3) { return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), Unsafe.Add(ref valueRef, 2), span.Length); } else { return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref valueRef, values.Length); } } if (typeof(T) == typeof(char)) { ref var valueRef = ref Unsafe.As(ref MemoryMarshal.GetReference(values)); if (values.Length == 5) { // Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), Unsafe.Add(ref valueRef, 2), Unsafe.Add(ref valueRef, 3), Unsafe.Add(ref valueRef, 4), span.Length); } else if (values.Length == 2) { // Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), span.Length); } else if (values.Length == 4) { // Length 4 before 3 as 3 has an explicit overload return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), Unsafe.Add(ref valueRef, 2), Unsafe.Add(ref valueRef, 3), span.Length); } else if (values.Length == 3) { return SpanHelpers.IndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, Unsafe.Add(ref valueRef, 1), Unsafe.Add(ref valueRef, 2), span.Length); } else if (values.Length == 1) { // Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value // is already throwing away a bunch of performance vs just calling IndexOf return SpanHelpers.IndexOf( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), valueRef, span.Length); } } return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); } /// /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// One of the values to search for. /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this Span span, T value0, T value1) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), span.Length); return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); } /// /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// One of the values to search for. /// One of the values to search for. /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this Span span, T value0, T value1, T value2) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), Unsafe.As(ref value2), span.Length); return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); } /// /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// The set of values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this Span span, ReadOnlySpan values) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(values)), values.Length); return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); } /// /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// One of the values to search for. /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this ReadOnlySpan span, T value0, T value1) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), span.Length); return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); } /// /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// One of the values to search for. /// One of the values to search for. /// One of the values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this ReadOnlySpan span, T value0, T value1, T value2) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), Unsafe.As(ref value0), Unsafe.As(ref value1), Unsafe.As(ref value2), span.Length); return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); } /// /// Searches for the last index of any of the specified values similar to calling LastIndexOf several times with the logical OR operator. If not found, returns -1. /// /// The span to search. /// The set of values to search for. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LastIndexOfAny(this ReadOnlySpan span, ReadOnlySpan values) where T : IEquatable { if (typeof(T) == typeof(byte)) return SpanHelpers.LastIndexOfAny( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(values)), values.Length); return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); } /// /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool SequenceEqual(this ReadOnlySpan span, ReadOnlySpan other) where T : IEquatable { int length = span.Length; if (default(T) != null && IsTypeComparableAsBytes(out nuint size)) return length == other.Length && SpanHelpers.SequenceEqual( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref Unsafe.As(ref MemoryMarshal.GetReference(other)), ((nuint)length) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking. return length == other.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length); } /// /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SequenceCompareTo(this ReadOnlySpan span, ReadOnlySpan other) where T : IComparable { if (typeof(T) == typeof(byte)) return SpanHelpers.SequenceCompareTo( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(other)), other.Length); if (typeof(T) == typeof(char)) return SpanHelpers.SequenceCompareTo( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref Unsafe.As(ref MemoryMarshal.GetReference(other)), other.Length); return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length); } /// /// Determines whether the specified sequence appears at the start of the span. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool StartsWith(this Span span, ReadOnlySpan value) where T : IEquatable { int valueLength = value.Length; if (default(T) != null && IsTypeComparableAsBytes(out nuint size)) return valueLength <= span.Length && SpanHelpers.SequenceEqual( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref Unsafe.As(ref MemoryMarshal.GetReference(value)), ((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking. return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength); } /// /// Determines whether the specified sequence appears at the start of the span. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool StartsWith(this ReadOnlySpan span, ReadOnlySpan value) where T : IEquatable { int valueLength = value.Length; if (default(T) != null && IsTypeComparableAsBytes(out nuint size)) return valueLength <= span.Length && SpanHelpers.SequenceEqual( ref Unsafe.As(ref MemoryMarshal.GetReference(span)), ref Unsafe.As(ref MemoryMarshal.GetReference(value)), ((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking. return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength); } /// /// Determines whether the specified sequence appears at the end of the span. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EndsWith(this Span span, ReadOnlySpan value) where T : IEquatable { int spanLength = span.Length; int valueLength = value.Length; if (default(T) != null && IsTypeComparableAsBytes(out nuint size)) return valueLength <= spanLength && SpanHelpers.SequenceEqual( ref Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)), ref Unsafe.As(ref MemoryMarshal.GetReference(value)), ((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking. return valueLength <= spanLength && SpanHelpers.SequenceEqual( ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength), ref MemoryMarshal.GetReference(value), valueLength); } /// /// Determines whether the specified sequence appears at the end of the span. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EndsWith(this ReadOnlySpan span, ReadOnlySpan value) where T : IEquatable { int spanLength = span.Length; int valueLength = value.Length; if (default(T) != null && IsTypeComparableAsBytes(out nuint size)) return valueLength <= spanLength && SpanHelpers.SequenceEqual( ref Unsafe.As(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)), ref Unsafe.As(ref MemoryMarshal.GetReference(value)), ((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking. return valueLength <= spanLength && SpanHelpers.SequenceEqual( ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength), ref MemoryMarshal.GetReference(value), valueLength); } /// /// Returns an enumeration of from the provided span. /// /// /// Invalid sequences will be represented in the enumeration by . /// public static SpanRuneEnumerator EnumerateRunes(this ReadOnlySpan span) { return new SpanRuneEnumerator(span); } /// /// Returns an enumeration of from the provided span. /// /// /// Invalid sequences will be represented in the enumeration by . /// public static SpanRuneEnumerator EnumerateRunes(this Span span) { return new SpanRuneEnumerator(span); } /// /// 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 '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 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 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. /// 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³². // // 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³² // [------------------) first sequence // xRef xRef + xLength // [--------------------------) . second sequence // yRef . yRef + yLength // : . . . // : . . . // . . . // . . . // . . . // [----------------------------------------------) relative address space // 0 . . 2³² // [------------------) : 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³²). 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³²) // // Inversely, they do overlap if // // (y1 < x2) || (y2 > 2³²) // // After substituting x2 and y2 with their respective definition: // // == (y1 < xLength) || (y1 + yLength > 2³²) // // Since yLength can't be greater than the size of the address space, // the overflow can be avoided as follows: // // == (y1 < xLength) || (y1 > 2³² - yLength) // // However, 2³² 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³² - 0 is 0 and not 2³². 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); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsTypeComparableAsBytes(out nuint size) { if (typeof(T) == typeof(byte) || typeof(T) == typeof(sbyte)) { size = sizeof(byte); return true; } if (typeof(T) == typeof(char) || typeof(T) == typeof(short) || typeof(T) == typeof(ushort)) { size = sizeof(char); return true; } if (typeof(T) == typeof(int) || typeof(T) == typeof(uint)) { size = sizeof(int); return true; } if (typeof(T) == typeof(long) || typeof(T) == typeof(ulong)) { size = sizeof(long); return true; } size = default; return false; } } }