Skip to content

Commit 3b150b7

Browse files
committed
Auto merge of #81094 - ssomers:btree_drainy_refactor_3, r=Mark-Simulacrum
BTreeMap: split up range_search into two stages `range_search` expects the caller to pass the same root twice and starts searching a node for both bounds of a range. It's not very clear that in the early iterations, it searches twice in the same node. This PR splits that search up in an initial `find_leaf_edges_spanning_range` that postpones aliasing until the last second, and a second phase for continuing the search for the range in the each subtree independently (`find_lower_bound_edge` & `find_upper_bound_edge`), which greatly helps for use in #81075. It also moves those functions over to the search module. r? `@Mark-Simulacrum`
2 parents 43fc1b3 + deebb63 commit 3b150b7

File tree

5 files changed

+228
-118
lines changed

5 files changed

+228
-118
lines changed

library/alloc/src/collections/btree/navigate.rs

+45-97
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
use core::borrow::Borrow;
2-
use core::cmp::Ordering;
3-
use core::ops::Bound::{Excluded, Included, Unbounded};
42
use core::ops::RangeBounds;
53
use core::ptr;
64

75
use super::node::{marker, ForceResult::*, Handle, NodeRef};
8-
use super::search::SearchResult;
96

107
pub struct LeafRange<BorrowType, K, V> {
118
pub front: Option<Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge>>,
@@ -30,100 +27,50 @@ impl<BorrowType, K, V> LeafRange<BorrowType, K, V> {
3027
}
3128
}
3229

33-
/// Finds the leaf edges delimiting a specified range in or underneath a node.
34-
///
35-
/// The result is meaningful only if the tree is ordered by key, like the tree
36-
/// in a `BTreeMap` is.
37-
fn range_search<BorrowType: marker::BorrowType, K, V, Q, R>(
38-
root1: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
39-
root2: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
40-
range: R,
41-
) -> LeafRange<BorrowType, K, V>
42-
where
43-
Q: ?Sized + Ord,
44-
K: Borrow<Q>,
45-
R: RangeBounds<Q>,
46-
{
47-
// WARNING: Inlining these variables would be unsound (#81138)
48-
// We assume the bounds reported by `range` remain the same, but
49-
// an adversarial implementation could change between calls
50-
let start = range.start_bound();
51-
let end = range.end_bound();
52-
match (start, end) {
53-
(Excluded(s), Excluded(e)) if s == e => {
54-
panic!("range start and end are equal and excluded in BTreeMap")
55-
}
56-
(Included(s) | Excluded(s), Included(e) | Excluded(e)) if s > e => {
57-
panic!("range start is greater than range end in BTreeMap")
58-
}
59-
_ => {}
60-
};
61-
62-
let mut min_node = root1;
63-
let mut max_node = root2;
64-
let mut min_found = false;
65-
let mut max_found = false;
66-
67-
loop {
68-
// Using `range` again would be unsound (#81138)
69-
let front = match (min_found, start) {
70-
(false, Included(key)) => match min_node.search_node(key) {
71-
SearchResult::Found(kv) => {
72-
min_found = true;
73-
kv.left_edge()
74-
}
75-
SearchResult::GoDown(edge) => edge,
76-
},
77-
(false, Excluded(key)) => match min_node.search_node(key) {
78-
SearchResult::Found(kv) => {
79-
min_found = true;
80-
kv.right_edge()
81-
}
82-
SearchResult::GoDown(edge) => edge,
83-
},
84-
(true, Included(_)) => min_node.last_edge(),
85-
(true, Excluded(_)) => min_node.first_edge(),
86-
(_, Unbounded) => min_node.first_edge(),
87-
};
88-
89-
// Using `range` again would be unsound (#81138)
90-
let back = match (max_found, end) {
91-
(false, Included(key)) => match max_node.search_node(key) {
92-
SearchResult::Found(kv) => {
93-
max_found = true;
94-
kv.right_edge()
95-
}
96-
SearchResult::GoDown(edge) => edge,
97-
},
98-
(false, Excluded(key)) => match max_node.search_node(key) {
99-
SearchResult::Found(kv) => {
100-
max_found = true;
101-
kv.left_edge()
30+
impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
31+
/// 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.
34+
/// # Safety
35+
/// Unless `BorrowType` is `Immut`, do not use the duplicate handles to
36+
/// visit the same KV twice.
37+
unsafe fn find_leaf_edges_spanning_range<Q: ?Sized, R>(
38+
self,
39+
range: R,
40+
) -> LeafRange<BorrowType, K, V>
41+
where
42+
Q: Ord,
43+
K: Borrow<Q>,
44+
R: RangeBounds<Q>,
45+
{
46+
match self.search_tree_for_bifurcation(&range) {
47+
Err(_) => LeafRange::none(),
48+
Ok((
49+
node,
50+
lower_edge_idx,
51+
upper_edge_idx,
52+
mut lower_child_bound,
53+
mut upper_child_bound,
54+
)) => {
55+
let mut lower_edge = unsafe { Handle::new_edge(ptr::read(&node), lower_edge_idx) };
56+
let mut upper_edge = unsafe { Handle::new_edge(node, upper_edge_idx) };
57+
loop {
58+
match (lower_edge.force(), upper_edge.force()) {
59+
(Leaf(f), Leaf(b)) => return LeafRange { front: Some(f), back: Some(b) },
60+
(Internal(f), Internal(b)) => {
61+
(lower_edge, lower_child_bound) =
62+
f.descend().find_lower_bound_edge(lower_child_bound);
63+
(upper_edge, upper_child_bound) =
64+
b.descend().find_upper_bound_edge(upper_child_bound);
65+
}
66+
_ => unreachable!("BTreeMap has different depths"),
67+
}
10268
}
103-
SearchResult::GoDown(edge) => edge,
104-
},
105-
(true, Included(_)) => max_node.first_edge(),
106-
(true, Excluded(_)) => max_node.last_edge(),
107-
(_, Unbounded) => max_node.last_edge(),
108-
};
109-
110-
if front.partial_cmp(&back) == Some(Ordering::Greater) {
111-
panic!("Ord is ill-defined in BTreeMap range");
112-
}
113-
match (front.force(), back.force()) {
114-
(Leaf(f), Leaf(b)) => {
115-
return LeafRange { front: Some(f), back: Some(b) };
11669
}
117-
(Internal(min_int), Internal(max_int)) => {
118-
min_node = min_int.descend();
119-
max_node = max_int.descend();
120-
}
121-
_ => unreachable!("BTreeMap has different depths"),
122-
};
70+
}
12371
}
12472
}
12573

126-
/// Equivalent to `range_search(root1, root2, ..)` but without the `Ord` bound.
12774
/// Equivalent to `(root1.first_leaf_edge(), root2.last_leaf_edge())` but more efficient.
12875
fn full_range<BorrowType: marker::BorrowType, K, V>(
12976
root1: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
@@ -158,7 +105,8 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal>
158105
K: Borrow<Q>,
159106
R: RangeBounds<Q>,
160107
{
161-
range_search(self, self, range)
108+
// SAFETY: our borrow type is immutable.
109+
unsafe { self.find_leaf_edges_spanning_range(range) }
162110
}
163111

164112
/// Finds the pair of leaf edges delimiting an entire tree.
@@ -174,16 +122,16 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::ValMut<'a>, K, V, marker::LeafOrInternal>
174122
///
175123
/// The result is meaningful only if the tree is ordered by key, like the tree
176124
/// in a `BTreeMap` is.
125+
///
126+
/// # Safety
127+
/// Do not use the duplicate handles to visit the same KV twice.
177128
pub fn range_search<Q, R>(self, range: R) -> LeafRange<marker::ValMut<'a>, K, V>
178129
where
179130
Q: ?Sized + Ord,
180131
K: Borrow<Q>,
181132
R: RangeBounds<Q>,
182133
{
183-
// We duplicate the root NodeRef here -- we will never visit the same KV
184-
// twice, and never end up with overlapping value references.
185-
let self2 = unsafe { ptr::read(&self) };
186-
range_search(self, self2, range)
134+
unsafe { self.find_leaf_edges_spanning_range(range) }
187135
}
188136

189137
/// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree.

library/alloc/src/collections/btree/node.rs

-10
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
// since leaf edges are empty and need no data representation. In an internal node,
3232
// an edge both identifies a position and contains a pointer to a child node.
3333

34-
use core::cmp::Ordering;
3534
use core::marker::PhantomData;
3635
use core::mem::{self, MaybeUninit};
3736
use core::ptr::{self, NonNull};
@@ -742,15 +741,6 @@ impl<BorrowType, K, V, NodeType, HandleType> PartialEq
742741
}
743742
}
744743

745-
impl<BorrowType, K, V, NodeType, HandleType> PartialOrd
746-
for Handle<NodeRef<BorrowType, K, V, NodeType>, HandleType>
747-
{
748-
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
749-
let Self { node, idx, _marker } = self;
750-
if node.eq(&other.node) { Some(idx.cmp(&other.idx)) } else { None }
751-
}
752-
}
753-
754744
impl<BorrowType, K, V, NodeType, HandleType>
755745
Handle<NodeRef<BorrowType, K, V, NodeType>, HandleType>
756746
{

library/alloc/src/collections/btree/node/tests.rs

+1-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use super::super::navigate;
22
use super::*;
33
use crate::fmt::Debug;
44
use crate::string::String;
5-
use core::cmp::Ordering::*;
65

76
impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> {
87
// Asserts that the back pointer in each reachable node points to its parent.
@@ -67,7 +66,7 @@ fn test_splitpoint() {
6766
}
6867

6968
#[test]
70-
fn test_partial_cmp_eq() {
69+
fn test_partial_eq() {
7170
let mut root1 = NodeRef::new_leaf();
7271
root1.borrow_mut().push(1, ());
7372
let mut root1 = NodeRef::new_internal(root1.forget_type()).forget_type();
@@ -87,13 +86,6 @@ fn test_partial_cmp_eq() {
8786
assert!(top_edge_1 == top_edge_1);
8887
assert!(top_edge_1 != top_edge_2);
8988

90-
assert_eq!(leaf_edge_1a.partial_cmp(&leaf_edge_1a), Some(Equal));
91-
assert_eq!(leaf_edge_1a.partial_cmp(&leaf_edge_1b), Some(Less));
92-
assert_eq!(leaf_edge_1a.partial_cmp(&top_edge_1), None);
93-
assert_eq!(leaf_edge_1a.partial_cmp(&top_edge_2), None);
94-
assert_eq!(top_edge_1.partial_cmp(&top_edge_1), Some(Equal));
95-
assert_eq!(top_edge_1.partial_cmp(&top_edge_2), None);
96-
9789
root1.pop_internal_level();
9890
unsafe { root1.into_dying().deallocate_and_ascend() };
9991
unsafe { root2.into_dying().deallocate_and_ascend() };

0 commit comments

Comments
 (0)