Rewrite the rest of the array non-generic fast paths in C# (dotnet/coreclr#27703)
authorJan Kotas <jkotas@microsoft.com>
Sat, 9 Nov 2019 21:24:03 +0000 (22:24 +0100)
committerGitHub <noreply@github.com>
Sat, 9 Nov 2019 21:24:03 +0000 (22:24 +0100)
* Rewrite the rest of the array non-generic fast paths in C#

* Add missing resource string

* Use CorElementType and handle native ints to match the unmanaged implementation

* Avoid try pattern for Sort and Reverse

* Match the native implementation more closely

Commit migrated from https://github.com/dotnet/coreclr/commit/89f5caabf0939f89a5081373ae21b0669616db6b

17 files changed:
src/coreclr/src/System.Private.CoreLib/Resources/Strings.resx
src/coreclr/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs
src/coreclr/src/System.Private.CoreLib/src/System/Buffer.CoreCLR.cs
src/coreclr/src/classlibnative/bcltype/CMakeLists.txt
src/coreclr/src/classlibnative/bcltype/arrayhelpers.cpp [deleted file]
src/coreclr/src/classlibnative/bcltype/arrayhelpers.h [deleted file]
src/coreclr/src/classlibnative/bcltype/arraynative.cpp
src/coreclr/src/classlibnative/bcltype/arraynative.h
src/coreclr/src/vm/comutilnative.cpp
src/coreclr/src/vm/comutilnative.h
src/coreclr/src/vm/ecalllist.h
src/coreclr/src/vm/mscorlib.cpp
src/libraries/System.Private.CoreLib/src/System/Array.cs
src/libraries/System.Private.CoreLib/src/System/Buffer.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs
src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs

index 41547d7..f1a5053 100644 (file)
   <data name="Argument_CannotBeNullOrEmpty" xml:space="preserve">
     <value>Argument cannot be null or empty.</value>
   </data>
+  <data name="Argument_SpansMustHaveSameLength" xml:space="preserve">
+    <value>Length of items must be same as length of keys.</value>
+  </data>
 </root>
index d10460a..d332101 100644 (file)
@@ -6,6 +6,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
+using System.Reflection;
 using Internal.Runtime.CompilerServices;
 
 #pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
@@ -475,16 +476,10 @@ namespace System
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool TrySZBinarySearch(Array sourceArray, int sourceIndex, int count, object? value, out int retVal);
+        internal extern CorElementType GetCorElementTypeOfElementType();
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool TrySZIndexOf(Array sourceArray, int sourceIndex, int count, object? value, out int retVal);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool TrySZLastIndexOf(Array sourceArray, int sourceIndex, int count, object? value, out int retVal);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool TrySZReverse(Array array, int index, int count);
+        private extern bool IsValueOfElementType(object value);
 
         // if this is an array of value classes and that value class has a default constructor
         // then this calls this default constructor on every element in the value class array.
index c5db412..c99b2ac 100644 (file)
@@ -20,11 +20,6 @@ namespace System
 {
     public partial class Buffer
     {
-        // Returns a bool to indicate if the array is of primitive data types
-        // or not.
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool IsPrimitiveTypeArray(Array array);
-
         // Non-inlinable wrapper around the QCall that avoids polluting the fast path
         // with P/Invoke prolog/epilog.
         [MethodImpl(MethodImplOptions.NoInlining)]
index 67d1ad2..391f70e 100644 (file)
@@ -2,7 +2,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
 set(BCLTYPE_SOURCES
     arraynative.cpp
-    arrayhelpers.cpp
     oavariant.cpp
     objectnative.cpp
     stringnative.cpp
diff --git a/src/coreclr/src/classlibnative/bcltype/arrayhelpers.cpp b/src/coreclr/src/classlibnative/bcltype/arrayhelpers.cpp
deleted file mode 100644 (file)
index 27d332a..0000000
+++ /dev/null
@@ -1,324 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-//
-// File: ArrayHelpers.cpp
-//
-
-//
-
-#include "common.h"
-
-#include <object.h>
-#include "ceeload.h"
-
-#include "excep.h"
-#include "frames.h"
-#include "vars.hpp"
-#include "classnames.h"
-#include "arrayhelpers.h"
-#include <memory.h>
-
-INT32 ArrayHelper::IndexOfUINT8( UINT8* array, UINT32 index, UINT32 count, UINT8 value) {
-    LIMITED_METHOD_CONTRACT;
-    UINT8 * pvalue = (UINT8 *)memchr(array + index, value, count);
-    if ( NULL == pvalue ) {
-        return -1;
-    }
-    else {
-        return static_cast<INT32>(pvalue - array);
-    }
-}
-
-
-// A fast IndexOf method for arrays of primitive types.  Returns TRUE or FALSE
-// if it succeeds, and stores result in retVal.
-FCIMPL5(FC_BOOL_RET, ArrayHelper::TrySZIndexOf, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal)
-    FCALL_CONTRACT;
-
-    VALIDATEOBJECT(array);
-    _ASSERTE(array != NULL);
-
-    // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
-    // non-zero lower bounds.  VB might care.  </TODO>
-    if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
-        FC_RETURN_BOOL(FALSE);
-
-    _ASSERTE(retVal != NULL);
-       _ASSERTE(index <= array->GetNumComponents());
-       _ASSERTE(count <= array->GetNumComponents());
-       _ASSERTE(array->GetNumComponents() >= index + count);
-    *retVal = 0xdeadbeef;  // Initialize the return value.
-    // value can be NULL, but of course, will not be in primitive arrays.
-
-    TypeHandle arrayTH = array->GetArrayElementTypeHandle();
-    const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
-    if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
-        FC_RETURN_BOOL(FALSE);
-    // Handle special case of looking for a NULL object in a primitive array.
-    if (value == NULL) {
-        *retVal = -1;
-        FC_RETURN_BOOL(TRUE);
-    }
-    TypeHandle valueTH = value->GetTypeHandle();
-    if (arrayTH != valueTH)
-        FC_RETURN_BOOL(FALSE);
-
-
-    switch(arrayElType) {
-    case ELEMENT_TYPE_I1:
-    case ELEMENT_TYPE_U1:
-    case ELEMENT_TYPE_BOOLEAN:
-        *retVal = IndexOfUINT8((U1*) array->GetDataPtr(), index, count, *(U1*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_I2:
-    case ELEMENT_TYPE_U2:
-    case ELEMENT_TYPE_CHAR:
-        *retVal = ArrayHelpers<U2>::IndexOf((U2*) array->GetDataPtr(), index, count, *(U2*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_I4:
-    case ELEMENT_TYPE_U4:
-    case ELEMENT_TYPE_R4:
-    IN_TARGET_32BIT(case ELEMENT_TYPE_I:)
-    IN_TARGET_32BIT(case ELEMENT_TYPE_U:)
-        *retVal = ArrayHelpers<U4>::IndexOf((U4*) array->GetDataPtr(), index, count, *(U4*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_I8:
-    case ELEMENT_TYPE_U8:
-    case ELEMENT_TYPE_R8:
-    IN_TARGET_64BIT(case ELEMENT_TYPE_I:)
-    IN_TARGET_64BIT(case ELEMENT_TYPE_U:)
-        *retVal = ArrayHelpers<U8>::IndexOf((U8*) array->GetDataPtr(), index, count, *(U8*)value->UnBox());
-        break;
-
-    default:
-        _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZIndexOf");
-        FC_RETURN_BOOL(FALSE);
-    }
-    FC_RETURN_BOOL(TRUE);
-FCIMPLEND
-
-// A fast LastIndexOf method for arrays of primitive types.  Returns TRUE or FALSE
-// if it succeeds, and stores result in retVal.
-FCIMPL5(FC_BOOL_RET, ArrayHelper::TrySZLastIndexOf, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal)
-{
-    FCALL_CONTRACT;
-
-    VALIDATEOBJECT(array);
-    _ASSERTE(array != NULL);
-
-    // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
-    // non-zero lower bounds.  VB might care.  </TODO>
-    if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
-        FC_RETURN_BOOL(FALSE);
-
-    _ASSERTE(retVal != NULL);
-    *retVal = 0xdeadbeef;  // Initialize the return value.
-    // value can be NULL, but of course, will not be in primitive arrays.
-
-    TypeHandle arrayTH = array->GetArrayElementTypeHandle();
-    const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
-    if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
-        FC_RETURN_BOOL(FALSE);
-    // Handle special case of looking for a NULL object in a primitive array.
-    // Also handle case where the array is of 0 length.
-    if (value == NULL || array->GetNumComponents() == 0) {
-        *retVal = -1;
-        FC_RETURN_BOOL(TRUE);
-    }
-
-       _ASSERTE(index < array->GetNumComponents());
-       _ASSERTE(count <= array->GetNumComponents());
-    _ASSERTE(index + 1 >= count);
-
-    TypeHandle valueTH = value->GetTypeHandle();
-    if (arrayTH != valueTH)
-        FC_RETURN_BOOL(FALSE);
-
-
-    switch(arrayElType) {
-    case ELEMENT_TYPE_I1:
-    case ELEMENT_TYPE_U1:
-    case ELEMENT_TYPE_BOOLEAN:
-        *retVal = ArrayHelpers<U1>::LastIndexOf((U1*) array->GetDataPtr(), index, count, *(U1*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_I2:
-    case ELEMENT_TYPE_U2:
-    case ELEMENT_TYPE_CHAR:
-        *retVal = ArrayHelpers<U2>::LastIndexOf((U2*) array->GetDataPtr(), index, count, *(U2*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_I4:
-    case ELEMENT_TYPE_U4:
-    case ELEMENT_TYPE_R4:
-    IN_TARGET_32BIT(case ELEMENT_TYPE_I:)
-    IN_TARGET_32BIT(case ELEMENT_TYPE_U:)
-        *retVal = ArrayHelpers<U4>::LastIndexOf((U4*) array->GetDataPtr(), index, count, *(U4*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_I8:
-    case ELEMENT_TYPE_U8:
-    case ELEMENT_TYPE_R8:
-    IN_TARGET_64BIT(case ELEMENT_TYPE_I:)
-    IN_TARGET_64BIT(case ELEMENT_TYPE_U:)
-        *retVal = ArrayHelpers<U8>::LastIndexOf((U8*) array->GetDataPtr(), index, count, *(U8*)value->UnBox());
-        break;
-
-    default:
-        _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZLastIndexOf");
-        FC_RETURN_BOOL(FALSE);
-    }
-    FC_RETURN_BOOL(TRUE);
-}
-FCIMPLEND
-
-
-FCIMPL5(FC_BOOL_RET, ArrayHelper::TrySZBinarySearch, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal)
-    FCALL_CONTRACT;
-
-    VALIDATEOBJECT(array);
-    _ASSERTE(array != NULL);
-
-    // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
-    // non-zero lower bounds.  VB might care.  </TODO>
-    if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
-        FC_RETURN_BOOL(FALSE);
-
-    _ASSERTE(retVal != NULL);
-       _ASSERTE(index <= array->GetNumComponents());
-       _ASSERTE(count <= array->GetNumComponents());
-       _ASSERTE(array->GetNumComponents() >= index + count);
-    *retVal = 0xdeadbeef;  // Initialize the return value.
-    // value can be NULL, but of course, will not be in primitive arrays.
-    TypeHandle arrayTH = array->GetArrayElementTypeHandle();
-    const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
-    if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
-        FC_RETURN_BOOL(FALSE);
-    // Handle special case of looking for a NULL object in a primitive array.
-    if (value == NULL) {
-        *retVal = -1;
-        FC_RETURN_BOOL(TRUE);
-    }
-
-    TypeHandle valueTH = value->GetTypeHandle();
-    if (arrayTH != valueTH)
-        FC_RETURN_BOOL(FALSE);
-
-    switch(arrayElType) {
-    case ELEMENT_TYPE_I1:
-               *retVal = ArrayHelpers<I1>::BinarySearchBitwiseEquals((I1*) array->GetDataPtr(), index, count, *(I1*)value->UnBox());
-               break;
-
-    case ELEMENT_TYPE_U1:
-    case ELEMENT_TYPE_BOOLEAN:
-        *retVal = ArrayHelpers<U1>::BinarySearchBitwiseEquals((U1*) array->GetDataPtr(), index, count, *(U1*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_I2:
-        *retVal = ArrayHelpers<I2>::BinarySearchBitwiseEquals((I2*) array->GetDataPtr(), index, count, *(I2*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_U2:
-    case ELEMENT_TYPE_CHAR:
-        *retVal = ArrayHelpers<U2>::BinarySearchBitwiseEquals((U2*) array->GetDataPtr(), index, count, *(U2*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_I4:
-        *retVal = ArrayHelpers<I4>::BinarySearchBitwiseEquals((I4*) array->GetDataPtr(), index, count, *(I4*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_U4:
-        *retVal = ArrayHelpers<U4>::BinarySearchBitwiseEquals((U4*) array->GetDataPtr(), index, count, *(U4*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_R4:
-        *retVal = ArrayHelpers<R4>::BinarySearchBitwiseEquals((R4*) array->GetDataPtr(), index, count, *(R4*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_I8:
-        *retVal = ArrayHelpers<I8>::BinarySearchBitwiseEquals((I8*) array->GetDataPtr(), index, count, *(I8*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_U8:
-        *retVal = ArrayHelpers<U8>::BinarySearchBitwiseEquals((U8*) array->GetDataPtr(), index, count, *(U8*)value->UnBox());
-        break;
-
-       case ELEMENT_TYPE_R8:
-        *retVal = ArrayHelpers<R8>::BinarySearchBitwiseEquals((R8*) array->GetDataPtr(), index, count, *(R8*)value->UnBox());
-        break;
-
-    case ELEMENT_TYPE_I:
-    case ELEMENT_TYPE_U:
-        // In V1.0, IntPtr & UIntPtr are not fully supported types.  They do
-        // not implement IComparable, so searching & sorting for them should
-        // fail.  In V1.1 or V2.0, this should change.
-        FC_RETURN_BOOL(FALSE);
-
-    default:
-        _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZBinarySearch");
-        FC_RETURN_BOOL(FALSE);
-    }
-    FC_RETURN_BOOL(TRUE);
-FCIMPLEND
-
-FCIMPL3(FC_BOOL_RET, ArrayHelper::TrySZReverse, ArrayBase * array, UINT32 index, UINT32 count)
-{
-    FCALL_CONTRACT;
-
-    VALIDATEOBJECT(array);
-    _ASSERTE(array != NULL);
-
-    // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
-    // non-zero lower bounds.  VB might care.  </TODO>
-    if (array->GetRank() != 1 || array->GetLowerBoundsPtr()[0] != 0)
-        FC_RETURN_BOOL(FALSE);
-
-       _ASSERTE(index <= array->GetNumComponents());
-       _ASSERTE(count <= array->GetNumComponents());
-       _ASSERTE(array->GetNumComponents() >= index + count);
-
-    TypeHandle arrayTH = array->GetArrayElementTypeHandle();
-    const CorElementType arrayElType = arrayTH.GetVerifierCorElementType();
-    if (!CorTypeInfo::IsPrimitiveType_NoThrow(arrayElType))
-        FC_RETURN_BOOL(FALSE);
-
-    switch(arrayElType) {
-    case ELEMENT_TYPE_I1:
-    case ELEMENT_TYPE_U1:
-    case ELEMENT_TYPE_BOOLEAN:
-        ArrayHelpers<U1>::Reverse((U1*) array->GetDataPtr(), index, count);
-        break;
-
-    case ELEMENT_TYPE_I2:
-    case ELEMENT_TYPE_U2:
-    case ELEMENT_TYPE_CHAR:
-        ArrayHelpers<U2>::Reverse((U2*) array->GetDataPtr(), index, count);
-        break;
-
-    case ELEMENT_TYPE_I4:
-    case ELEMENT_TYPE_U4:
-    case ELEMENT_TYPE_R4:
-    IN_TARGET_32BIT(case ELEMENT_TYPE_I:)
-    IN_TARGET_32BIT(case ELEMENT_TYPE_U:)
-        ArrayHelpers<U4>::Reverse((U4*) array->GetDataPtr(), index, count);
-        break;
-
-    case ELEMENT_TYPE_I8:
-    case ELEMENT_TYPE_U8:
-    case ELEMENT_TYPE_R8:
-    IN_TARGET_64BIT(case ELEMENT_TYPE_I:)
-    IN_TARGET_64BIT(case ELEMENT_TYPE_U:)
-        ArrayHelpers<U8>::Reverse((U8*) array->GetDataPtr(), index, count);
-        break;
-
-    default:
-        _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZReverse");
-        FC_RETURN_BOOL(FALSE);
-    }
-    FC_RETURN_BOOL(TRUE);
-}
-FCIMPLEND
diff --git a/src/coreclr/src/classlibnative/bcltype/arrayhelpers.h b/src/coreclr/src/classlibnative/bcltype/arrayhelpers.h
deleted file mode 100644 (file)
index 4b35459..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-//
-// File: ArrayHelpers.h
-//
-
-//
-// Helper methods for the Array class
-// Specifically, this contains indexing, sorting & searching templates.
-
-
-#ifndef _ARRAYHELPERS_H_
-#define _ARRAYHELPERS_H_
-
-#if defined(_MSC_VER) && defined(_TARGET_X86_) && !defined(FPO_ON)
-#pragma optimize("y", on)              // Small critical routines, don't put in EBP frame
-#define FPO_ON 1
-#define COMARRAYHELPERS_TURNED_FPO_ON 1
-#endif
-
-#include "fcall.h"
-
-
-template <class KIND>
-class ArrayHelpers
-{
-public:
-    static int IndexOf(KIND array[], UINT32 index, UINT32 count, KIND value) {
-        LIMITED_METHOD_CONTRACT;
-
-        _ASSERTE(array != NULL && index >= 0 && count >= 0);
-        for(UINT32 i=index; i<index+count; i++)
-            if (array[i] == value)
-                return i;
-        return -1;
-    }
-
-    static int LastIndexOf(KIND array[], UINT32 index, UINT32 count, KIND value) {
-        LIMITED_METHOD_CONTRACT;
-
-        INT32 startIndex = (INT32)index;
-        INT32 n = (INT32)count;
-        _ASSERTE(array != NULL);
-        _ASSERTE(startIndex >= 0);
-        _ASSERTE(n >= 0);
-
-        // Note (startIndex- n) may be -1 when startIndex is 0 and n is 1.
-        _ASSERTE(startIndex >= n - 1);
-
-        // Prefast: caller asserts guarantee that startIndex - n won't underflow, but we need to spell
-        // this out for prefast.
-        PREFIX_ASSUME(startIndex >= startIndex - n);
-        INT32 endIndex = max(startIndex - n, -1);
-
-        for(INT32 i=startIndex; i> endIndex; i--)
-            if (array[i] == value)
-                return i;
-        return -1;
-    }
-
-    static int BinarySearchBitwiseEquals(KIND array[], int index, int length, KIND value) {
-        WRAPPER_NO_CONTRACT;
-
-        _ASSERTE(array != NULL);
-        _ASSERTE(length >= 0);
-        _ASSERTE(index >= 0);
-
-        int lo = index;
-
-        // Prefast: mscorlib.dll!System.Array.BinarySearch(Array,int,int,Object,IComparer)
-        // guarantees index and length are in the array bounds and do not overflow
-        PREFIX_ASSUME(index >= 0 && length >= 0 && INT32_MAX >= index + length - 1);
-        int hi = index + length - 1;
-
-        // Note: if length == 0, hi will be Int32.MinValue, and our comparison
-        // here between 0 & -1 will prevent us from breaking anything.
-        while (lo <= hi) {
-            int i = lo + ((hi - lo) >> 1);
-            if (array[i] < value) {
-                lo = i + 1;
-            }
-            else if (array[i] > value){
-                hi = i - 1;
-            }
-            else {
-                return i;
-            }
-        }
-        return ~lo;
-    }
-
-    static void Reverse(KIND array[], UINT32 index, UINT32 count) {
-        LIMITED_METHOD_CONTRACT;
-
-        _ASSERTE(array != NULL);
-        if (count == 0) {
-            return;
-        }
-        UINT32 i = index;
-        UINT32 j = index + count - 1;
-        while(i < j) {
-            KIND temp = array[i];
-            array[i] = array[j];
-            array[j] = temp;
-            i++;
-            j--;
-        }
-    }
-};
-
-
-class ArrayHelper
-{
-public:
-    // These methods return TRUE or FALSE for success or failure, and the real
-    // result is an out param.  They're helpers to make operations on SZ arrays of
-    // primitives significantly faster.
-    static FCDECL5(FC_BOOL_RET, TrySZIndexOf, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal);
-    static FCDECL5(FC_BOOL_RET, TrySZLastIndexOf, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal);
-    static FCDECL5(FC_BOOL_RET, TrySZBinarySearch, ArrayBase * array, UINT32 index, UINT32 count, Object * value, INT32 * retVal);
-
-    static FCDECL3(FC_BOOL_RET, TrySZReverse, ArrayBase * array, UINT32 index, UINT32 count);
-
-    // Helper methods
-    static INT32 IndexOfUINT8( UINT8* array, UINT32 index, UINT32 count, UINT8 value);
-};
-
-#if defined(COMARRAYHELPERS_TURNED_FPO_ON)
-#pragma optimize("", on)               // Go back to command line default optimizations
-#undef COMARRAYHELPERS_TURNED_FPO_ON
-#undef FPO_ON
-#endif
-
-#endif // _ARRAYHELPERS_H_
index 9eb71d4..624c8a7 100644 (file)
 
 #include "arraynative.inl"
 
+// Returns a bool to indicate if the array is of primitive types or not.
+FCIMPL1(INT32, ArrayNative::GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE)
+{
+    FCALL_CONTRACT;
+
+    _ASSERTE(arrayUNSAFE != NULL);
+
+    return arrayUNSAFE->GetArrayElementTypeHandle().GetVerifierCorElementType();
+}
+FCIMPLEND
+
+FCIMPL2(FC_BOOL_RET, ArrayNative::IsValueOfElementType, ArrayBase* arrayUNSAFE, Object* valueUNSAFE)
+{
+    _ASSERTE(arrayUNSAFE != NULL);
+    _ASSERTE(valueUNSAFE != NULL);
+
+    FC_RETURN_BOOL(arrayUNSAFE->GetArrayElementTypeHandle() == valueUNSAFE->GetTypeHandle());
+}
+FCIMPLEND
+
 // array is GC protected by caller
 void ArrayInitializeWorker(ARRAYBASEREF * arrayRef,
                            MethodTable* pArrayMT,
index 2f587ad..10b3c17 100644 (file)
@@ -25,6 +25,9 @@ struct FCALLRuntimeFieldHandle
 class ArrayNative
 {
 public:
+    static FCDECL1(INT32, GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE);
+    static FCDECL2(FC_BOOL_RET, IsValueOfElementType, ArrayBase* arrayUNSAFE, Object* valueUNSAFE);
+
     static FCDECL1(void, Initialize, ArrayBase* pArray);
 
     static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst);
index 352b243..fa0d8a2 100644 (file)
@@ -668,22 +668,6 @@ void QCALLTYPE Buffer::MemMove(void *dst, void *src, size_t length)
     memmove(dst, src, length);
 }
 
-// Returns a bool to indicate if the array is of primitive types or not.
-FCIMPL1(FC_BOOL_RET, Buffer::IsPrimitiveTypeArray, ArrayBase *arrayUNSAFE)
-{
-    FCALL_CONTRACT;
-
-    _ASSERTE(arrayUNSAFE != NULL);
-
-    // Check the type from the contained element's handle
-    TypeHandle elementTH = arrayUNSAFE->GetArrayElementTypeHandle();
-    BOOL fIsPrimitiveTypeArray = CorTypeInfo::IsPrimitiveType_NoThrow(elementTH.GetVerifierCorElementType());
-
-    FC_RETURN_BOOL(fIsPrimitiveTypeArray);
-
-}
-FCIMPLEND
-
 //
 // GCInterface
 //
index 797637a..17442d9 100644 (file)
@@ -68,8 +68,6 @@ public:
 class Buffer
 {
 public:
-    static FCDECL1(FC_BOOL_RET, IsPrimitiveTypeArray, ArrayBase *arrayUNSAFE);
-
     static FCDECL3(VOID, BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount);
 
     static void QCALLTYPE MemMove(void *dst, void *src, size_t length);
index a3396f9..c41a2f2 100644 (file)
@@ -712,20 +712,17 @@ FCFuncStart(gClrConfig)
 FCFuncEnd()
 
 FCFuncStart(gArrayFuncs)
+    FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType)
+    FCFuncElement("IsValueOfElementType", ArrayNative::IsValueOfElementType)
     FCFuncElement("Initialize", ArrayNative::Initialize)
     FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy)
     FCFuncElement("CopySlow", ArrayNative::CopySlow)
     FCFuncElement("InternalCreate", ArrayNative::CreateInstance)
     FCFuncElement("InternalGetReference", ArrayNative::GetReference)
     FCFuncElement("InternalSetValue", ArrayNative::SetValue)
-    FCFuncElement("TrySZIndexOf", ArrayHelper::TrySZIndexOf)
-    FCFuncElement("TrySZLastIndexOf", ArrayHelper::TrySZLastIndexOf)
-    FCFuncElement("TrySZBinarySearch", ArrayHelper::TrySZBinarySearch)
-    FCFuncElement("TrySZReverse", ArrayHelper::TrySZReverse)
 FCFuncEnd()
 
 FCFuncStart(gBufferFuncs)
-    FCFuncElement("IsPrimitiveTypeArray", Buffer::IsPrimitiveTypeArray)
     QCFuncElement("__ZeroMemory", Buffer::Clear)
     FCFuncElement("__BulkMoveWithWriteBarrier", Buffer::BulkMoveWithWriteBarrier)
     QCFuncElement("__Memmove", Buffer::MemMove)
index f501305..9c79797 100644 (file)
@@ -53,7 +53,6 @@
 #include "proftoeeinterfaceimpl.h"
 
 #include "appdomainnative.hpp"
-#include "arrayhelpers.h"
 #include "runtimehandles.h"
 #include "reflectioninvocation.h"
 #include "managedmdimport.hpp"
index 39ddaca..f280123 100644 (file)
@@ -7,6 +7,7 @@ using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
 using System.Runtime.CompilerServices;
 using Internal.Runtime.CompilerServices;
 
@@ -472,15 +473,6 @@ namespace System
                 ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
 
             comparer ??= Comparer.Default;
-#if !CORERT
-            if (comparer == Comparer.Default)
-            {
-                int retval;
-                bool r = TrySZBinarySearch(array, index, length, value, out retval);
-                if (r)
-                    return retval;
-            }
-#endif
 
             int lo = index;
             int hi = index + length - 1;
@@ -511,34 +503,94 @@ namespace System
                         hi = i - 1;
                     }
                 }
+                return ~lo;
             }
-            else
+
+            if (comparer == Comparer.Default)
             {
-                while (lo <= hi)
+                CorElementType et = array.GetCorElementTypeOfElementType();
+                if (et.IsPrimitiveType()
+                    // IntPtr/UIntPtr does not implement IComparable
+                    && (et != CorElementType.ELEMENT_TYPE_I) && (et != CorElementType.ELEMENT_TYPE_U))
                 {
-                    int i = GetMedian(lo, hi);
+                    if (value == null)
+                        return ~index;
 
-                    int c;
-                    try
+                    if (array.IsValueOfElementType(value))
                     {
-                        c = comparer.Compare(array.GetValue(i), value);
-                    }
-                    catch (Exception e)
-                    {
-                        ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
-                        return default;
-                    }
-                    if (c == 0) return i;
-                    if (c < 0)
-                    {
-                        lo = i + 1;
-                    }
-                    else
-                    {
-                        hi = i - 1;
+                        int adjustedIndex = index - lb;
+                        int result = -1;
+                        switch (et)
+                        {
+                            case CorElementType.ELEMENT_TYPE_I1:
+                                result = GenericBinarySearch<sbyte>(array, adjustedIndex, length, value);
+                                break;
+                            case CorElementType.ELEMENT_TYPE_U1:
+                            case CorElementType.ELEMENT_TYPE_BOOLEAN:
+                                result = GenericBinarySearch<byte>(array, adjustedIndex, length, value);
+                                break;
+                            case CorElementType.ELEMENT_TYPE_I2:
+                                result = GenericBinarySearch<short>(array, adjustedIndex, length, value);
+                                break;
+                            case CorElementType.ELEMENT_TYPE_U2:
+                            case CorElementType.ELEMENT_TYPE_CHAR:
+                                result = GenericBinarySearch<ushort>(array, adjustedIndex, length, value);
+                                break;
+                            case CorElementType.ELEMENT_TYPE_I4:
+                                result = GenericBinarySearch<int>(array, adjustedIndex, length, value);
+                                break;
+                            case CorElementType.ELEMENT_TYPE_U4:
+                                result = GenericBinarySearch<uint>(array, adjustedIndex, length, value);
+                                break;
+                            case CorElementType.ELEMENT_TYPE_I8:
+                                result = GenericBinarySearch<long>(array, adjustedIndex, length, value);
+                                break;
+                            case CorElementType.ELEMENT_TYPE_U8:
+                                result = GenericBinarySearch<ulong>(array, adjustedIndex, length, value);
+                                break;
+                            case CorElementType.ELEMENT_TYPE_R4:
+                                result = GenericBinarySearch<float>(array, adjustedIndex, length, value);
+                                break;
+                            case CorElementType.ELEMENT_TYPE_R8:
+                                result = GenericBinarySearch<double>(array, adjustedIndex, length, value);
+                                break;
+                            default:
+                                Debug.Fail("All primitive types should be handled above");
+                                break;
+                        }
+
+                        return (result >= 0) ? (index + result) : ~(index + ~result);
+
+                        static int GenericBinarySearch<T>(Array array, int adjustedIndex, int length, object value) where T: struct, IComparable<T>
+                            => UnsafeArrayAsSpan<T>(array, adjustedIndex, length).BinarySearch(Unsafe.As<byte, T>(ref value.GetRawData()));
                     }
                 }
             }
+
+            while (lo <= hi)
+            {
+                int i = GetMedian(lo, hi);
+
+                int c;
+                try
+                {
+                    c = comparer.Compare(array.GetValue(i), value);
+                }
+                catch (Exception e)
+                {
+                    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
+                    return default;
+                }
+                if (c == 0) return i;
+                if (c < 0)
+                {
+                    lo = i + 1;
+                }
+                else
+                {
+                    hi = i - 1;
+                }
+            }
             return ~lo;
         }
 
@@ -921,14 +973,6 @@ namespace System
             if (count < 0 || count > array.Length - startIndex + lb)
                 ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
 
-#if !CORERT
-            // Try calling a quick native method to handle primitive types.
-            int retVal;
-            bool r = TrySZIndexOf(array, startIndex, count, value, out retVal);
-            if (r)
-                return retVal;
-#endif
-
             int endIndex = startIndex + count;
             if (array is object[] objArray)
             {
@@ -949,22 +993,77 @@ namespace System
                             return i;
                     }
                 }
+                return -1;
             }
-            else
+
+            CorElementType et = array.GetCorElementTypeOfElementType();
+            if (et.IsPrimitiveType())
             {
-                for (int i = startIndex; i < endIndex; i++)
+                if (value == null)
+                    return lb - 1;
+
+                if (array.IsValueOfElementType(value))
                 {
-                    object? obj = array.GetValue(i);
-                    if (obj == null)
+                    int adjustedIndex = startIndex - lb;
+                    int result = -1;
+                    switch (et)
                     {
-                        if (value == null)
-                            return i;
-                    }
-                    else
-                    {
-                        if (obj.Equals(value))
-                            return i;
+                        case CorElementType.ELEMENT_TYPE_I1:
+                        case CorElementType.ELEMENT_TYPE_U1:
+                        case CorElementType.ELEMENT_TYPE_BOOLEAN:
+                            result = GenericIndexOf<byte>(array, value, adjustedIndex, count);
+                            break;
+                        case CorElementType.ELEMENT_TYPE_I2:
+                        case CorElementType.ELEMENT_TYPE_U2:
+                        case CorElementType.ELEMENT_TYPE_CHAR:
+                            result = GenericIndexOf<char>(array, value, adjustedIndex, count);
+                            break;
+                        case CorElementType.ELEMENT_TYPE_I4:
+                        case CorElementType.ELEMENT_TYPE_U4:
+#if !BIT64
+                        case CorElementType.ELEMENT_TYPE_I:
+                        case CorElementType.ELEMENT_TYPE_U:
+#endif
+                            result = GenericIndexOf<int>(array, value, adjustedIndex, count);
+                            break;
+                        case CorElementType.ELEMENT_TYPE_I8:
+                        case CorElementType.ELEMENT_TYPE_U8:
+#if BIT64
+                        case CorElementType.ELEMENT_TYPE_I:
+                        case CorElementType.ELEMENT_TYPE_U:
+#endif
+                            result = GenericIndexOf<long>(array, value, adjustedIndex, count);
+                            break;
+                        case CorElementType.ELEMENT_TYPE_R4:
+                            result = GenericIndexOf<float>(array, value, adjustedIndex, count);
+                            break;
+                        case CorElementType.ELEMENT_TYPE_R8:
+                            result = GenericIndexOf<double>(array, value, adjustedIndex, count);
+                            break;
+                        default:
+                            Debug.Fail("All primitive types should be handled above");
+                            break;
                     }
+
+                    return (result >= 0 ? startIndex : lb) + result;
+
+                    static int GenericIndexOf<T>(Array array, object value, int adjustedIndex, int length) where T : struct, IEquatable<T>
+                        => UnsafeArrayAsSpan<T>(array, adjustedIndex, length).IndexOf(Unsafe.As<byte, T>(ref value.GetRawData()));
+                }
+            }
+
+            for (int i = startIndex; i < endIndex; i++)
+            {
+                object? obj = array.GetValue(i);
+                if (obj == null)
+                {
+                    if (value == null)
+                        return i;
+                }
+                else
+                {
+                    if (obj.Equals(value))
+                        return i;
                 }
             }
             // Return one less than the lower bound of the array.  This way,
@@ -1104,14 +1203,6 @@ namespace System
             if (array.Rank != 1)
                 ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
 
-#if !CORERT
-            // Try calling a quick native method to handle primitive types.
-            int retVal;
-            bool r = TrySZLastIndexOf(array, startIndex, count, value, out retVal);
-            if (r)
-                return retVal;
-#endif
-
             int endIndex = startIndex - count + 1;
             if (array is object[] objArray)
             {
@@ -1132,22 +1223,77 @@ namespace System
                             return i;
                     }
                 }
+                return -1;
             }
-            else
+
+            CorElementType et = array.GetCorElementTypeOfElementType();
+            if (et.IsPrimitiveType())
             {
-                for (int i = startIndex; i >= endIndex; i--)
+                if (value == null)
+                    return lb - 1;
+
+                if (array.IsValueOfElementType(value))
                 {
-                    object? obj = array.GetValue(i);
-                    if (obj == null)
+                    int adjustedIndex = endIndex - lb;
+                    int result = -1;
+                    switch (et)
                     {
-                        if (value == null)
-                            return i;
-                    }
-                    else
-                    {
-                        if (obj.Equals(value))
-                            return i;
+                        case CorElementType.ELEMENT_TYPE_I1:
+                        case CorElementType.ELEMENT_TYPE_U1:
+                        case CorElementType.ELEMENT_TYPE_BOOLEAN:
+                            result = GenericLastIndexOf<byte>(array, value, adjustedIndex, count);
+                            break;
+                        case CorElementType.ELEMENT_TYPE_I2:
+                        case CorElementType.ELEMENT_TYPE_U2:
+                        case CorElementType.ELEMENT_TYPE_CHAR:
+                            result = GenericLastIndexOf<char>(array, value, adjustedIndex, count);
+                            break;
+                        case CorElementType.ELEMENT_TYPE_I4:
+                        case CorElementType.ELEMENT_TYPE_U4:
+#if !BIT64
+                        case CorElementType.ELEMENT_TYPE_I:
+                        case CorElementType.ELEMENT_TYPE_U:
+#endif
+                            result = GenericLastIndexOf<int>(array, value, adjustedIndex, count);
+                            break;
+                        case CorElementType.ELEMENT_TYPE_I8:
+                        case CorElementType.ELEMENT_TYPE_U8:
+#if BIT64
+                        case CorElementType.ELEMENT_TYPE_I:
+                        case CorElementType.ELEMENT_TYPE_U:
+#endif
+                            result = GenericLastIndexOf<long>(array, value, adjustedIndex, count);
+                            break;
+                        case CorElementType.ELEMENT_TYPE_R4:
+                            result = GenericLastIndexOf<float>(array, value, adjustedIndex, count);
+                            break;
+                        case CorElementType.ELEMENT_TYPE_R8:
+                            result = GenericLastIndexOf<double>(array, value, adjustedIndex, count);
+                            break;
+                        default:
+                            Debug.Fail("All primitive types should be handled above");
+                            break;
                     }
+
+                    return (result >= 0 ? endIndex : lb) + result;
+
+                    static int GenericLastIndexOf<T>(Array array, object value, int adjustedIndex, int length) where T : struct, IEquatable<T>
+                        => UnsafeArrayAsSpan<T>(array, adjustedIndex, length).LastIndexOf(Unsafe.As<byte, T>(ref value.GetRawData()));
+                }
+            }
+
+            for (int i = startIndex; i >= endIndex; i--)
+            {
+                object? obj = array.GetValue(i);
+                if (obj == null)
+                {
+                    if (value == null)
+                        return i;
+                }
+                else
+                {
+                    if (obj.Equals(value))
+                        return i;
                 }
             }
             return lb - 1;  // Return lb-1 for arrays with negative lower bounds.
@@ -1298,28 +1444,53 @@ namespace System
             if (length <= 1)
                 return;
 
-#if !CORERT
-            bool r = TrySZReverse(array, index, length);
-            if (r)
-                return;
-#endif
-
-            if (array is object[] objArray)
+            int adjustedIndex = index - lowerBound;
+            switch (array.GetCorElementTypeOfElementType())
             {
-                Array.Reverse<object>(objArray, index, length);
+                case CorElementType.ELEMENT_TYPE_I1:
+                case CorElementType.ELEMENT_TYPE_U1:
+                case CorElementType.ELEMENT_TYPE_BOOLEAN:
+                    UnsafeArrayAsSpan<byte>(array, adjustedIndex, length).Reverse();
+                    return;
+                case CorElementType.ELEMENT_TYPE_I2:
+                case CorElementType.ELEMENT_TYPE_U2:
+                case CorElementType.ELEMENT_TYPE_CHAR:
+                    UnsafeArrayAsSpan<short>(array, adjustedIndex, length).Reverse();
+                    return;
+                case CorElementType.ELEMENT_TYPE_I4:
+                case CorElementType.ELEMENT_TYPE_U4:
+#if !BIT64
+                case CorElementType.ELEMENT_TYPE_I:
+                case CorElementType.ELEMENT_TYPE_U:
+#endif
+                case CorElementType.ELEMENT_TYPE_R4:
+                    UnsafeArrayAsSpan<int>(array, adjustedIndex, length).Reverse();
+                    return;
+                case CorElementType.ELEMENT_TYPE_I8:
+                case CorElementType.ELEMENT_TYPE_U8:
+#if BIT64
+                case CorElementType.ELEMENT_TYPE_I:
+                case CorElementType.ELEMENT_TYPE_U:
+#endif
+                case CorElementType.ELEMENT_TYPE_R8:
+                    UnsafeArrayAsSpan<long>(array, adjustedIndex, length).Reverse();
+                    return;
+                case CorElementType.ELEMENT_TYPE_OBJECT:
+                case CorElementType.ELEMENT_TYPE_ARRAY:
+                case CorElementType.ELEMENT_TYPE_SZARRAY:
+                    UnsafeArrayAsSpan<object>(array, adjustedIndex, length).Reverse();
+                    return;
             }
-            else
+
+            int i = index;
+            int j = index + length - 1;
+            while (i < j)
             {
-                int i = index;
-                int j = index + length - 1;
-                while (i < j)
-                {
-                    object? temp = array.GetValue(i);
-                    array.SetValue(array.GetValue(j), i);
-                    array.SetValue(temp, j);
-                    i++;
-                    j--;
-                }
+                object? temp = array.GetValue(i);
+                array.SetValue(array.GetValue(j), i);
+                array.SetValue(temp, j);
+                i++;
+                j--;
             }
         }
 
@@ -1464,127 +1635,83 @@ namespace System
             if (keys.Length - (index - keysLowerBound) < length || (items != null && (index - keysLowerBound) > items.Length - length))
                 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
 
-            if (length > 1)
+            if (length <= 1)
+                return;
+
+            comparer ??= Comparer.Default;
+
+            if (keys is object[] objKeys)
             {
-                comparer ??= Comparer.Default;
+                object[]? objItems = items as object[];
+                if (items == null || objItems != null)
+                {
+                    new SorterObjectArray(objKeys, objItems, comparer).Sort(index, length);
+                    return;
+                }
+            }
 
-                if (comparer == Comparer.Default)
+            if (comparer == Comparer.Default)
+            {
+                CorElementType et = keys.GetCorElementTypeOfElementType();
+                if (items == null || items.GetCorElementTypeOfElementType() == et)
                 {
-                    switch (Type.GetTypeCode(keys.GetType().GetElementType()))
+                    int adjustedIndex = index - keysLowerBound;
+                    switch (et)
                     {
-                        case TypeCode.Boolean:
-                            if (TryGenericSort(keys as bool[], items, index, length)) return;
-                            break;
-                        case TypeCode.Byte:
-                            if (TryGenericSort(keys as byte[], items, index, length)) return;
-                            break;
-                        case TypeCode.Char:
-                            if (TryGenericSort(keys as char[], items, index, length)) return;
-                            break;
-                        case TypeCode.Double:
-                            if (TryGenericSort(keys as double[], items, index, length)) return;
-                            break;
-                        case TypeCode.Int16:
-                            if (TryGenericSort(keys as short[], items, index, length)) return;
-                            break;
-                        case TypeCode.Int32:
-                            if (TryGenericSort(keys as int[], items, index, length)) return;
-                            break;
-                        case TypeCode.Int64:
-                            if (TryGenericSort(keys as long[], items, index, length)) return;
-                            break;
-                        case TypeCode.SByte:
-                            if (TryGenericSort(keys as sbyte[], items, index, length)) return;
-                            break;
-                        case TypeCode.Single:
-                            if (TryGenericSort(keys as float[], items, index, length)) return;
-                            break;
-                        case TypeCode.String:
-                            if (TryGenericSort(keys as string[], items, index, length)) return;
-                            break;
-                        case TypeCode.UInt16:
-                            if (TryGenericSort(keys as ushort[], items, index, length)) return;
-                            break;
-                        case TypeCode.UInt32:
-                            if (TryGenericSort(keys as uint[], items, index, length)) return;
-                            break;
-                        case TypeCode.UInt64:
-                            if (TryGenericSort(keys as ulong[], items, index, length)) return;
+                        case CorElementType.ELEMENT_TYPE_I1:
+                            GenericSort<sbyte>(keys, items, adjustedIndex, length);
+                            return;
+                        case CorElementType.ELEMENT_TYPE_U1:
+                        case CorElementType.ELEMENT_TYPE_BOOLEAN:
+                            GenericSort<byte>(keys, items, adjustedIndex, length);
+                            return;
+                        case CorElementType.ELEMENT_TYPE_I2:
+                            GenericSort<short>(keys, items, adjustedIndex, length);
+                            return;
+                        case CorElementType.ELEMENT_TYPE_U2:
+                        case CorElementType.ELEMENT_TYPE_CHAR:
+                            GenericSort<ushort>(keys, items, adjustedIndex, length);
+                            return;
+                        case CorElementType.ELEMENT_TYPE_I4:
+                            GenericSort<int>(keys, items, adjustedIndex, length);
+                            return;
+                        case CorElementType.ELEMENT_TYPE_U4:
+                            GenericSort<uint>(keys, items, adjustedIndex, length);
+                            return;
+                        case CorElementType.ELEMENT_TYPE_I8:
+                            GenericSort<long>(keys, items, adjustedIndex, length);
+                            return;
+                        case CorElementType.ELEMENT_TYPE_U8:
+                            GenericSort<ulong>(keys, items, adjustedIndex, length);
+                            return;
+                        case CorElementType.ELEMENT_TYPE_R4:
+                            GenericSort<float>(keys, items, adjustedIndex, length);
+                            return;
+                        case CorElementType.ELEMENT_TYPE_R8:
+                            GenericSort<double>(keys, items, adjustedIndex, length);
+                            return;
+                        case CorElementType.ELEMENT_TYPE_I:
+                        case CorElementType.ELEMENT_TYPE_U:
+                            // IntPtr/UIntPtr does not implement IComparable
                             break;
                     }
 
-                    static bool TryGenericSort<TKey>(TKey[]? keys, Array? items, int index, int length)
+                    static void GenericSort<T>(Array keys, Array? items, int adjustedIndex, int length) where T: struct
                     {
-                        if (keys != null)
+                        Span<T> keysSpan = UnsafeArrayAsSpan<T>(keys, adjustedIndex, length);
+                        if (items != null)
                         {
-                            if (items is null)
-                            {
-                                Sort(keys, index, length);
-                                return true;
-                            }
-
-                            switch (Type.GetTypeCode(items.GetType().GetElementType()))
-                            {
-                                case TypeCode.Boolean:
-                                    if (items is bool[] boolItems) { Sort(keys, boolItems, index, length); return true; }
-                                    break;
-                                case TypeCode.Byte:
-                                    if (items is byte[] byteItems) { Sort(keys, byteItems, index, length); return true; }
-                                    break;
-                                case TypeCode.Char:
-                                    if (items is char[] charItems) { Sort(keys, charItems, index, length); return true; }
-                                    break;
-                                case TypeCode.Double:
-                                    if (items is double[] doubleItems) { Sort(keys, doubleItems, index, length); return true; }
-                                    break;
-                                case TypeCode.Int16:
-                                    if (items is short[] shortItems) { Sort(keys, shortItems, index, length); return true; }
-                                    break;
-                                case TypeCode.Int32:
-                                    if (items is int[] intItems) { Sort(keys, intItems, index, length); return true; }
-                                    break;
-                                case TypeCode.Int64:
-                                    if (items is long[] longItems) { Sort(keys, longItems, index, length); return true; }
-                                    break;
-                                case TypeCode.SByte:
-                                    if (items is sbyte[] sbyteItems) { Sort(keys, sbyteItems, index, length); return true; }
-                                    break;
-                                case TypeCode.Single:
-                                    if (items is float[] floatItems) { Sort(keys, floatItems, index, length); return true; }
-                                    break;
-                                case TypeCode.String:
-                                    if (items is string[] stringItems) { Sort(keys, stringItems, index, length); return true; }
-                                    break;
-                                case TypeCode.UInt16:
-                                    if (items is ushort[] ushortItems) { Sort(keys, ushortItems, index, length); return true; }
-                                    break;
-                                case TypeCode.UInt32:
-                                    if (items is uint[] uintItems) { Sort(keys, uintItems, index, length); return true; }
-                                    break;
-                                case TypeCode.UInt64:
-                                    if (items is ulong[] ulongItems) { Sort(keys, ulongItems, index, length); return true; }
-                                    break;
-                            }
+                            keysSpan.Sort<T, T>(UnsafeArrayAsSpan<T>(items, adjustedIndex, length));
+                        }
+                        else
+                        {
+                            keysSpan.Sort<T>();
                         }
-
-                        return false;
                     }
                 }
-
-                object[]? objKeys = keys as object[];
-                object[]? objItems = null;
-                if (objKeys != null)
-                    objItems = items as object[];
-
-                if (objKeys != null && (items == null || objItems != null))
-                {
-                    new SorterObjectArray(objKeys, objItems, comparer).Sort(index, length);
-                }
-                else
-                {
-                    new SorterGenericArray(keys, items, comparer).Sort(index, length);
-                }
             }
+
+            new SorterGenericArray(keys, items, comparer).Sort(index, length);
         }
 
         public static void Sort<T>(T[] array)
@@ -1711,7 +1838,6 @@ namespace System
             return true;
         }
 
-#if !CORERT
         // Private value type used by the Sort methods.
         private readonly struct SorterObjectArray
         {
@@ -1726,7 +1852,7 @@ namespace System
                 this.comparer = comparer;
             }
 
-            internal void SwapIfGreaterWithItems(int a, int b)
+            internal void SwapIfGreater(int a, int b)
             {
                 if (a != b)
                 {
@@ -1796,14 +1922,14 @@ namespace System
                         }
                         if (partitionSize == 2)
                         {
-                            SwapIfGreaterWithItems(lo, hi);
+                            SwapIfGreater(lo, hi);
                             return;
                         }
                         if (partitionSize == 3)
                         {
-                            SwapIfGreaterWithItems(lo, hi - 1);
-                            SwapIfGreaterWithItems(lo, hi);
-                            SwapIfGreaterWithItems(hi - 1, hi);
+                            SwapIfGreater(lo, hi - 1);
+                            SwapIfGreater(lo, hi);
+                            SwapIfGreater(hi - 1, hi);
                             return;
                         }
 
@@ -1829,9 +1955,9 @@ namespace System
                 // Compute median-of-three.  But also partition them, since we've done the comparison.
                 int mid = lo + (hi - lo) / 2;
                 // Sort lo, mid and hi appropriately, then pick mid as the pivot.
-                SwapIfGreaterWithItems(lo, mid);
-                SwapIfGreaterWithItems(lo, hi);
-                SwapIfGreaterWithItems(mid, hi);
+                SwapIfGreater(lo, mid);
+                SwapIfGreater(lo, hi);
+                SwapIfGreater(mid, hi);
 
                 object pivot = keys[mid];
                 Swap(mid, hi - 1);
@@ -1932,7 +2058,7 @@ namespace System
                 this.comparer = comparer;
             }
 
-            internal void SwapIfGreaterWithItems(int a, int b)
+            internal void SwapIfGreater(int a, int b)
             {
                 if (a != b)
                 {
@@ -2002,14 +2128,14 @@ namespace System
                         }
                         if (partitionSize == 2)
                         {
-                            SwapIfGreaterWithItems(lo, hi);
+                            SwapIfGreater(lo, hi);
                             return;
                         }
                         if (partitionSize == 3)
                         {
-                            SwapIfGreaterWithItems(lo, hi - 1);
-                            SwapIfGreaterWithItems(lo, hi);
-                            SwapIfGreaterWithItems(hi - 1, hi);
+                            SwapIfGreater(lo, hi - 1);
+                            SwapIfGreater(lo, hi);
+                            SwapIfGreater(hi - 1, hi);
                             return;
                         }
 
@@ -2035,9 +2161,9 @@ namespace System
                 // Compute median-of-three.  But also partition them, since we've done the comparison.
                 int mid = lo + (hi - lo) / 2;
 
-                SwapIfGreaterWithItems(lo, mid);
-                SwapIfGreaterWithItems(lo, hi);
-                SwapIfGreaterWithItems(mid, hi);
+                SwapIfGreater(lo, mid);
+                SwapIfGreater(lo, hi);
+                SwapIfGreater(mid, hi);
 
                 object? pivot = keys.GetValue(mid);
                 Swap(mid, hi - 1);
@@ -2126,6 +2252,10 @@ namespace System
             }
         }
 
+        private static Span<T> UnsafeArrayAsSpan<T>(Array array, int adjustedIndex, int length) =>
+            new Span<T>(ref Unsafe.As<byte, T>(ref array.GetRawArrayData()), array.Length).Slice(adjustedIndex, length);
+
+#if !CORERT
         public IEnumerator GetEnumerator()
         {
             int lowerBound = GetLowerBound(0);
index 3320579..18064bc 100644 (file)
@@ -40,7 +40,7 @@ namespace System
             nuint uSrcLen = (nuint)src.LongLength;
             if (src.GetType() != typeof(byte[]))
             {
-                if (!IsPrimitiveTypeArray(src))
+                if (!src.GetCorElementTypeOfElementType().IsPrimitiveType())
                     throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(src));
                 uSrcLen *= (nuint)src.GetElementSize();
             }
@@ -51,7 +51,7 @@ namespace System
                 uDstLen = (nuint)dst.LongLength;
                 if (dst.GetType() != typeof(byte[]))
                 {
-                    if (!IsPrimitiveTypeArray(dst))
+                    if (!dst.GetCorElementTypeOfElementType().IsPrimitiveType())
                         throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(dst));
                     uDstLen *= (nuint)dst.GetElementSize();
                 }
@@ -81,7 +81,7 @@ namespace System
                 throw new ArgumentNullException(nameof(array));
 
             // Is it of primitive types?
-            if (!IsPrimitiveTypeArray(array))
+            if (!array.GetCorElementTypeOfElementType().IsPrimitiveType())
                 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
 
             nuint byteLength = (nuint)array.LongLength * (nuint)array.GetElementSize();
index b49f721..cd2971f 100644 (file)
@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using System.Runtime.Serialization;
+using System.Reflection;
 using Internal.Runtime.CompilerServices;
 
 namespace System.Runtime.CompilerServices
@@ -100,5 +101,9 @@ namespace System.Runtime.CompilerServices
         public static void PrepareConstrainedRegionsNoOP()
         {
         }
+
+        internal static bool IsPrimitiveType(this CorElementType et)
+            // COR_ELEMENT_TYPE_I1,I2,I4,I8,U1,U2,U4,U8,R4,R8,I,U,CHAR,BOOLEAN
+            => ((1 << (int)et) & 0b_0011_0000_0000_0011_1111_1111_1100) != 0;
     }
 }
index 89c271e..96cb3ce 100644 (file)
@@ -187,8 +187,6 @@ namespace System
                     typeCode = TypeCode.Single; break;
                 case CorElementType.ELEMENT_TYPE_R8:
                     typeCode = TypeCode.Double; break;
-                case CorElementType.ELEMENT_TYPE_STRING:
-                    typeCode = TypeCode.String; break;
                 case CorElementType.ELEMENT_TYPE_VALUETYPE:
                     if (this == Convert.ConvertTypes[(int)TypeCode.Decimal])
                         typeCode = TypeCode.Decimal;
index 3ec07eb..d233e40 100644 (file)
@@ -834,6 +834,8 @@ namespace System
                     return SR.Rank_MultiDimNotSupported;
                 case ExceptionResource.Arg_TypeNotSupported:
                     return SR.Arg_TypeNotSupported;
+                case ExceptionResource.Argument_SpansMustHaveSameLength:
+                    return SR.Argument_SpansMustHaveSameLength;
                 default:
                     Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum.");
                     return "";