Skip to content

Commit 4541aa9

Browse files
committed
Add safety comments in private core::slice::rotate::ptr_rotate function
1 parent a08f25a commit 4541aa9

File tree

1 file changed

+54
-2
lines changed

1 file changed

+54
-2
lines changed

library/core/src/slice/rotate.rs

+54-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// ignore-tidy-undocumented-unsafe
2-
31
use crate::cmp;
42
use crate::mem::{self, MaybeUninit};
53
use 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

Comments
 (0)