Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1b9ee99

Browse files
authoredNov 3, 2019
Rewrite of Array.Copy fast path in C# (#27634)
Contributes to #27106
1 parent 1bf3a15 commit 1b9ee99

File tree

6 files changed

+183
-209
lines changed

6 files changed

+183
-209
lines changed
 

‎src/System.Private.CoreLib/src/System/Array.CoreCLR.cs

+112-9
Original file line numberDiff line numberDiff line change
@@ -135,30 +135,133 @@ public static unsafe Array CreateInstance(Type elementType, int[] lengths, int[]
135135
// Copies length elements from sourceArray, starting at index 0, to
136136
// destinationArray, starting at index 0.
137137
//
138-
public static void Copy(Array sourceArray, Array destinationArray, int length)
138+
public static unsafe void Copy(Array sourceArray, Array destinationArray, int length)
139139
{
140140
if (sourceArray == null)
141141
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.sourceArray);
142142
if (destinationArray == null)
143143
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray);
144144

145-
Copy(sourceArray, sourceArray.GetLowerBound(0), destinationArray, destinationArray.GetLowerBound(0), length, false);
145+
MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray);
146+
if (pMT == RuntimeHelpers.GetMethodTable(destinationArray) &&
147+
!pMT->IsMultiDimensionalArray &&
148+
(uint)length <= (uint)sourceArray.Length &&
149+
(uint)length <= (uint)destinationArray.Length)
150+
{
151+
nuint byteCount = (uint)length * (nuint)pMT->ComponentSize;
152+
ref byte src = ref sourceArray.GetRawSzArrayData();
153+
ref byte dst = ref destinationArray.GetRawSzArrayData();
154+
155+
if (pMT->ContainsGCPointers)
156+
Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
157+
else
158+
Buffer.Memmove(ref dst, ref src, byteCount);
159+
160+
// GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
161+
return;
162+
}
163+
164+
// Less common
165+
Copy(sourceArray, sourceArray.GetLowerBound(0), destinationArray, destinationArray.GetLowerBound(0), length, reliable: false);
146166
}
147167

148168
// Copies length elements from sourceArray, starting at sourceIndex, to
149169
// destinationArray, starting at destinationIndex.
150170
//
151-
public static void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
171+
public static unsafe void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
172+
{
173+
if (sourceArray != null && destinationArray != null)
174+
{
175+
MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray);
176+
if (pMT == RuntimeHelpers.GetMethodTable(destinationArray) &&
177+
!pMT->IsMultiDimensionalArray &&
178+
length >= 0 && sourceIndex >= 0 && destinationIndex >= 0 &&
179+
(uint)(sourceIndex + length) <= (uint)sourceArray.Length &&
180+
(uint)(destinationIndex + length) <= (uint)destinationArray.Length)
181+
{
182+
nuint elementSize = (nuint)pMT->ComponentSize;
183+
nuint byteCount = (uint)length * elementSize;
184+
ref byte src = ref Unsafe.AddByteOffset(ref sourceArray.GetRawSzArrayData(), (uint)sourceIndex * elementSize);
185+
ref byte dst = ref Unsafe.AddByteOffset(ref destinationArray.GetRawSzArrayData(), (uint)destinationIndex * elementSize);
186+
187+
if (pMT->ContainsGCPointers)
188+
Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
189+
else
190+
Buffer.Memmove(ref dst, ref src, byteCount);
191+
192+
// GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
193+
return;
194+
}
195+
}
196+
197+
// Less common
198+
Copy(sourceArray!, sourceIndex, destinationArray!, destinationIndex, length, reliable: false);
199+
}
200+
201+
private static unsafe void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable)
152202
{
153-
Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length, false);
203+
if (sourceArray == null)
204+
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.sourceArray);
205+
if (destinationArray == null)
206+
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray);
207+
208+
if (sourceArray.GetType() != destinationArray.GetType() && sourceArray.Rank != destinationArray.Rank)
209+
throw new RankException(SR.Rank_MustMatch);
210+
211+
if (length < 0)
212+
throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
213+
214+
int srcLB = sourceArray.GetLowerBound(0);
215+
if (sourceIndex < srcLB || sourceIndex - srcLB < 0)
216+
throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_ArrayLB);
217+
sourceIndex -= srcLB;
218+
219+
int dstLB = destinationArray.GetLowerBound(0);
220+
if (destinationIndex < dstLB || destinationIndex - dstLB < 0)
221+
throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_ArrayLB);
222+
destinationIndex -= dstLB;
223+
224+
if ((uint)(sourceIndex + length) > (uint)sourceArray.Length)
225+
throw new ArgumentException(SR.Arg_LongerThanSrcArray, nameof(sourceArray));
226+
if ((uint)(destinationIndex + length) > (uint)destinationArray.Length)
227+
throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray));
228+
229+
if (sourceArray.GetType() == destinationArray.GetType() || IsSimpleCopy(sourceArray, destinationArray))
230+
{
231+
MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray);
232+
233+
nuint elementSize = (nuint)pMT->ComponentSize;
234+
nuint byteCount = (uint)length * elementSize;
235+
ref byte src = ref Unsafe.AddByteOffset(ref sourceArray.GetRawArrayData(), (uint)sourceIndex * elementSize);
236+
ref byte dst = ref Unsafe.AddByteOffset(ref destinationArray.GetRawArrayData(), (uint)destinationIndex * elementSize);
237+
238+
if (pMT->ContainsGCPointers)
239+
Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
240+
else
241+
Buffer.Memmove(ref dst, ref src, byteCount);
242+
243+
// GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
244+
return;
245+
}
246+
247+
// If we were called from Array.ConstrainedCopy, ensure that the array copy
248+
// is guaranteed to succeed.
249+
if (reliable)
250+
throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy);
251+
252+
// Rare
253+
CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length);
154254
}
155255

256+
[MethodImpl(MethodImplOptions.InternalCall)]
257+
private static extern bool IsSimpleCopy(Array sourceArray, Array destinationArray);
258+
156259
// Reliability-wise, this method will either possibly corrupt your
157260
// instance & might fail when called from within a CER, or if the
158261
// reliable flag is true, it will either always succeed or always
159262
// throw an exception with no side effects.
160263
[MethodImpl(MethodImplOptions.InternalCall)]
161-
internal static extern void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable);
264+
private static extern void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length);
162265

163266
// Provides a strong exception guarantee - either it succeeds, or
164267
// it throws an exception with no side effects. The arrays must be
@@ -167,7 +270,7 @@ public static void Copy(Array sourceArray, int sourceIndex, Array destinationArr
167270
// It will up-cast, assuming the array types are correct.
168271
public static void ConstrainedCopy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
169272
{
170-
Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length, true);
273+
Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable: true);
171274
}
172275

173276
// Sets length elements in array to 0 (or null for Object arrays), starting
@@ -194,10 +297,10 @@ public static unsafe void Clear(Array array, int index, int length)
194297
if (index < lowerBound || offset < 0 || length < 0 || (uint)(offset + length) > (uint)array.Length)
195298
ThrowHelper.ThrowIndexOutOfRangeException();
196299

197-
uint elementSize = pMT->ComponentSize;
300+
nuint elementSize = pMT->ComponentSize;
198301

199-
ref byte ptr = ref Unsafe.AddByteOffset(ref p, (uint)offset * (nuint)elementSize);
200-
nuint byteLength = (uint)length * (nuint)elementSize;
302+
ref byte ptr = ref Unsafe.AddByteOffset(ref p, (uint)offset * elementSize);
303+
nuint byteLength = (uint)length * elementSize;
201304

202305
if (pMT->ContainsGCPointers)
203306
SpanHelpers.ClearWithReferences(ref Unsafe.As<byte, IntPtr>(ref ptr), byteLength / (uint)sizeof(IntPtr));

‎src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ private static void PrelinkCore(MethodInfo m)
193193
/// structure contains pointers to allocated blocks and "fDeleteOld" is
194194
/// true, this routine will call DestroyStructure() first.
195195
/// </summary>
196-
[MethodImpl(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
196+
[MethodImpl(MethodImplOptions.InternalCall)]
197197
public static extern void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld);
198198

199199
private static object PtrToStructureHelper(IntPtr ptr, Type structureType)

‎src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ private void SetCultureOnUnstartedThreadNoCheck(CultureInfo value, bool uiCultur
276276
[MethodImpl(MethodImplOptions.NoInlining)]
277277
private static Thread InitializeCurrentThread() => t_currentThread = GetCurrentThreadNative();
278278

279-
[MethodImpl(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
279+
[MethodImpl(MethodImplOptions.InternalCall)]
280280
private static extern Thread GetCurrentThreadNative();
281281

282282
private void SetStartHelper(Delegate start, int maxStackSize)

‎src/classlibnative/bcltype/arraynative.cpp

+65-192
Original file line numberDiff line numberDiff line change
@@ -129,95 +129,71 @@ FCIMPL1(void, ArrayNative::Initialize, ArrayBase* array)
129129
FCIMPLEND
130130

131131

132-
133-
134-
135-
136-
// Returns an enum saying whether you can copy an array of srcType into destType.
137-
ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayTypeNoGC(const BASEARRAYREF pSrc, const BASEARRAYREF pDest)
132+
// Returns whether you can directly copy an array of srcType into destType.
133+
FCIMPL2(FC_BOOL_RET, ArrayNative::IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst)
138134
{
139-
CONTRACTL
140-
{
141-
NOTHROW;
142-
GC_NOTRIGGER;
143-
MODE_COOPERATIVE;
144-
PRECONDITION(pSrc != NULL);
145-
PRECONDITION(pDest != NULL);
146-
}
147-
CONTRACTL_END;
135+
FCALL_CONTRACT;
148136

149-
// The next 50 lines are a little tricky. Change them with great care.
150-
//
137+
_ASSERTE(pSrc != NULL);
138+
_ASSERTE(pDst != NULL);
151139

152-
// This first bit is a minor optimization: e.g. when copying byte[] to byte[]
153-
// we do not need to call GetArrayElementTypeHandle().
154-
MethodTable *pSrcMT = pSrc->GetMethodTable();
155-
MethodTable *pDestMT = pDest->GetMethodTable();
156-
if (pSrcMT == pDestMT)
157-
return AssignWillWork;
140+
// This case is expected to be handled by the fast path
141+
_ASSERTE(pSrc->GetMethodTable() != pDst->GetMethodTable());
158142

159-
TypeHandle srcTH = pSrcMT->GetApproxArrayElementTypeHandle();
160-
TypeHandle destTH = pDestMT->GetApproxArrayElementTypeHandle();
143+
TypeHandle srcTH = pSrc->GetMethodTable()->GetApproxArrayElementTypeHandle();
144+
TypeHandle destTH = pDst->GetMethodTable()->GetApproxArrayElementTypeHandle();
161145
if (srcTH == destTH) // This check kicks for different array kind or dimensions
162-
return AssignWillWork;
146+
FC_RETURN_BOOL(true);
163147

164-
// Value class boxing
165-
if (srcTH.IsValueType() && !destTH.IsValueType())
148+
if (srcTH.IsValueType())
166149
{
167-
switch (srcTH.CanCastToCached(destTH))
150+
// Value class boxing
151+
if (!destTH.IsValueType())
152+
FC_RETURN_BOOL(false);
153+
154+
const CorElementType srcElType = srcTH.GetVerifierCorElementType();
155+
const CorElementType destElType = destTH.GetVerifierCorElementType();
156+
_ASSERTE(srcElType < ELEMENT_TYPE_MAX);
157+
_ASSERTE(destElType < ELEMENT_TYPE_MAX);
158+
159+
// Copying primitives from one type to another
160+
if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType))
168161
{
169-
case TypeHandle::CanCast : return AssignBoxValueClassOrPrimitive;
170-
case TypeHandle::CannotCast : return AssignWrongType;
171-
default : return AssignDontKnow;
162+
if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType))
163+
FC_RETURN_BOOL(true);
172164
}
173165
}
174-
175-
// Value class unboxing.
176-
if (!srcTH.IsValueType() && destTH.IsValueType())
166+
else
177167
{
178-
if (srcTH.CanCastToCached(destTH) == TypeHandle::CanCast)
179-
return AssignUnboxValueClass;
180-
else if (destTH.CanCastToCached(srcTH) == TypeHandle::CanCast) // V extends IV. Copying from IV to V, or Object to V.
181-
return AssignUnboxValueClass;
182-
else
183-
return AssignDontKnow;
168+
// Value class unboxing
169+
if (destTH.IsValueType())
170+
FC_RETURN_BOOL(false);
184171
}
185172

186-
const CorElementType srcElType = srcTH.GetVerifierCorElementType();
187-
const CorElementType destElType = destTH.GetVerifierCorElementType();
188-
_ASSERTE(srcElType < ELEMENT_TYPE_MAX);
189-
_ASSERTE(destElType < ELEMENT_TYPE_MAX);
190-
191-
// Copying primitives from one type to another
192-
if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType))
173+
TypeHandle::CastResult r = srcTH.CanCastToCached(destTH);
174+
if (r != TypeHandle::MaybeCast)
193175
{
194-
if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType))
195-
return AssignWillWork;
196-
197-
if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType))
198-
return AssignPrimitiveWiden;
199-
else
200-
return AssignWrongType;
176+
FC_RETURN_BOOL(r);
201177
}
202178

203-
// dest Object extends src
204-
if (srcTH.CanCastToCached(destTH) == TypeHandle::CanCast)
205-
return AssignWillWork;
179+
struct
180+
{
181+
OBJECTREF src;
182+
OBJECTREF dst;
183+
} gc;
206184

207-
// src Object extends dest
208-
if (destTH.CanCastToCached(srcTH) == TypeHandle::CanCast)
209-
return AssignMustCast;
185+
gc.src = ObjectToOBJECTREF(pSrc);
186+
gc.dst = ObjectToOBJECTREF(pDst);
210187

211-
// class X extends/implements src and implements dest.
212-
if (destTH.IsInterface() && srcElType != ELEMENT_TYPE_VALUETYPE)
213-
return AssignMustCast;
188+
BOOL iRetVal = FALSE;
214189

215-
// class X implements src and extends/implements dest
216-
if (srcTH.IsInterface() && destElType != ELEMENT_TYPE_VALUETYPE)
217-
return AssignMustCast;
190+
HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
191+
iRetVal = srcTH.CanCastTo(destTH);
192+
HELPER_METHOD_FRAME_END();
218193

219-
return AssignDontKnow;
194+
FC_RETURN_BOOL(iRetVal);
220195
}
196+
FCIMPLEND
221197

222198

223199
// Returns an enum saying whether you can copy an array of srcType into destType.
@@ -233,21 +209,16 @@ ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const BASEARRAYREF
233209
}
234210
CONTRACTL_END;
235211

236-
// The next 50 lines are a little tricky. Change them with great care.
237-
//
238-
239212
// This first bit is a minor optimization: e.g. when copying byte[] to byte[]
240213
// we do not need to call GetArrayElementTypeHandle().
241214
MethodTable *pSrcMT = pSrc->GetMethodTable();
242215
MethodTable *pDestMT = pDest->GetMethodTable();
243-
if (pSrcMT == pDestMT)
244-
return AssignWillWork;
216+
_ASSERTE(pSrcMT != pDestMT); // Handled by fast path
245217

246218
TypeHandle srcTH = pSrcMT->GetApproxArrayElementTypeHandle();
247219
TypeHandle destTH = pDestMT->GetApproxArrayElementTypeHandle();
248-
if (srcTH == destTH) // This check kicks for different array kind or dimensions
249-
return AssignWillWork;
250-
220+
_ASSERTE(srcTH != destTH); // Handled by fast path
221+
251222
// Value class boxing
252223
if (srcTH.IsValueType() && !destTH.IsValueType())
253224
{
@@ -276,17 +247,15 @@ ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const BASEARRAYREF
276247
// Copying primitives from one type to another
277248
if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType))
278249
{
279-
if (srcElType == destElType)
280-
return AssignWillWork;
250+
_ASSERTE(srcElType != destElType); // Handled by fast path
281251
if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType))
282252
return AssignPrimitiveWiden;
283253
else
284254
return AssignWrongType;
285255
}
286256

287257
// dest Object extends src
288-
if (srcTH.CanCastTo(destTH))
289-
return AssignWillWork;
258+
_ASSERTE(!srcTH.CanCastTo(destTH)); // Handled by fast path
290259

291260
// src Object extends dest
292261
if (destTH.CanCastTo(srcTH))
@@ -766,38 +735,7 @@ void memmoveGCRefs(void *dest, const void *src, size_t len)
766735
}
767736
}
768737

769-
void ArrayNative::ArrayCopyNoTypeCheck(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length)
770-
{
771-
CONTRACTL
772-
{
773-
NOTHROW;
774-
GC_NOTRIGGER;
775-
MODE_COOPERATIVE;
776-
PRECONDITION(pSrc != NULL);
777-
PRECONDITION(srcIndex >= 0);
778-
PRECONDITION(pDest != NULL);
779-
PRECONDITION(length > 0);
780-
}
781-
CONTRACTL_END;
782-
783-
BYTE *src = (BYTE*)pSrc->GetDataPtr();
784-
BYTE *dst = (BYTE*)pDest->GetDataPtr();
785-
SIZE_T size = pSrc->GetComponentSize();
786-
787-
src += srcIndex * size;
788-
dst += destIndex * size;
789-
790-
if (pDest->GetMethodTable()->ContainsPointers())
791-
{
792-
memmoveGCRefs(dst, src, length * size);
793-
}
794-
else
795-
{
796-
memmove(dst, src, length * size);
797-
}
798-
}
799-
800-
FCIMPL6(void, ArrayNative::ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, ArrayBase* m_pDst, INT32 m_iDstIndex, INT32 m_iLength, CLR_BOOL reliable)
738+
FCIMPL5(void, ArrayNative::CopySlow, ArrayBase* pSrc, INT32 iSrcIndex, ArrayBase* pDst, INT32 iDstIndex, INT32 iLength)
801739
{
802740
FCALL_CONTRACT;
803741

@@ -807,115 +745,50 @@ FCIMPL6(void, ArrayNative::ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, Arra
807745
BASEARRAYREF pDst;
808746
} gc;
809747

810-
gc.pSrc = (BASEARRAYREF)m_pSrc;
811-
gc.pDst = (BASEARRAYREF)m_pDst;
812-
813-
//
814-
// creating a HelperMethodFrame is quite expensive,
815-
// so we want to delay this for the most common case which doesn't trigger a GC.
816-
// FCThrow is needed to throw an exception without a HelperMethodFrame
817-
//
748+
gc.pSrc = (BASEARRAYREF)pSrc;
749+
gc.pDst = (BASEARRAYREF)pDst;
818750

819751
// cannot pass null for source or destination
820-
if (gc.pSrc == NULL || gc.pDst == NULL) {
821-
FCThrowArgumentNullVoid(gc.pSrc==NULL ? W("sourceArray") : W("destinationArray"));
822-
}
752+
_ASSERTE(gc.pSrc != NULL && gc.pDst != NULL);
823753

824754
// source and destination must be arrays
825755
_ASSERTE(gc.pSrc->GetMethodTable()->IsArray());
826756
_ASSERTE(gc.pDst->GetMethodTable()->IsArray());
827757

828-
// Equal method tables should imply equal rank
829-
_ASSERTE(!(gc.pSrc->GetMethodTable() == gc.pDst->GetMethodTable() && gc.pSrc->GetRank() != gc.pDst->GetRank()));
758+
_ASSERTE(gc.pSrc->GetRank() == gc.pDst->GetRank());
830759

831-
// Which enables us to avoid touching the EEClass in simple cases
832-
if (gc.pSrc->GetMethodTable() != gc.pDst->GetMethodTable() && gc.pSrc->GetRank() != gc.pDst->GetRank()) {
833-
FCThrowResVoid(kRankException, W("Rank_MustMatch"));
834-
}
835-
836-
g_IBCLogger.LogMethodTableAccess(gc.pSrc->GetMethodTable());
837-
g_IBCLogger.LogMethodTableAccess(gc.pDst->GetMethodTable());
838-
839-
int srcLB = gc.pSrc->GetLowerBoundsPtr()[0];
840-
int destLB = gc.pDst->GetLowerBoundsPtr()[0];
841760
// array bounds checking
842-
const unsigned int srcLen = gc.pSrc->GetNumComponents();
843-
const unsigned int destLen = gc.pDst->GetNumComponents();
844-
if (m_iLength < 0)
845-
FCThrowArgumentOutOfRangeVoid(W("length"), W("ArgumentOutOfRange_NeedNonNegNum"));
846-
847-
if (m_iSrcIndex < srcLB || (m_iSrcIndex - srcLB < 0))
848-
FCThrowArgumentOutOfRangeVoid(W("sourceIndex"), W("ArgumentOutOfRange_ArrayLB"));
849-
850-
if (m_iDstIndex < destLB || (m_iDstIndex - destLB < 0))
851-
FCThrowArgumentOutOfRangeVoid(W("destinationIndex"), W("ArgumentOutOfRange_ArrayLB"));
852-
853-
if ((DWORD)(m_iSrcIndex - srcLB + m_iLength) > srcLen)
854-
FCThrowArgumentVoid(W("sourceArray"), W("Arg_LongerThanSrcArray"));
855-
856-
if ((DWORD)(m_iDstIndex - destLB + m_iLength) > destLen)
857-
FCThrowArgumentVoid(W("destinationArray"), W("Arg_LongerThanDestArray"));
858-
859-
int r = 0;
860-
861-
// Small perf optimization - we copy from one portion of an array back to
862-
// itself a lot when resizing collections, etc. The cost of doing the type
863-
// checking is significant for copying small numbers of bytes (~half of the time
864-
// for copying 1 byte within one array from element 0 to element 1).
865-
if (gc.pSrc == gc.pDst)
866-
r = AssignWillWork;
867-
else
868-
r = CanAssignArrayTypeNoGC(gc.pSrc, gc.pDst);
869-
870-
if (r == AssignWrongType) {
871-
FCThrowResVoid(kArrayTypeMismatchException, W("ArrayTypeMismatch_CantAssignType"));
872-
}
873-
874-
if (r == AssignWillWork) {
875-
if (m_iLength > 0)
876-
ArrayCopyNoTypeCheck(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
877-
878-
FC_GC_POLL();
879-
return;
880-
}
761+
_ASSERTE(iLength >= 0);
762+
_ASSERTE(iSrcIndex >= 0);
763+
_ASSERTE(iDstIndex >= 0);
764+
_ASSERTE((DWORD)(iSrcIndex + iLength) <= gc.pSrc->GetNumComponents());
765+
_ASSERTE((DWORD)(iDstIndex + iLength) <= gc.pDst->GetNumComponents());
881766

882767
HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
883-
if (r == AssignDontKnow)
884-
{
885-
r = CanAssignArrayType(gc.pSrc, gc.pDst);
886-
}
887-
CONSISTENCY_CHECK(r != AssignDontKnow);
888768

889-
// If we were called from Array.ConstrainedCopy, ensure that the array copy
890-
// is guaranteed to succeed.
891-
if (reliable && r != AssignWillWork)
892-
COMPlusThrow(kArrayTypeMismatchException, W("ArrayTypeMismatch_ConstrainedCopy"));
769+
int r = CanAssignArrayType(gc.pSrc, gc.pDst);
893770

894771
if (r == AssignWrongType)
895772
COMPlusThrow(kArrayTypeMismatchException, W("ArrayTypeMismatch_CantAssignType"));
896773

897-
if (m_iLength > 0)
774+
if (iLength > 0)
898775
{
899776
switch (r)
900777
{
901-
case AssignWillWork:
902-
ArrayCopyNoTypeCheck(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
903-
break;
904-
905778
case AssignUnboxValueClass:
906-
UnBoxEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
779+
UnBoxEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength);
907780
break;
908781

909782
case AssignBoxValueClassOrPrimitive:
910-
BoxEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
783+
BoxEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength);
911784
break;
912785

913786
case AssignMustCast:
914-
CastCheckEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
787+
CastCheckEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength);
915788
break;
916789

917790
case AssignPrimitiveWiden:
918-
PrimitiveWiden(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
791+
PrimitiveWiden(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength);
919792
break;
920793

921794
default:

‎src/classlibnative/bcltype/arraynative.h

+2-5
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class ArrayNative
2727
public:
2828
static FCDECL1(void, Initialize, ArrayBase* pArray);
2929

30-
static FCDECL6(void, ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, ArrayBase* m_pDst, INT32 m_iDstIndex, INT32 m_iLength, CLR_BOOL reliable);
30+
static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst);
31+
static FCDECL5(void, CopySlow, ArrayBase* pSrc, INT32 iSrcIndex, ArrayBase* pDst, INT32 iDstIndex, INT32 iLength);
3132

3233
// This method will create a new array of type type, with zero lower
3334
// bounds and rank.
@@ -51,18 +52,14 @@ class ArrayNative
5152
enum AssignArrayEnum
5253
{
5354
AssignWrongType,
54-
AssignWillWork,
5555
AssignMustCast,
5656
AssignBoxValueClassOrPrimitive,
5757
AssignUnboxValueClass,
5858
AssignPrimitiveWiden,
59-
AssignDontKnow,
6059
};
6160

6261
// The following functions are all helpers for ArrayCopy
63-
static AssignArrayEnum CanAssignArrayTypeNoGC(const BASEARRAYREF pSrc, const BASEARRAYREF pDest);
6462
static AssignArrayEnum CanAssignArrayType(const BASEARRAYREF pSrc, const BASEARRAYREF pDest);
65-
static void ArrayCopyNoTypeCheck(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
6663
static void CastCheckEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
6764
static void BoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
6865
static void UnBoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);

‎src/vm/ecalllist.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,8 @@ FCFuncEnd()
716716

717717
FCFuncStart(gArrayFuncs)
718718
FCFuncElement("Initialize", ArrayNative::Initialize)
719-
FCFuncElement("Copy", ArrayNative::ArrayCopy)
719+
FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy)
720+
FCFuncElement("CopySlow", ArrayNative::CopySlow)
720721
FCFuncElement("InternalCreate", ArrayNative::CreateInstance)
721722
FCFuncElement("InternalGetReference", ArrayNative::GetReference)
722723
FCFuncElement("InternalSetValue", ArrayNative::SetValue)

0 commit comments

Comments
 (0)
This repository has been archived.