diff --git a/src/libraries/System.Private.CoreLib/src/System/Array.cs b/src/libraries/System.Private.CoreLib/src/System/Array.cs index d3753a1b6bf785..888f58689d0fb2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Array.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Array.cs @@ -51,7 +51,7 @@ public static void Resize([NotNull] ref T[]? array, int newSize) if (newSize < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.newSize, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); - T[]? larray = array; + T[]? larray = array; // local copy if (larray == null) { array = new T[newSize]; @@ -60,8 +60,17 @@ public static void Resize([NotNull] ref T[]? array, int newSize) if (larray.Length != newSize) { + // Due to array variance, it's possible that the incoming array is + // actually of type U[], where U:T; or that an int[] <-> uint[] or + // similar cast has occurred. In any case, since it's always legal + // to reinterpret U as T in this scenario (but not necessarily the + // other way around), we can use Buffer.Memmove here. + T[] newArray = new T[newSize]; - Copy(larray, 0, newArray, 0, larray.Length > newSize ? newSize : larray.Length); + Buffer.Memmove( + ref MemoryMarshal.GetArrayDataReference(newArray), + ref MemoryMarshal.GetArrayDataReference(larray), + (uint)Math.Min(newSize, larray.Length)); array = newArray; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs index 3f06b3def7f29c..921f447bb6fcf4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -27,29 +27,40 @@ public static T[] GetSubArray(T[] array, Range range) (int offset, int length) = range.GetOffsetAndLength(array.Length); + T[] dest; + if (typeof(T).IsValueType || typeof(T[]) == array.GetType()) { - // We know the type of the array to be exactly T[]. + // We know the type of the array to be exactly T[] or an array variance + // compatible value type substitution like int[] <-> uint[]. if (length == 0) { return Array.Empty(); } - var dest = new T[length]; - Buffer.Memmove( - ref MemoryMarshal.GetArrayDataReference(dest), - ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), offset), - (uint)length); - return dest; + dest = new T[length]; } else { - // The array is actually a U[] where U:T. - T[] dest = (T[])Array.CreateInstance(array.GetType().GetElementType()!, length); - Array.Copy(array, offset, dest, 0, length); - return dest; + // The array is actually a U[] where U:T. We'll make sure to create + // an array of the exact same backing type. The cast to T[] will + // never fail. + + dest = Unsafe.As(Array.CreateInstance(array.GetType().GetElementType()!, length)); } + + // In either case, the newly-allocated array is the exact same type as the + // original incoming array. It's safe for us to Buffer.Memmove the contents + // from the source array to the destination array, otherwise the contents + // wouldn't have been valid for the source array in the first place. + + Buffer.Memmove( + ref MemoryMarshal.GetArrayDataReference(dest), + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), offset), + (uint)length); + + return dest; } public static object GetUninitializedObject(Type type)