@@ -102,6 +102,7 @@ use core::mem::size_of;
102
102
use core:: mem;
103
103
use core:: ptr;
104
104
use core:: slice as core_slice;
105
+ use core:: { u8, u16, u32} ;
105
106
106
107
use borrow:: { Borrow , BorrowMut , ToOwned } ;
107
108
use boxed:: Box ;
@@ -1302,7 +1303,8 @@ impl<T> [T] {
1302
1303
1303
1304
/// Sorts the slice with a key extraction function.
1304
1305
///
1305
- /// This sort is stable (i.e. does not reorder equal elements) and `O(n log n)` worst-case.
1306
+ /// This sort is stable (i.e. does not reorder equal elements) and `O(m n log(m n))`
1307
+ /// worst-case, where the key function is `O(m)`.
1306
1308
///
1307
1309
/// When applicable, unstable sorting is preferred because it is generally faster than stable
1308
1310
/// sorting and it doesn't allocate auxiliary memory.
@@ -1328,12 +1330,82 @@ impl<T> [T] {
1328
1330
/// ```
1329
1331
#[ stable( feature = "slice_sort_by_key" , since = "1.7.0" ) ]
1330
1332
#[ inline]
1331
- pub fn sort_by_key < B , F > ( & mut self , mut f : F )
1332
- where F : FnMut ( & T ) -> B , B : Ord
1333
+ pub fn sort_by_key < K , F > ( & mut self , mut f : F )
1334
+ where F : FnMut ( & T ) -> K , K : Ord
1333
1335
{
1334
1336
merge_sort ( self , |a, b| f ( a) . lt ( & f ( b) ) ) ;
1335
1337
}
1336
1338
1339
+ /// Sorts the slice with a key extraction function.
1340
+ ///
1341
+ /// During sorting, the key function is called only once per element.
1342
+ ///
1343
+ /// This sort is stable (i.e. does not reorder equal elements) and `O(m n + n log n)`
1344
+ /// worst-case, where the key function is `O(m)`.
1345
+ ///
1346
+ /// For simple key functions (e.g. functions that are property accesses or
1347
+ /// basic operations), [`sort_by_key`](#method.sort_by_key) is likely to be
1348
+ /// faster.
1349
+ ///
1350
+ /// # Current implementation
1351
+ ///
1352
+ /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters,
1353
+ /// which combines the fast average case of randomized quicksort with the fast worst case of
1354
+ /// heapsort, while achieving linear time on slices with certain patterns. It uses some
1355
+ /// randomization to avoid degenerate cases, but with a fixed seed to always provide
1356
+ /// deterministic behavior.
1357
+ ///
1358
+ /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the
1359
+ /// length of the slice.
1360
+ ///
1361
+ /// # Examples
1362
+ ///
1363
+ /// ```
1364
+ /// #![feature(slice_sort_by_cached_key)]
1365
+ /// let mut v = [-5i32, 4, 32, -3, 2];
1366
+ ///
1367
+ /// v.sort_by_cached_key(|k| k.to_string());
1368
+ /// assert!(v == [-3, -5, 2, 32, 4]);
1369
+ /// ```
1370
+ ///
1371
+ /// [pdqsort]: https://github.com/orlp/pdqsort
1372
+ #[ unstable( feature = "slice_sort_by_cached_key" , issue = "34447" ) ]
1373
+ #[ inline]
1374
+ pub fn sort_by_cached_key < K , F > ( & mut self , f : F )
1375
+ where F : FnMut ( & T ) -> K , K : Ord
1376
+ {
1377
+ // Helper macro for indexing our vector by the smallest possible type, to reduce allocation.
1378
+ macro_rules! sort_by_key {
1379
+ ( $t: ty, $slice: ident, $f: ident) => ( {
1380
+ let mut indices: Vec <_> =
1381
+ $slice. iter( ) . map( $f) . enumerate( ) . map( |( i, k) | ( k, i as $t) ) . collect( ) ;
1382
+ // The elements of `indices` are unique, as they are indexed, so any sort will be
1383
+ // stable with respect to the original slice. We use `sort_unstable` here because
1384
+ // it requires less memory allocation.
1385
+ indices. sort_unstable( ) ;
1386
+ for i in 0 ..$slice. len( ) {
1387
+ let mut index = indices[ i] . 1 ;
1388
+ while ( index as usize ) < i {
1389
+ index = indices[ index as usize ] . 1 ;
1390
+ }
1391
+ indices[ i] . 1 = index;
1392
+ $slice. swap( i, index as usize ) ;
1393
+ }
1394
+ } )
1395
+ }
1396
+
1397
+ let sz_u8 = mem:: size_of :: < ( K , u8 ) > ( ) ;
1398
+ let sz_u16 = mem:: size_of :: < ( K , u16 ) > ( ) ;
1399
+ let sz_u32 = mem:: size_of :: < ( K , u32 ) > ( ) ;
1400
+ let sz_usize = mem:: size_of :: < ( K , usize ) > ( ) ;
1401
+
1402
+ let len = self . len ( ) ;
1403
+ if sz_u8 < sz_u16 && len <= ( u8:: MAX as usize ) { return sort_by_key ! ( u8 , self , f) }
1404
+ if sz_u16 < sz_u32 && len <= ( u16:: MAX as usize ) { return sort_by_key ! ( u16 , self , f) }
1405
+ if sz_u32 < sz_usize && len <= ( u32:: MAX as usize ) { return sort_by_key ! ( u32 , self , f) }
1406
+ sort_by_key ! ( usize , self , f)
1407
+ }
1408
+
1337
1409
/// Sorts the slice, but may not preserve the order of equal elements.
1338
1410
///
1339
1411
/// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate),
@@ -1410,7 +1482,7 @@ impl<T> [T] {
1410
1482
/// elements.
1411
1483
///
1412
1484
/// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate),
1413
- /// and `O(n log n)` worst-case.
1485
+ /// and `O(m n log(m n)) ` worst-case, where the key function is `O(m)` .
1414
1486
///
1415
1487
/// # Current implementation
1416
1488
///
@@ -1420,9 +1492,6 @@ impl<T> [T] {
1420
1492
/// randomization to avoid degenerate cases, but with a fixed seed to always provide
1421
1493
/// deterministic behavior.
1422
1494
///
1423
- /// It is typically faster than stable sorting, except in a few special cases, e.g. when the
1424
- /// slice consists of several concatenated sorted sequences.
1425
- ///
1426
1495
/// # Examples
1427
1496
///
1428
1497
/// ```
@@ -1435,9 +1504,8 @@ impl<T> [T] {
1435
1504
/// [pdqsort]: https://github.com/orlp/pdqsort
1436
1505
#[ stable( feature = "sort_unstable" , since = "1.20.0" ) ]
1437
1506
#[ inline]
1438
- pub fn sort_unstable_by_key < B , F > ( & mut self , f : F )
1439
- where F : FnMut ( & T ) -> B ,
1440
- B : Ord
1507
+ pub fn sort_unstable_by_key < K , F > ( & mut self , f : F )
1508
+ where F : FnMut ( & T ) -> K , K : Ord
1441
1509
{
1442
1510
core_slice:: SliceExt :: sort_unstable_by_key ( self , f) ;
1443
1511
}
0 commit comments