1
1
//! Slice sorting
2
2
//!
3
- //! This module contains an sort algorithm based on Orson Peters' pattern-defeating quicksort,
3
+ //! This module contains a sorting algorithm based on Orson Peters' pattern-defeating quicksort,
4
4
//! published at: https://github.com/orlp/pdqsort
5
5
//!
6
6
//! Unstable sorting is compatible with libcore because it doesn't allocate memory, unlike our
@@ -20,6 +20,9 @@ struct CopyOnDrop<T> {
20
20
21
21
impl < T > Drop for CopyOnDrop < T > {
22
22
fn drop ( & mut self ) {
23
+ // SAFETY: This is a helper class.
24
+ // Please refer to its usage for correctness.
25
+ // Namely, one must be sure that `src` and `dst` does not overlap as required by `ptr::copy_nonoverlapping`.
23
26
unsafe {
24
27
ptr:: copy_nonoverlapping ( self . src , self . dest , 1 ) ;
25
28
}
32
35
F : FnMut ( & T , & T ) -> bool ,
33
36
{
34
37
let len = v. len ( ) ;
38
+ // SAFETY: The unsafe operations below involves indexing without a bound check (`get_unchecked` and `get_unchecked_mut`)
39
+ // and copying memory (`ptr::copy_nonoverlapping`).
40
+ //
41
+ // a. Indexing:
42
+ // 1. We checked the size of the array to >=2.
43
+ // 2. All the indexing that we will do is always between {0 <= index < len} at most.
44
+ //
45
+ // b. Memory copying
46
+ // 1. We are obtaining pointers to references which are guaranteed to be valid.
47
+ // 2. They cannot overlap because we obtain pointers to difference indices of the slice.
48
+ // Namely, `i` and `i-1`.
49
+ // 3. If the slice is properly aligned, the elements are properly aligned.
50
+ // It is the caller's responsibility to make sure the slice is properly aligned.
51
+ //
52
+ // See comments below for further detail.
35
53
unsafe {
36
54
// If the first two elements are out-of-order...
37
55
if len >= 2 && is_less ( v. get_unchecked ( 1 ) , v. get_unchecked ( 0 ) ) {
62
80
F : FnMut ( & T , & T ) -> bool ,
63
81
{
64
82
let len = v. len ( ) ;
83
+ // SAFETY: The unsafe operations below involves indexing without a bound check (`get_unchecked` and `get_unchecked_mut`)
84
+ // and copying memory (`ptr::copy_nonoverlapping`).
85
+ //
86
+ // a. Indexing:
87
+ // 1. We checked the size of the array to >= 2.
88
+ // 2. All the indexing that we will do is always between `0 <= index < len-1` at most.
89
+ //
90
+ // b. Memory copying
91
+ // 1. We are obtaining pointers to references which are guaranteed to be valid.
92
+ // 2. They cannot overlap because we obtain pointers to difference indices of the slice.
93
+ // Namely, `i` and `i+1`.
94
+ // 3. If the slice is properly aligned, the elements are properly aligned.
95
+ // It is the caller's responsibility to make sure the slice is properly aligned.
96
+ //
97
+ // See comments below for further detail.
65
98
unsafe {
66
99
// If the last two elements are out-of-order...
67
100
if len >= 2 && is_less ( v. get_unchecked ( len - 1 ) , v. get_unchecked ( len - 2 ) ) {
@@ -103,6 +136,8 @@ where
103
136
let mut i = 1 ;
104
137
105
138
for _ in 0 ..MAX_STEPS {
139
+ // SAFETY: We already explicitly did the bound checking with `i < len`.
140
+ // All our subsequent indexing is only in the range `0 <= index < len`
106
141
unsafe {
107
142
// Find the next pair of adjacent out-of-order elements.
108
143
while i < len && !is_less ( v. get_unchecked ( i) , v. get_unchecked ( i - 1 ) ) {
@@ -220,6 +255,7 @@ where
220
255
let mut offsets_l = [ MaybeUninit :: < u8 > :: uninit ( ) ; BLOCK ] ;
221
256
222
257
// The current block on the right side (from `r.sub(block_r)` to `r`).
258
+ // SAFETY: The documentation for .add() specifically mention that `vec.as_ptr().add(vec.len())` is always safe`
223
259
let mut r = unsafe { l. add ( v. len ( ) ) } ;
224
260
let mut block_r = BLOCK ;
225
261
let mut start_r = ptr:: null_mut ( ) ;
@@ -268,6 +304,16 @@ where
268
304
let mut elem = l;
269
305
270
306
for i in 0 ..block_l {
307
+ // SAFETY: The unsafety operations below involve the usage of the `offset`.
308
+ // According to the conditions required by the function, we satisfy them because:
309
+ // 1. `offsets_l` is stack-allocated, and thus considered separate allocated object.
310
+ // 2. The function `is_less` returns a `bool`.
311
+ // Casting a `bool` will never overflow `isize`.
312
+ // 3. We have guaranteed that `block_l` will be `<= BLOCK`.
313
+ // Plus, `end_l` was initially set to the begin pointer of `offsets_` which was declared on the stack.
314
+ // Thus, we know that even in the worst case (all invocations of `is_less` returns false) we will only be at most 1 byte pass the end.
315
+ // Another unsafety operation here is dereferencing `elem`.
316
+ // However, `elem` was initially the begin pointer to the slice which is always valid.
271
317
unsafe {
272
318
// Branchless comparison.
273
319
* end_l = i as u8 ;
@@ -284,6 +330,17 @@ where
284
330
let mut elem = r;
285
331
286
332
for i in 0 ..block_r {
333
+ // SAFETY: The unsafety operations below involve the usage of the `offset`.
334
+ // According to the conditions required by the function, we satisfy them because:
335
+ // 1. `offsets_r` is stack-allocated, and thus considered separate allocated object.
336
+ // 2. The function `is_less` returns a `bool`.
337
+ // Casting a `bool` will never overflow `isize`.
338
+ // 3. We have guaranteed that `block_r` will be `<= BLOCK`.
339
+ // Plus, `end_r` was initially set to the begin pointer of `offsets_` which was declared on the stack.
340
+ // Thus, we know that even in the worst case (all invocations of `is_less` returns true) we will only be at most 1 byte pass the end.
341
+ // Another unsafety operation here is dereferencing `elem`.
342
+ // However, `elem` was initially `1 * sizeof(T)` past the end and we decrement it by `1 * sizeof(T)` before accessing it.
343
+ // Plus, `block_r` was asserted to be less than `BLOCK` and `elem` will therefore at most be pointing to the beginning of the slice.
287
344
unsafe {
288
345
// Branchless comparison.
289
346
elem = elem. offset ( -1 ) ;
@@ -404,8 +461,13 @@ where
404
461
// Find the first pair of out-of-order elements.
405
462
let mut l = 0 ;
406
463
let mut r = v. len ( ) ;
464
+
465
+ // SAFETY: The unsafety below involves indexing an array.
466
+ // For the first one: We already do the bounds checking here with `l < r`.
467
+ // For the second one: We initially have `l == 0` and `r == v.len()` and we checked that `l < r` at every indexing operation.
468
+ // From here we know that `r` must be at least `r == l` which was shown to be valid from the first one.
407
469
unsafe {
408
- // Find the first element greater then or equal to the pivot.
470
+ // Find the first element greater than or equal to the pivot.
409
471
while l < r && is_less ( v. get_unchecked ( l) , pivot) {
410
472
l += 1 ;
411
473
}
@@ -444,6 +506,7 @@ where
444
506
445
507
// Read the pivot into a stack-allocated variable for efficiency. If a following comparison
446
508
// operation panics, the pivot will be automatically written back into the slice.
509
+ // SAFETY: The pointer here is valid because it is obtained from a reference to a slice.
447
510
let mut tmp = mem:: ManuallyDrop :: new ( unsafe { ptr:: read ( pivot) } ) ;
448
511
let _pivot_guard = CopyOnDrop { src : & mut * tmp, dest : pivot } ;
449
512
let pivot = & * tmp;
@@ -452,8 +515,12 @@ where
452
515
let mut l = 0 ;
453
516
let mut r = v. len ( ) ;
454
517
loop {
518
+ // SAFETY: The unsafety below involves indexing an array.
519
+ // For the first one: We already do the bounds checking here with `l < r`.
520
+ // For the second one: We initially have `l == 0` and `r == v.len()` and we checked that `l < r` at every indexing operation.
521
+ // From here we know that `r` must be at least `r == l` which was shown to be valid from the first one.
455
522
unsafe {
456
- // Find the first element greater that the pivot.
523
+ // Find the first element greater than the pivot.
457
524
while l < r && !is_less ( pivot, v. get_unchecked ( l) ) {
458
525
l += 1 ;
459
526
}
0 commit comments