Add Span.Sort, and make Array.Sort span-based (dotnet/coreclr#27700)
authorStephen Toub <stoub@microsoft.com>
Wed, 6 Nov 2019 06:39:01 +0000 (01:39 -0500)
committerJan Kotas <jkotas@microsoft.com>
Wed, 6 Nov 2019 06:39:01 +0000 (22:39 -0800)
* Add Span<T>.Sort

Shares existing Array-based implementation by changing that implementation to be span based.

* Additional work to enable span-based sorts

- Cleans up changes from the previous commit, e.g. corrects nullable annotations, removes TODOs, using GetRawSzArrayData instead of GetRawArrayData, etc.
- Passes spans around rather than spans+lo+hi.
- Deletes the native TrySZSort, preferring to use the managed implementation in all cases.

Commit migrated from https://github.com/dotnet/coreclr/commit/50d73a21afcc8eaa3e7f459253314db8a5553333

src/coreclr/src/System.Private.CoreLib/src/System/Array.CoreCLR.cs
src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs
src/coreclr/src/classlibnative/bcltype/arrayhelpers.cpp
src/coreclr/src/classlibnative/bcltype/arrayhelpers.h
src/coreclr/src/vm/ecalllist.h
src/libraries/System.Private.CoreLib/src/System/Array.cs
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.cs
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs
src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs
src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs

index 97e6eb4912ca80a85e39f56d15493e5cd517c1c5..d10460aa1769a24a7891cf7213ae5793c2f42213 100644 (file)
@@ -415,33 +415,6 @@ namespace System
             InternalSetValue(&elemref, value);
         }
 
-        private static void SortImpl(Array keys, Array? items, int index, int length, IComparer comparer)
-        {
-            Debug.Assert(comparer != null);
-
-            if (comparer == Comparer.Default)
-            {
-                bool r = TrySZSort(keys, items, index, index + length - 1);
-                if (r)
-                    return;
-            }
-
-            object[]? objKeys = keys as object[];
-            object[]? objItems = null;
-            if (objKeys != null)
-                objItems = items as object[];
-            if (objKeys != null && (items == null || objItems != null))
-            {
-                SorterObjectArray sorter = new SorterObjectArray(objKeys, objItems, comparer);
-                sorter.Sort(index, length);
-            }
-            else
-            {
-                SorterGenericArray sorter = new SorterGenericArray(keys, items, comparer);
-                sorter.Sort(index, length);
-            }
-        }
-
         [MethodImpl(MethodImplOptions.InternalCall)]
         // reference to TypedReference is banned, so have to pass result as pointer
         private extern unsafe void InternalGetReference(void* elemRef, int rank, int* pIndices);
@@ -513,9 +486,6 @@ namespace System
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern bool TrySZReverse(Array array, int index, int count);
 
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool TrySZSort(Array keys, Array? items, int left, int right);
-
         // 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.
         // otherwise this is a no-op.  Generally this method is called automatically by the compiler
index dc27d977c823fb8a89037d792546a79cc8bc5b70..0b658345c0a1e7aae4ea3c3e033b3bca1a2267d7 100644 (file)
@@ -8,7 +8,7 @@ namespace System.Collections.Generic
 {
     internal interface IArraySortHelper<TKey>
     {
-        void Sort(TKey[] keys, int index, int length, IComparer<TKey>? comparer);
+        void Sort(Span<TKey> keys, IComparer<TKey>? comparer);
         int BinarySearch(TKey[] keys, int index, int length, TKey value, IComparer<TKey>? comparer);
     }
 
@@ -43,7 +43,7 @@ namespace System.Collections.Generic
 
     internal interface IArraySortHelper<TKey, TValue>
     {
-        void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey>? comparer);
+        void Sort(Span<TKey> keys, Span<TValue> values, IComparer<TKey>? comparer);
     }
 
     [TypeDependency("System.Collections.Generic.GenericArraySortHelper`2")]
index 974bfe401b9231250c1485f13ecc57dc52a52a2f..27d332a14193966e018141b1c70e9475d4729e06 100644 (file)
@@ -265,109 +265,6 @@ FCIMPL5(FC_BOOL_RET, ArrayHelper::TrySZBinarySearch, ArrayBase * array, UINT32 i
     FC_RETURN_BOOL(TRUE);
 FCIMPLEND
 
-FCIMPL4(FC_BOOL_RET, ArrayHelper::TrySZSort, ArrayBase * keys, ArrayBase * items, UINT32 left, UINT32 right)
-    FCALL_CONTRACT;
-
-    VALIDATEOBJECT(keys);
-    VALIDATEOBJECT(items);
-    _ASSERTE(keys != NULL);
-
-    // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with
-    // non-zero lower bounds.  VB might care.  </TODO>
-    if (keys->GetRank() != 1 || keys->GetLowerBoundsPtr()[0] != 0)
-        FC_RETURN_BOOL(FALSE);
-
-       _ASSERTE(left <= right);
-       _ASSERTE(right < keys->GetNumComponents() || keys->GetNumComponents() == 0);
-
-    TypeHandle keysTH = keys->GetArrayElementTypeHandle();
-    const CorElementType keysElType = keysTH.GetVerifierCorElementType();
-    if (!CorTypeInfo::IsPrimitiveType_NoThrow(keysElType))
-        FC_RETURN_BOOL(FALSE);
-    if (items != NULL) {
-        TypeHandle itemsTH = items->GetArrayElementTypeHandle();
-        if (keysTH != itemsTH)
-            FC_RETURN_BOOL(FALSE);   // Can't currently handle sorting different types of arrays.
-    }
-
-    // Handle special case of a 0 element range to sort.
-    // Consider both Sort(array, x, x) and Sort(zeroLen, 0, zeroLen.Length-1);
-    if (left == right || right == 0xffffffff)
-        FC_RETURN_BOOL(TRUE);
-
-    switch(keysElType) {
-    case ELEMENT_TYPE_I1:
-               ArrayHelpers<I1>::IntrospectiveSort((I1*) keys->GetDataPtr(), (I1*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
-               break;
-
-    case ELEMENT_TYPE_U1:
-    case ELEMENT_TYPE_BOOLEAN:
-        ArrayHelpers<U1>::IntrospectiveSort((U1*) keys->GetDataPtr(), (U1*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
-        break;
-
-    case ELEMENT_TYPE_I2:
-        ArrayHelpers<I2>::IntrospectiveSort((I2*) keys->GetDataPtr(), (I2*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
-        break;
-
-    case ELEMENT_TYPE_U2:
-    case ELEMENT_TYPE_CHAR:
-               ArrayHelpers<U2>::IntrospectiveSort((U2*) keys->GetDataPtr(), (U2*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
-        break;
-
-    case ELEMENT_TYPE_I4:
-               ArrayHelpers<I4>::IntrospectiveSort((I4*) keys->GetDataPtr(), (I4*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
-        break;
-
-    case ELEMENT_TYPE_U4:
-        ArrayHelpers<U4>::IntrospectiveSort((U4*) keys->GetDataPtr(), (U4*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
-        break;
-
-    case ELEMENT_TYPE_R4:
-    {
-        R4 * R4Keys = (R4*) keys->GetDataPtr();
-        R4 * R4Items = (R4*) (items == NULL ? NULL : items->GetDataPtr());
-
-        // Comparison to NaN is always false, so do a linear pass
-        // and swap all NaNs to the front of the array
-        left = ArrayHelpers<R4>::NaNPrepass(R4Keys, R4Items, left, right);
-        if(left != right) ArrayHelpers<R4>::IntrospectiveSort(R4Keys, R4Items, left, right);
-        break;
-    };
-
-    case ELEMENT_TYPE_I8:
-        ArrayHelpers<I8>::IntrospectiveSort((I8*) keys->GetDataPtr(), (I8*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
-        break;
-
-    case ELEMENT_TYPE_U8:
-        ArrayHelpers<U8>::IntrospectiveSort((U8*) keys->GetDataPtr(), (U8*) (items == NULL ? NULL : items->GetDataPtr()), left, right);
-        break;
-
-    case ELEMENT_TYPE_R8:
-    {
-        R8 * R8Keys = (R8*) keys->GetDataPtr();
-        R8 * R8Items = (R8*) (items == NULL ? NULL : items->GetDataPtr());
-
-        // Comparison to NaN is always false, so do a linear pass
-        // and swap all NaNs to the front of the array
-        left = ArrayHelpers<R8>::NaNPrepass(R8Keys, R8Items, left, right);
-        if(left != right) ArrayHelpers<R8>::IntrospectiveSort(R8Keys, R8Items, left, right);
-        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::TrySZSort");
-        FC_RETURN_BOOL(FALSE);
-    }
-    FC_RETURN_BOOL(TRUE);
-FCIMPLEND
-
 FCIMPL3(FC_BOOL_RET, ArrayHelper::TrySZReverse, ArrayBase * array, UINT32 index, UINT32 count)
 {
     FCALL_CONTRACT;
index 437ff1d09f895f1507bca02b9727ee7d09b0018c..4b35459874c49f3583db8f64ef2d190c4dffd5b8 100644 (file)
@@ -90,218 +90,6 @@ public:
         return ~lo;
     }
 
-    inline static void SwapIfGreaterWithItems(KIND keys[], KIND items[], int a, int b) {
-        if (a != b) {
-            if (keys[a] > keys[b]) {
-                KIND key = keys[a];
-                keys[a] = keys[b];
-                keys[b] = key;
-                if (items != NULL) {
-                    KIND item = items[a];
-                    items[a] = items[b];
-                    items[b] = item;
-                }
-            }
-        }
-    }
-
-    // For sorting, move all NaN instances to front of the input array
-    template <class REAL>
-    static unsigned int NaNPrepass(REAL keys[], REAL items[], unsigned int left, unsigned int right) {
-        for (unsigned int i = left; i <= right; i++) {
-            if (_isnan(keys[i])) {
-                REAL temp = keys[left];
-                keys[left] = keys[i];
-                keys[i] = temp;
-                if (items != NULL) {
-                    temp = items[left];
-                    items[left] = items[i];
-                    items[i] = temp;
-                }
-                left++;
-            }
-        }
-        return left;
-    }
-
-    // Implementation of Introspection Sort
-    static void IntrospectiveSort(KIND keys[], KIND items[], int left, int right) {
-        WRAPPER_NO_CONTRACT;
-
-        // Make sure left != right in your own code.
-        _ASSERTE(keys != NULL && left < right);
-
-        int length = right - left + 1;
-
-        if (length < 2)
-            return;
-
-        IntroSort(keys, items, left, right, 2 * FloorLog2(length));
-    }
-
-    static const int introsortSizeThreshold = 16;
-
-    static int FloorLog2(int n)
-    {
-        int result = 0;
-        while (n >= 1)
-        {
-            result++;
-            n = n / 2;
-        }
-        return result;
-    }
-
-    static void IntroSort(KIND keys[], KIND items[], int lo, int hi, int depthLimit)
-    {
-        while (hi > lo)
-        {
-            int partitionSize = hi - lo + 1;
-            if(partitionSize <= introsortSizeThreshold)
-            {
-                if (partitionSize == 1)
-                {
-                    return;
-                }
-                if (partitionSize == 2)
-                {
-                    SwapIfGreaterWithItems(keys, items, lo, hi);
-                    return;
-                }
-                if (partitionSize == 3)
-                {
-                    SwapIfGreaterWithItems(keys, items, lo, hi-1);
-                    SwapIfGreaterWithItems(keys, items, lo, hi);
-                    SwapIfGreaterWithItems(keys, items, hi-1, hi);
-                    return;
-                }
-
-                InsertionSort(keys, items, lo, hi);
-                return;
-            }
-
-            if (depthLimit == 0)
-            {
-                Heapsort(keys, items, lo, hi);
-                return;
-            }
-            depthLimit--;
-
-            int p = PickPivotAndPartition(keys, items, lo, hi);
-            IntroSort(keys, items, p + 1, hi, depthLimit);
-            hi = p - 1;
-        }
-        return;
-    }
-
-    static void Swap(KIND keys[], KIND items[], int i, int j)
-    {
-        KIND t = keys[i];
-        keys[i] = keys[j];
-        keys[j] = t;
-
-        if (items != NULL)
-        {
-            KIND item = items[i];
-            items[i] = items[j];
-            items[j] = item;
-        }
-    }
-
-    static int PickPivotAndPartition(KIND keys[], KIND items[], int lo, int hi)
-    {
-        // 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(keys, items, lo, mid);
-        SwapIfGreaterWithItems(keys, items, lo, hi);
-        SwapIfGreaterWithItems(keys, items, mid, hi);
-
-        KIND pivot = keys[mid];
-        Swap(keys, items, mid, hi - 1);
-
-        int left = lo, right = hi - 1;  // We already partitioned lo and hi and put the pivot in hi - 1.  And we pre-increment & decrement below.
-
-        while (left < right)
-        {
-            while (left < (hi - 1) && keys[++left] < pivot);
-            while (right > lo && pivot < keys[--right]);
-
-            if ((left >= right))
-                break;
-
-            Swap(keys, items, left, right);
-        }
-
-        // Put pivot in the right location.
-        Swap(keys, items, left, (hi - 1));
-        return left;
-    }
-
-    static void Heapsort(KIND keys[], KIND items[], int lo, int hi)
-    {
-        int n = hi - lo + 1;
-        for (int i = n / 2; i >= 1; i = i - 1)
-        {
-            DownHeap(keys, items, i, n, lo);
-        }
-        for (int i = n; i > 1; i = i - 1)
-        {
-            Swap(keys, items, lo, lo + i -1);
-            DownHeap(keys, items, 1, i - 1, lo);
-        }
-    }
-
-    static void DownHeap(KIND keys[], KIND items[], int i, int n, int lo)
-    {
-        KIND d = keys[lo + i - 1];
-        KIND di = (items != NULL) ? items[lo + i - 1] : NULL;
-        int child;
-
-        while (i <= n / 2)
-        {
-            child = 2 * i;
-            if (child < n && keys[lo + child - 1] < keys[lo + child])
-            {
-                child++;
-            }
-            if (!(d < keys[lo + child - 1]))
-                break;
-
-            keys[lo + i - 1] = keys[lo + child - 1];
-            if(items != NULL)
-                items[lo + i - 1] = items[ lo + child - 1];
-            i = child;
-        }
-        keys[lo + i - 1] = d;
-        if(items != NULL)
-            items[lo + i - 1] = di;
-    }
-
-    static void InsertionSort(KIND keys[], KIND items[], int lo, int hi)
-    {
-        int i, j;
-        KIND t, ti = NULL;
-        for (i = lo; i < hi; i++)
-        {
-            j = i;
-            t = keys[i + 1];
-            if(items != NULL)
-                ti = items[i + 1];
-            while (j >= lo && t < keys[j])
-            {
-                keys[j + 1] = keys[j];
-                if(items != NULL)
-                    items[j + 1] = items[j];
-                j--;
-            }
-            keys[j + 1] = t;
-            if(items != NULL)
-                items[j + 1] = ti;
-        }
-    }
-
     static void Reverse(KIND array[], UINT32 index, UINT32 count) {
         LIMITED_METHOD_CONTRACT;
 
@@ -332,7 +120,6 @@ public:
     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 FCDECL4(FC_BOOL_RET, TrySZSort, ArrayBase * keys, ArrayBase * items, UINT32 left, UINT32 right);
     static FCDECL3(FC_BOOL_RET, TrySZReverse, ArrayBase * array, UINT32 index, UINT32 count);
 
     // Helper methods
index 4818343de101de5283bb8d9f10c79de05226c840..a3396f92bb49f1726ad28b86b2c6dfb49b2a64cb 100644 (file)
@@ -721,7 +721,6 @@ FCFuncStart(gArrayFuncs)
     FCFuncElement("TrySZIndexOf", ArrayHelper::TrySZIndexOf)
     FCFuncElement("TrySZLastIndexOf", ArrayHelper::TrySZLastIndexOf)
     FCFuncElement("TrySZBinarySearch", ArrayHelper::TrySZBinarySearch)
-    FCFuncElement("TrySZSort", ArrayHelper::TrySZSort)
     FCFuncElement("TrySZReverse", ArrayHelper::TrySZReverse)
 FCFuncEnd()
 
index 06f8bba0185249e648adf2ea6e5b058a5673f9ed..39ddaca34da14329d3af2f195090b4aed36ce8b3 100644 (file)
@@ -1466,7 +1466,124 @@ namespace System
 
             if (length > 1)
             {
-                SortImpl(keys, items, index, length, comparer ?? Comparer.Default);
+                comparer ??= Comparer.Default;
+
+                if (comparer == Comparer.Default)
+                {
+                    switch (Type.GetTypeCode(keys.GetType().GetElementType()))
+                    {
+                        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;
+                            break;
+                    }
+
+                    static bool TryGenericSort<TKey>(TKey[]? keys, Array? items, int index, int length)
+                    {
+                        if (keys != 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;
+                            }
+                        }
+
+                        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);
+                }
             }
         }
 
@@ -1474,7 +1591,12 @@ namespace System
         {
             if (array == null)
                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
-            Sort<T>(array, 0, array.Length, null);
+
+            if (array.Length > 1)
+            {
+                var span = new Span<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), array.Length);
+                ArraySortHelper<T>.Default.Sort(span, null);
+            }
         }
 
         public static void Sort<TKey, TValue>(TKey[] keys, TValue[]? items)
@@ -1521,17 +1643,8 @@ namespace System
 
             if (length > 1)
             {
-#if !CORERT
-                if (comparer == null || comparer == Comparer<T>.Default)
-                {
-                    if (TrySZSort(array, null, index, index + length - 1))
-                    {
-                        return;
-                    }
-                }
-#endif
-
-                ArraySortHelper<T>.Default.Sort(array, index, length, comparer);
+                var span = new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), index), length);
+                ArraySortHelper<T>.Default.Sort(span, comparer);
             }
         }
 
@@ -1548,23 +1661,15 @@ namespace System
 
             if (length > 1)
             {
-#if !CORERT
-                if (comparer == null || comparer == Comparer<TKey>.Default)
-                {
-                    if (TrySZSort(keys, items, index, index + length - 1))
-                    {
-                        return;
-                    }
-                }
-#endif
-
                 if (items == null)
                 {
                     Sort<TKey>(keys, index, length, comparer);
                     return;
                 }
 
-                ArraySortHelper<TKey, TValue>.Default.Sort(keys, items, index, length, comparer);
+                var spanKeys = new Span<TKey>(ref Unsafe.Add(ref Unsafe.As<byte, TKey>(ref keys.GetRawSzArrayData()), index), length);
+                var spanItems = new Span<TValue>(ref Unsafe.Add(ref Unsafe.As<byte, TValue>(ref items.GetRawSzArrayData()), index), length);
+                ArraySortHelper<TKey, TValue>.Default.Sort(spanKeys, spanItems, comparer);
             }
         }
 
@@ -1580,7 +1685,8 @@ namespace System
                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison);
             }
 
-            ArraySortHelper<T>.Sort(array, 0, array.Length, comparison!);
+            var span = new Span<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), array.Length);
+            ArraySortHelper<T>.Sort(span, comparison);
         }
 
         public static bool TrueForAll<T>(T[] array, Predicate<T> match)
index 96bab3002fe91494842c9c9cd06445d8980b2bcc..46b9890dfe474142218692e9e21f81b354d842e6 100644 (file)
@@ -15,6 +15,7 @@
 
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
 
 namespace System.Collections.Generic
 {
@@ -49,17 +50,14 @@ namespace System.Collections.Generic
     {
         #region IArraySortHelper<T> Members
 
-        public void Sort(T[] keys, int index, int length, IComparer<T>? comparer)
+        public void Sort(Span<T> keys, IComparer<T>? comparer)
         {
-            Debug.Assert(keys != null, "Check the arguments in the caller!");
-            Debug.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
-
             // Add a try block here to detect IComparers (or their
             // underlying IComparables, etc) that are bogus.
             try
             {
                 comparer ??= Comparer<T>.Default;
-                IntrospectiveSort(keys, index, length, comparer.Compare);
+                IntrospectiveSort(keys, comparer.Compare);
             }
             catch (IndexOutOfRangeException)
             {
@@ -86,16 +84,14 @@ namespace System.Collections.Generic
 
         #endregion
 
-        internal static void Sort(T[] keys, int index, int length, Comparison<T> comparer)
+        internal static void Sort(Span<T> keys, Comparison<T> comparer)
         {
-            Debug.Assert(keys != null, "Check the arguments in the caller!");
-            Debug.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
             Debug.Assert(comparer != null, "Check the arguments in the caller!");
 
             // Add a try block here to detect bogus comparisons
             try
             {
-                IntrospectiveSort(keys, index, length, comparer);
+                IntrospectiveSort(keys, comparer);
             }
             catch (IndexOutOfRangeException)
             {
@@ -133,20 +129,21 @@ namespace System.Collections.Generic
             return ~lo;
         }
 
-        private static void SwapIfGreater(T[] keys, Comparison<T> comparer, int a, int b)
+        private static void SwapIfGreater(Span<T> keys, Comparison<T> comparer, int i, int j)
         {
-            if (a != b)
+            if (i != j)
             {
-                if (comparer(keys[a], keys[b]) > 0)
+                if (comparer(keys[i], keys[j]) > 0)
                 {
-                    T key = keys[a];
-                    keys[a] = keys[b];
-                    keys[b] = key;
+                    T key = keys[i];
+                    keys[i] = keys[j];
+                    keys[j] = key;
                 }
             }
         }
 
-        private static void Swap(T[] a, int i, int j)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static void Swap(Span<T> a, int i, int j)
         {
             if (i != j)
             {
@@ -156,27 +153,22 @@ namespace System.Collections.Generic
             }
         }
 
-        internal static void IntrospectiveSort(T[] keys, int left, int length, Comparison<T> comparer)
+        internal static void IntrospectiveSort(Span<T> keys, Comparison<T> comparer)
         {
-            Debug.Assert(keys != null);
             Debug.Assert(comparer != null);
-            Debug.Assert(left >= 0);
-            Debug.Assert(length >= 0);
-            Debug.Assert(length <= keys.Length);
-            Debug.Assert(length + left <= keys.Length);
-
-            if (length < 2)
-                return;
 
-            IntroSort(keys, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(length), comparer);
+            if (keys.Length > 1)
+            {
+                IntroSort(keys, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length), comparer);
+            }
         }
 
-        private static void IntroSort(T[] keys, int lo, int hi, int depthLimit, Comparison<T> comparer)
+        private static void IntroSort(Span<T> keys, int depthLimit, Comparison<T> comparer)
         {
-            Debug.Assert(keys != null);
+            int lo = 0;
+            int hi = keys.Length - 1;
+
             Debug.Assert(comparer != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi < keys.Length);
 
             while (hi > lo)
             {
@@ -187,11 +179,13 @@ namespace System.Collections.Generic
                     {
                         return;
                     }
+
                     if (partitionSize == 2)
                     {
                         SwapIfGreater(keys, comparer, lo, hi);
                         return;
                     }
+
                     if (partitionSize == 3)
                     {
                         SwapIfGreater(keys, comparer, lo, hi - 1);
@@ -200,31 +194,32 @@ namespace System.Collections.Generic
                         return;
                     }
 
-                    InsertionSort(keys, lo, hi, comparer);
+                    InsertionSort(keys[lo..(hi+1)], comparer);
                     return;
                 }
 
                 if (depthLimit == 0)
                 {
-                    Heapsort(keys, lo, hi, comparer);
+                    HeapSort(keys[lo..(hi+1)], comparer);
                     return;
                 }
                 depthLimit--;
 
-                int p = PickPivotAndPartition(keys, lo, hi, comparer);
+                int p = PickPivotAndPartition(keys[lo..(hi+1)], comparer);
+
                 // Note we've already partitioned around the pivot and do not have to move the pivot again.
-                IntroSort(keys, p + 1, hi, depthLimit, comparer);
+                IntroSort(keys[(p+1)..(hi+1)], depthLimit, comparer);
                 hi = p - 1;
             }
         }
 
-        private static int PickPivotAndPartition(T[] keys, int lo, int hi, Comparison<T> comparer)
+        private static int PickPivotAndPartition(Span<T> keys, Comparison<T> comparer)
         {
-            Debug.Assert(keys != null);
             Debug.Assert(comparer != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi > lo);
-            Debug.Assert(hi < keys.Length);
+            Debug.Assert(!keys.IsEmpty);
+
+            int lo = 0;
+            int hi = keys.Length - 1;
 
             // Compute median-of-three.  But also partition them, since we've done the comparison.
             int middle = lo + ((hi - lo) / 2);
@@ -254,19 +249,21 @@ namespace System.Collections.Generic
             return left;
         }
 
-        private static void Heapsort(T[] keys, int lo, int hi, Comparison<T> comparer)
+        private static void HeapSort(Span<T> keys, Comparison<T> comparer)
         {
-            Debug.Assert(keys != null);
             Debug.Assert(comparer != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi > lo);
-            Debug.Assert(hi < keys.Length);
+            Debug.Assert(!keys.IsEmpty);
+
+            int lo = 0;
+            int hi = keys.Length - 1;
 
             int n = hi - lo + 1;
+
             for (int i = n / 2; i >= 1; i--)
             {
                 DownHeap(keys, i, n, lo, comparer);
             }
+
             for (int i = n; i > 1; i--)
             {
                 Swap(keys, lo, lo + i - 1);
@@ -274,48 +271,44 @@ namespace System.Collections.Generic
             }
         }
 
-        private static void DownHeap(T[] keys, int i, int n, int lo, Comparison<T> comparer)
+        private static void DownHeap(Span<T> keys, int i, int n, int lo, Comparison<T> comparer)
         {
-            Debug.Assert(keys != null);
             Debug.Assert(comparer != null);
             Debug.Assert(lo >= 0);
             Debug.Assert(lo < keys.Length);
 
             T d = keys[lo + i - 1];
-            int child;
             while (i <= n / 2)
             {
-                child = 2 * i;
+                int child = 2 * i;
                 if (child < n && comparer(keys[lo + child - 1], keys[lo + child]) < 0)
                 {
                     child++;
                 }
+
                 if (!(comparer(d, keys[lo + child - 1]) < 0))
                     break;
+
                 keys[lo + i - 1] = keys[lo + child - 1];
                 i = child;
             }
+
             keys[lo + i - 1] = d;
         }
 
-        private static void InsertionSort(T[] keys, int lo, int hi, Comparison<T> comparer)
+        private static void InsertionSort(Span<T> keys, Comparison<T> comparer)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi >= lo);
-            Debug.Assert(hi <= keys.Length);
-
-            int i, j;
-            T t;
-            for (i = lo; i < hi; i++)
+            for (int i = 0; i < keys.Length - 1; i++)
             {
-                j = i;
-                t = keys[i + 1];
-                while (j >= lo && comparer(t, keys[j]) < 0)
+                T t = keys[i + 1];
+
+                int j = i;
+                while (j >= 0 && comparer(t, keys[j]) < 0)
                 {
                     keys[j + 1] = keys[j];
                     j--;
                 }
+
                 keys[j + 1] = t;
             }
         }
@@ -328,20 +321,17 @@ namespace System.Collections.Generic
 
         #region IArraySortHelper<T> Members
 
-        public void Sort(T[] keys, int index, int length, IComparer<T>? comparer)
+        public void Sort(Span<T> keys, IComparer<T>? comparer)
         {
-            Debug.Assert(keys != null, "Check the arguments in the caller!");
-            Debug.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
-
             try
             {
                 if (comparer == null || comparer == Comparer<T>.Default)
                 {
-                    IntrospectiveSort(keys, index, length);
+                    IntrospectiveSort(keys);
                 }
                 else
                 {
-                    ArraySortHelper<T>.IntrospectiveSort(keys, index, length, comparer.Compare);
+                    ArraySortHelper<T>.IntrospectiveSort(keys, comparer.Compare);
                 }
             }
             catch (IndexOutOfRangeException)
@@ -416,24 +406,24 @@ namespace System.Collections.Generic
             return ~lo;
         }
 
-        private static void SwapIfGreaterWithItems(T[] keys, int a, int b)
+        private static void SwapIfGreater(Span<T> keys, int i, int j)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(0 <= a && a < keys.Length);
-            Debug.Assert(0 <= b && b < keys.Length);
+            Debug.Assert(0 <= i && i < keys.Length);
+            Debug.Assert(0 <= j && j < keys.Length);
 
-            if (a != b)
+            if (i != j)
             {
-                if (keys[a] != null && keys[a].CompareTo(keys[b]) > 0)
+                if (keys[i] != null && keys[i].CompareTo(keys[j]) > 0)
                 {
-                    T key = keys[a];
-                    keys[a] = keys[b];
-                    keys[b] = key;
+                    T key = keys[i];
+                    keys[i] = keys[j];
+                    keys[j] = key;
                 }
             }
         }
 
-        private static void Swap(T[] a, int i, int j)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static void Swap(Span<T> a, int i, int j)
         {
             if (i != j)
             {
@@ -443,25 +433,18 @@ namespace System.Collections.Generic
             }
         }
 
-        internal static void IntrospectiveSort(T[] keys, int left, int length)
+        internal static void IntrospectiveSort(Span<T> keys)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(left >= 0);
-            Debug.Assert(length >= 0);
-            Debug.Assert(length <= keys.Length);
-            Debug.Assert(length + left <= keys.Length);
-
-            if (length < 2)
-                return;
-
-            IntroSort(keys, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(length));
+            if (keys.Length > 1)
+            {
+                IntroSort(keys, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length));
+            }
         }
 
-        private static void IntroSort(T[] keys, int lo, int hi, int depthLimit)
+        private static void IntroSort(Span<T> keys, int depthLimit)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi < keys.Length);
+            int lo = 0;
+            int hi = keys.Length - 1;
 
             while (hi > lo)
             {
@@ -474,49 +457,50 @@ namespace System.Collections.Generic
                     }
                     if (partitionSize == 2)
                     {
-                        SwapIfGreaterWithItems(keys, lo, hi);
+                        SwapIfGreater(keys, lo, hi);
                         return;
                     }
                     if (partitionSize == 3)
                     {
-                        SwapIfGreaterWithItems(keys, lo, hi - 1);
-                        SwapIfGreaterWithItems(keys, lo, hi);
-                        SwapIfGreaterWithItems(keys, hi - 1, hi);
+                        SwapIfGreater(keys, lo, hi - 1);
+                        SwapIfGreater(keys, lo, hi);
+                        SwapIfGreater(keys, hi - 1, hi);
                         return;
                     }
 
-                    InsertionSort(keys, lo, hi);
+                    InsertionSort(keys[lo..(hi+1)]);
                     return;
                 }
 
                 if (depthLimit == 0)
                 {
-                    Heapsort(keys, lo, hi);
+                    HeapSort(keys[lo..(hi+1)]);
                     return;
                 }
                 depthLimit--;
 
-                int p = PickPivotAndPartition(keys, lo, hi);
+                int p = PickPivotAndPartition(keys[lo..(hi+1)]);
+
                 // Note we've already partitioned around the pivot and do not have to move the pivot again.
-                IntroSort(keys, p + 1, hi, depthLimit);
+                IntroSort(keys[(p+1)..(hi+1)], depthLimit);
                 hi = p - 1;
             }
         }
 
-        private static int PickPivotAndPartition(T[] keys, int lo, int hi)
+        private static int PickPivotAndPartition(Span<T> keys)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi > lo);
-            Debug.Assert(hi < keys.Length);
+            Debug.Assert(!keys.IsEmpty);
+
+            int lo = 0;
+            int hi = keys.Length - 1;
 
             // Compute median-of-three.  But also partition them, since we've done the comparison.
             int middle = lo + ((hi - lo) / 2);
 
             // Sort lo, mid and hi appropriately, then pick mid as the pivot.
-            SwapIfGreaterWithItems(keys, lo, middle);  // swap the low with the mid point
-            SwapIfGreaterWithItems(keys, lo, hi);   // swap the low with the high
-            SwapIfGreaterWithItems(keys, middle, hi); // swap the middle with the high
+            SwapIfGreater(keys, lo, middle);  // swap the low with the mid point
+            SwapIfGreater(keys, lo, hi);   // swap the low with the high
+            SwapIfGreater(keys, middle, hi); // swap the middle with the high
 
             T pivot = keys[middle];
             Swap(keys, middle, hi - 1);
@@ -546,18 +530,19 @@ namespace System.Collections.Generic
             return left;
         }
 
-        private static void Heapsort(T[] keys, int lo, int hi)
+        private static void HeapSort(Span<T> keys)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi > lo);
-            Debug.Assert(hi < keys.Length);
+            Debug.Assert(!keys.IsEmpty);
+
+            int lo = 0;
+            int hi = keys.Length - 1;
 
             int n = hi - lo + 1;
-            for (int i = n / 2; i >= 1; i--)
+            for (int i = n / 2; i >= 1; i = i - 1)
             {
                 DownHeap(keys, i, n, lo);
             }
+
             for (int i = n; i > 1; i--)
             {
                 Swap(keys, lo, lo + i - 1);
@@ -565,47 +550,43 @@ namespace System.Collections.Generic
             }
         }
 
-        private static void DownHeap(T[] keys, int i, int n, int lo)
+        private static void DownHeap(Span<T> keys, int i, int n, int lo)
         {
-            Debug.Assert(keys != null);
             Debug.Assert(lo >= 0);
             Debug.Assert(lo < keys.Length);
 
             T d = keys[lo + i - 1];
-            int child;
             while (i <= n / 2)
             {
-                child = 2 * i;
+                int child = 2 * i;
                 if (child < n && (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
                 {
                     child++;
                 }
+
                 if (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(d) < 0)
                     break;
+
                 keys[lo + i - 1] = keys[lo + child - 1];
                 i = child;
             }
+
             keys[lo + i - 1] = d;
         }
 
-        private static void InsertionSort(T[] keys, int lo, int hi)
+        private static void InsertionSort(Span<T> keys)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi >= lo);
-            Debug.Assert(hi <= keys.Length);
-
-            int i, j;
-            T t;
-            for (i = lo; i < hi; i++)
+            for (int i = 0; i < keys.Length - 1; i++)
             {
-                j = i;
-                t = keys[i + 1];
-                while (j >= lo && (t == null || t.CompareTo(keys[j]) < 0))
+                T t = keys[i + 1];
+
+                int j = i;
+                while (j >= 0 && (t == null || t.CompareTo(keys[j]) < 0))
                 {
                     keys[j + 1] = keys[j];
                     j--;
                 }
+
                 keys[j + 1] = t;
             }
         }
@@ -617,22 +598,13 @@ namespace System.Collections.Generic
 
     internal partial class ArraySortHelper<TKey, TValue>
     {
-        public void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey>? comparer)
+        public void Sort(Span<TKey> keys, Span<TValue> values, IComparer<TKey>? comparer)
         {
-            Debug.Assert(keys != null, "Check the arguments in the caller!");  // Precondition on interface method
-            Debug.Assert(values != null, "Check the arguments in the caller!");
-            Debug.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
-
             // Add a try block here to detect IComparers (or their
             // underlying IComparables, etc) that are bogus.
             try
             {
-                if (comparer == null || comparer == Comparer<TKey>.Default)
-                {
-                    comparer = Comparer<TKey>.Default;
-                }
-
-                IntrospectiveSort(keys, values, index, length, comparer);
+                IntrospectiveSort(keys, values, comparer ?? Comparer<TKey>.Default);
             }
             catch (IndexOutOfRangeException)
             {
@@ -644,30 +616,29 @@ namespace System.Collections.Generic
             }
         }
 
-        private static void SwapIfGreaterWithItems(TKey[] keys, TValue[] values, IComparer<TKey> comparer, int a, int b)
+        private static void SwapIfGreaterWithValues(Span<TKey> keys, Span<TValue> values, IComparer<TKey> comparer, int i, int j)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
             Debug.Assert(comparer != null);
-            Debug.Assert(0 <= a && a < keys.Length && a < values.Length);
-            Debug.Assert(0 <= b && b < keys.Length && b < values.Length);
+            Debug.Assert(0 <= i && i < keys.Length && i < values.Length);
+            Debug.Assert(0 <= j && j < keys.Length && j < values.Length);
 
-            if (a != b)
+            if (i != j)
             {
-                if (comparer.Compare(keys[a], keys[b]) > 0)
+                if (comparer.Compare(keys[i], keys[j]) > 0)
                 {
-                    TKey key = keys[a];
-                    keys[a] = keys[b];
-                    keys[b] = key;
+                    TKey key = keys[i];
+                    keys[i] = keys[j];
+                    keys[j] = key;
 
-                    TValue value = values[a];
-                    values[a] = values[b];
-                    values[b] = value;
+                    TValue value = values[i];
+                    values[i] = values[j];
+                    values[j] = value;
                 }
             }
         }
 
-        private static void Swap(TKey[] keys, TValue[] values, int i, int j)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static void Swap(Span<TKey> keys, Span<TValue> values, int i, int j)
         {
             if (i != j)
             {
@@ -681,30 +652,23 @@ namespace System.Collections.Generic
             }
         }
 
-        internal static void IntrospectiveSort(TKey[] keys, TValue[] values, int left, int length, IComparer<TKey> comparer)
+        internal static void IntrospectiveSort(Span<TKey> keys, Span<TValue> values, IComparer<TKey> comparer)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
             Debug.Assert(comparer != null);
-            Debug.Assert(left >= 0);
-            Debug.Assert(length >= 0);
-            Debug.Assert(length <= keys.Length);
-            Debug.Assert(length + left <= keys.Length);
-            Debug.Assert(length + left <= values.Length);
-
-            if (length < 2)
-                return;
+            Debug.Assert(keys.Length == values.Length);
 
-            IntroSort(keys, values, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(length), comparer);
+            if (keys.Length > 1)
+            {
+                IntroSort(keys, values, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length), comparer);
+            }
         }
 
-        private static void IntroSort(TKey[] keys, TValue[] values, int lo, int hi, int depthLimit, IComparer<TKey> comparer)
+        private static void IntroSort(Span<TKey> keys, Span<TValue> values, int depthLimit, IComparer<TKey> comparer)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
             Debug.Assert(comparer != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi < keys.Length);
+
+            int lo = 0;
+            int hi = keys.Length - 1;
 
             while (hi > lo)
             {
@@ -715,53 +679,55 @@ namespace System.Collections.Generic
                     {
                         return;
                     }
+
                     if (partitionSize == 2)
                     {
-                        SwapIfGreaterWithItems(keys, values, comparer, lo, hi);
+                        SwapIfGreaterWithValues(keys, values, comparer, lo, hi);
                         return;
                     }
+
                     if (partitionSize == 3)
                     {
-                        SwapIfGreaterWithItems(keys, values, comparer, lo, hi - 1);
-                        SwapIfGreaterWithItems(keys, values, comparer, lo, hi);
-                        SwapIfGreaterWithItems(keys, values, comparer, hi - 1, hi);
+                        SwapIfGreaterWithValues(keys, values, comparer, lo, hi - 1);
+                        SwapIfGreaterWithValues(keys, values, comparer, lo, hi);
+                        SwapIfGreaterWithValues(keys, values, comparer, hi - 1, hi);
                         return;
                     }
 
-                    InsertionSort(keys, values, lo, hi, comparer);
+                    InsertionSort(keys[lo..(hi+1)], values[lo..(hi+1)], comparer);
                     return;
                 }
 
                 if (depthLimit == 0)
                 {
-                    Heapsort(keys, values, lo, hi, comparer);
+                    HeapSort(keys[lo..(hi+1)], values[lo..(hi+1)], comparer);
                     return;
                 }
                 depthLimit--;
 
-                int p = PickPivotAndPartition(keys, values, lo, hi, comparer);
+                int p = PickPivotAndPartition(keys[lo..(hi+1)], values[lo..(hi+1)], comparer);
+
                 // Note we've already partitioned around the pivot and do not have to move the pivot again.
-                IntroSort(keys, values, p + 1, hi, depthLimit, comparer);
+                IntroSort(keys[(p+1)..(hi+1)], values[(p+1)..(hi+1)], depthLimit, comparer);
                 hi = p - 1;
             }
         }
 
-        private static int PickPivotAndPartition(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
+        private static int PickPivotAndPartition(Span<TKey> keys, Span<TValue> values, IComparer<TKey> comparer)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
             Debug.Assert(comparer != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi > lo);
-            Debug.Assert(hi < keys.Length);
+            Debug.Assert(!keys.IsEmpty);
+
+            int lo = 0;
+            int hi = keys.Length - 1;
 
             // Compute median-of-three.  But also partition them, since we've done the comparison.
             int middle = lo + ((hi - lo) / 2);
 
             // Sort lo, mid and hi appropriately, then pick mid as the pivot.
-            SwapIfGreaterWithItems(keys, values, comparer, lo, middle);  // swap the low with the mid point
-            SwapIfGreaterWithItems(keys, values, comparer, lo, hi);   // swap the low with the high
-            SwapIfGreaterWithItems(keys, values, comparer, middle, hi); // swap the middle with the high
+            SwapIfGreaterWithValues(keys, values, comparer, lo, middle);  // swap the low with the mid point
+            SwapIfGreaterWithValues(keys, values, comparer, lo, hi);   // swap the low with the high
+            SwapIfGreaterWithValues(keys, values, comparer, middle, hi); // swap the middle with the high
 
             TKey pivot = keys[middle];
             Swap(keys, values, middle, hi - 1);
@@ -783,20 +749,20 @@ namespace System.Collections.Generic
             return left;
         }
 
-        private static void Heapsort(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
+        private static void HeapSort(Span<TKey> keys, Span<TValue> values, IComparer<TKey> comparer)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
             Debug.Assert(comparer != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi > lo);
-            Debug.Assert(hi < keys.Length);
+            Debug.Assert(!keys.IsEmpty);
+
+            int lo = 0;
+            int hi = keys.Length - 1;
 
             int n = hi - lo + 1;
             for (int i = n / 2; i >= 1; i--)
             {
                 DownHeap(keys, values, i, n, lo, comparer);
             }
+
             for (int i = n; i > 1; i--)
             {
                 Swap(keys, values, lo, lo + i - 1);
@@ -804,57 +770,52 @@ namespace System.Collections.Generic
             }
         }
 
-        private static void DownHeap(TKey[] keys, TValue[] values, int i, int n, int lo, IComparer<TKey> comparer)
+        private static void DownHeap(Span<TKey> keys, Span<TValue> values, int i, int n, int lo, IComparer<TKey> comparer)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
             Debug.Assert(comparer != null);
             Debug.Assert(lo >= 0);
             Debug.Assert(lo < keys.Length);
 
             TKey d = keys[lo + i - 1];
             TValue dValue = values[lo + i - 1];
-            int child;
+
             while (i <= n / 2)
             {
-                child = 2 * i;
+                int child = 2 * i;
                 if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
                 {
                     child++;
                 }
+
                 if (!(comparer.Compare(d, keys[lo + child - 1]) < 0))
                     break;
+
                 keys[lo + i - 1] = keys[lo + child - 1];
                 values[lo + i - 1] = values[lo + child - 1];
                 i = child;
             }
+
             keys[lo + i - 1] = d;
             values[lo + i - 1] = dValue;
         }
 
-        private static void InsertionSort(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
+        private static void InsertionSort(Span<TKey> keys, Span<TValue> values, IComparer<TKey> comparer)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
             Debug.Assert(comparer != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi >= lo);
-            Debug.Assert(hi <= keys.Length);
 
-            int i, j;
-            TKey t;
-            TValue tValue;
-            for (i = lo; i < hi; i++)
+            for (int i = 0; i < keys.Length - 1; i++)
             {
-                j = i;
-                t = keys[i + 1];
-                tValue = values[i + 1];
-                while (j >= lo && comparer.Compare(t, keys[j]) < 0)
+                TKey t = keys[i + 1];
+                TValue tValue = values[i + 1];
+
+                int j = i;
+                while (j >= 0 && comparer.Compare(t, keys[j]) < 0)
                 {
                     keys[j + 1] = keys[j];
                     values[j + 1] = values[j];
                     j--;
                 }
+
                 keys[j + 1] = t;
                 values[j + 1] = tValue;
             }
@@ -864,22 +825,19 @@ namespace System.Collections.Generic
     internal partial class GenericArraySortHelper<TKey, TValue>
         where TKey : IComparable<TKey>
     {
-        public void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey>? comparer)
+        public void Sort(Span<TKey> keys, Span<TValue> values, IComparer<TKey>? comparer)
         {
-            Debug.Assert(keys != null, "Check the arguments in the caller!");
-            Debug.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
-
             // Add a try block here to detect IComparers (or their
             // underlying IComparables, etc) that are bogus.
             try
             {
                 if (comparer == null || comparer == Comparer<TKey>.Default)
                 {
-                    IntrospectiveSort(keys, values, index, length);
+                    IntrospectiveSort(keys, values);
                 }
                 else
                 {
-                    ArraySortHelper<TKey, TValue>.IntrospectiveSort(keys, values, index, length, comparer);
+                    ArraySortHelper<TKey, TValue>.IntrospectiveSort(keys, values, comparer);
                 }
             }
             catch (IndexOutOfRangeException)
@@ -892,24 +850,25 @@ namespace System.Collections.Generic
             }
         }
 
-        private static void SwapIfGreaterWithItems(TKey[] keys, TValue[] values, int a, int b)
+        private static void SwapIfGreaterWithValues(Span<TKey> keys, Span<TValue> values, int i, int j)
         {
-            if (a != b)
+            if (i != j)
             {
-                if (keys[a] != null && keys[a].CompareTo(keys[b]) > 0)
+                if (keys[i] != null && keys[i].CompareTo(keys[j]) > 0)
                 {
-                    TKey key = keys[a];
-                    keys[a] = keys[b];
-                    keys[b] = key;
+                    TKey key = keys[i];
+                    keys[i] = keys[j];
+                    keys[j] = key;
 
-                    TValue value = values[a];
-                    values[a] = values[b];
-                    values[b] = value;
+                    TValue value = values[i];
+                    values[i] = values[j];
+                    values[j] = value;
                 }
             }
         }
 
-        private static void Swap(TKey[] keys, TValue[] values, int i, int j)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static void Swap(Span<TKey> keys, Span<TValue> values, int i, int j)
         {
             if (i != j)
             {
@@ -923,28 +882,20 @@ namespace System.Collections.Generic
             }
         }
 
-        internal static void IntrospectiveSort(TKey[] keys, TValue[] values, int left, int length)
+        internal static void IntrospectiveSort(Span<TKey> keys, Span<TValue> values)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
-            Debug.Assert(left >= 0);
-            Debug.Assert(length >= 0);
-            Debug.Assert(length <= keys.Length);
-            Debug.Assert(length + left <= keys.Length);
-            Debug.Assert(length + left <= values.Length);
-
-            if (length < 2)
-                return;
-
-            IntroSort(keys, values, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(length));
+            Debug.Assert(keys.Length == values.Length);
+
+            if (keys.Length > 1)
+            {
+                IntroSort(keys, values, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(keys.Length));
+            }
         }
 
-        private static void IntroSort(TKey[] keys, TValue[] values, int lo, int hi, int depthLimit)
+        private static void IntroSort(Span<TKey> keys, Span<TValue> values, int depthLimit)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi < keys.Length);
+            int lo = 0;
+            int hi = keys.Length - 1;
 
             while (hi > lo)
             {
@@ -955,52 +906,54 @@ namespace System.Collections.Generic
                     {
                         return;
                     }
+
                     if (partitionSize == 2)
                     {
-                        SwapIfGreaterWithItems(keys, values, lo, hi);
+                        SwapIfGreaterWithValues(keys, values, lo, hi);
                         return;
                     }
+
                     if (partitionSize == 3)
                     {
-                        SwapIfGreaterWithItems(keys, values, lo, hi - 1);
-                        SwapIfGreaterWithItems(keys, values, lo, hi);
-                        SwapIfGreaterWithItems(keys, values, hi - 1, hi);
+                        SwapIfGreaterWithValues(keys, values, lo, hi - 1);
+                        SwapIfGreaterWithValues(keys, values, lo, hi);
+                        SwapIfGreaterWithValues(keys, values, hi - 1, hi);
                         return;
                     }
 
-                    InsertionSort(keys, values, lo, hi);
+                    InsertionSort(keys[lo..(hi+1)], values[lo..(hi+1)]);
                     return;
                 }
 
                 if (depthLimit == 0)
                 {
-                    Heapsort(keys, values, lo, hi);
+                    HeapSort(keys[lo..(hi+1)], values[lo..(hi+1)]);
                     return;
                 }
                 depthLimit--;
 
-                int p = PickPivotAndPartition(keys, values, lo, hi);
+                int p = PickPivotAndPartition(keys[lo..(hi+1)], values[lo..(hi+1)]);
+
                 // Note we've already partitioned around the pivot and do not have to move the pivot again.
-                IntroSort(keys, values, p + 1, hi, depthLimit);
+                IntroSort(keys[(p+1)..(hi+1)], values[(p+1)..(hi+1)], depthLimit);
                 hi = p - 1;
             }
         }
 
-        private static int PickPivotAndPartition(TKey[] keys, TValue[] values, int lo, int hi)
+        private static int PickPivotAndPartition(Span<TKey> keys, Span<TValue> values)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi > lo);
-            Debug.Assert(hi < keys.Length);
+            Debug.Assert(!keys.IsEmpty);
+
+            int lo = 0;
+            int hi = keys.Length - 1;
 
             // Compute median-of-three.  But also partition them, since we've done the comparison.
             int middle = lo + ((hi - lo) / 2);
 
             // Sort lo, mid and hi appropriately, then pick mid as the pivot.
-            SwapIfGreaterWithItems(keys, values, lo, middle);  // swap the low with the mid point
-            SwapIfGreaterWithItems(keys, values, lo, hi);   // swap the low with the high
-            SwapIfGreaterWithItems(keys, values, middle, hi); // swap the middle with the high
+            SwapIfGreaterWithValues(keys, values, lo, middle);  // swap the low with the mid point
+            SwapIfGreaterWithValues(keys, values, lo, hi);   // swap the low with the high
+            SwapIfGreaterWithValues(keys, values, middle, hi); // swap the middle with the high
 
             TKey pivot = keys[middle];
             Swap(keys, values, middle, hi - 1);
@@ -1030,19 +983,19 @@ namespace System.Collections.Generic
             return left;
         }
 
-        private static void Heapsort(TKey[] keys, TValue[] values, int lo, int hi)
+        private static void HeapSort(Span<TKey> keys, Span<TValue> values)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi > lo);
-            Debug.Assert(hi < keys.Length);
+            Debug.Assert(!keys.IsEmpty);
+
+            int lo = 0;
+            int hi = keys.Length - 1;
 
             int n = hi - lo + 1;
             for (int i = n / 2; i >= 1; i--)
             {
                 DownHeap(keys, values, i, n, lo);
             }
+
             for (int i = n; i > 1; i--)
             {
                 Swap(keys, values, lo, lo + i - 1);
@@ -1050,54 +1003,49 @@ namespace System.Collections.Generic
             }
         }
 
-        private static void DownHeap(TKey[] keys, TValue[] values, int i, int n, int lo)
+        private static void DownHeap(Span<TKey> keys, Span<TValue> values, int i, int n, int lo)
         {
-            Debug.Assert(keys != null);
             Debug.Assert(lo >= 0);
             Debug.Assert(lo < keys.Length);
 
             TKey d = keys[lo + i - 1];
             TValue dValue = values[lo + i - 1];
-            int child;
+
             while (i <= n / 2)
             {
-                child = 2 * i;
+                int child = 2 * i;
                 if (child < n && (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
                 {
                     child++;
                 }
+
                 if (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(d) < 0)
                     break;
+
                 keys[lo + i - 1] = keys[lo + child - 1];
                 values[lo + i - 1] = values[lo + child - 1];
                 i = child;
             }
+
             keys[lo + i - 1] = d;
             values[lo + i - 1] = dValue;
         }
 
-        private static void InsertionSort(TKey[] keys, TValue[] values, int lo, int hi)
+        private static void InsertionSort(Span<TKey> keys, Span<TValue> values)
         {
-            Debug.Assert(keys != null);
-            Debug.Assert(values != null);
-            Debug.Assert(lo >= 0);
-            Debug.Assert(hi >= lo);
-            Debug.Assert(hi <= keys.Length);
-
-            int i, j;
-            TKey t;
-            TValue tValue;
-            for (i = lo; i < hi; i++)
+            for (int i = 0; i < keys.Length - 1; i++)
             {
-                j = i;
-                t = keys[i + 1];
-                tValue = values[i + 1];
-                while (j >= lo && (t == null || t.CompareTo(keys[j]) < 0))
+                TKey t = keys[i + 1];
+                TValue tValue = values[i + 1];
+
+                int j = i;
+                while (j >= 0 && (t == null || t.CompareTo(keys[j]) < 0))
                 {
                     keys[j + 1] = keys[j];
                     values[j + 1] = values[j];
                     j--;
                 }
+
                 keys[j + 1] = t;
                 values[j + 1] = tValue;
             }
index 0211e31039d90ebe4f97a1119ed975363c22017e..ac626c57d8f94032ff5290d4ec8dd73c3ffd7ec6 100644 (file)
@@ -1020,7 +1020,7 @@ namespace System.Collections.Generic
 
             if (_size > 1)
             {
-                ArraySortHelper<T>.Sort(_items, 0, _size, comparison);
+                ArraySortHelper<T>.Sort(new Span<T>(_items, 0, _size), comparison);
             }
             _version++;
         }
index 227b2a9a3153be086e0a2cb718727a82fc0a79e4..97da535e586a3a6d276e43a8eb418b5938d86f37 100644 (file)
@@ -1868,5 +1868,137 @@ namespace System
                 value, comparer);
             return BinarySearch(span, comparable);
         }
+
+        /// <summary>
+        /// Sorts the elements in the entire <see cref="Span{T}" /> using the <see cref="IComparable{T}" /> implementation
+        /// of each element of the <see cref= "Span{T}" />
+        /// </summary>
+        /// <typeparam name="T">The type of the elements of the span.</typeparam>
+        /// <param name="span">The <see cref="Span{T}"/> to sort.</param>
+        /// <exception cref="InvalidOperationException">
+        /// One or more elements in <paramref name="span"/> do not implement the <see cref="IComparable{T}" /> interface.
+        /// </exception>
+        public static void Sort<T>(this Span<T> span) =>
+            Sort(span, (IComparer<T>?)null);
+
+        /// <summary>
+        /// Sorts the elements in the entire <see cref="Span{T}" /> using the <typeparamref name="TComparer" />.
+        /// </summary>
+        /// <typeparam name="T">The type of the elements of the span.</typeparam>
+        /// <typeparam name="TComparer">The type of the comparer to use to compare elements.</typeparam>
+        /// <param name="span">The <see cref="Span{T}"/> to sort.</param>
+        /// <param name="comparer">
+        /// The <see cref="IComparer{T}"/> implementation to use when comparing elements, or null to
+        /// use the <see cref="IComparable{T}"/> interface implementation of each element.
+        /// </param>
+        /// <exception cref="InvalidOperationException">
+        /// <paramref name="comparer"/> is null, and one or more elements in <paramref name="span"/> do not
+        /// implement the <see cref="IComparable{T}" /> interface.
+        /// </exception>
+        /// <exception cref="ArgumentException">
+        /// The implementation of <paramref name="comparer"/> caused an error during the sort.
+        /// </exception>
+        public static void Sort<T, TComparer>(this Span<T> span, TComparer comparer)
+            where TComparer : IComparer<T>?
+        {
+            if (span.Length > 1)
+            {
+                ArraySortHelper<T>.Default.Sort(span, comparer); // value-type comparer will be boxed
+            }
+        }
+
+        /// <summary>
+        /// Sorts the elements in the entire <see cref="Span{T}" /> using the specified <see cref="Comparison{T}" />.
+        /// </summary>
+        /// <typeparam name="T">The type of the elements of the span.</typeparam>
+        /// <param name="span">The <see cref="Span{T}"/> to sort.</param>
+        /// <param name="comparison">The <see cref="Comparison{T}"/> to use when comparing elements.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="comparison"/> is null.</exception>
+        public static void Sort<T>(this Span<T> span, Comparison<T> comparison)
+        {
+            if (comparison == null)
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison);
+
+            if (span.Length > 1)
+            {
+                ArraySortHelper<T>.Sort(span, comparison);
+            }
+        }
+
+        /// <summary>
+        /// Sorts a pair of spans (one containing the keys and the other containing the corresponding items)
+        /// based on the keys in the first <see cref="Span{TKey}" /> using the <see cref="IComparable{T}" />
+        /// implementation of each key.
+        /// </summary>
+        /// <typeparam name="TKey">The type of the elements of the key span.</typeparam>
+        /// <typeparam name="TValue">The type of the elements of the items span.</typeparam>
+        /// <param name="keys">The span that contains the keys to sort.</param>
+        /// <param name="items">The span that contains the items that correspond to the keys in <paramref name="keys"/>.</param>
+        /// <exception cref="ArgumentException">
+        /// The length of <paramref name="keys"/> isn't equal to the length of <paramref name="items"/>.
+        /// </exception>
+        /// <exception cref="InvalidOperationException">
+        /// One or more elements in <paramref name="keys"/> do not implement the <see cref="IComparable{T}" /> interface.
+        /// </exception>
+        public static void Sort<TKey, TValue>(this Span<TKey> keys, Span<TValue> items) =>
+            Sort(keys, items, (IComparer<TKey>?)null);
+
+        /// <summary>
+        /// Sorts a pair of spans (one containing the keys and the other containing the corresponding items)
+        /// based on the keys in the first <see cref="Span{TKey}" /> using the specified comparer.
+        /// </summary>
+        /// <typeparam name="TKey">The type of the elements of the key span.</typeparam>
+        /// <typeparam name="TValue">The type of the elements of the items span.</typeparam>
+        /// <typeparam name="TComparer">The type of the comparer to use to compare elements.</typeparam>
+        /// <param name="keys">The span that contains the keys to sort.</param>
+        /// <param name="items">The span that contains the items that correspond to the keys in <paramref name="keys"/>.</param>
+        /// <param name="comparer">
+        /// The <see cref="IComparer{T}"/> implementation to use when comparing elements, or null to
+        /// use the <see cref="IComparable{T}"/> interface implementation of each element.
+        /// </param>
+        /// <exception cref="ArgumentException">
+        /// The length of <paramref name="keys"/> isn't equal to the length of <paramref name="items"/>.
+        /// </exception>
+        /// <exception cref="InvalidOperationException">
+        /// <paramref name="comparer"/> is null, and one or more elements in <paramref name="keys"/> do not
+        /// implement the <see cref="IComparable{T}" /> interface.
+        /// </exception>
+        public static void Sort<TKey, TValue, TComparer>(this Span<TKey> keys, Span<TValue> items, TComparer comparer)
+            where TComparer : IComparer<TKey>?
+        {
+            if (keys.Length != items.Length)
+                ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_SpansMustHaveSameLength);
+
+            if (keys.Length > 1)
+            {
+                ArraySortHelper<TKey, TValue>.Default.Sort(keys, items, comparer); // value-type comparer will be boxed
+            }
+        }
+
+        /// <summary>
+        /// Sorts a pair of spans (one containing the keys and the other containing the corresponding items)
+        /// based on the keys in the first <see cref="Span{TKey}" /> using the specified comparison.
+        /// </summary>
+        /// <typeparam name="TKey">The type of the elements of the key span.</typeparam>
+        /// <typeparam name="TValue">The type of the elements of the items span.</typeparam>
+        /// <param name="keys">The span that contains the keys to sort.</param>
+        /// <param name="items">The span that contains the items that correspond to the keys in <paramref name="keys"/>.</param>
+        /// <param name="comparison">The <see cref="Comparison{T}"/> to use when comparing elements.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="comparison"/> is null.</exception>
+        /// <exception cref="ArgumentException">
+        /// The length of <paramref name="keys"/> isn't equal to the length of <paramref name="items"/>.
+        /// </exception>
+        public static void Sort<TKey, TValue>(this Span<TKey> keys, Span<TValue> items, Comparison<TKey> comparison)
+        {
+            if (comparison == null)
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison);
+            if (keys.Length != items.Length)
+                ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_SpansMustHaveSameLength);
+
+            if (keys.Length > 1)
+            {
+                ArraySortHelper<TKey, TValue>.Default.Sort(keys, items, new ComparisonComparer<TKey>(comparison));
+            }
+        }
     }
 }
index 928d7b6f658176966d9bf5680b5e2519a6f8cf6f..3ec07ebac80d53340d8268a0caf2cbd1903195e8 100644 (file)
@@ -1002,5 +1002,6 @@ namespace System
         NotSupported_FixedSizeCollection,
         Rank_MultiDimNotSupported,
         Arg_TypeNotSupported,
+        Argument_SpansMustHaveSameLength,
     }
 }