1- // ignore-tidy-undocumented-unsafe
2-
31use crate :: cmp;
42use crate :: mem:: { self , MaybeUninit } ;
53use crate :: ptr;
@@ -79,8 +77,10 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
7977 // the way until about `left + right == 32`, but the worst case performance breaks even
8078 // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4
8179 // `usize`s, this algorithm also outperforms other algorithms.
80+ // SAFETY: callers must ensure `mid - left` is valid for reading and writing.
8281 let x = unsafe { mid. sub ( left) } ;
8382 // beginning of first round
83+ // SAFETY: see previous comment.
8484 let mut tmp: T = unsafe { x. read ( ) } ;
8585 let mut i = right;
8686 // `gcd` can be found before hand by calculating `gcd(left + right, right)`,
@@ -92,6 +92,21 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
9292 // the very end. This is possibly due to the fact that swapping or replacing temporaries
9393 // uses only one memory address in the loop instead of needing to manage two.
9494 loop {
95+ // [long-safety-expl]
96+ // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and
97+ // writing.
98+ //
99+ // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right`
100+ // - `i <= left+right-1` is always true
101+ // - if `i < left`, `right` is added so `i < left+right` and on the next
102+ // iteration `left` is removed from `i` so it doesn't go further
103+ // - if `i >= left`, `left` is removed immediately and so it doesn't go further.
104+ // - overflows cannot happen for `i` since the function's safety contract ask for
105+ // `mid+right-1 = x+left+right` to be valid for writing
106+ // - underflows cannot happen because `i` must be bigger or equal to `left` for
107+ // a substraction of `left` to happen.
108+ //
109+ // So `x+i` is valid for reading and writing if the caller respected the contract
95110 tmp = unsafe { x. add ( i) . replace ( tmp) } ;
96111 // instead of incrementing `i` and then checking if it is outside the bounds, we
97112 // check if `i` will go outside the bounds on the next increment. This prevents
@@ -100,6 +115,8 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
100115 i -= left;
101116 if i == 0 {
102117 // end of first round
118+ // SAFETY: tmp has been read from a valid source and x is valid for writing
119+ // according to the caller.
103120 unsafe { x. write ( tmp) } ;
104121 break ;
105122 }
@@ -113,13 +130,24 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
113130 }
114131 // finish the chunk with more rounds
115132 for start in 1 ..gcd {
133+ // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for
134+ // reading and writing as per the function's safety contract, see [long-safety-expl]
135+ // above
116136 tmp = unsafe { x. add ( start) . read ( ) } ;
137+ // [safety-expl-addition]
138+ //
139+ // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the
140+ // greatest common divisor of `(left+right, right)` means that `left = right` so
141+ // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing
142+ // according to the function's safety contract.
117143 i = start + right;
118144 loop {
145+ // SAFETY: see [long-safety-expl] and [safety-expl-addition]
119146 tmp = unsafe { x. add ( i) . replace ( tmp) } ;
120147 if i >= left {
121148 i -= left;
122149 if i == start {
150+ // SAFETY: see [long-safety-expl] and [safety-expl-addition]
123151 unsafe { x. add ( start) . write ( tmp) } ;
124152 break ;
125153 }
@@ -135,14 +163,30 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
135163 // The `[T; 0]` here is to ensure this is appropriately aligned for T
136164 let mut rawarray = MaybeUninit :: < ( BufType , [ T ; 0 ] ) > :: uninit ( ) ;
137165 let buf = rawarray. as_mut_ptr ( ) as * mut T ;
166+ // SAFETY: `mid-left <= mid-left+right < mid+right`
138167 let dim = unsafe { mid. sub ( left) . add ( right) } ;
139168 if left <= right {
169+ // SAFETY:
170+ //
171+ // 1) The `else if` condition about the sizes ensures `[mid-left; left]` will fit in
172+ // `buf` without overflow and `buf` was created just above and so cannot be
173+ // overlapped with any value of `[mid-left; left]`
174+ // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care
175+ // about overlaps here.
176+ // 3) The `if` condition about `left <= right` ensures writing `left` elements to
177+ // `dim = mid-left+right` is valid because:
178+ // - `buf` is valid and `left` elements were written in it in 1)
179+ // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)`
140180 unsafe {
181+ // 1)
141182 ptr:: copy_nonoverlapping ( mid. sub ( left) , buf, left) ;
183+ // 2)
142184 ptr:: copy ( mid, mid. sub ( left) , right) ;
185+ // 3)
143186 ptr:: copy_nonoverlapping ( buf, dim, left) ;
144187 }
145188 } else {
189+ // SAFETY: same reasoning as above but with `left` and `right` reversed
146190 unsafe {
147191 ptr:: copy_nonoverlapping ( mid, buf, right) ;
148192 ptr:: copy ( mid. sub ( left) , dim, left) ;
@@ -156,6 +200,10 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
156200 // of this algorithm would be, and swapping using that last chunk instead of swapping
157201 // adjacent chunks like this algorithm is doing, but this way is still faster.
158202 loop {
203+ // SAFETY:
204+ // `left >= right` so `[mid-right, mid+right)` is valid for reading and writing
205+ // Substracting `right` from `mid` each turn is counterbalanced by the addition and
206+ // check after it.
159207 unsafe {
160208 ptr:: swap_nonoverlapping ( mid. sub ( right) , mid, right) ;
161209 mid = mid. sub ( right) ;
@@ -168,6 +216,10 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
168216 } else {
169217 // Algorithm 3, `left < right`
170218 loop {
219+ // SAFETY: `[mid-left, mid+left)` is valid for reading and writing because
220+ // `left < right` so `mid+left < mid+right`.
221+ // Adding `left` to `mid` each turn is counterbalanced by the substraction and check
222+ // after it.
171223 unsafe {
172224 ptr:: swap_nonoverlapping ( mid. sub ( left) , mid, left) ;
173225 mid = mid. add ( left) ;
0 commit comments