diff options
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/MemoryExtensions.cs')
-rw-r--r-- | src/System.Private.CoreLib/shared/System/MemoryExtensions.cs | 479 |
1 files changed, 373 insertions, 106 deletions
diff --git a/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs b/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs index effdecf92..f0937c448 100644 --- a/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs +++ b/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs @@ -6,9 +6,13 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -#if !netstandard using Internal.Runtime.CompilerServices; -#endif + +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif // BIT64 namespace System { @@ -58,7 +62,6 @@ namespace System /// </summary> /// <param name="span">The source span from which the character is removed.</param> /// <param name="trimChar">The specified character to look for and remove.</param> - //[MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, char trimChar) { return span.TrimStart(trimChar).TrimEnd(trimChar); @@ -69,7 +72,6 @@ namespace System /// </summary> /// <param name="span">The source span from which the character is removed.</param> /// <param name="trimChar">The specified character to look for and remove.</param> - //[MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, char trimChar) { int start = 0; @@ -103,6 +105,7 @@ namespace System /// </summary> /// <param name="span">The source span from which the characters are removed.</param> /// <param name="trimChars">The span which contains the set of characters to remove.</param> + /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks> public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars) { return span.TrimStart(trimChars).TrimEnd(trimChars); @@ -114,8 +117,14 @@ namespace System /// </summary> /// <param name="span">The source span from which the characters are removed.</param> /// <param name="trimChars">The span which contains the set of characters to remove.</param> + /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks> public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars) { + if (trimChars.IsEmpty) + { + return span.TrimStart(); + } + int start = 0; for (; start < span.Length; start++) { @@ -137,8 +146,14 @@ namespace System /// </summary> /// <param name="span">The source span from which the characters are removed.</param> /// <param name="trimChars">The span which contains the set of characters to remove.</param> + /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks> public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars) { + if (trimChars.IsEmpty) + { + return span.TrimEnd(); + } + int end = span.Length - 1; for (; end >= 0; end--) { @@ -168,6 +183,56 @@ namespace System } /// <summary> + /// Searches for the specified value and returns true if found. If not found, returns false. Values are compared using IEquatable{T}.Equals(T). + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="span">The span to search.</param> + /// <param name="value">The value to search for.</param> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Contains<T>(this Span<T> span, T value) + where T : IEquatable<T> + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.Contains( + ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), + Unsafe.As<T, byte>(ref value), + span.Length); + + if (typeof(T) == typeof(char)) + return SpanHelpers.Contains( + ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)), + Unsafe.As<T, char>(ref value), + span.Length); + + return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length); + } + + /// <summary> + /// Searches for the specified value and returns true if found. If not found, returns false. Values are compared using IEquatable{T}.Equals(T). + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="span">The span to search.</param> + /// <param name="value">The value to search for.</param> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Contains<T>(this ReadOnlySpan<T> span, T value) + where T : IEquatable<T> + { + if (typeof(T) == typeof(byte)) + return SpanHelpers.Contains( + ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), + Unsafe.As<T, byte>(ref value), + span.Length); + + if (typeof(T) == typeof(char)) + return SpanHelpers.Contains( + ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)), + Unsafe.As<T, char>(ref value), + span.Length); + + return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length); + } + + /// <summary> /// 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). /// </summary> /// <param name="span">The span to search.</param> @@ -181,7 +246,14 @@ namespace System ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), Unsafe.As<T, byte>(ref value), span.Length); - return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length); + + if (typeof(T) == typeof(char)) + return SpanHelpers.IndexOf( + ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)), + Unsafe.As<T, char>(ref value), + span.Length); + + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } /// <summary> @@ -199,7 +271,8 @@ namespace System span.Length, ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)), value.Length); - return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); + + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } /// <summary> @@ -216,6 +289,13 @@ namespace System ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), Unsafe.As<T, byte>(ref value), span.Length); + + if (typeof(T) == typeof(char)) + return SpanHelpers.LastIndexOf( + ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)), + Unsafe.As<T, char>(ref value), + span.Length); + return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length); } @@ -234,6 +314,7 @@ namespace System span.Length, ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)), value.Length); + return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } @@ -241,50 +322,42 @@ namespace System /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T). /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool SequenceEqual<T>(this Span<T> first, ReadOnlySpan<T> second) + public static bool SequenceEqual<T>(this Span<T> span, ReadOnlySpan<T> other) where T : IEquatable<T> { - int length = first.Length; - if (typeof(T) == typeof(byte)) - return length == second.Length && + int length = span.Length; + + if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size)) + return length == other.Length && SpanHelpers.SequenceEqual( - ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(first)), - ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(second)), - length); - return length == second.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(first), ref MemoryMarshal.GetReference(second), length); + ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), + ref Unsafe.As<T, byte>(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); } /// <summary> /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T). /// </summary> - public static int SequenceCompareTo<T>(this Span<T> first, ReadOnlySpan<T> second) + public static int SequenceCompareTo<T>(this Span<T> span, ReadOnlySpan<T> other) where T : IComparable<T> { if (typeof(T) == typeof(byte)) return SpanHelpers.SequenceCompareTo( - ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(first)), - first.Length, - ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(second)), - second.Length); - return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(first), first.Length, ref MemoryMarshal.GetReference(second), second.Length); - } + ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(other)), + other.Length); - /// <summary> - /// Reverses the sequence of the elements in the entire span. - /// </summary> - public static void Reverse<T>(this Span<T> span) - { - ref T p = ref MemoryMarshal.GetReference(span); - int i = 0; - int j = span.Length - 1; - while (i < j) - { - T temp = Unsafe.Add(ref p, i); - Unsafe.Add(ref p, i) = Unsafe.Add(ref p, j); - Unsafe.Add(ref p, j) = temp; - i++; - j--; - } + if (typeof(T) == typeof(char)) + return SpanHelpers.SequenceCompareTo( + ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(other)), + other.Length); + + return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length); } /// <summary> @@ -301,7 +374,14 @@ namespace System ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), Unsafe.As<T, byte>(ref value), span.Length); - return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length); + + if (typeof(T) == typeof(char)) + return SpanHelpers.IndexOf( + ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)), + Unsafe.As<T, char>(ref value), + span.Length); + + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length); } /// <summary> @@ -319,7 +399,8 @@ namespace System span.Length, ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)), value.Length); - return SpanHelpers.IndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); + + return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } /// <summary> @@ -336,6 +417,13 @@ namespace System ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), Unsafe.As<T, byte>(ref value), span.Length); + + if (typeof(T) == typeof(char)) + return SpanHelpers.LastIndexOf( + ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)), + Unsafe.As<T, char>(ref value), + span.Length); + return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length); } @@ -354,6 +442,7 @@ namespace System span.Length, ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)), value.Length); + return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length); } @@ -495,6 +584,7 @@ namespace System Unsafe.As<T, byte>(ref value0), Unsafe.As<T, byte>(ref value1), span.Length); + return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); } @@ -516,6 +606,7 @@ namespace System Unsafe.As<T, byte>(ref value1), Unsafe.As<T, byte>(ref value2), span.Length); + return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); } @@ -534,6 +625,7 @@ namespace System span.Length, ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)), values.Length); + return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); } @@ -553,6 +645,7 @@ namespace System Unsafe.As<T, byte>(ref value0), Unsafe.As<T, byte>(ref value1), span.Length); + return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length); } @@ -574,6 +667,7 @@ namespace System Unsafe.As<T, byte>(ref value1), Unsafe.As<T, byte>(ref value2), span.Length); + return SpanHelpers.LastIndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length); } @@ -592,6 +686,7 @@ namespace System span.Length, ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values)), values.Length); + return SpanHelpers.LastIndexOfAny<T>(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(values), values.Length); } @@ -599,33 +694,42 @@ namespace System /// Determines whether two sequences are equal by comparing the elements using IEquatable{T}.Equals(T). /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool SequenceEqual<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second) + public static bool SequenceEqual<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other) where T : IEquatable<T> { - int length = first.Length; - if (typeof(T) == typeof(byte)) - return length == second.Length && + int length = span.Length; + if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size)) + return length == other.Length && SpanHelpers.SequenceEqual( - ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(first)), - ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(second)), - length); - return length == second.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(first), ref MemoryMarshal.GetReference(second), length); + ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), + ref Unsafe.As<T, byte>(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); } /// <summary> /// Determines the relative order of the sequences being compared by comparing the elements using IComparable{T}.CompareTo(T). /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SequenceCompareTo<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second) + public static int SequenceCompareTo<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other) where T : IComparable<T> { if (typeof(T) == typeof(byte)) return SpanHelpers.SequenceCompareTo( - ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(first)), - first.Length, - ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(second)), - second.Length); - return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(first), first.Length, ref MemoryMarshal.GetReference(second), second.Length); + ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(other)), + other.Length); + + if (typeof(T) == typeof(char)) + return SpanHelpers.SequenceCompareTo( + ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)), + span.Length, + ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(other)), + other.Length); + + return SpanHelpers.SequenceCompareTo(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(other), other.Length); } /// <summary> @@ -636,12 +740,13 @@ namespace System where T : IEquatable<T> { int valueLength = value.Length; - if (typeof(T) == typeof(byte)) + if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size)) return valueLength <= span.Length && SpanHelpers.SequenceEqual( ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)), - valueLength); + ((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); } @@ -653,12 +758,13 @@ namespace System where T : IEquatable<T> { int valueLength = value.Length; - if (typeof(T) == typeof(byte)) + if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size)) return valueLength <= span.Length && SpanHelpers.SequenceEqual( ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)), - valueLength); + ((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); } @@ -671,12 +777,13 @@ namespace System { int spanLength = span.Length; int valueLength = value.Length; - if (typeof(T) == typeof(byte)) + if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size)) return valueLength <= spanLength && SpanHelpers.SequenceEqual( ref Unsafe.As<T, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)), ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)), - valueLength); + ((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), @@ -693,12 +800,13 @@ namespace System { int spanLength = span.Length; int valueLength = value.Length; - if (typeof(T) == typeof(byte)) + if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size)) return valueLength <= spanLength && SpanHelpers.SequenceEqual( ref Unsafe.As<T, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)), ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)), - valueLength); + ((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), @@ -707,7 +815,29 @@ namespace System } /// <summary> - /// Creates a new span over the portion of the target array. + /// Reverses the sequence of the elements in the entire span. + /// </summary> + public static void Reverse<T>(this Span<T> 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)); + } + + /// <summary> + /// Creates a new span over the target array. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<T> AsSpan<T>(this T[] array) @@ -716,62 +846,168 @@ namespace System } /// <summary> - /// Creates a new span over the portion of the target array segment. + /// Creates a new Span over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). /// </summary> + /// <param name="array">The target array.</param> + /// <param name="start">The index at which to begin the Span.</param> + /// <param name="length">The number of items in the Span.</param> + /// <remarks>Returns default when <paramref name="array"/> is null.</remarks> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length). + /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span<T> AsSpan<T>(this ArraySegment<T> arraySegment) + public static Span<T> AsSpan<T>(this T[] array, int start, int length) { - return new Span<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + return new Span<T>(array, start, length); } /// <summary> - /// Creates a new readonly span over the entire target array. + /// Creates a new span over the portion of the target array segment. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan<T> AsReadOnlySpan<T>(this T[] array) + public static Span<T> AsSpan<T>(this ArraySegment<T> segment) { - return new ReadOnlySpan<T>(array); + return new Span<T>(segment.Array, segment.Offset, segment.Count); } /// <summary> - /// Creates a new readonly span over the entire target span. + /// Creates a new Span over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). /// </summary> - public static ReadOnlySpan<T> AsReadOnlySpan<T>(this Span<T> span) => span; + /// <param name="segment">The target array.</param> + /// <param name="start">The index at which to begin the Span.</param> + /// <remarks>Returns default when <paramref name="segment"/> is null.</remarks> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="segment"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=segment.Count). + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span<T> AsSpan<T>(this ArraySegment<T> segment, int start) + { + if (((uint)start) > segment.Count) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + + return new Span<T>(segment.Array, segment.Offset + start, segment.Count - start); + } /// <summary> - /// Creates a new readonly span over the target array segment. + /// Creates a new Span over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). /// </summary> + /// <param name="segment">The target array.</param> + /// <param name="start">The index at which to begin the Span.</param> + /// <param name="length">The number of items in the Span.</param> + /// <remarks>Returns default when <paramref name="segment"/> is null.</remarks> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="segment"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=segment.Count). + /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan<T> AsReadOnlySpan<T>(this ArraySegment<T> arraySegment) + public static Span<T> AsSpan<T>(this ArraySegment<T> segment, int start, int length) { - return new ReadOnlySpan<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + if (((uint)start) > segment.Count) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + if (((uint)length) > segment.Count - start) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); + + return new Span<T>(segment.Array, segment.Offset + start, length); } /// <summary> - /// Creates a new readonly memory over the entire target memory. + /// Creates a new memory over the target array. /// </summary> - public static ReadOnlyMemory<T> AsReadOnlyMemory<T>(this Memory<T> memory) => memory; + public static Memory<T> AsMemory<T>(this T[] array) => new Memory<T>(array); /// <summary> - /// Creates a new memory over the portion of the target array. + /// Creates a new memory over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). /// </summary> + /// <param name="array">The target array.</param> + /// <param name="start">The index at which to begin the memory.</param> + /// <remarks>Returns default when <paramref name="array"/> is null.</remarks> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=array.Length). + /// </exception> public static Memory<T> AsMemory<T>(this T[] array, int start) => new Memory<T>(array, start); /// <summary> + /// Creates a new memory over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// </summary> + /// <param name="array">The target array.</param> + /// <param name="start">The index at which to begin the memory.</param> + /// <param name="length">The number of items in the memory.</param> + /// <remarks>Returns default when <paramref name="array"/> is null.</remarks> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length). + /// </exception> + public static Memory<T> AsMemory<T>(this T[] array, int start, int length) => new Memory<T>(array, start, length); + + /// <summary> + /// Creates a new memory over the portion of the target array. + /// </summary> + public static Memory<T> AsMemory<T>(this ArraySegment<T> segment) => new Memory<T>(segment.Array, segment.Offset, segment.Count); + + /// <summary> + /// Creates a new memory over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// </summary> + /// <param name="segment">The target array.</param> + /// <param name="start">The index at which to begin the memory.</param> + /// <remarks>Returns default when <paramref name="segment"/> is null.</remarks> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="segment"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=segment.Count). + /// </exception> + public static Memory<T> AsMemory<T>(this ArraySegment<T> segment, int start) + { + if (((uint)start) > segment.Count) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + + return new Memory<T>(segment.Array, segment.Offset + start, segment.Count - start); + } + + /// <summary> + /// Creates a new memory over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// </summary> + /// <param name="segment">The target array.</param> + /// <param name="start">The index at which to begin the memory.</param> + /// <param name="length">The number of items in the memory.</param> + /// <remarks>Returns default when <paramref name="segment"/> is null.</remarks> + /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="segment"/> is covariant and array's type is not exactly T[].</exception> + /// <exception cref="System.ArgumentOutOfRangeException"> + /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=segment.Count). + /// </exception> + public static Memory<T> AsMemory<T>(this ArraySegment<T> segment, int start, int length) + { + if (((uint)start) > segment.Count) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); + if (((uint)length) > segment.Count - start) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); + + return new Memory<T>(segment.Array, segment.Offset + start, length); + } + + /// <summary> /// 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. /// - ///<param name="array">The array to copy items from.</param> + ///<param name="source">The array to copy items from.</param> /// <param name="destination">The span to copy items into.</param> /// <exception cref="System.ArgumentException"> /// Thrown when the destination Span is shorter than the source array. /// </exception> /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CopyTo<T>(this T[] array, Span<T> destination) + public static void CopyTo<T>(this T[] source, Span<T> destination) { - new ReadOnlySpan<T>(array).CopyTo(destination); + new ReadOnlySpan<T>(source).CopyTo(destination); } /// <summary> @@ -779,16 +1015,16 @@ namespace System /// and destinations overlap, this method behaves as if the original values are in /// a temporary location before the destination is overwritten. /// - ///<param name="array">The array to copy items from.</param> + ///<param name="source">The array to copy items from.</param> /// <param name="destination">The memory to copy items into.</param> /// <exception cref="System.ArgumentException"> /// Thrown when the destination is shorter than the source array. /// </exception> /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CopyTo<T>(this T[] array, Memory<T> destination) + public static void CopyTo<T>(this T[] source, Memory<T> destination) { - array.CopyTo(destination.Span); + source.CopyTo(destination.Span); } // @@ -923,65 +1159,65 @@ namespace System /// Determines whether two sequences overlap in memory. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Overlaps<T>(this Span<T> first, ReadOnlySpan<T> second) + public static bool Overlaps<T>(this Span<T> span, ReadOnlySpan<T> other) { - return Overlaps((ReadOnlySpan<T>)first, second); + return Overlaps((ReadOnlySpan<T>)span, other); } /// <summary> /// Determines whether two sequences overlap in memory and outputs the element offset. /// </summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Overlaps<T>(this Span<T> first, ReadOnlySpan<T> second, out int elementOffset) + public static bool Overlaps<T>(this Span<T> span, ReadOnlySpan<T> other, out int elementOffset) { - return Overlaps((ReadOnlySpan<T>)first, second, out elementOffset); + return Overlaps((ReadOnlySpan<T>)span, other, out elementOffset); } /// <summary> /// Determines whether two sequences overlap in memory. /// </summary> - public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second) + public static bool Overlaps<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other) { - if (first.IsEmpty || second.IsEmpty) + if (span.IsEmpty || other.IsEmpty) { return false; } IntPtr byteOffset = Unsafe.ByteOffset( - ref MemoryMarshal.GetReference(first), - ref MemoryMarshal.GetReference(second)); + ref MemoryMarshal.GetReference(span), + ref MemoryMarshal.GetReference(other)); if (Unsafe.SizeOf<IntPtr>() == sizeof(int)) { - return (uint)byteOffset < (uint)(first.Length * Unsafe.SizeOf<T>()) || - (uint)byteOffset > (uint)-(second.Length * Unsafe.SizeOf<T>()); + return (uint)byteOffset < (uint)(span.Length * Unsafe.SizeOf<T>()) || + (uint)byteOffset > (uint)-(other.Length * Unsafe.SizeOf<T>()); } else { - return (ulong)byteOffset < (ulong)((long)first.Length * Unsafe.SizeOf<T>()) || - (ulong)byteOffset > (ulong)-((long)second.Length * Unsafe.SizeOf<T>()); + return (ulong)byteOffset < (ulong)((long)span.Length * Unsafe.SizeOf<T>()) || + (ulong)byteOffset > (ulong)-((long)other.Length * Unsafe.SizeOf<T>()); } } /// <summary> /// Determines whether two sequences overlap in memory and outputs the element offset. /// </summary> - public static bool Overlaps<T>(this ReadOnlySpan<T> first, ReadOnlySpan<T> second, out int elementOffset) + public static bool Overlaps<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other, out int elementOffset) { - if (first.IsEmpty || second.IsEmpty) + if (span.IsEmpty || other.IsEmpty) { elementOffset = 0; return false; } IntPtr byteOffset = Unsafe.ByteOffset( - ref MemoryMarshal.GetReference(first), - ref MemoryMarshal.GetReference(second)); + ref MemoryMarshal.GetReference(span), + ref MemoryMarshal.GetReference(other)); if (Unsafe.SizeOf<IntPtr>() == sizeof(int)) { - if ((uint)byteOffset < (uint)(first.Length * Unsafe.SizeOf<T>()) || - (uint)byteOffset > (uint)-(second.Length * Unsafe.SizeOf<T>())) + if ((uint)byteOffset < (uint)(span.Length * Unsafe.SizeOf<T>()) || + (uint)byteOffset > (uint)-(other.Length * Unsafe.SizeOf<T>())) { if ((int)byteOffset % Unsafe.SizeOf<T>() != 0) ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch(); @@ -997,8 +1233,8 @@ namespace System } else { - if ((ulong)byteOffset < (ulong)((long)first.Length * Unsafe.SizeOf<T>()) || - (ulong)byteOffset > (ulong)-((long)second.Length * Unsafe.SizeOf<T>())) + if ((ulong)byteOffset < (ulong)((long)span.Length * Unsafe.SizeOf<T>()) || + (ulong)byteOffset > (ulong)-((long)other.Length * Unsafe.SizeOf<T>())) { if ((long)byteOffset % Unsafe.SizeOf<T>() != 0) ThrowHelper.ThrowArgumentException_OverlapAlignmentMismatch(); @@ -1028,7 +1264,7 @@ namespace System /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>. /// </returns> /// <exception cref="T:System.ArgumentNullException"> - /// <paramref name = "comparable" /> is <see langword="null"/> . + /// <paramref name = "comparable" /> is <see langword="null"/> . /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch<T>( @@ -1052,7 +1288,7 @@ namespace System /// no larger element, the bitwise complement of <see cref="Span{T}.Length"/>. /// </returns> /// <exception cref="T:System.ArgumentNullException"> - /// <paramref name = "comparable" /> is <see langword="null"/> . + /// <paramref name = "comparable" /> is <see langword="null"/> . /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch<T, TComparable>( @@ -1102,7 +1338,7 @@ namespace System /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>. /// </returns> /// <exception cref="T:System.ArgumentNullException"> - /// <paramref name = "comparable" /> is <see langword="null"/> . + /// <paramref name = "comparable" /> is <see langword="null"/> . /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch<T>( @@ -1126,7 +1362,7 @@ namespace System /// no larger element, the bitwise complement of <see cref="ReadOnlySpan{T}.Length"/>. /// </returns> /// <exception cref="T:System.ArgumentNullException"> - /// <paramref name = "comparable" /> is <see langword="null"/> . + /// <paramref name = "comparable" /> is <see langword="null"/> . /// </exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int BinarySearch<T, TComparable>( @@ -1166,5 +1402,36 @@ namespace System value, comparer); return BinarySearch(span, comparable); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsTypeComparableAsBytes<T>(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; + } } } |