Rewrite of Array.Copy fast path in C# (dotnet/coreclr#27634)
authorJan Kotas <jkotas@microsoft.com>
Sun, 3 Nov 2019 05:12:46 +0000 (22:12 -0700)
committerGitHub <noreply@github.com>
Sun, 3 Nov 2019 05:12:46 +0000 (22:12 -0700)
Contributes to dotnet/coreclr#27106

Commit migrated from https://github.com/dotnet/coreclr/commit/1b9ee9974bb68d3eab329154203ab1b6c55b198a

src/coreclr/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs
src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs
src/coreclr/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
src/coreclr/src/classlibnative/bcltype/arraynative.cpp
src/coreclr/src/classlibnative/bcltype/arraynative.h
src/coreclr/src/vm/ecalllist.h

index 1a65a8f7112952ebba64b0aa6101b6a6c5a8980a..0665278a5ec1432db55a9ed8a121dc12ac4b510c 100644 (file)
@@ -135,30 +135,133 @@ namespace System
         // Copies length elements from sourceArray, starting at index 0, to
         // destinationArray, starting at index 0.
         //
-        public static void Copy(Array sourceArray, Array destinationArray, int length)
+        public static unsafe void Copy(Array sourceArray, Array destinationArray, int length)
         {
             if (sourceArray == null)
                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.sourceArray);
             if (destinationArray == null)
                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray);
 
-            Copy(sourceArray, sourceArray.GetLowerBound(0), destinationArray, destinationArray.GetLowerBound(0), length, false);
+            MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray);
+            if (pMT == RuntimeHelpers.GetMethodTable(destinationArray) &&
+                !pMT->IsMultiDimensionalArray &&
+                (uint)length <= (uint)sourceArray.Length &&
+                (uint)length <= (uint)destinationArray.Length)
+            {
+                nuint byteCount = (uint)length * (nuint)pMT->ComponentSize;
+                ref byte src = ref sourceArray.GetRawSzArrayData();
+                ref byte dst = ref destinationArray.GetRawSzArrayData();
+
+                if (pMT->ContainsGCPointers)
+                    Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
+                else
+                    Buffer.Memmove(ref dst, ref src, byteCount);
+
+                // GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
+                return;
+            }
+
+            // Less common
+            Copy(sourceArray, sourceArray.GetLowerBound(0), destinationArray, destinationArray.GetLowerBound(0), length, reliable: false);
         }
 
         // Copies length elements from sourceArray, starting at sourceIndex, to
         // destinationArray, starting at destinationIndex.
         //
-        public static void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
+        public static unsafe void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
+        {
+            if (sourceArray != null && destinationArray != null)
+            {
+                MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray);
+                if (pMT == RuntimeHelpers.GetMethodTable(destinationArray) &&
+                    !pMT->IsMultiDimensionalArray &&
+                    length >= 0 && sourceIndex >= 0 && destinationIndex >= 0 &&
+                    (uint)(sourceIndex + length) <= (uint)sourceArray.Length &&
+                    (uint)(destinationIndex + length) <= (uint)destinationArray.Length)
+                {
+                    nuint elementSize = (nuint)pMT->ComponentSize;
+                    nuint byteCount = (uint)length * elementSize;
+                    ref byte src = ref Unsafe.AddByteOffset(ref sourceArray.GetRawSzArrayData(), (uint)sourceIndex * elementSize);
+                    ref byte dst = ref Unsafe.AddByteOffset(ref destinationArray.GetRawSzArrayData(), (uint)destinationIndex * elementSize);
+
+                    if (pMT->ContainsGCPointers)
+                        Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
+                    else
+                        Buffer.Memmove(ref dst, ref src, byteCount);
+
+                    // GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
+                    return;
+                }
+            }
+
+            // Less common
+            Copy(sourceArray!, sourceIndex, destinationArray!, destinationIndex, length, reliable: false);
+        }
+
+        private static unsafe void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable)
         {
-            Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length, false);
+            if (sourceArray == null)
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.sourceArray);
+            if (destinationArray == null)
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray);
+
+            if (sourceArray.GetType() != destinationArray.GetType() && sourceArray.Rank != destinationArray.Rank)
+                throw new RankException(SR.Rank_MustMatch);
+
+            if (length < 0)
+                throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+            int srcLB = sourceArray.GetLowerBound(0);
+            if (sourceIndex < srcLB || sourceIndex - srcLB < 0)
+                throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_ArrayLB);
+            sourceIndex -= srcLB;
+
+            int dstLB = destinationArray.GetLowerBound(0);
+            if (destinationIndex < dstLB || destinationIndex - dstLB < 0)
+                throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_ArrayLB);
+            destinationIndex -= dstLB;
+
+            if ((uint)(sourceIndex + length) > (uint)sourceArray.Length)
+                throw new ArgumentException(SR.Arg_LongerThanSrcArray, nameof(sourceArray));
+            if ((uint)(destinationIndex + length) > (uint)destinationArray.Length)
+                throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray));
+
+            if (sourceArray.GetType() == destinationArray.GetType() || IsSimpleCopy(sourceArray, destinationArray))
+            {
+                MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray);
+
+                nuint elementSize = (nuint)pMT->ComponentSize;
+                nuint byteCount = (uint)length * elementSize;
+                ref byte src = ref Unsafe.AddByteOffset(ref sourceArray.GetRawArrayData(), (uint)sourceIndex * elementSize);
+                ref byte dst = ref Unsafe.AddByteOffset(ref destinationArray.GetRawArrayData(), (uint)destinationIndex * elementSize);
+
+                if (pMT->ContainsGCPointers)
+                    Buffer.BulkMoveWithWriteBarrier(ref dst, ref src, byteCount);
+                else
+                    Buffer.Memmove(ref dst, ref src, byteCount);
+
+                // GC.KeepAlive(sourceArray) not required. pMT kept alive via sourceArray
+                return;
+            }
+
+            // If we were called from Array.ConstrainedCopy, ensure that the array copy
+            // is guaranteed to succeed.
+            if (reliable)
+                throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy);
+
+            // Rare
+            CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length);
         }
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool IsSimpleCopy(Array sourceArray, Array destinationArray);
+
         // Reliability-wise, this method will either possibly corrupt your
         // instance & might fail when called from within a CER, or if the
         // reliable flag is true, it will either always succeed or always
         // throw an exception with no side effects.
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable);
+        private static extern void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length);
 
         // Provides a strong exception guarantee - either it succeeds, or
         // it throws an exception with no side effects.  The arrays must be
@@ -167,7 +270,7 @@ namespace System
         // It will up-cast, assuming the array types are correct.
         public static void ConstrainedCopy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length)
         {
-            Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length, true);
+            Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length, reliable: true);
         }
 
         // Sets length elements in array to 0 (or null for Object arrays), starting
@@ -194,10 +297,10 @@ namespace System
             if (index < lowerBound || offset < 0 || length < 0 || (uint)(offset + length) > (uint)array.Length)
                 ThrowHelper.ThrowIndexOutOfRangeException();
 
-            uint elementSize = pMT->ComponentSize;
+            nuint elementSize = pMT->ComponentSize;
 
-            ref byte ptr = ref Unsafe.AddByteOffset(ref p, (uint)offset * (nuint)elementSize);
-            nuint byteLength = (uint)length * (nuint)elementSize;
+            ref byte ptr = ref Unsafe.AddByteOffset(ref p, (uint)offset * elementSize);
+            nuint byteLength = (uint)length * elementSize;
 
             if (pMT->ContainsGCPointers)
                 SpanHelpers.ClearWithReferences(ref Unsafe.As<byte, IntPtr>(ref ptr), byteLength / (uint)sizeof(IntPtr));
index 34dd7c9c2531dffdd2aebd7ae31455ad3294b86a..91ada9172ffb4bc4cb1b663c9e520e8e2df0b516 100644 (file)
@@ -193,7 +193,7 @@ namespace System.Runtime.InteropServices
         /// structure contains pointers to allocated blocks and "fDeleteOld" is
         /// true, this routine will call DestroyStructure() first.
         /// </summary>
-        [MethodImpl(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+        [MethodImpl(MethodImplOptions.InternalCall)]
         public static extern void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld);
 
         private static object PtrToStructureHelper(IntPtr ptr, Type structureType)
index 5e3899a92dfb1c3f017aaa78d98470d2bdd81277..fe03fd6f5c4c1d54741498734aaf56d6f814a227 100644 (file)
@@ -276,7 +276,7 @@ namespace System.Threading
         [MethodImpl(MethodImplOptions.NoInlining)]
         private static Thread InitializeCurrentThread() => t_currentThread = GetCurrentThreadNative();
 
-        [MethodImpl(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+        [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern Thread GetCurrentThreadNative();
 
         private void SetStartHelper(Delegate start, int maxStackSize)
index ac885f69b6d39e162801dafa4870d97be9ad156a..6c9d655cf0d3a609ff877acdb99d042059d04e37 100644 (file)
@@ -129,95 +129,71 @@ FCIMPL1(void, ArrayNative::Initialize, ArrayBase* array)
 FCIMPLEND
 
 
-
-
-
-
-    // Returns an enum saying whether you can copy an array of srcType into destType.
-ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayTypeNoGC(const BASEARRAYREF pSrc, const BASEARRAYREF pDest)
+    // Returns whether you can directly copy an array of srcType into destType.
+FCIMPL2(FC_BOOL_RET, ArrayNative::IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst)
 {
-    CONTRACTL
-    {
-        NOTHROW;
-        GC_NOTRIGGER;
-        MODE_COOPERATIVE;
-        PRECONDITION(pSrc != NULL);
-        PRECONDITION(pDest != NULL);
-    }
-    CONTRACTL_END;
+    FCALL_CONTRACT;
 
-    // The next 50 lines are a little tricky.  Change them with great care.
-    //
+    _ASSERTE(pSrc != NULL);
+    _ASSERTE(pDst != NULL);
 
-    // This first bit is a minor optimization: e.g. when copying byte[] to byte[]
-    // we do not need to call GetArrayElementTypeHandle().
-    MethodTable *pSrcMT = pSrc->GetMethodTable();
-    MethodTable *pDestMT = pDest->GetMethodTable();
-    if (pSrcMT == pDestMT)
-        return AssignWillWork;
+    // This case is expected to be handled by the fast path
+    _ASSERTE(pSrc->GetMethodTable() != pDst->GetMethodTable());
 
-    TypeHandle srcTH = pSrcMT->GetApproxArrayElementTypeHandle();
-    TypeHandle destTH = pDestMT->GetApproxArrayElementTypeHandle();
+    TypeHandle srcTH = pSrc->GetMethodTable()->GetApproxArrayElementTypeHandle();
+    TypeHandle destTH = pDst->GetMethodTable()->GetApproxArrayElementTypeHandle();
     if (srcTH == destTH) // This check kicks for different array kind or dimensions
-        return AssignWillWork;
+        FC_RETURN_BOOL(true);
 
-    // Value class boxing
-    if (srcTH.IsValueType() && !destTH.IsValueType())
+    if (srcTH.IsValueType())
     {
-        switch (srcTH.CanCastToCached(destTH))
+        // Value class boxing
+        if (!destTH.IsValueType())
+            FC_RETURN_BOOL(false);
+
+        const CorElementType srcElType = srcTH.GetVerifierCorElementType();
+        const CorElementType destElType = destTH.GetVerifierCorElementType();
+        _ASSERTE(srcElType < ELEMENT_TYPE_MAX);
+        _ASSERTE(destElType < ELEMENT_TYPE_MAX);
+
+        // Copying primitives from one type to another
+        if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType))
         {
-        case TypeHandle::CanCast : return AssignBoxValueClassOrPrimitive;
-        case TypeHandle::CannotCast : return AssignWrongType;
-        default : return AssignDontKnow;
+            if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType))
+                FC_RETURN_BOOL(true);
         }
     }
-
-    // Value class unboxing.
-    if (!srcTH.IsValueType() && destTH.IsValueType())
+    else
     {
-        if (srcTH.CanCastToCached(destTH) == TypeHandle::CanCast)
-            return AssignUnboxValueClass;
-        else if (destTH.CanCastToCached(srcTH) == TypeHandle::CanCast)   // V extends IV. Copying from IV to V, or Object to V.
-            return AssignUnboxValueClass;
-        else
-            return AssignDontKnow;
+        // Value class unboxing
+        if (destTH.IsValueType())
+            FC_RETURN_BOOL(false);
     }
 
-    const CorElementType srcElType = srcTH.GetVerifierCorElementType();
-    const CorElementType destElType = destTH.GetVerifierCorElementType();
-    _ASSERTE(srcElType < ELEMENT_TYPE_MAX);
-    _ASSERTE(destElType < ELEMENT_TYPE_MAX);
-
-    // Copying primitives from one type to another
-    if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType))
+    TypeHandle::CastResult r = srcTH.CanCastToCached(destTH);
+    if (r != TypeHandle::MaybeCast)
     {
-        if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType))
-            return AssignWillWork;
-
-        if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType))
-            return AssignPrimitiveWiden;
-        else
-            return AssignWrongType;
+        FC_RETURN_BOOL(r);
     }
 
-    // dest Object extends src
-    if (srcTH.CanCastToCached(destTH) == TypeHandle::CanCast)
-        return AssignWillWork;
+    struct
+    {
+        OBJECTREF   src;
+        OBJECTREF   dst;
+    } gc;
 
-    // src Object extends dest
-    if (destTH.CanCastToCached(srcTH) == TypeHandle::CanCast)
-        return AssignMustCast;
+    gc.src = ObjectToOBJECTREF(pSrc);
+    gc.dst = ObjectToOBJECTREF(pDst);
 
-    // class X extends/implements src and implements dest.
-    if (destTH.IsInterface() && srcElType != ELEMENT_TYPE_VALUETYPE)
-        return AssignMustCast;
+    BOOL iRetVal = FALSE;
 
-    // class X implements src and extends/implements dest
-    if (srcTH.IsInterface() && destElType != ELEMENT_TYPE_VALUETYPE)
-        return AssignMustCast;
+    HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
+    iRetVal = srcTH.CanCastTo(destTH);
+    HELPER_METHOD_FRAME_END();
 
-    return AssignDontKnow;
+    FC_RETURN_BOOL(iRetVal);
 }
+FCIMPLEND
 
 
 // Returns an enum saying whether you can copy an array of srcType into destType.
@@ -233,21 +209,16 @@ ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const BASEARRAYREF
     }
     CONTRACTL_END;
 
-    // The next 50 lines are a little tricky.  Change them with great care.
-    //
-
     // This first bit is a minor optimization: e.g. when copying byte[] to byte[]
     // we do not need to call GetArrayElementTypeHandle().
     MethodTable *pSrcMT = pSrc->GetMethodTable();
     MethodTable *pDestMT = pDest->GetMethodTable();
-    if (pSrcMT == pDestMT)
-        return AssignWillWork;
+    _ASSERTE(pSrcMT != pDestMT); // Handled by fast path
 
     TypeHandle srcTH = pSrcMT->GetApproxArrayElementTypeHandle();
     TypeHandle destTH = pDestMT->GetApproxArrayElementTypeHandle();
-    if (srcTH == destTH) // This check kicks for different array kind or dimensions
-        return AssignWillWork;
-
+    _ASSERTE(srcTH != destTH);  // Handled by fast path
+    
     // Value class boxing
     if (srcTH.IsValueType() && !destTH.IsValueType())
     {
@@ -276,8 +247,7 @@ ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const BASEARRAYREF
     // Copying primitives from one type to another
     if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType))
     {
-        if (srcElType == destElType)
-            return AssignWillWork;
+        _ASSERTE(srcElType != destElType); // Handled by fast path
         if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType))
             return AssignPrimitiveWiden;
         else
@@ -285,8 +255,7 @@ ArrayNative::AssignArrayEnum ArrayNative::CanAssignArrayType(const BASEARRAYREF
     }
 
     // dest Object extends src
-    if (srcTH.CanCastTo(destTH))
-        return AssignWillWork;
+    _ASSERTE(!srcTH.CanCastTo(destTH)); // Handled by fast path
 
     // src Object extends dest
     if (destTH.CanCastTo(srcTH))
@@ -766,38 +735,7 @@ void memmoveGCRefs(void *dest, const void *src, size_t len)
     }
 }
 
-void ArrayNative::ArrayCopyNoTypeCheck(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length)
-{
-    CONTRACTL
-    {
-        NOTHROW;
-        GC_NOTRIGGER;
-        MODE_COOPERATIVE;
-        PRECONDITION(pSrc != NULL);
-        PRECONDITION(srcIndex >= 0);
-        PRECONDITION(pDest != NULL);
-        PRECONDITION(length > 0);
-    }
-    CONTRACTL_END;
-
-    BYTE *src = (BYTE*)pSrc->GetDataPtr();
-    BYTE *dst = (BYTE*)pDest->GetDataPtr();
-    SIZE_T size = pSrc->GetComponentSize();
-
-    src += srcIndex * size;
-    dst += destIndex * size;
-
-    if (pDest->GetMethodTable()->ContainsPointers())
-    {
-        memmoveGCRefs(dst, src, length * size);
-    }
-    else
-    {
-        memmove(dst, src, length * size);
-    }
-}
-
-FCIMPL6(void, ArrayNative::ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, ArrayBase* m_pDst, INT32 m_iDstIndex, INT32 m_iLength, CLR_BOOL reliable)
+FCIMPL5(void, ArrayNative::CopySlow, ArrayBase* pSrc, INT32 iSrcIndex, ArrayBase* pDst, INT32 iDstIndex, INT32 iLength)
 {
     FCALL_CONTRACT;
 
@@ -807,115 +745,50 @@ FCIMPL6(void, ArrayNative::ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, Arra
         BASEARRAYREF pDst;
     } gc;
 
-    gc.pSrc = (BASEARRAYREF)m_pSrc;
-    gc.pDst = (BASEARRAYREF)m_pDst;
-
-    //
-    // creating a HelperMethodFrame is quite expensive,
-    // so we want to delay this for the most common case which doesn't trigger a GC.
-    // FCThrow is needed to throw an exception without a HelperMethodFrame
-    //
+    gc.pSrc = (BASEARRAYREF)pSrc;
+    gc.pDst = (BASEARRAYREF)pDst;
 
     // cannot pass null for source or destination
-    if (gc.pSrc == NULL || gc.pDst == NULL) {
-        FCThrowArgumentNullVoid(gc.pSrc==NULL ? W("sourceArray") : W("destinationArray"));
-    }
+    _ASSERTE(gc.pSrc != NULL && gc.pDst != NULL);
 
     // source and destination must be arrays
     _ASSERTE(gc.pSrc->GetMethodTable()->IsArray());
     _ASSERTE(gc.pDst->GetMethodTable()->IsArray());
 
-    // Equal method tables should imply equal rank
-    _ASSERTE(!(gc.pSrc->GetMethodTable() == gc.pDst->GetMethodTable() && gc.pSrc->GetRank() != gc.pDst->GetRank()));
+    _ASSERTE(gc.pSrc->GetRank() == gc.pDst->GetRank());
 
-    // Which enables us to avoid touching the EEClass in simple cases
-    if (gc.pSrc->GetMethodTable() != gc.pDst->GetMethodTable() && gc.pSrc->GetRank() != gc.pDst->GetRank()) {
-        FCThrowResVoid(kRankException, W("Rank_MustMatch"));
-    }
-
-    g_IBCLogger.LogMethodTableAccess(gc.pSrc->GetMethodTable());
-    g_IBCLogger.LogMethodTableAccess(gc.pDst->GetMethodTable());
-
-    int srcLB = gc.pSrc->GetLowerBoundsPtr()[0];
-    int destLB = gc.pDst->GetLowerBoundsPtr()[0];
     // array bounds checking
-    const unsigned int srcLen = gc.pSrc->GetNumComponents();
-    const unsigned int destLen = gc.pDst->GetNumComponents();
-    if (m_iLength < 0)
-        FCThrowArgumentOutOfRangeVoid(W("length"), W("ArgumentOutOfRange_NeedNonNegNum"));
-
-    if (m_iSrcIndex < srcLB || (m_iSrcIndex - srcLB < 0))
-        FCThrowArgumentOutOfRangeVoid(W("sourceIndex"), W("ArgumentOutOfRange_ArrayLB"));
-
-    if (m_iDstIndex < destLB || (m_iDstIndex - destLB < 0))
-        FCThrowArgumentOutOfRangeVoid(W("destinationIndex"), W("ArgumentOutOfRange_ArrayLB"));
-
-    if ((DWORD)(m_iSrcIndex - srcLB + m_iLength) > srcLen)
-        FCThrowArgumentVoid(W("sourceArray"), W("Arg_LongerThanSrcArray"));
-
-    if ((DWORD)(m_iDstIndex - destLB + m_iLength) > destLen)
-        FCThrowArgumentVoid(W("destinationArray"), W("Arg_LongerThanDestArray"));
-
-    int r = 0;
-
-    // Small perf optimization - we copy from one portion of an array back to
-    // itself a lot when resizing collections, etc.  The cost of doing the type
-    // checking is significant for copying small numbers of bytes (~half of the time
-    // for copying 1 byte within one array from element 0 to element 1).
-    if (gc.pSrc == gc.pDst)
-        r = AssignWillWork;
-    else
-        r = CanAssignArrayTypeNoGC(gc.pSrc, gc.pDst);
-
-    if (r == AssignWrongType) {
-        FCThrowResVoid(kArrayTypeMismatchException, W("ArrayTypeMismatch_CantAssignType"));
-    }
-
-    if (r == AssignWillWork) {
-        if (m_iLength > 0)
-            ArrayCopyNoTypeCheck(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
-
-        FC_GC_POLL();
-        return;
-    }
+    _ASSERTE(iLength >= 0);
+    _ASSERTE(iSrcIndex >= 0);
+    _ASSERTE(iDstIndex >= 0);
+    _ASSERTE((DWORD)(iSrcIndex + iLength) <= gc.pSrc->GetNumComponents());
+    _ASSERTE((DWORD)(iDstIndex + iLength) <= gc.pDst->GetNumComponents());
 
     HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
-    if (r == AssignDontKnow)
-    {
-        r = CanAssignArrayType(gc.pSrc, gc.pDst);
-    }
-    CONSISTENCY_CHECK(r != AssignDontKnow);
 
-    // If we were called from Array.ConstrainedCopy, ensure that the array copy
-    // is guaranteed to succeed.
-    if (reliable && r != AssignWillWork)
-        COMPlusThrow(kArrayTypeMismatchException, W("ArrayTypeMismatch_ConstrainedCopy"));
+    int r = CanAssignArrayType(gc.pSrc, gc.pDst);
 
     if (r == AssignWrongType)
         COMPlusThrow(kArrayTypeMismatchException, W("ArrayTypeMismatch_CantAssignType"));
 
-    if (m_iLength > 0)
+    if (iLength > 0)
     {
         switch (r)
         {
-            case AssignWillWork:
-                ArrayCopyNoTypeCheck(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
-                break;
-
             case AssignUnboxValueClass:
-                UnBoxEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
+                UnBoxEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength);
                 break;
 
             case AssignBoxValueClassOrPrimitive:
-                BoxEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
+                BoxEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength);
                 break;
 
             case AssignMustCast:
-                CastCheckEachElement(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
+                CastCheckEachElement(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength);
                 break;
 
             case AssignPrimitiveWiden:
-                PrimitiveWiden(gc.pSrc, m_iSrcIndex - srcLB, gc.pDst, m_iDstIndex - destLB, m_iLength);
+                PrimitiveWiden(gc.pSrc, iSrcIndex, gc.pDst, iDstIndex, iLength);
                 break;
 
             default:
index 02197bfdedfbc6fa4c934cac4e8106c6ace5ecc8..2f587ade415718f1b4af42817403b41ca47cba9c 100644 (file)
@@ -27,7 +27,8 @@ class ArrayNative
 public:
     static FCDECL1(void, Initialize, ArrayBase* pArray);
 
-    static FCDECL6(void, ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, ArrayBase* m_pDst, INT32 m_iDstIndex, INT32 m_iLength, CLR_BOOL reliable);
+    static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst);
+    static FCDECL5(void, CopySlow, ArrayBase* pSrc, INT32 iSrcIndex, ArrayBase* pDst, INT32 iDstIndex, INT32 iLength);
 
     // This method will create a new array of type type, with zero lower
     // bounds and rank.
@@ -51,18 +52,14 @@ private:
     enum AssignArrayEnum
     {
         AssignWrongType,
-        AssignWillWork,
         AssignMustCast,
         AssignBoxValueClassOrPrimitive,
         AssignUnboxValueClass,
         AssignPrimitiveWiden,
-        AssignDontKnow,
     };
 
     // The following functions are all helpers for ArrayCopy
-    static AssignArrayEnum CanAssignArrayTypeNoGC(const BASEARRAYREF pSrc, const BASEARRAYREF pDest);
     static AssignArrayEnum CanAssignArrayType(const BASEARRAYREF pSrc, const BASEARRAYREF pDest);
-    static void ArrayCopyNoTypeCheck(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
     static void CastCheckEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
     static void BoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
     static void UnBoxEachElement(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length);
index 56de255796abd764e308b6a640842924c61e4008..746bd011493a7e8b10f20ea8b3058e7328c6061a 100644 (file)
@@ -716,7 +716,8 @@ FCFuncEnd()
 
 FCFuncStart(gArrayFuncs)
     FCFuncElement("Initialize", ArrayNative::Initialize)
-    FCFuncElement("Copy", ArrayNative::ArrayCopy)
+    FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy)
+    FCFuncElement("CopySlow", ArrayNative::CopySlow)
     FCFuncElement("InternalCreate", ArrayNative::CreateInstance)
     FCFuncElement("InternalGetReference", ArrayNative::GetReference)
     FCFuncElement("InternalSetValue", ArrayNative::SetValue)