Skip to content

Commit af9862b

Browse files
committed
Auto merge of rust-lang#83267 - ssomers:btree_prune_range_search_overlap, r=Mark-Simulacrum
BTree: no longer search arrays twice to check Ord A possible addition to / partial replacement of rust-lang#83147: no longer linearly search the upper bound of a range in the initial portion of the keys we already know are below the lower bound. - Should be faster: fewer key comparisons at the cost of some instructions dealing with offsets - Makes code a little more complicated. - No longer detects ill-defined `Ord` implementations, but that wasn't a publicised feature, and was quite incomplete, and was only done in the `range` and `range_mut` methods. r? `@Mark-Simulacrum`
2 parents d8796dc + cfced5f commit af9862b

File tree

3 files changed

+43
-27
lines changed

3 files changed

+43
-27
lines changed

alloc/src/collections/btree/map/tests.rs

-2
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,6 @@ fn test_range_backwards_4() {
776776
}
777777

778778
#[test]
779-
#[should_panic]
780779
fn test_range_finding_ill_order_in_map() {
781780
let mut map = BTreeMap::new();
782781
map.insert(Cyclic3::B, ());
@@ -789,7 +788,6 @@ fn test_range_finding_ill_order_in_map() {
789788
}
790789

791790
#[test]
792-
#[should_panic]
793791
fn test_range_finding_ill_order_in_range_ord() {
794792
// Has proper order the first time asked, then flips around.
795793
struct EvilTwin(i32);

alloc/src/collections/btree/navigate.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,18 @@ impl<BorrowType, K, V> LeafRange<BorrowType, K, V> {
2929

3030
impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
3131
/// Finds the distinct leaf edges delimiting a specified range in a tree.
32-
/// Returns either a pair of different handles into the same tree or a pair
33-
/// of empty options.
32+
///
33+
/// If such distinct edges exist, returns them in ascending order, meaning
34+
/// that a non-zero number of calls to `next_unchecked` on the `front` of
35+
/// the result and/or calls to `next_back_unchecked` on the `back` of the
36+
/// result will eventually reach the same edge.
37+
///
38+
/// If there are no such edges, i.e., if the tree contains no key within
39+
/// the range, returns a pair of empty options.
40+
///
3441
/// # Safety
35-
/// Unless `BorrowType` is `Immut`, do not use the duplicate handles to
36-
/// visit the same KV twice.
42+
/// Unless `BorrowType` is `Immut`, do not use the handles to visit the same
43+
/// KV twice.
3744
unsafe fn find_leaf_edges_spanning_range<Q: ?Sized, R>(
3845
self,
3946
range: R,

alloc/src/collections/btree/search.rs

+32-21
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,16 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea
6868
/// of the range is different from the edge matching the upper bound, i.e.,
6969
/// the nearest node that has at least one key contained in the range.
7070
///
71-
/// If found, returns an `Ok` with that node, the pair of edge indices in it
72-
/// delimiting the range, and the corresponding pair of bounds for
73-
/// continuing the search in the child nodes, in case the node is internal.
71+
/// If found, returns an `Ok` with that node, the strictly ascending pair of
72+
/// edge indices in the node delimiting the range, and the corresponding
73+
/// pair of bounds for continuing the search in the child nodes, in case
74+
/// the node is internal.
7475
///
7576
/// If not found, returns an `Err` with the leaf edge matching the entire
7677
/// range.
7778
///
79+
/// As a diagnostic service, panics if the range specifies impossible bounds.
80+
///
7881
/// The result is meaningful only if the tree is ordered by key.
7982
pub fn search_tree_for_bifurcation<'r, Q: ?Sized, R>(
8083
mut self,
@@ -112,10 +115,8 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea
112115
let mut upper_bound = SearchBound::from_range(end);
113116
loop {
114117
let (lower_edge_idx, lower_child_bound) = self.find_lower_bound_index(lower_bound);
115-
let (upper_edge_idx, upper_child_bound) = self.find_upper_bound_index(upper_bound);
116-
if lower_edge_idx > upper_edge_idx {
117-
panic!("Ord is ill-defined in BTreeMap range")
118-
}
118+
let (upper_edge_idx, upper_child_bound) =
119+
unsafe { self.find_upper_bound_index(upper_bound, lower_edge_idx) };
119120
if lower_edge_idx < upper_edge_idx {
120121
return Ok((
121122
self,
@@ -125,6 +126,7 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea
125126
upper_child_bound,
126127
));
127128
}
129+
debug_assert_eq!(lower_edge_idx, upper_edge_idx);
128130
let common_edge = unsafe { Handle::new_edge(self, lower_edge_idx) };
129131
match common_edge.force() {
130132
Leaf(common_edge) => return Err(common_edge),
@@ -164,7 +166,7 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea
164166
Q: ?Sized + Ord,
165167
K: Borrow<Q>,
166168
{
167-
let (edge_idx, bound) = self.find_upper_bound_index(bound);
169+
let (edge_idx, bound) = unsafe { self.find_upper_bound_index(bound, 0) };
168170
let edge = unsafe { Handle::new_edge(self, edge_idx) };
169171
(edge, bound)
170172
}
@@ -183,29 +185,33 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
183185
Q: Ord,
184186
K: Borrow<Q>,
185187
{
186-
match self.find_key_index(key) {
188+
match unsafe { self.find_key_index(key, 0) } {
187189
IndexResult::KV(idx) => Found(unsafe { Handle::new_kv(self, idx) }),
188190
IndexResult::Edge(idx) => GoDown(unsafe { Handle::new_edge(self, idx) }),
189191
}
190192
}
191193

192194
/// Returns either the KV index in the node at which the key (or an equivalent)
193-
/// exists, or the edge index where the key belongs.
195+
/// exists, or the edge index where the key belongs, starting from a particular index.
194196
///
195197
/// The result is meaningful only if the tree is ordered by key, like the tree
196198
/// in a `BTreeMap` is.
197-
fn find_key_index<Q: ?Sized>(&self, key: &Q) -> IndexResult
199+
///
200+
/// # Safety
201+
/// `start_index` must be a valid edge index for the node.
202+
unsafe fn find_key_index<Q: ?Sized>(&self, key: &Q, start_index: usize) -> IndexResult
198203
where
199204
Q: Ord,
200205
K: Borrow<Q>,
201206
{
202207
let node = self.reborrow();
203208
let keys = node.keys();
204-
for (i, k) in keys.iter().enumerate() {
209+
debug_assert!(start_index <= keys.len());
210+
for (offset, k) in unsafe { keys.get_unchecked(start_index..) }.iter().enumerate() {
205211
match key.cmp(k.borrow()) {
206212
Ordering::Greater => {}
207-
Ordering::Equal => return IndexResult::KV(i),
208-
Ordering::Less => return IndexResult::Edge(i),
213+
Ordering::Equal => return IndexResult::KV(start_index + offset),
214+
Ordering::Less => return IndexResult::Edge(start_index + offset),
209215
}
210216
}
211217
IndexResult::Edge(keys.len())
@@ -225,11 +231,11 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
225231
K: Borrow<Q>,
226232
{
227233
match bound {
228-
Included(key) => match self.find_key_index(key) {
234+
Included(key) => match unsafe { self.find_key_index(key, 0) } {
229235
IndexResult::KV(idx) => (idx, AllExcluded),
230236
IndexResult::Edge(idx) => (idx, bound),
231237
},
232-
Excluded(key) => match self.find_key_index(key) {
238+
Excluded(key) => match unsafe { self.find_key_index(key, 0) } {
233239
IndexResult::KV(idx) => (idx + 1, AllIncluded),
234240
IndexResult::Edge(idx) => (idx, bound),
235241
},
@@ -238,26 +244,31 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
238244
}
239245
}
240246

241-
/// Clone of `find_lower_bound_index` for the upper bound.
242-
fn find_upper_bound_index<'r, Q>(
247+
/// Mirror image of `find_lower_bound_index` for the upper bound,
248+
/// with an additional parameter to skip part of the key array.
249+
///
250+
/// # Safety
251+
/// `start_index` must be a valid edge index for the node.
252+
unsafe fn find_upper_bound_index<'r, Q>(
243253
&self,
244254
bound: SearchBound<&'r Q>,
255+
start_index: usize,
245256
) -> (usize, SearchBound<&'r Q>)
246257
where
247258
Q: ?Sized + Ord,
248259
K: Borrow<Q>,
249260
{
250261
match bound {
251-
Included(key) => match self.find_key_index(key) {
262+
Included(key) => match unsafe { self.find_key_index(key, start_index) } {
252263
IndexResult::KV(idx) => (idx + 1, AllExcluded),
253264
IndexResult::Edge(idx) => (idx, bound),
254265
},
255-
Excluded(key) => match self.find_key_index(key) {
266+
Excluded(key) => match unsafe { self.find_key_index(key, start_index) } {
256267
IndexResult::KV(idx) => (idx, AllIncluded),
257268
IndexResult::Edge(idx) => (idx, bound),
258269
},
259270
AllIncluded => (self.len(), AllIncluded),
260-
AllExcluded => (0, AllExcluded),
271+
AllExcluded => (start_index, AllExcluded),
261272
}
262273
}
263274
}

0 commit comments

Comments
 (0)