3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 /*============================================================
8 ** Class: ArraySortHelper
10 ** <OWNER>Microsoft</OWNER>
13 ** Purpose: class to sort arrays
16 ===========================================================*/
17 namespace System.Collections.Generic
20 using System.Globalization;
21 using System.Runtime.CompilerServices;
22 using System.Diagnostics.Contracts;
23 using System.Runtime.Versioning;
25 #region ArraySortHelper for single arrays
28 [ContractClass(typeof(IArraySortHelperContract<>))]
29 #endif // CONTRACTS_FULL
30 internal interface IArraySortHelper<TKey>
32 void Sort(TKey[] keys, int index, int length, IComparer<TKey> comparer);
33 int BinarySearch(TKey[] keys, int index, int length, TKey value, IComparer<TKey> comparer);
37 [ContractClassFor(typeof(IArraySortHelper<>))]
38 internal abstract class IArraySortHelperContract<TKey> : IArraySortHelper<TKey>
40 void IArraySortHelper<TKey>.Sort(TKey[] keys, int index, int length, IComparer<TKey> comparer)
42 Contract.Requires(keys != null, "Check the arguments in the caller!");
43 Contract.Requires(index >= 0 && index <= keys.Length); // allow 0?
44 Contract.Requires(length >= 0 && index + length <= keys.Length);
47 int IArraySortHelper<TKey>.BinarySearch(TKey[] keys, int index, int length, TKey value, IComparer<TKey> comparer)
49 Contract.Requires(index >= 0 && index <= keys.Length); // allow 0?
50 Contract.Requires(length >= 0 && index + length <= keys.Length);
51 Contract.Ensures((Contract.Result<int>() >= index && Contract.Result<int>() <= index + length) ||
52 (~Contract.Result<int>() >= index && ~Contract.Result<int>() <= index + length), "Binary search returned a bad value");
57 #endif // CONTRACTS_FULL
59 internal static class IntrospectiveSortUtilities
61 // This is the threshold where Introspective sort switches to Insertion sort.
62 // Imperically, 16 seems to speed up most cases without slowing down others, at least for integers.
63 // Large value types may benefit from a smaller number.
64 internal const int IntrosortSizeThreshold = 16;
66 internal const int QuickSortDepthThreshold = 32;
68 internal static int FloorLog2(int n)
79 internal static void ThrowOrIgnoreBadComparer(Object comparer) {
80 // This is hit when an invarant of QuickSort is violated due to a bad IComparer implementation (for
81 // example, imagine an IComparer that returns 0 when items are equal but -1 all other times).
83 // We could have thrown this exception on v4, but due to changes in v4.5 around how we partition arrays
84 // there are different sets of input where we would throw this exception. In order to reduce overall risk from
85 // an app compat persective, we're changing to never throw on v4. Instead, we'll return with a partially
87 if(BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) {
88 throw new ArgumentException(Environment.GetResourceString("Arg_BogusIComparer", comparer));
94 [TypeDependencyAttribute("System.Collections.Generic.GenericArraySortHelper`1")]
95 internal class ArraySortHelper<T>
98 static volatile IArraySortHelper<T> defaultArraySortHelper;
100 public static IArraySortHelper<T> Default
104 IArraySortHelper<T> sorter = defaultArraySortHelper;
106 sorter = CreateArraySortHelper();
112 [System.Security.SecuritySafeCritical] // auto-generated
113 private static IArraySortHelper<T> CreateArraySortHelper()
115 if (typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
117 defaultArraySortHelper = (IArraySortHelper<T>)RuntimeTypeHandle.Allocate(typeof(GenericArraySortHelper<string>).TypeHandle.Instantiate(new Type[] { typeof(T) }));
121 defaultArraySortHelper = new ArraySortHelper<T>();
123 return defaultArraySortHelper;
126 #region IArraySortHelper<T> Members
128 public void Sort(T[] keys, int index, int length, IComparer<T> comparer)
130 Contract.Assert(keys != null, "Check the arguments in the caller!");
131 Contract.Assert( index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
133 // Add a try block here to detect IComparers (or their
134 // underlying IComparables, etc) that are bogus.
137 if (comparer == null)
139 comparer = Comparer<T>.Default;
143 // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
144 // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
145 // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
147 IntrospectiveSort(keys, index, length, comparer);
149 if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
151 IntrospectiveSort(keys, index, length, comparer);
155 DepthLimitedQuickSort(keys, index, length + index - 1, comparer, IntrospectiveSortUtilities.QuickSortDepthThreshold);
159 catch (IndexOutOfRangeException)
161 IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
165 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
169 public int BinarySearch(T[] array, int index, int length, T value, IComparer<T> comparer)
173 if (comparer == null)
175 comparer = Comparer<T>.Default;
178 return InternalBinarySearch(array, index, length, value, comparer);
182 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
188 internal static int InternalBinarySearch(T[] array, int index, int length, T value, IComparer<T> comparer)
190 Contract.Requires(array != null, "Check the arguments in the caller!");
191 Contract.Requires(index >= 0 && length >= 0 && (array.Length - index >= length), "Check the arguments in the caller!");
194 int hi = index + length - 1;
197 int i = lo + ((hi - lo) >> 1);
198 int order = comparer.Compare(array[i], value);
200 if (order == 0) return i;
214 private static void SwapIfGreater(T[] keys, IComparer<T> comparer, int a, int b)
218 if (comparer.Compare(keys[a], keys[b]) > 0)
227 private static void Swap(T[] a, int i, int j)
237 internal static void DepthLimitedQuickSort(T[] keys, int left, int right, IComparer<T> comparer, int depthLimit)
243 Heapsort(keys, left, right, comparer);
250 // pre-sort the low, middle (pivot), and high values in place.
251 // this improves performance in the face of already sorted data, or
252 // data that is made up of multiple sorted runs appended together.
253 int middle = i + ((j - i) >> 1);
254 SwapIfGreater(keys, comparer, i, middle); // swap the low with the mid point
255 SwapIfGreater(keys, comparer, i, j); // swap the low with the high
256 SwapIfGreater(keys, comparer, middle, j); // swap the middle with the high
261 while (comparer.Compare(keys[i], x) < 0) i++;
262 while (comparer.Compare(x, keys[j]) < 0) j--;
263 Contract.Assert(i >= left && j <= right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?");
275 // The next iteration of the while loop is to "recursively" sort the larger half of the array and the
276 // following calls recrusively sort the smaller half. So we subtrack one from depthLimit here so
277 // both sorts see the new value.
280 if (j - left <= right - i)
282 if (left < j) DepthLimitedQuickSort(keys, left, j, comparer, depthLimit);
287 if (i < right) DepthLimitedQuickSort(keys, i, right, comparer, depthLimit);
290 } while (left < right);
293 internal static void IntrospectiveSort(T[] keys, int left, int length, IComparer<T> comparer)
295 Contract.Requires(keys != null);
296 Contract.Requires(comparer != null);
297 Contract.Requires(left >= 0);
298 Contract.Requires(length >= 0);
299 Contract.Requires(length <= keys.Length);
300 Contract.Requires(length + left <= keys.Length);
305 IntroSort(keys, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length), comparer);
308 private static void IntroSort(T[] keys, int lo, int hi, int depthLimit, IComparer<T> comparer)
310 Contract.Requires(keys != null);
311 Contract.Requires(comparer != null);
312 Contract.Requires(lo >= 0);
313 Contract.Requires(hi < keys.Length);
317 int partitionSize = hi - lo + 1;
318 if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
320 if (partitionSize == 1)
324 if (partitionSize == 2)
326 SwapIfGreater(keys, comparer, lo, hi);
329 if (partitionSize == 3)
331 SwapIfGreater(keys, comparer, lo, hi-1);
332 SwapIfGreater(keys, comparer, lo, hi);
333 SwapIfGreater(keys, comparer, hi-1, hi);
337 InsertionSort(keys, lo, hi, comparer);
343 Heapsort(keys, lo, hi, comparer);
348 int p = PickPivotAndPartition(keys, lo, hi, comparer);
349 // Note we've already partitioned around the pivot and do not have to move the pivot again.
350 IntroSort(keys, p + 1, hi, depthLimit, comparer);
355 private static int PickPivotAndPartition(T[] keys, int lo, int hi, IComparer<T> comparer)
357 Contract.Requires(keys != null);
358 Contract.Requires(comparer != null);
359 Contract.Requires(lo >= 0);
360 Contract.Requires(hi > lo);
361 Contract.Requires(hi < keys.Length);
362 Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
364 // Compute median-of-three. But also partition them, since we've done the comparison.
365 int middle = lo + ((hi - lo) / 2);
367 // Sort lo, mid and hi appropriately, then pick mid as the pivot.
368 SwapIfGreater(keys, comparer, lo, middle); // swap the low with the mid point
369 SwapIfGreater(keys, comparer, lo, hi); // swap the low with the high
370 SwapIfGreater(keys, comparer, middle, hi); // swap the middle with the high
372 T pivot = keys[middle];
373 Swap(keys, middle, hi - 1);
374 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.
378 while (comparer.Compare(keys[++left], pivot) < 0) ;
379 while (comparer.Compare(pivot, keys[--right]) < 0) ;
384 Swap(keys, left, right);
387 // Put pivot in the right location.
388 Swap(keys, left, (hi - 1));
392 private static void Heapsort(T[] keys, int lo, int hi, IComparer<T> comparer)
394 Contract.Requires(keys != null);
395 Contract.Requires(comparer != null);
396 Contract.Requires(lo >= 0);
397 Contract.Requires(hi > lo);
398 Contract.Requires(hi < keys.Length);
401 for (int i = n / 2; i >= 1; i = i - 1)
403 DownHeap(keys, i, n, lo, comparer);
405 for (int i = n; i > 1; i = i - 1)
407 Swap(keys, lo, lo + i - 1);
408 DownHeap(keys, 1, i - 1, lo, comparer);
412 private static void DownHeap(T[] keys, int i, int n, int lo, IComparer<T> comparer)
414 Contract.Requires(keys != null);
415 Contract.Requires(comparer != null);
416 Contract.Requires(lo >= 0);
417 Contract.Requires(lo < keys.Length);
419 T d = keys[lo + i - 1];
424 if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
428 if (!(comparer.Compare(d, keys[lo + child - 1]) < 0))
430 keys[lo + i - 1] = keys[lo + child - 1];
433 keys[lo + i - 1] = d;
436 private static void InsertionSort(T[] keys, int lo, int hi, IComparer<T> comparer)
438 Contract.Requires(keys != null);
439 Contract.Requires(lo >= 0);
440 Contract.Requires(hi >= lo);
441 Contract.Requires(hi <= keys.Length);
445 for (i = lo; i < hi; i++)
449 while (j >= lo && comparer.Compare(t, keys[j]) < 0)
451 keys[j + 1] = keys[j];
460 internal class GenericArraySortHelper<T>
461 : IArraySortHelper<T>
462 where T : IComparable<T>
464 // Do not add a constructor to this class because ArraySortHelper<T>.CreateSortHelper will not execute it
466 #region IArraySortHelper<T> Members
468 public void Sort(T[] keys, int index, int length, IComparer<T> comparer)
470 Contract.Assert(keys != null, "Check the arguments in the caller!");
471 Contract.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
475 #if FEATURE_LEGACYNETCF
476 // Pre-Apollo Windows Phone call the overload that sorts the keys, not values this achieves the same result
477 if (comparer == null && CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)
478 comparer = Comparer<T>.Default;
480 if (comparer == null || (comparer == Comparer<T>.Default && !CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)) {
482 if (comparer == null || comparer == Comparer<T>.Default) {
486 // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
487 // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
488 // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
490 IntrospectiveSort(keys, index, length);
492 // call the faster version of our sort algorithm if the user doesn't provide a comparer
493 if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
495 IntrospectiveSort(keys, index, length);
499 DepthLimitedQuickSort(keys, index, length + index - 1, IntrospectiveSortUtilities.QuickSortDepthThreshold);
506 // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
507 // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
508 // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
510 ArraySortHelper<T>.IntrospectiveSort(keys, index, length, comparer);
512 if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
514 ArraySortHelper<T>.IntrospectiveSort(keys, index, length, comparer);
518 ArraySortHelper<T>.DepthLimitedQuickSort(keys, index, length + index - 1, comparer, IntrospectiveSortUtilities.QuickSortDepthThreshold);
523 catch (IndexOutOfRangeException)
525 IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
529 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
533 public int BinarySearch(T[] array, int index, int length, T value, IComparer<T> comparer)
535 Contract.Assert(array != null, "Check the arguments in the caller!");
536 Contract.Assert(index >= 0 && length >= 0 && (array.Length - index >= length), "Check the arguments in the caller!");
540 if (comparer == null || comparer == Comparer<T>.Default)
542 return BinarySearch(array, index, length, value);
546 return ArraySortHelper<T>.InternalBinarySearch(array, index, length, value, comparer);
551 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
557 // This function is called when the user doesn't specify any comparer.
558 // Since T is constrained here, we can call IComparable<T>.CompareTo here.
559 // We can avoid boxing for value type and casting for reference types.
560 private static int BinarySearch(T[] array, int index, int length, T value)
563 int hi = index + length - 1;
566 int i = lo + ((hi - lo) >> 1);
568 if (array[i] == null)
570 order = (value == null) ? 0 : -1;
574 order = array[i].CompareTo(value);
595 private static void SwapIfGreaterWithItems(T[] keys, int a, int b)
597 Contract.Requires(keys != null);
598 Contract.Requires(0 <= a && a < keys.Length);
599 Contract.Requires(0 <= b && b < keys.Length);
603 if (keys[a] != null && keys[a].CompareTo(keys[b]) > 0)
612 private static void Swap(T[] a, int i, int j)
622 private static void DepthLimitedQuickSort(T[] keys, int left, int right, int depthLimit)
624 Contract.Requires(keys != null);
625 Contract.Requires(0 <= left && left < keys.Length);
626 Contract.Requires(0 <= right && right < keys.Length);
628 // The code in this function looks very similar to QuickSort in ArraySortHelper<T> class.
629 // The difference is that T is constrainted to IComparable<T> here.
630 // So the IL code will be different. This function is faster than the one in ArraySortHelper<T>.
636 Heapsort(keys, left, right);
643 // pre-sort the low, middle (pivot), and high values in place.
644 // this improves performance in the face of already sorted data, or
645 // data that is made up of multiple sorted runs appended together.
646 int middle = i + ((j - i) >> 1);
647 SwapIfGreaterWithItems(keys, i, middle); // swap the low with the mid point
648 SwapIfGreaterWithItems(keys, i, j); // swap the low with the high
649 SwapIfGreaterWithItems(keys, middle, j); // swap the middle with the high
656 // if x null, the loop to find two elements to be switched can be reduced.
657 while (keys[j] != null) j--;
661 while (x.CompareTo(keys[i]) > 0) i++;
662 while (x.CompareTo(keys[j]) < 0) j--;
664 Contract.Assert(i >= left && j <= right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?");
676 // The next iteration of the while loop is to "recursively" sort the larger half of the array and the
677 // following calls recrusively sort the smaller half. So we subtrack one from depthLimit here so
678 // both sorts see the new value.
681 if (j - left <= right - i)
683 if (left < j) DepthLimitedQuickSort(keys, left, j, depthLimit);
688 if (i < right) DepthLimitedQuickSort(keys, i, right, depthLimit);
691 } while (left < right);
694 internal static void IntrospectiveSort(T[] keys, int left, int length)
696 Contract.Requires(keys != null);
697 Contract.Requires(left >= 0);
698 Contract.Requires(length >= 0);
699 Contract.Requires(length <= keys.Length);
700 Contract.Requires(length + left <= keys.Length);
705 IntroSort(keys, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length));
708 private static void IntroSort(T[] keys, int lo, int hi, int depthLimit)
710 Contract.Requires(keys != null);
711 Contract.Requires(lo >= 0);
712 Contract.Requires(hi < keys.Length);
716 int partitionSize = hi - lo + 1;
717 if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
719 if (partitionSize == 1)
723 if (partitionSize == 2)
725 SwapIfGreaterWithItems(keys, lo, hi);
728 if (partitionSize == 3)
730 SwapIfGreaterWithItems(keys, lo, hi-1);
731 SwapIfGreaterWithItems(keys, lo, hi);
732 SwapIfGreaterWithItems(keys, hi-1, hi);
736 InsertionSort(keys, lo, hi);
742 Heapsort(keys, lo, hi);
747 int p = PickPivotAndPartition(keys, lo, hi);
748 // Note we've already partitioned around the pivot and do not have to move the pivot again.
749 IntroSort(keys, p + 1, hi, depthLimit);
754 private static int PickPivotAndPartition(T[] keys, int lo, int hi)
756 Contract.Requires(keys != null);
757 Contract.Requires(lo >= 0);
758 Contract.Requires(hi > lo);
759 Contract.Requires(hi < keys.Length);
760 Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
762 // Compute median-of-three. But also partition them, since we've done the comparison.
763 int middle = lo + ((hi - lo) / 2);
765 // Sort lo, mid and hi appropriately, then pick mid as the pivot.
766 SwapIfGreaterWithItems(keys, lo, middle); // swap the low with the mid point
767 SwapIfGreaterWithItems(keys, lo, hi); // swap the low with the high
768 SwapIfGreaterWithItems(keys, middle, hi); // swap the middle with the high
770 T pivot = keys[middle];
771 Swap(keys, middle, hi - 1);
772 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.
778 while (left < (hi - 1) && keys[++left] == null) ;
779 while (right > lo && keys[--right] != null) ;
783 while (pivot.CompareTo(keys[++left]) > 0) ;
784 while (pivot.CompareTo(keys[--right]) < 0) ;
790 Swap(keys, left, right);
793 // Put pivot in the right location.
794 Swap(keys, left, (hi - 1));
798 private static void Heapsort(T[] keys, int lo, int hi)
800 Contract.Requires(keys != null);
801 Contract.Requires(lo >= 0);
802 Contract.Requires(hi > lo);
803 Contract.Requires(hi < keys.Length);
806 for (int i = n / 2; i >= 1; i = i - 1)
808 DownHeap(keys, i, n, lo);
810 for (int i = n; i > 1; i = i - 1)
812 Swap(keys, lo, lo + i - 1);
813 DownHeap(keys, 1, i - 1, lo);
817 private static void DownHeap(T[] keys, int i, int n, int lo)
819 Contract.Requires(keys != null);
820 Contract.Requires(lo >= 0);
821 Contract.Requires(lo < keys.Length);
823 T d = keys[lo + i - 1];
828 if (child < n && (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
832 if (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(d) < 0)
834 keys[lo + i - 1] = keys[lo + child - 1];
837 keys[lo + i - 1] = d;
840 private static void InsertionSort(T[] keys, int lo, int hi)
842 Contract.Requires(keys != null);
843 Contract.Requires(lo >= 0);
844 Contract.Requires(hi >= lo);
845 Contract.Requires(hi <= keys.Length);
849 for (i = lo; i < hi; i++)
853 while (j >= lo && (t == null || t.CompareTo(keys[j]) < 0))
855 keys[j + 1] = keys[j];
865 #region ArraySortHelper for paired key and value arrays
867 internal interface IArraySortHelper<TKey, TValue>
869 void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey> comparer);
872 [TypeDependencyAttribute("System.Collections.Generic.GenericArraySortHelper`2")]
873 internal class ArraySortHelper<TKey, TValue>
874 : IArraySortHelper<TKey, TValue>
876 static volatile IArraySortHelper<TKey, TValue> defaultArraySortHelper;
878 public static IArraySortHelper<TKey, TValue> Default
882 IArraySortHelper<TKey, TValue> sorter = defaultArraySortHelper;
884 sorter = CreateArraySortHelper();
890 [System.Security.SecuritySafeCritical] // auto-generated
891 private static IArraySortHelper<TKey, TValue> CreateArraySortHelper()
893 if (typeof(IComparable<TKey>).IsAssignableFrom(typeof(TKey)))
895 defaultArraySortHelper = (IArraySortHelper<TKey, TValue>)RuntimeTypeHandle.Allocate(typeof(GenericArraySortHelper<string, string>).TypeHandle.Instantiate(new Type[] { typeof(TKey), typeof(TValue) }));
899 defaultArraySortHelper = new ArraySortHelper<TKey, TValue>();
901 return defaultArraySortHelper;
904 public void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey> comparer)
906 Contract.Assert(keys != null, "Check the arguments in the caller!"); // Precondition on interface method
907 Contract.Assert(index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
909 // Add a try block here to detect IComparers (or their
910 // underlying IComparables, etc) that are bogus.
913 if (comparer == null || comparer == Comparer<TKey>.Default)
915 comparer = Comparer<TKey>.Default;
919 // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
920 // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
921 // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
923 IntrospectiveSort(keys, values, index, length, comparer);
925 if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
927 IntrospectiveSort(keys, values, index, length, comparer);
931 DepthLimitedQuickSort(keys, values, index, length + index - 1, comparer, IntrospectiveSortUtilities.QuickSortDepthThreshold);
935 catch (IndexOutOfRangeException)
937 IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
941 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
945 private static void SwapIfGreaterWithItems(TKey[] keys, TValue[] values, IComparer<TKey> comparer, int a, int b)
947 Contract.Requires(keys != null);
948 Contract.Requires(values == null || values.Length >= keys.Length);
949 Contract.Requires(comparer != null);
950 Contract.Requires(0 <= a && a < keys.Length);
951 Contract.Requires(0 <= b && b < keys.Length);
955 if (comparer.Compare(keys[a], keys[b]) > 0)
962 TValue value = values[a];
963 values[a] = values[b];
970 private static void Swap(TKey[] keys, TValue[] values, int i, int j)
979 TValue v = values[i];
980 values[i] = values[j];
986 internal static void DepthLimitedQuickSort(TKey[] keys, TValue[] values, int left, int right, IComparer<TKey> comparer, int depthLimit)
992 Heapsort(keys, values, left, right, comparer);
999 // pre-sort the low, middle (pivot), and high values in place.
1000 // this improves performance in the face of already sorted data, or
1001 // data that is made up of multiple sorted runs appended together.
1002 int middle = i + ((j - i) >> 1);
1003 SwapIfGreaterWithItems(keys, values, comparer, i, middle); // swap the low with the mid point
1004 SwapIfGreaterWithItems(keys, values, comparer, i, j); // swap the low with the high
1005 SwapIfGreaterWithItems(keys, values, comparer, middle, j); // swap the middle with the high
1007 TKey x = keys[middle];
1010 while (comparer.Compare(keys[i], x) < 0) i++;
1011 while (comparer.Compare(x, keys[j]) < 0) j--;
1012 Contract.Assert(i >= left && j <= right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?");
1021 TValue value = values[i];
1022 values[i] = values[j];
1030 // The next iteration of the while loop is to "recursively" sort the larger half of the array and the
1031 // following calls recrusively sort the smaller half. So we subtrack one from depthLimit here so
1032 // both sorts see the new value.
1035 if (j - left <= right - i)
1037 if (left < j) DepthLimitedQuickSort(keys, values, left, j, comparer, depthLimit);
1042 if (i < right) DepthLimitedQuickSort(keys, values, i, right, comparer, depthLimit);
1045 } while (left < right);
1048 internal static void IntrospectiveSort(TKey[] keys, TValue[] values, int left, int length, IComparer<TKey> comparer)
1050 Contract.Requires(keys != null);
1051 Contract.Requires(values != null);
1052 Contract.Requires(comparer != null);
1053 Contract.Requires(left >= 0);
1054 Contract.Requires(length >= 0);
1055 Contract.Requires(length <= keys.Length);
1056 Contract.Requires(length + left <= keys.Length);
1057 Contract.Requires(length + left <= values.Length);
1062 IntroSort(keys, values, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length), comparer);
1065 private static void IntroSort(TKey[] keys, TValue[] values, int lo, int hi, int depthLimit, IComparer<TKey> comparer)
1067 Contract.Requires(keys != null);
1068 Contract.Requires(values != null);
1069 Contract.Requires(comparer != null);
1070 Contract.Requires(lo >= 0);
1071 Contract.Requires(hi < keys.Length);
1075 int partitionSize = hi - lo + 1;
1076 if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
1078 if (partitionSize == 1)
1082 if (partitionSize == 2)
1084 SwapIfGreaterWithItems(keys, values, comparer, lo, hi);
1087 if (partitionSize == 3)
1089 SwapIfGreaterWithItems(keys, values, comparer, lo, hi-1);
1090 SwapIfGreaterWithItems(keys, values, comparer, lo, hi);
1091 SwapIfGreaterWithItems(keys, values, comparer, hi-1, hi);
1095 InsertionSort(keys, values, lo, hi, comparer);
1099 if (depthLimit == 0)
1101 Heapsort(keys, values, lo, hi, comparer);
1106 int p = PickPivotAndPartition(keys, values, lo, hi, comparer);
1107 // Note we've already partitioned around the pivot and do not have to move the pivot again.
1108 IntroSort(keys, values, p + 1, hi, depthLimit, comparer);
1113 private static int PickPivotAndPartition(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
1115 Contract.Requires(keys != null);
1116 Contract.Requires(values != null);
1117 Contract.Requires(comparer != null);
1118 Contract.Requires(lo >= 0);
1119 Contract.Requires(hi > lo);
1120 Contract.Requires(hi < keys.Length);
1121 Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
1123 // Compute median-of-three. But also partition them, since we've done the comparison.
1124 int middle = lo + ((hi - lo) / 2);
1126 // Sort lo, mid and hi appropriately, then pick mid as the pivot.
1127 SwapIfGreaterWithItems(keys, values, comparer, lo, middle); // swap the low with the mid point
1128 SwapIfGreaterWithItems(keys, values, comparer, lo, hi); // swap the low with the high
1129 SwapIfGreaterWithItems(keys, values, comparer, middle, hi); // swap the middle with the high
1131 TKey pivot = keys[middle];
1132 Swap(keys, values, middle, hi - 1);
1133 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.
1135 while (left < right)
1137 while (comparer.Compare(keys[++left], pivot) < 0) ;
1138 while (comparer.Compare(pivot, keys[--right]) < 0) ;
1143 Swap(keys, values, left, right);
1146 // Put pivot in the right location.
1147 Swap(keys, values, left, (hi - 1));
1151 private static void Heapsort(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
1153 Contract.Requires(keys != null);
1154 Contract.Requires(values != null);
1155 Contract.Requires(comparer != null);
1156 Contract.Requires(lo >= 0);
1157 Contract.Requires(hi > lo);
1158 Contract.Requires(hi < keys.Length);
1160 int n = hi - lo + 1;
1161 for (int i = n / 2; i >= 1; i = i - 1)
1163 DownHeap(keys, values, i, n, lo, comparer);
1165 for (int i = n; i > 1; i = i - 1)
1167 Swap(keys, values, lo, lo + i - 1);
1168 DownHeap(keys, values, 1, i - 1, lo, comparer);
1172 private static void DownHeap(TKey[] keys, TValue[] values, int i, int n, int lo, IComparer<TKey> comparer)
1174 Contract.Requires(keys != null);
1175 Contract.Requires(comparer != null);
1176 Contract.Requires(lo >= 0);
1177 Contract.Requires(lo < keys.Length);
1179 TKey d = keys[lo + i - 1];
1180 TValue dValue = (values != null) ? values[lo + i - 1] : default(TValue);
1185 if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
1189 if (!(comparer.Compare(d, keys[lo + child - 1]) < 0))
1191 keys[lo + i - 1] = keys[lo + child - 1];
1193 values[lo + i - 1] = values[lo + child - 1];
1196 keys[lo + i - 1] = d;
1198 values[lo + i - 1] = dValue;
1201 private static void InsertionSort(TKey[] keys, TValue[] values, int lo, int hi, IComparer<TKey> comparer)
1203 Contract.Requires(keys != null);
1204 Contract.Requires(values != null);
1205 Contract.Requires(comparer != null);
1206 Contract.Requires(lo >= 0);
1207 Contract.Requires(hi >= lo);
1208 Contract.Requires(hi <= keys.Length);
1213 for (i = lo; i < hi; i++)
1217 tValue = (values != null) ? values[i + 1] : default(TValue);
1218 while (j >= lo && comparer.Compare(t, keys[j]) < 0)
1220 keys[j + 1] = keys[j];
1222 values[j + 1] = values[j];
1227 values[j + 1] = tValue;
1232 internal class GenericArraySortHelper<TKey, TValue>
1233 : IArraySortHelper<TKey, TValue>
1234 where TKey : IComparable<TKey>
1236 public void Sort(TKey[] keys, TValue[] values, int index, int length, IComparer<TKey> comparer)
1238 Contract.Assert(keys != null, "Check the arguments in the caller!");
1239 Contract.Assert( index >= 0 && length >= 0 && (keys.Length - index >= length), "Check the arguments in the caller!");
1241 // Add a try block here to detect IComparers (or their
1242 // underlying IComparables, etc) that are bogus.
1245 if (comparer == null || comparer == Comparer<TKey>.Default)
1248 // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
1249 // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
1250 // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
1252 IntrospectiveSort(keys, values, index, length);
1254 // call the faster version of our sort algorithm if the user doesn't provide a comparer
1255 if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
1257 IntrospectiveSort(keys, values, index, length);
1261 DepthLimitedQuickSort(keys, values, index, length + index - 1, IntrospectiveSortUtilities.QuickSortDepthThreshold);
1268 // Since QuickSort and IntrospectiveSort produce different sorting sequence for equal keys the upgrade
1269 // to IntrospectiveSort was quirked. However since the phone builds always shipped with the new sort aka
1270 // IntrospectiveSort and we would want to continue using this sort moving forward CoreCLR always uses the new sort.
1272 ArraySortHelper<TKey, TValue>.IntrospectiveSort(keys, values, index, length, comparer);
1274 if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
1276 ArraySortHelper<TKey, TValue>.IntrospectiveSort(keys, values, index, length, comparer);
1280 ArraySortHelper<TKey, TValue>.DepthLimitedQuickSort(keys, values, index, length + index - 1, comparer, IntrospectiveSortUtilities.QuickSortDepthThreshold);
1286 catch (IndexOutOfRangeException)
1288 IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
1292 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
1296 private static void SwapIfGreaterWithItems(TKey[] keys, TValue[] values, int a, int b)
1300 if (keys[a] != null && keys[a].CompareTo(keys[b]) > 0)
1307 TValue value = values[a];
1308 values[a] = values[b];
1315 private static void Swap(TKey[] keys, TValue[] values, int i, int j)
1324 TValue v = values[i];
1325 values[i] = values[j];
1331 private static void DepthLimitedQuickSort(TKey[] keys, TValue[] values, int left, int right, int depthLimit)
1333 // The code in this function looks very similar to QuickSort in ArraySortHelper<T> class.
1334 // The difference is that T is constrainted to IComparable<T> here.
1335 // So the IL code will be different. This function is faster than the one in ArraySortHelper<T>.
1339 if (depthLimit == 0)
1341 Heapsort(keys, values, left, right);
1348 // pre-sort the low, middle (pivot), and high values in place.
1349 // this improves performance in the face of already sorted data, or
1350 // data that is made up of multiple sorted runs appended together.
1351 int middle = i + ((j - i) >> 1);
1352 SwapIfGreaterWithItems(keys, values, i, middle); // swap the low with the mid point
1353 SwapIfGreaterWithItems(keys, values, i, j); // swap the low with the high
1354 SwapIfGreaterWithItems(keys, values, middle, j); // swap the middle with the high
1356 TKey x = keys[middle];
1361 // if x null, the loop to find two elements to be switched can be reduced.
1362 while (keys[j] != null) j--;
1366 while (x.CompareTo(keys[i]) > 0) i++;
1367 while (x.CompareTo(keys[j]) < 0) j--;
1369 Contract.Assert(i >= left && j <= right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?");
1378 TValue value = values[i];
1379 values[i] = values[j];
1387 // The next iteration of the while loop is to "recursively" sort the larger half of the array and the
1388 // following calls recrusively sort the smaller half. So we subtrack one from depthLimit here so
1389 // both sorts see the new value.
1392 if (j - left <= right - i)
1394 if (left < j) DepthLimitedQuickSort(keys, values, left, j, depthLimit);
1399 if (i < right) DepthLimitedQuickSort(keys, values, i, right, depthLimit);
1402 } while (left < right);
1405 internal static void IntrospectiveSort(TKey[] keys, TValue[] values, int left, int length)
1407 Contract.Requires(keys != null);
1408 Contract.Requires(values != null);
1409 Contract.Requires(left >= 0);
1410 Contract.Requires(length >= 0);
1411 Contract.Requires(length <= keys.Length);
1412 Contract.Requires(length + left <= keys.Length);
1413 Contract.Requires(length + left <= values.Length);
1418 IntroSort(keys, values, left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(keys.Length));
1421 private static void IntroSort(TKey[] keys, TValue[] values, int lo, int hi, int depthLimit)
1423 Contract.Requires(keys != null);
1424 Contract.Requires(values != null);
1425 Contract.Requires(lo >= 0);
1426 Contract.Requires(hi < keys.Length);
1430 int partitionSize = hi - lo + 1;
1431 if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
1433 if (partitionSize == 1)
1437 if (partitionSize == 2)
1439 SwapIfGreaterWithItems(keys, values, lo, hi);
1442 if (partitionSize == 3)
1444 SwapIfGreaterWithItems(keys, values, lo, hi-1);
1445 SwapIfGreaterWithItems(keys, values, lo, hi);
1446 SwapIfGreaterWithItems(keys, values, hi-1, hi);
1450 InsertionSort(keys, values, lo, hi);
1454 if (depthLimit == 0)
1456 Heapsort(keys, values, lo, hi);
1461 int p = PickPivotAndPartition(keys, values, lo, hi);
1462 // Note we've already partitioned around the pivot and do not have to move the pivot again.
1463 IntroSort(keys, values, p + 1, hi, depthLimit);
1468 private static int PickPivotAndPartition(TKey[] keys, TValue[] values, int lo, int hi)
1470 Contract.Requires(keys != null);
1471 Contract.Requires(values != null);
1472 Contract.Requires(lo >= 0);
1473 Contract.Requires(hi > lo);
1474 Contract.Requires(hi < keys.Length);
1475 Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
1477 // Compute median-of-three. But also partition them, since we've done the comparison.
1478 int middle = lo + ((hi - lo) / 2);
1480 // Sort lo, mid and hi appropriately, then pick mid as the pivot.
1481 SwapIfGreaterWithItems(keys, values, lo, middle); // swap the low with the mid point
1482 SwapIfGreaterWithItems(keys, values, lo, hi); // swap the low with the high
1483 SwapIfGreaterWithItems(keys, values, middle, hi); // swap the middle with the high
1485 TKey pivot = keys[middle];
1486 Swap(keys, values, middle, hi - 1);
1487 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.
1489 while (left < right)
1493 while (left < (hi - 1) && keys[++left] == null) ;
1494 while (right > lo && keys[--right] != null);
1498 while (pivot.CompareTo(keys[++left]) > 0) ;
1499 while (pivot.CompareTo(keys[--right]) < 0) ;
1505 Swap(keys, values, left, right);
1508 // Put pivot in the right location.
1509 Swap(keys, values, left, (hi - 1));
1513 private static void Heapsort(TKey[] keys, TValue[] values, int lo, int hi)
1515 Contract.Requires(keys != null);
1516 Contract.Requires(values != null);
1517 Contract.Requires(lo >= 0);
1518 Contract.Requires(hi > lo);
1519 Contract.Requires(hi < keys.Length);
1521 int n = hi - lo + 1;
1522 for (int i = n / 2; i >= 1; i = i - 1)
1524 DownHeap(keys, values, i, n, lo);
1526 for (int i = n; i > 1; i = i - 1)
1528 Swap(keys, values, lo, lo + i - 1);
1529 DownHeap(keys, values, 1, i - 1, lo);
1533 private static void DownHeap(TKey[] keys, TValue[] values, int i, int n, int lo)
1535 Contract.Requires(keys != null);
1536 Contract.Requires(lo >= 0);
1537 Contract.Requires(lo < keys.Length);
1539 TKey d = keys[lo + i - 1];
1540 TValue dValue = (values != null) ? values[lo + i - 1] : default(TValue);
1545 if (child < n && (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
1549 if (keys[lo + child - 1] == null || keys[lo + child - 1].CompareTo(d) < 0)
1551 keys[lo + i - 1] = keys[lo + child - 1];
1553 values[lo + i - 1] = values[lo + child - 1];
1556 keys[lo + i - 1] = d;
1558 values[lo + i - 1] = dValue;
1561 private static void InsertionSort(TKey[] keys, TValue[] values, int lo, int hi)
1563 Contract.Requires(keys != null);
1564 Contract.Requires(values != null);
1565 Contract.Requires(lo >= 0);
1566 Contract.Requires(hi >= lo);
1567 Contract.Requires(hi <= keys.Length);
1572 for (i = lo; i < hi; i++)
1576 tValue = (values != null)? values[i + 1] : default(TValue);
1577 while (j >= lo && (t == null || t.CompareTo(keys[j]) < 0))
1579 keys[j + 1] = keys[j];
1581 values[j + 1] = values[j];
1586 values[j + 1] = tValue;