Skip to content

Commit fff6267

Browse files
Rollup merge of rust-lang#138562 - kornelski:nth-panic, r=Noratrieb
Optimize slice {Chunks,Windows}::nth I've noticed that the `nth` functions on slice iters had non-optimized-out bounds checks. The new implementation even generates branchless code.
2 parents a3afaef + 2dbcfcd commit fff6267

File tree

1 file changed

+25
-25
lines changed

1 file changed

+25
-25
lines changed

core/src/slice/iter.rs

+25-25
Original file line numberDiff line numberDiff line change
@@ -1380,14 +1380,16 @@ impl<'a, T> Iterator for Windows<'a, T> {
13801380

13811381
#[inline]
13821382
fn nth(&mut self, n: usize) -> Option<Self::Item> {
1383-
let (end, overflow) = self.size.get().overflowing_add(n);
1384-
if end > self.v.len() || overflow {
1385-
self.v = &[];
1386-
None
1387-
} else {
1388-
let nth = &self.v[n..end];
1389-
self.v = &self.v[n + 1..];
1383+
let size = self.size.get();
1384+
if let Some(rest) = self.v.get(n..)
1385+
&& let Some(nth) = rest.get(..size)
1386+
{
1387+
self.v = &rest[1..];
13901388
Some(nth)
1389+
} else {
1390+
// setting length to 0 is cheaper than overwriting the pointer when assigning &[]
1391+
self.v = &self.v[..0]; // cheaper than &[]
1392+
None
13911393
}
13921394
}
13931395

@@ -1427,7 +1429,7 @@ impl<'a, T> DoubleEndedIterator for Windows<'a, T> {
14271429
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
14281430
let (end, overflow) = self.v.len().overflowing_sub(n);
14291431
if end < self.size.get() || overflow {
1430-
self.v = &[];
1432+
self.v = &self.v[..0]; // cheaper than &[]
14311433
None
14321434
} else {
14331435
let ret = &self.v[end - self.size.get()..end];
@@ -1536,17 +1538,15 @@ impl<'a, T> Iterator for Chunks<'a, T> {
15361538
#[inline]
15371539
fn nth(&mut self, n: usize) -> Option<Self::Item> {
15381540
let (start, overflow) = n.overflowing_mul(self.chunk_size);
1539-
if start >= self.v.len() || overflow {
1540-
self.v = &[];
1541-
None
1542-
} else {
1543-
let end = match start.checked_add(self.chunk_size) {
1544-
Some(sum) => cmp::min(self.v.len(), sum),
1545-
None => self.v.len(),
1546-
};
1547-
let nth = &self.v[start..end];
1548-
self.v = &self.v[end..];
1541+
// min(len) makes a wrong start harmless, but enables optimizing this to brachless code
1542+
let chunk_start = &self.v[start.min(self.v.len())..];
1543+
let (nth, remainder) = chunk_start.split_at(self.chunk_size.min(chunk_start.len()));
1544+
if !overflow && start < self.v.len() {
1545+
self.v = remainder;
15491546
Some(nth)
1547+
} else {
1548+
self.v = &self.v[..0]; // cheaper than &[]
1549+
None
15501550
}
15511551
}
15521552

@@ -1609,7 +1609,7 @@ impl<'a, T> DoubleEndedIterator for Chunks<'a, T> {
16091609
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
16101610
let len = self.len();
16111611
if n >= len {
1612-
self.v = &[];
1612+
self.v = &self.v[..0]; // cheaper than &[]
16131613
None
16141614
} else {
16151615
let start = (len - 1 - n) * self.chunk_size;
@@ -1933,7 +1933,7 @@ impl<'a, T> Iterator for ChunksExact<'a, T> {
19331933
fn nth(&mut self, n: usize) -> Option<Self::Item> {
19341934
let (start, overflow) = n.overflowing_mul(self.chunk_size);
19351935
if start >= self.v.len() || overflow {
1936-
self.v = &[];
1936+
self.v = &self.v[..0]; // cheaper than &[]
19371937
None
19381938
} else {
19391939
let (_, snd) = self.v.split_at(start);
@@ -1971,7 +1971,7 @@ impl<'a, T> DoubleEndedIterator for ChunksExact<'a, T> {
19711971
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
19721972
let len = self.len();
19731973
if n >= len {
1974-
self.v = &[];
1974+
self.v = &self.v[..0]; // cheaper than &[]
19751975
None
19761976
} else {
19771977
let start = (len - 1 - n) * self.chunk_size;
@@ -2638,7 +2638,7 @@ impl<'a, T> Iterator for RChunks<'a, T> {
26382638
fn nth(&mut self, n: usize) -> Option<Self::Item> {
26392639
let (end, overflow) = n.overflowing_mul(self.chunk_size);
26402640
if end >= self.v.len() || overflow {
2641-
self.v = &[];
2641+
self.v = &self.v[..0]; // cheaper than &[]
26422642
None
26432643
} else {
26442644
// Can't underflow because of the check above
@@ -2695,7 +2695,7 @@ impl<'a, T> DoubleEndedIterator for RChunks<'a, T> {
26952695
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
26962696
let len = self.len();
26972697
if n >= len {
2698-
self.v = &[];
2698+
self.v = &self.v[..0]; // cheaper than &[]
26992699
None
27002700
} else {
27012701
// can't underflow because `n < len`
@@ -3023,7 +3023,7 @@ impl<'a, T> Iterator for RChunksExact<'a, T> {
30233023
fn nth(&mut self, n: usize) -> Option<Self::Item> {
30243024
let (end, overflow) = n.overflowing_mul(self.chunk_size);
30253025
if end >= self.v.len() || overflow {
3026-
self.v = &[];
3026+
self.v = &self.v[..0]; // cheaper than &[]
30273027
None
30283028
} else {
30293029
let (fst, _) = self.v.split_at(self.v.len() - end);
@@ -3062,7 +3062,7 @@ impl<'a, T> DoubleEndedIterator for RChunksExact<'a, T> {
30623062
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
30633063
let len = self.len();
30643064
if n >= len {
3065-
self.v = &[];
3065+
self.v = &self.v[..0]; // cheaper than &[]
30663066
None
30673067
} else {
30683068
// now that we know that `n` corresponds to a chunk,

0 commit comments

Comments
 (0)