Skip to content

Commit a545bb3

Browse files
committed
Improve slice::binary_search_by
1 parent 4bc39f0 commit a545bb3

File tree

2 files changed

+16
-14
lines changed

2 files changed

+16
-14
lines changed

library/core/src/slice/mod.rs

+15-13
Original file line numberDiff line numberDiff line change
@@ -2787,14 +2787,16 @@ impl<T> [T] {
27872787
where
27882788
F: FnMut(&'a T) -> Ordering,
27892789
{
2790+
if self.len() == 0 {
2791+
return Err(0);
2792+
}
27902793
// INVARIANTS:
2791-
// - 0 <= left <= left + size = right <= self.len()
2794+
// - 0 <= left <= left + size <= self.len()
27922795
// - f returns Less for everything in self[..left]
2793-
// - f returns Greater for everything in self[right..]
2796+
// - f returns Greater for everything in self[left + size..]
27942797
let mut size = self.len();
27952798
let mut left = 0;
2796-
let mut right = size;
2797-
while left < right {
2799+
while size > 1 {
27982800
let mid = left + size / 2;
27992801

28002802
// SAFETY: the while condition means `size` is strictly positive, so
@@ -2807,21 +2809,21 @@ impl<T> [T] {
28072809
// fewer branches and instructions than if/else or matching on
28082810
// cmp::Ordering.
28092811
// This is x86 asm for u8: https://rust.godbolt.org/z/698eYffTx.
2810-
left = if cmp == Less { mid + 1 } else { left };
2811-
right = if cmp == Greater { mid } else { right };
2812+
2813+
left = if cmp == Less { mid } else { left };
2814+
size = if cmp == Greater { size / 2 } else { size - size / 2 };
28122815
if cmp == Equal {
28132816
// SAFETY: same as the `get_unchecked` above
28142817
unsafe { hint::assert_unchecked(mid < self.len()) };
28152818
return Ok(mid);
28162819
}
2817-
2818-
size = right - left;
28192820
}
2820-
2821-
// SAFETY: directly true from the overall invariant.
2822-
// Note that this is `<=`, unlike the assume in the `Ok` path.
2823-
unsafe { hint::assert_unchecked(left <= self.len()) };
2824-
Err(left)
2821+
// SAFETY: allowed per the invariants
2822+
let cmp = f(unsafe { self.get_unchecked(left) });
2823+
let res_idx = if cmp == Less { left + 1 } else { left };
2824+
// SAFETY: invariants guarantee that this is in bounds
2825+
unsafe { hint::assert_unchecked(res_idx <= self.len()) };
2826+
if cmp == Equal { Ok(res_idx) } else { Err(res_idx) }
28252827
}
28262828

28272829
/// Binary searches this slice with a key extraction function.

library/core/tests/slice.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ fn test_binary_search_implementation_details() {
9090
assert_eq!(b.binary_search(&3), Ok(5));
9191
let b = [1, 1, 1, 1, 1, 3, 3, 3, 3];
9292
assert_eq!(b.binary_search(&1), Ok(4));
93-
assert_eq!(b.binary_search(&3), Ok(7));
93+
assert_eq!(b.binary_search(&3), Ok(6));
9494
let b = [1, 1, 1, 1, 3, 3, 3, 3, 3];
9595
assert_eq!(b.binary_search(&1), Ok(2));
9696
assert_eq!(b.binary_search(&3), Ok(4));

0 commit comments

Comments
 (0)