@@ -70,6 +70,26 @@ internal static void Sort(Span<T> keys, Comparison<T> comparer)
70
70
}
71
71
}
72
72
73
+ internal static void Sort < TComparer > ( Span < T > keys , TComparer comparer )
74
+ where TComparer : IComparer < T >
75
+ {
76
+ Debug . Assert ( comparer != null , "Check the arguments in the caller!" ) ;
77
+
78
+ // Add a try block here to detect bogus comparisons
79
+ try
80
+ {
81
+ IntrospectiveSort ( keys , comparer ) ;
82
+ }
83
+ catch ( IndexOutOfRangeException )
84
+ {
85
+ ThrowHelper . ThrowArgumentException_BadComparer ( comparer ) ;
86
+ }
87
+ catch ( Exception e )
88
+ {
89
+ ThrowHelper . ThrowInvalidOperationException ( ExceptionResource . InvalidOperation_IComparerFailed , e ) ;
90
+ }
91
+ }
92
+
73
93
internal static int InternalBinarySearch ( T [ ] array , int index , int length , T value , IComparer < T > comparer )
74
94
{
75
95
Debug . Assert ( array != null , "Check the arguments in the caller!" ) ;
@@ -108,6 +128,19 @@ private static void SwapIfGreater(Span<T> keys, Comparison<T> comparer, int i, i
108
128
}
109
129
}
110
130
131
+ private static void SwapIfGreater < TComparer > ( Span < T > keys , TComparer comparer , int i , int j )
132
+ where TComparer : IComparer < T > , allows ref struct
133
+ {
134
+ Debug . Assert ( i != j ) ;
135
+
136
+ if ( comparer . Compare ( keys [ i ] , keys [ j ] ) > 0 )
137
+ {
138
+ T key = keys [ i ] ;
139
+ keys [ i ] = keys [ j ] ;
140
+ keys [ j ] = key ;
141
+ }
142
+ }
143
+
111
144
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
112
145
private static void Swap ( Span < T > a , int i , int j )
113
146
{
@@ -128,6 +161,17 @@ internal static void IntrospectiveSort(Span<T> keys, Comparison<T> comparer)
128
161
}
129
162
}
130
163
164
+ internal static void IntrospectiveSort < TComparer > ( Span < T > keys , TComparer comparer )
165
+ where TComparer : IComparer < T > , allows ref struct
166
+ {
167
+ Debug . Assert ( comparer != null ) ;
168
+
169
+ if ( keys . Length > 1 )
170
+ {
171
+ IntroSort ( keys , 2 * ( BitOperations . Log2 ( ( uint ) keys . Length ) + 1 ) , comparer ) ;
172
+ }
173
+ }
174
+
131
175
// IntroSort is recursive; block it from being inlined into itself as
132
176
// this is currenly not profitable.
133
177
[ MethodImpl ( MethodImplOptions . NoInlining ) ]
@@ -176,6 +220,53 @@ private static void IntroSort(Span<T> keys, int depthLimit, Comparison<T> compar
176
220
}
177
221
}
178
222
223
+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
224
+ private static void IntroSort < TComparer > ( Span < T > keys , int depthLimit , TComparer comparer )
225
+ where TComparer : IComparer < T > , allows ref struct
226
+ {
227
+ Debug . Assert ( ! keys . IsEmpty ) ;
228
+ Debug . Assert ( depthLimit >= 0 ) ;
229
+ Debug . Assert ( comparer != null ) ;
230
+
231
+ int partitionSize = keys . Length ;
232
+ while ( partitionSize > 1 )
233
+ {
234
+ if ( partitionSize <= Array . IntrosortSizeThreshold )
235
+ {
236
+
237
+ if ( partitionSize == 2 )
238
+ {
239
+ SwapIfGreater ( keys , comparer , 0 , 1 ) ;
240
+ return ;
241
+ }
242
+
243
+ if ( partitionSize == 3 )
244
+ {
245
+ SwapIfGreater ( keys , comparer , 0 , 1 ) ;
246
+ SwapIfGreater ( keys , comparer , 0 , 2 ) ;
247
+ SwapIfGreater ( keys , comparer , 1 , 2 ) ;
248
+ return ;
249
+ }
250
+
251
+ InsertionSort ( keys . Slice ( 0 , partitionSize ) , comparer ) ;
252
+ return ;
253
+ }
254
+
255
+ if ( depthLimit == 0 )
256
+ {
257
+ HeapSort ( keys . Slice ( 0 , partitionSize ) , comparer ) ;
258
+ return ;
259
+ }
260
+ depthLimit -- ;
261
+
262
+ int p = PickPivotAndPartition ( keys . Slice ( 0 , partitionSize ) , comparer ) ;
263
+
264
+ // Note we've already partitioned around the pivot and do not have to move the pivot again.
265
+ IntroSort ( keys [ ( p + 1 ) ..partitionSize ] , depthLimit , comparer ) ;
266
+ partitionSize = p ;
267
+ }
268
+ }
269
+
179
270
private static int PickPivotAndPartition ( Span < T > keys , Comparison < T > comparer )
180
271
{
181
272
Debug . Assert ( keys . Length >= Array . IntrosortSizeThreshold ) ;
@@ -214,6 +305,45 @@ private static int PickPivotAndPartition(Span<T> keys, Comparison<T> comparer)
214
305
return left ;
215
306
}
216
307
308
+ private static int PickPivotAndPartition < TComparer > ( Span < T > keys , TComparer comparer )
309
+ where TComparer : IComparer < T > , allows ref struct
310
+ {
311
+ Debug . Assert ( keys . Length >= Array . IntrosortSizeThreshold ) ;
312
+ Debug . Assert ( comparer != null ) ;
313
+
314
+ int hi = keys . Length - 1 ;
315
+
316
+ // Compute median-of-three. But also partition them, since we've done the comparison.
317
+ int middle = hi >> 1 ;
318
+
319
+ // Sort lo, mid and hi appropriately, then pick mid as the pivot.
320
+ SwapIfGreater ( keys , comparer , 0 , middle ) ; // swap the low with the mid point
321
+ SwapIfGreater ( keys , comparer , 0 , hi ) ; // swap the low with the high
322
+ SwapIfGreater ( keys , comparer , middle , hi ) ; // swap the middle with the high
323
+
324
+ T pivot = keys [ middle ] ;
325
+ Swap ( keys , middle , hi - 1 ) ;
326
+ int left = 0 , right = hi - 1 ; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
327
+
328
+ while ( left < right )
329
+ {
330
+ while ( comparer . Compare ( keys [ ++ left ] , pivot ) < 0 ) ;
331
+ while ( comparer . Compare ( pivot , keys [ -- right ] ) < 0 ) ;
332
+
333
+ if ( left >= right )
334
+ break ;
335
+
336
+ Swap ( keys , left , right ) ;
337
+ }
338
+
339
+ // Put pivot in the right location.
340
+ if ( left != hi - 1 )
341
+ {
342
+ Swap ( keys , left , hi - 1 ) ;
343
+ }
344
+ return left ;
345
+ }
346
+
217
347
private static void HeapSort ( Span < T > keys , Comparison < T > comparer )
218
348
{
219
349
Debug . Assert ( comparer != null ) ;
@@ -232,6 +362,25 @@ private static void HeapSort(Span<T> keys, Comparison<T> comparer)
232
362
}
233
363
}
234
364
365
+ private static void HeapSort < TComparer > ( Span < T > keys , TComparer comparer )
366
+ where TComparer : IComparer < T > , allows ref struct
367
+ {
368
+ Debug . Assert ( comparer != null ) ;
369
+ Debug . Assert ( ! keys . IsEmpty ) ;
370
+
371
+ int n = keys . Length ;
372
+ for ( int i = n >> 1 ; i >= 1 ; i -- )
373
+ {
374
+ DownHeap ( keys , i , n , comparer ) ;
375
+ }
376
+
377
+ for ( int i = n ; i > 1 ; i -- )
378
+ {
379
+ Swap ( keys , 0 , i - 1 ) ;
380
+ DownHeap ( keys , 1 , i - 1 , comparer ) ;
381
+ }
382
+ }
383
+
235
384
private static void DownHeap ( Span < T > keys , int i , int n , Comparison < T > comparer )
236
385
{
237
386
Debug . Assert ( comparer != null ) ;
@@ -255,6 +404,30 @@ private static void DownHeap(Span<T> keys, int i, int n, Comparison<T> comparer)
255
404
keys [ i - 1 ] = d ;
256
405
}
257
406
407
+ private static void DownHeap < TComparer > ( Span < T > keys , int i , int n , TComparer comparer )
408
+ where TComparer : IComparer < T > , allows ref struct
409
+ {
410
+ Debug . Assert ( comparer != null ) ;
411
+
412
+ T d = keys [ i - 1 ] ;
413
+ while ( i <= n >> 1 )
414
+ {
415
+ int child = 2 * i ;
416
+ if ( child < n && comparer . Compare ( keys [ child - 1 ] , keys [ child ] ) < 0 )
417
+ {
418
+ child ++ ;
419
+ }
420
+
421
+ if ( ! ( comparer . Compare ( d , keys [ child - 1 ] ) < 0 ) )
422
+ break ;
423
+
424
+ keys [ i - 1 ] = keys [ child - 1 ] ;
425
+ i = child ;
426
+ }
427
+
428
+ keys [ i - 1 ] = d ;
429
+ }
430
+
258
431
private static void InsertionSort ( Span < T > keys , Comparison < T > comparer )
259
432
{
260
433
for ( int i = 0 ; i < keys . Length - 1 ; i ++ )
@@ -271,6 +444,24 @@ private static void InsertionSort(Span<T> keys, Comparison<T> comparer)
271
444
keys [ j + 1 ] = t ;
272
445
}
273
446
}
447
+
448
+ private static void InsertionSort < TComparer > ( Span < T > keys , TComparer comparer )
449
+ where TComparer : IComparer < T > , allows ref struct
450
+ {
451
+ for ( int i = 0 ; i < keys . Length - 1 ; i ++ )
452
+ {
453
+ T t = keys [ i + 1 ] ;
454
+
455
+ int j = i ;
456
+ while ( j >= 0 && comparer . Compare ( t , keys [ j ] ) < 0 )
457
+ {
458
+ keys [ j + 1 ] = keys [ j ] ;
459
+ j -- ;
460
+ }
461
+
462
+ keys [ j + 1 ] = t ;
463
+ }
464
+ }
274
465
}
275
466
276
467
internal sealed partial class GenericArraySortHelper < T >
@@ -478,7 +669,7 @@ private static unsafe int PickPivotAndPartition(Span<T> keys)
478
669
{
479
670
if ( pivot == null )
480
671
{
481
- while ( Unsafe . IsAddressLessThan ( ref leftRef , ref nextToLastRef ) && ( leftRef = ref Unsafe . Add ( ref leftRef , 1 ) ) == null ) ;
672
+ while ( Unsafe . IsAddressLessThan ( ref leftRef , ref nextToLastRef ) && ( leftRef = ref Unsafe . Add ( ref leftRef ! , 1 ) ) == null ) ;
482
673
while ( Unsafe . IsAddressGreaterThan ( ref rightRef , ref zeroRef ) && ( rightRef = ref Unsafe . Add ( ref rightRef , - 1 ) ) != null ) ;
483
674
}
484
675
else
@@ -492,13 +683,13 @@ private static unsafe int PickPivotAndPartition(Span<T> keys)
492
683
break ;
493
684
}
494
685
495
- Swap ( ref leftRef , ref rightRef ) ;
686
+ Swap ( ref leftRef ! , ref rightRef ! ) ;
496
687
}
497
688
498
689
// Put the pivot in the correct location.
499
690
if ( ! Unsafe . AreSame ( ref leftRef , ref nextToLastRef ) )
500
691
{
501
- Swap ( ref leftRef , ref nextToLastRef ) ;
692
+ Swap ( ref leftRef ! , ref nextToLastRef ) ;
502
693
}
503
694
504
695
return ( int ) ( ( nint ) Unsafe . ByteOffset ( ref zeroRef , ref leftRef ) / sizeof ( T ) ) ;
@@ -630,7 +821,27 @@ public void Sort(Span<TKey> keys, Span<TValue> values, IComparer<TKey>? comparer
630
821
}
631
822
}
632
823
633
- private static void SwapIfGreaterWithValues ( Span < TKey > keys , Span < TValue > values , IComparer < TKey > comparer , int i , int j )
824
+ public static void Sort < TComparer > ( Span < TKey > keys , Span < TValue > values , TComparer comparer )
825
+ where TComparer : IComparer < TKey >
826
+ {
827
+ // Add a try block here to detect IComparers (or their
828
+ // underlying IComparables, etc) that are bogus.
829
+ try
830
+ {
831
+ IntrospectiveSort ( keys , values , comparer ) ;
832
+ }
833
+ catch ( IndexOutOfRangeException )
834
+ {
835
+ ThrowHelper . ThrowArgumentException_BadComparer ( comparer ) ;
836
+ }
837
+ catch ( Exception e )
838
+ {
839
+ ThrowHelper . ThrowInvalidOperationException ( ExceptionResource . InvalidOperation_IComparerFailed , e ) ;
840
+ }
841
+ }
842
+
843
+ private static void SwapIfGreaterWithValues < TComparer > ( Span < TKey > keys , Span < TValue > values , TComparer comparer , int i , int j )
844
+ where TComparer : IComparer < TKey > , allows ref struct
634
845
{
635
846
Debug . Assert ( comparer != null ) ;
636
847
Debug . Assert ( 0 <= i && i < keys . Length && i < values . Length ) ;
@@ -663,7 +874,8 @@ private static void Swap(Span<TKey> keys, Span<TValue> values, int i, int j)
663
874
values [ j ] = v ;
664
875
}
665
876
666
- internal static void IntrospectiveSort ( Span < TKey > keys , Span < TValue > values , IComparer < TKey > comparer )
877
+ internal static void IntrospectiveSort < TComparer > ( Span < TKey > keys , Span < TValue > values , TComparer comparer )
878
+ where TComparer : IComparer < TKey > , allows ref struct
667
879
{
668
880
Debug . Assert ( comparer != null ) ;
669
881
Debug . Assert ( keys . Length == values . Length ) ;
@@ -674,7 +886,8 @@ internal static void IntrospectiveSort(Span<TKey> keys, Span<TValue> values, ICo
674
886
}
675
887
}
676
888
677
- private static void IntroSort ( Span < TKey > keys , Span < TValue > values , int depthLimit , IComparer < TKey > comparer )
889
+ private static void IntroSort < TComparer > ( Span < TKey > keys , Span < TValue > values , int depthLimit , TComparer comparer )
890
+ where TComparer : IComparer < TKey > , allows ref struct
678
891
{
679
892
Debug . Assert ( ! keys . IsEmpty ) ;
680
893
Debug . Assert ( values . Length == keys . Length ) ;
@@ -720,7 +933,8 @@ private static void IntroSort(Span<TKey> keys, Span<TValue> values, int depthLim
720
933
}
721
934
}
722
935
723
- private static int PickPivotAndPartition ( Span < TKey > keys , Span < TValue > values , IComparer < TKey > comparer )
936
+ private static int PickPivotAndPartition < TComparer > ( Span < TKey > keys , Span < TValue > values , TComparer comparer )
937
+ where TComparer : IComparer < TKey > , allows ref struct
724
938
{
725
939
Debug . Assert ( keys . Length >= Array . IntrosortSizeThreshold ) ;
726
940
Debug . Assert ( comparer != null ) ;
@@ -758,7 +972,8 @@ private static int PickPivotAndPartition(Span<TKey> keys, Span<TValue> values, I
758
972
return left ;
759
973
}
760
974
761
- private static void HeapSort ( Span < TKey > keys , Span < TValue > values , IComparer < TKey > comparer )
975
+ private static void HeapSort < TComparer > ( Span < TKey > keys , Span < TValue > values , TComparer comparer )
976
+ where TComparer : IComparer < TKey > , allows ref struct
762
977
{
763
978
Debug . Assert ( comparer != null ) ;
764
979
Debug . Assert ( ! keys . IsEmpty ) ;
@@ -776,7 +991,8 @@ private static void HeapSort(Span<TKey> keys, Span<TValue> values, IComparer<TKe
776
991
}
777
992
}
778
993
779
- private static void DownHeap ( Span < TKey > keys , Span < TValue > values , int i , int n , IComparer < TKey > comparer )
994
+ private static void DownHeap < TComparer > ( Span < TKey > keys , Span < TValue > values , int i , int n , TComparer comparer )
995
+ where TComparer : IComparer < TKey > , allows ref struct
780
996
{
781
997
Debug . Assert ( comparer != null ) ;
782
998
@@ -803,7 +1019,8 @@ private static void DownHeap(Span<TKey> keys, Span<TValue> values, int i, int n,
803
1019
values [ i - 1 ] = dValue ;
804
1020
}
805
1021
806
- private static void InsertionSort ( Span < TKey > keys , Span < TValue > values , IComparer < TKey > comparer )
1022
+ private static void InsertionSort < TComparer > ( Span < TKey > keys , Span < TValue > values , TComparer comparer )
1023
+ where TComparer : IComparer < TKey > , allows ref struct
807
1024
{
808
1025
Debug . Assert ( comparer != null ) ;
809
1026
0 commit comments