Skip to content

Commit 897ef3a

Browse files
committed
Auto merge of #75936 - sdroege:chunks-exact-construction-bounds-check, r=nagisa
Get rid of bounds check in slice::chunks_exact() and related function… …s during construction LLVM can't figure out in let rem = self.len() % chunk_size; let len = self.len() - rem; let (fst, snd) = self.split_at(len); and let rem = self.len() % chunk_size; let (fst, snd) = self.split_at(rem); that the index passed to split_at() is smaller than the slice length and adds a bounds check plus panic for it. Apart from removing the overhead of the bounds check this also allows LLVM to optimize code around the ChunksExact iterator better.
2 parents 1fd8636 + 8d3cf92 commit 897ef3a

File tree

1 file changed

+127
-27
lines changed

1 file changed

+127
-27
lines changed

library/core/src/slice/mod.rs

+127-27
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,12 @@ impl<T> [T] {
288288
/// Returns a reference to an element or subslice, without doing bounds
289289
/// checking.
290290
///
291-
/// This is generally not recommended, use with caution!
291+
/// For a safe alternative see [`get`].
292+
///
293+
/// # Safety
294+
///
292295
/// Calling this method with an out-of-bounds index is *[undefined behavior]*
293296
/// even if the resulting reference is not used.
294-
/// For a safe alternative see [`get`].
295297
///
296298
/// [`get`]: #method.get
297299
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
@@ -320,10 +322,12 @@ impl<T> [T] {
320322
/// Returns a mutable reference to an element or subslice, without doing
321323
/// bounds checking.
322324
///
323-
/// This is generally not recommended, use with caution!
325+
/// For a safe alternative see [`get_mut`].
326+
///
327+
/// # Safety
328+
///
324329
/// Calling this method with an out-of-bounds index is *[undefined behavior]*
325330
/// even if the resulting reference is not used.
326-
/// For a safe alternative see [`get_mut`].
327331
///
328332
/// [`get_mut`]: #method.get_mut
329333
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
@@ -865,8 +869,9 @@ impl<T> [T] {
865869
pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> {
866870
assert_ne!(chunk_size, 0);
867871
let rem = self.len() % chunk_size;
868-
let len = self.len() - rem;
869-
let (fst, snd) = self.split_at(len);
872+
let fst_len = self.len() - rem;
873+
// SAFETY: 0 <= fst_len <= self.len() by construction above
874+
let (fst, snd) = unsafe { self.split_at_unchecked(fst_len) };
870875
ChunksExact { v: fst, rem: snd, chunk_size }
871876
}
872877

@@ -910,8 +915,9 @@ impl<T> [T] {
910915
pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> {
911916
assert_ne!(chunk_size, 0);
912917
let rem = self.len() % chunk_size;
913-
let len = self.len() - rem;
914-
let (fst, snd) = self.split_at_mut(len);
918+
let fst_len = self.len() - rem;
919+
// SAFETY: 0 <= fst_len <= self.len() by construction above
920+
let (fst, snd) = unsafe { self.split_at_mut_unchecked(fst_len) };
915921
ChunksExactMut { v: fst, rem: snd, chunk_size }
916922
}
917923

@@ -1063,7 +1069,8 @@ impl<T> [T] {
10631069
pub fn rchunks_exact(&self, chunk_size: usize) -> RChunksExact<'_, T> {
10641070
assert!(chunk_size != 0);
10651071
let rem = self.len() % chunk_size;
1066-
let (fst, snd) = self.split_at(rem);
1072+
// SAFETY: 0 <= rem <= self.len() by construction above
1073+
let (fst, snd) = unsafe { self.split_at_unchecked(rem) };
10671074
RChunksExact { v: snd, rem: fst, chunk_size }
10681075
}
10691076

@@ -1108,7 +1115,8 @@ impl<T> [T] {
11081115
pub fn rchunks_exact_mut(&mut self, chunk_size: usize) -> RChunksExactMut<'_, T> {
11091116
assert!(chunk_size != 0);
11101117
let rem = self.len() % chunk_size;
1111-
let (fst, snd) = self.split_at_mut(rem);
1118+
// SAFETY: 0 <= rem <= self.len() by construction above
1119+
let (fst, snd) = unsafe { self.split_at_mut_unchecked(rem) };
11121120
RChunksExactMut { v: snd, rem: fst, chunk_size }
11131121
}
11141122

@@ -1129,26 +1137,29 @@ impl<T> [T] {
11291137
///
11301138
/// {
11311139
/// let (left, right) = v.split_at(0);
1132-
/// assert!(left == []);
1133-
/// assert!(right == [1, 2, 3, 4, 5, 6]);
1140+
/// assert_eq!(left, []);
1141+
/// assert_eq!(right, [1, 2, 3, 4, 5, 6]);
11341142
/// }
11351143
///
11361144
/// {
11371145
/// let (left, right) = v.split_at(2);
1138-
/// assert!(left == [1, 2]);
1139-
/// assert!(right == [3, 4, 5, 6]);
1146+
/// assert_eq!(left, [1, 2]);
1147+
/// assert_eq!(right, [3, 4, 5, 6]);
11401148
/// }
11411149
///
11421150
/// {
11431151
/// let (left, right) = v.split_at(6);
1144-
/// assert!(left == [1, 2, 3, 4, 5, 6]);
1145-
/// assert!(right == []);
1152+
/// assert_eq!(left, [1, 2, 3, 4, 5, 6]);
1153+
/// assert_eq!(right, []);
11461154
/// }
11471155
/// ```
11481156
#[stable(feature = "rust1", since = "1.0.0")]
11491157
#[inline]
11501158
pub fn split_at(&self, mid: usize) -> (&[T], &[T]) {
1151-
(&self[..mid], &self[mid..])
1159+
assert!(mid <= self.len());
1160+
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
1161+
// fulfills the requirements of `from_raw_parts_mut`.
1162+
unsafe { self.split_at_unchecked(mid) }
11521163
}
11531164

11541165
/// Divides one mutable slice into two at an index.
@@ -1168,26 +1179,115 @@ impl<T> [T] {
11681179
/// // scoped to restrict the lifetime of the borrows
11691180
/// {
11701181
/// let (left, right) = v.split_at_mut(2);
1171-
/// assert!(left == [1, 0]);
1172-
/// assert!(right == [3, 0, 5, 6]);
1182+
/// assert_eq!(left, [1, 0]);
1183+
/// assert_eq!(right, [3, 0, 5, 6]);
11731184
/// left[1] = 2;
11741185
/// right[1] = 4;
11751186
/// }
1176-
/// assert!(v == [1, 2, 3, 4, 5, 6]);
1187+
/// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
11771188
/// ```
11781189
#[stable(feature = "rust1", since = "1.0.0")]
11791190
#[inline]
11801191
pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
1181-
let len = self.len();
1182-
let ptr = self.as_mut_ptr();
1183-
1192+
assert!(mid <= self.len());
11841193
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
11851194
// fulfills the requirements of `from_raw_parts_mut`.
1186-
unsafe {
1187-
assert!(mid <= len);
1195+
unsafe { self.split_at_mut_unchecked(mid) }
1196+
}
11881197

1189-
(from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid))
1190-
}
1198+
/// Divides one slice into two at an index, without doing bounds checking.
1199+
///
1200+
/// The first will contain all indices from `[0, mid)` (excluding
1201+
/// the index `mid` itself) and the second will contain all
1202+
/// indices from `[mid, len)` (excluding the index `len` itself).
1203+
///
1204+
/// For a safe alternative see [`split_at`].
1205+
///
1206+
/// # Safety
1207+
///
1208+
/// Calling this method with an out-of-bounds index is *[undefined behavior]*
1209+
/// even if the resulting reference is not used. The caller has to ensure that
1210+
/// `0 <= mid <= self.len()`.
1211+
///
1212+
/// [`split_at`]: #method.split_at
1213+
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
1214+
///
1215+
/// # Examples
1216+
///
1217+
/// ```compile_fail
1218+
/// #![feature(slice_split_at_unchecked)]
1219+
///
1220+
/// let v = [1, 2, 3, 4, 5, 6];
1221+
///
1222+
/// unsafe {
1223+
/// let (left, right) = v.split_at_unchecked(0);
1224+
/// assert_eq!(left, []);
1225+
/// assert_eq!(right, [1, 2, 3, 4, 5, 6]);
1226+
/// }
1227+
///
1228+
/// unsafe {
1229+
/// let (left, right) = v.split_at_unchecked(2);
1230+
/// assert_eq!(left, [1, 2]);
1231+
/// assert_eq!(right, [3, 4, 5, 6]);
1232+
/// }
1233+
///
1234+
/// unsafe {
1235+
/// let (left, right) = v.split_at_unchecked(6);
1236+
/// assert_eq!(left, [1, 2, 3, 4, 5, 6]);
1237+
/// assert_eq!(right, []);
1238+
/// }
1239+
/// ```
1240+
#[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")]
1241+
#[inline]
1242+
unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T]) {
1243+
// SAFETY: Caller has to check that `0 <= mid <= self.len()`
1244+
unsafe { (self.get_unchecked(..mid), self.get_unchecked(mid..)) }
1245+
}
1246+
1247+
/// Divides one mutable slice into two at an index, without doing bounds checking.
1248+
///
1249+
/// The first will contain all indices from `[0, mid)` (excluding
1250+
/// the index `mid` itself) and the second will contain all
1251+
/// indices from `[mid, len)` (excluding the index `len` itself).
1252+
///
1253+
/// For a safe alternative see [`split_at_mut`].
1254+
///
1255+
/// # Safety
1256+
///
1257+
/// Calling this method with an out-of-bounds index is *[undefined behavior]*
1258+
/// even if the resulting reference is not used. The caller has to ensure that
1259+
/// `0 <= mid <= self.len()`.
1260+
///
1261+
/// [`split_at_mut`]: #method.split_at_mut
1262+
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
1263+
///
1264+
/// # Examples
1265+
///
1266+
/// ```compile_fail
1267+
/// #![feature(slice_split_at_unchecked)]
1268+
///
1269+
/// let mut v = [1, 0, 3, 0, 5, 6];
1270+
/// // scoped to restrict the lifetime of the borrows
1271+
/// unsafe {
1272+
/// let (left, right) = v.split_at_mut_unchecked(2);
1273+
/// assert_eq!(left, [1, 0]);
1274+
/// assert_eq!(right, [3, 0, 5, 6]);
1275+
/// left[1] = 2;
1276+
/// right[1] = 4;
1277+
/// }
1278+
/// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
1279+
/// ```
1280+
#[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")]
1281+
#[inline]
1282+
unsafe fn split_at_mut_unchecked(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
1283+
let len = self.len();
1284+
let ptr = self.as_mut_ptr();
1285+
1286+
// SAFETY: Caller has to check that `0 <= mid <= self.len()`.
1287+
//
1288+
// `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference
1289+
// is fine.
1290+
unsafe { (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) }
11911291
}
11921292

11931293
/// Returns an iterator over subslices separated by elements that match

0 commit comments

Comments
 (0)