Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Improve RuntimeHelpers.GetSubArray (#23829)
Browse files Browse the repository at this point in the history
* Improve RuntimeHelpers.GetSubArray

This change does three things.

First, it fixes `GetSubArray` to work when the supplied array is actually a `U[]` where `U : T`.  Currently this case ends up throwing an exception inside of span, which doesn't like working with arrays covariantly.

Second, it fixes argument validation so that we throw an ArgumentNullException if the input array is null rather than NullReferenceException.

Third, it improves the performance of `GetSubArray` for the 95% common case where either `T` is a value type or the type of the array matches the `T` type specified.

* Only use `Array.Empty<T>` when `typeof(T[]) == array.GetType()`
  • Loading branch information
stephentoub committed Apr 9, 2019
1 parent 13421bf commit 5fd24c6
Showing 1 changed file with 27 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Runtime.Serialization;
using Internal.Runtime.CompilerServices;

namespace System.Runtime.CompilerServices
{
Expand All @@ -13,22 +14,39 @@ public static partial class RuntimeHelpers
public delegate void CleanupCode(object userData, bool exceptionThrown);

/// <summary>
/// GetSubArray helper method for the compiler to slice an array using a range.
/// Slices the specified array using the specified range.
/// </summary>
public static T[] GetSubArray<T>(T[] array, Range range)
{
Type elementType = array.GetType().GetElementType();
Span<T> source = array.AsSpan(range);
if (array == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}

Range.OffsetAndLength offLen = range.GetOffsetAndLength(array.Length);

if (elementType.IsValueType)
if (default(T) != null || typeof(T[]) == array.GetType())
{
return source.ToArray();
// We know the type of the array to be exactly T[].

if (offLen.Length == 0)
{
return Array.Empty<T>();
}

var dest = new T[offLen.Length];
Buffer.Memmove(
ref Unsafe.As<byte, T>(ref dest.GetRawSzArrayData()),
ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), offLen.Offset),
(uint)offLen.Length);
return dest;
}
else
{
T[] newArray = (T[])Array.CreateInstance(elementType, source.Length);
source.CopyTo(newArray);
return newArray;
// The array is actually a U[] where U:T.
T[] dest = (T[])Array.CreateInstance(array.GetType().GetElementType(), offLen.Length);
Array.Copy(array, offLen.Offset, dest, 0, offLen.Length);
return dest;
}
}

Expand Down Expand Up @@ -63,4 +81,4 @@ public static void PrepareConstrainedRegionsNoOP()
{
}
}
}
}

0 comments on commit 5fd24c6

Please sign in to comment.