11//! Slice sorting
22//!
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,
44//! published at: https://github.com/orlp/pdqsort
55//!
66//! Unstable sorting is compatible with libcore because it doesn't allocate memory, unlike our
@@ -20,6 +20,9 @@ struct CopyOnDrop<T> {
2020
2121impl < T > Drop for CopyOnDrop < T > {
2222 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`.
2326 unsafe {
2427 ptr:: copy_nonoverlapping ( self . src , self . dest , 1 ) ;
2528 }
3235 F : FnMut ( & T , & T ) -> bool ,
3336{
3437 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.
3553 unsafe {
3654 // If the first two elements are out-of-order...
3755 if len >= 2 && is_less ( v. get_unchecked ( 1 ) , v. get_unchecked ( 0 ) ) {
6280 F : FnMut ( & T , & T ) -> bool ,
6381{
6482 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.
6598 unsafe {
6699 // If the last two elements are out-of-order...
67100 if len >= 2 && is_less ( v. get_unchecked ( len - 1 ) , v. get_unchecked ( len - 2 ) ) {
@@ -103,6 +136,8 @@ where
103136 let mut i = 1 ;
104137
105138 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`
106141 unsafe {
107142 // Find the next pair of adjacent out-of-order elements.
108143 while i < len && !is_less ( v. get_unchecked ( i) , v. get_unchecked ( i - 1 ) ) {
@@ -220,6 +255,7 @@ where
220255 let mut offsets_l = [ MaybeUninit :: < u8 > :: uninit ( ) ; BLOCK ] ;
221256
222257 // 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`
223259 let mut r = unsafe { l. add ( v. len ( ) ) } ;
224260 let mut block_r = BLOCK ;
225261 let mut start_r = ptr:: null_mut ( ) ;
@@ -268,6 +304,16 @@ where
268304 let mut elem = l;
269305
270306 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.
271317 unsafe {
272318 // Branchless comparison.
273319 * end_l = i as u8 ;
@@ -284,6 +330,17 @@ where
284330 let mut elem = r;
285331
286332 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.
287344 unsafe {
288345 // Branchless comparison.
289346 elem = elem. offset ( -1 ) ;
@@ -404,8 +461,13 @@ where
404461 // Find the first pair of out-of-order elements.
405462 let mut l = 0 ;
406463 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.
407469 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.
409471 while l < r && is_less ( v. get_unchecked ( l) , pivot) {
410472 l += 1 ;
411473 }
@@ -444,6 +506,7 @@ where
444506
445507 // Read the pivot into a stack-allocated variable for efficiency. If a following comparison
446508 // 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.
447510 let mut tmp = mem:: ManuallyDrop :: new ( unsafe { ptr:: read ( pivot) } ) ;
448511 let _pivot_guard = CopyOnDrop { src : & mut * tmp, dest : pivot } ;
449512 let pivot = & * tmp;
@@ -452,8 +515,12 @@ where
452515 let mut l = 0 ;
453516 let mut r = v. len ( ) ;
454517 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.
455522 unsafe {
456- // Find the first element greater that the pivot.
523+ // Find the first element greater than the pivot.
457524 while l < r && !is_less ( pivot, v. get_unchecked ( l) ) {
458525 l += 1 ;
459526 }
0 commit comments