1
- // ignore-tidy-undocumented-unsafe
2
-
3
1
use crate :: cmp;
4
2
use crate :: mem:: { self , MaybeUninit } ;
5
3
use crate :: ptr;
@@ -79,8 +77,10 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
79
77
// the way until about `left + right == 32`, but the worst case performance breaks even
80
78
// around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4
81
79
// `usize`s, this algorithm also outperforms other algorithms.
80
+ // SAFETY: callers must ensure `mid - left` is valid for reading and writing.
82
81
let x = unsafe { mid. sub ( left) } ;
83
82
// beginning of first round
83
+ // SAFETY: see previous comment.
84
84
let mut tmp: T = unsafe { x. read ( ) } ;
85
85
let mut i = right;
86
86
// `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)
92
92
// the very end. This is possibly due to the fact that swapping or replacing temporaries
93
93
// uses only one memory address in the loop instead of needing to manage two.
94
94
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
95
110
tmp = unsafe { x. add ( i) . replace ( tmp) } ;
96
111
// instead of incrementing `i` and then checking if it is outside the bounds, we
97
112
// 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)
100
115
i -= left;
101
116
if i == 0 {
102
117
// 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.
103
120
unsafe { x. write ( tmp) } ;
104
121
break ;
105
122
}
@@ -113,13 +130,24 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
113
130
}
114
131
// finish the chunk with more rounds
115
132
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
116
136
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.
117
143
i = start + right;
118
144
loop {
145
+ // SAFETY: see [long-safety-expl] and [safety-expl-addition]
119
146
tmp = unsafe { x. add ( i) . replace ( tmp) } ;
120
147
if i >= left {
121
148
i -= left;
122
149
if i == start {
150
+ // SAFETY: see [long-safety-expl] and [safety-expl-addition]
123
151
unsafe { x. add ( start) . write ( tmp) } ;
124
152
break ;
125
153
}
@@ -135,14 +163,30 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
135
163
// The `[T; 0]` here is to ensure this is appropriately aligned for T
136
164
let mut rawarray = MaybeUninit :: < ( BufType , [ T ; 0 ] ) > :: uninit ( ) ;
137
165
let buf = rawarray. as_mut_ptr ( ) as * mut T ;
166
+ // SAFETY: `mid-left <= mid-left+right < mid+right`
138
167
let dim = unsafe { mid. sub ( left) . add ( right) } ;
139
168
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)`
140
180
unsafe {
181
+ // 1)
141
182
ptr:: copy_nonoverlapping ( mid. sub ( left) , buf, left) ;
183
+ // 2)
142
184
ptr:: copy ( mid, mid. sub ( left) , right) ;
185
+ // 3)
143
186
ptr:: copy_nonoverlapping ( buf, dim, left) ;
144
187
}
145
188
} else {
189
+ // SAFETY: same reasoning as above but with `left` and `right` reversed
146
190
unsafe {
147
191
ptr:: copy_nonoverlapping ( mid, buf, right) ;
148
192
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)
156
200
// of this algorithm would be, and swapping using that last chunk instead of swapping
157
201
// adjacent chunks like this algorithm is doing, but this way is still faster.
158
202
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.
159
207
unsafe {
160
208
ptr:: swap_nonoverlapping ( mid. sub ( right) , mid, right) ;
161
209
mid = mid. sub ( right) ;
@@ -168,6 +216,10 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize)
168
216
} else {
169
217
// Algorithm 3, `left < right`
170
218
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.
171
223
unsafe {
172
224
ptr:: swap_nonoverlapping ( mid. sub ( left) , mid, left) ;
173
225
mid = mid. add ( left) ;
0 commit comments