Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f5230fb

Browse files
committedNov 16, 2020
Auto merge of rust-lang#78631 - ssomers:btree-alias_for_underfull, r=Mark-Simulacrum
BTreeMap: fix pointer provenance rules in underfullness Continuing on rust-lang#78480, and for readability, and possibly for performance: avoid aliasing when handling underfull nodes, and consolidate the code doing that. In particular: - Avoid the rather explicit aliasing for internal nodes in `remove_kv_tracking`. - Climb down to the root to handle underfull nodes using a reborrowed handle, rather than one copied with `ptr::read`, before resuming on the leaf level. - Integrate the code tracking leaf edge position into the functions performing changes, rather than bolting it on. r? `@Mark-Simulacrum`
2 parents f4d014c + 4cfa5bd commit f5230fb

File tree

7 files changed

+320
-203
lines changed

7 files changed

+320
-203
lines changed
 

‎library/alloc/src/collections/btree/append.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,15 @@ impl<K, V> Root<K, V> {
8989
let mut cur_node = self.node_as_mut();
9090
while let Internal(internal) = cur_node.force() {
9191
// Check if right-most child is underfull.
92-
let mut last_edge = internal.last_edge();
93-
let right_child_len = last_edge.reborrow().descend().len();
92+
let mut last_kv = internal.last_kv().consider_for_balancing();
93+
let right_child_len = last_kv.right_child_len();
9494
if right_child_len < MIN_LEN {
9595
// We need to steal.
96-
let mut last_kv = match last_edge.left_kv() {
97-
Ok(left) => left,
98-
Err(_) => unreachable!(),
99-
};
10096
last_kv.bulk_steal_left(MIN_LEN - right_child_len);
101-
last_edge = last_kv.right_edge();
10297
}
10398

10499
// Go further down.
105-
cur_node = last_edge.descend();
100+
cur_node = last_kv.into_right_child();
106101
}
107102
}
108103
}

‎library/alloc/src/collections/btree/mem.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use core::ptr;
66
/// relevant function.
77
///
88
/// If a panic occurs in the `change` closure, the entire process will be aborted.
9+
#[allow(dead_code)] // keep as illustration and for future use
910
#[inline]
1011
pub fn take_mut<T>(v: &mut T, change: impl FnOnce(T) -> T) {
1112
replace(v, |value| (change(value), ()))

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

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -362,20 +362,6 @@ impl<'a, K, V> Handle<NodeRef<marker::ValMut<'a>, K, V, marker::Leaf>, marker::E
362362
}
363363
}
364364

365-
impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge> {
366-
/// Moves the leaf edge handle to the next leaf edge.
367-
///
368-
/// # Safety
369-
/// There must be another KV in the direction travelled.
370-
pub unsafe fn move_next_unchecked(&mut self) {
371-
super::mem::take_mut(self, |leaf_edge| {
372-
let kv = leaf_edge.next_kv();
373-
let kv = unsafe { unwrap_unchecked(kv.ok()) };
374-
kv.next_leaf_edge()
375-
})
376-
}
377-
}
378-
379365
impl<K, V> Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge> {
380366
/// Moves the leaf edge handle to the next leaf edge and returns the key and value
381367
/// in between, deallocating any node left behind while leaving the corresponding

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

Lines changed: 181 additions & 66 deletions
Large diffs are not rendered by default.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ fn test_splitpoint() {
5454
let mut left_len = middle_kv_idx;
5555
let mut right_len = CAPACITY - middle_kv_idx - 1;
5656
match insertion {
57-
InsertionPlace::Left(edge_idx) => {
57+
LeftOrRight::Left(edge_idx) => {
5858
assert!(edge_idx <= left_len);
5959
left_len += 1;
6060
}
61-
InsertionPlace::Right(edge_idx) => {
61+
LeftOrRight::Right(edge_idx) => {
6262
assert!(edge_idx <= right_len);
6363
right_len += 1;
6464
}
Lines changed: 125 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,153 @@
11
use super::map::MIN_LEN;
2-
use super::node::{marker, ForceResult, Handle, NodeRef};
2+
use super::node::{marker, ForceResult::*, Handle, LeftOrRight::*, NodeRef};
33
use super::unwrap_unchecked;
44
use core::mem;
5-
use core::ptr;
65

76
impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::KV> {
8-
/// Removes a key/value-pair from the map, and returns that pair, as well as
9-
/// the leaf edge corresponding to that former pair.
7+
/// Removes a key/value-pair from the tree, and returns that pair, as well as
8+
/// the leaf edge corresponding to that former pair. It's possible this empties
9+
/// a root node that is internal, which the caller should pop from the map
10+
/// holding the tree. The caller should also decrement the map's length.
1011
pub fn remove_kv_tracking<F: FnOnce()>(
1112
self,
1213
handle_emptied_internal_root: F,
1314
) -> ((K, V), Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>) {
14-
let (old_kv, mut pos, was_internal) = match self.force() {
15-
ForceResult::Leaf(leaf) => {
16-
let (old_kv, pos) = leaf.remove();
17-
(old_kv, pos, false)
18-
}
19-
ForceResult::Internal(mut internal) => {
20-
// Replace the location freed in the internal node with an
21-
// adjacent KV, and remove that adjacent KV from its leaf.
22-
// Always choose the adjacent KV on the left side because
23-
// it is typically faster to pop an element from the end
24-
// of the KV arrays without needing to shift other elements.
25-
26-
let key_loc = internal.kv_mut().0 as *mut K;
27-
let val_loc = internal.kv_mut().1 as *mut V;
28-
29-
let to_remove = internal.left_edge().descend().last_leaf_edge().left_kv().ok();
30-
let to_remove = unsafe { unwrap_unchecked(to_remove) };
31-
32-
let (kv, pos) = to_remove.remove();
33-
34-
let old_key = unsafe { mem::replace(&mut *key_loc, kv.0) };
35-
let old_val = unsafe { mem::replace(&mut *val_loc, kv.1) };
36-
37-
((old_key, old_val), pos, true)
38-
}
39-
};
40-
41-
// Handle underflow
42-
let mut cur_node = unsafe { ptr::read(&pos).into_node().forget_type() };
43-
let mut at_leaf = true;
44-
while cur_node.len() < MIN_LEN {
45-
match handle_underfull_node(cur_node) {
46-
UnderflowResult::AtRoot => break,
47-
UnderflowResult::Merged(edge, merged_with_left, offset) => {
48-
// If we merged with our right sibling then our tracked
49-
// position has not changed. However if we merged with our
50-
// left sibling then our tracked position is now dangling.
51-
if at_leaf && merged_with_left {
52-
let idx = pos.idx() + offset;
53-
let node = match unsafe { ptr::read(&edge).descend().force() } {
54-
ForceResult::Leaf(leaf) => leaf,
55-
ForceResult::Internal(_) => unreachable!(),
56-
};
57-
pos = unsafe { Handle::new_edge(node, idx) };
58-
}
15+
match self.force() {
16+
Leaf(node) => node.remove_leaf_kv(handle_emptied_internal_root),
17+
Internal(node) => node.remove_internal_kv(handle_emptied_internal_root),
18+
}
19+
}
20+
}
5921

60-
let parent = edge.into_node();
61-
if parent.len() == 0 {
62-
// The parent that was just emptied must be the root,
63-
// because nodes on a lower level would not have been
64-
// left with a single child.
65-
handle_emptied_internal_root();
66-
break;
22+
impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::KV> {
23+
fn remove_leaf_kv<F: FnOnce()>(
24+
self,
25+
handle_emptied_internal_root: F,
26+
) -> ((K, V), Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>) {
27+
let (old_kv, mut pos) = self.remove();
28+
let len = pos.reborrow().into_node().len();
29+
if len < MIN_LEN {
30+
let idx = pos.idx();
31+
// We have to temporarily forget the child type, because there is no
32+
// distinct node type for the immediate parents of a leaf.
33+
let new_pos = match pos.into_node().forget_type().choose_parent_kv() {
34+
Ok(Left(left_parent_kv)) => {
35+
debug_assert!(left_parent_kv.right_child_len() == MIN_LEN - 1);
36+
if left_parent_kv.can_merge() {
37+
left_parent_kv.merge(Some(Right(idx)))
6738
} else {
68-
cur_node = parent.forget_type();
69-
at_leaf = false;
39+
debug_assert!(left_parent_kv.left_child_len() > MIN_LEN);
40+
left_parent_kv.steal_left(idx)
7041
}
7142
}
72-
UnderflowResult::Stole(stole_from_left) => {
73-
// Adjust the tracked position if we stole from a left sibling
74-
if stole_from_left && at_leaf {
75-
// SAFETY: This is safe since we just added an element to our node.
76-
unsafe {
77-
pos.move_next_unchecked();
78-
}
43+
Ok(Right(right_parent_kv)) => {
44+
debug_assert!(right_parent_kv.left_child_len() == MIN_LEN - 1);
45+
if right_parent_kv.can_merge() {
46+
right_parent_kv.merge(Some(Left(idx)))
47+
} else {
48+
debug_assert!(right_parent_kv.right_child_len() > MIN_LEN);
49+
right_parent_kv.steal_right(idx)
7950
}
80-
break;
8151
}
82-
}
83-
}
52+
Err(pos) => unsafe { Handle::new_edge(pos, idx) },
53+
};
54+
// SAFETY: `new_pos` is the leaf we started from or a sibling.
55+
pos = unsafe { new_pos.cast_to_leaf_unchecked() };
8456

85-
// If we deleted from an internal node then we need to compensate for
86-
// the earlier swap and adjust the tracked position to point to the
87-
// next element.
88-
if was_internal {
89-
pos = unsafe { unwrap_unchecked(pos.next_kv().ok()).next_leaf_edge() };
57+
// Only if we merged, the parent (if any) has shrunk, but skipping
58+
// the following step does not pay off in benchmarks.
59+
//
60+
// SAFETY: We won't destroy or rearrange the leaf where `pos` is at
61+
// by handling its parent recursively; at worst we will destroy or
62+
// rearrange the parent through the grandparent, thus change the
63+
// leaf's parent pointer.
64+
if let Ok(parent) = unsafe { pos.reborrow_mut() }.into_node().ascend() {
65+
parent.into_node().handle_shrunk_node_recursively(handle_emptied_internal_root);
66+
}
9067
}
91-
9268
(old_kv, pos)
9369
}
9470
}
9571

96-
enum UnderflowResult<'a, K, V> {
97-
AtRoot,
98-
Merged(Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::Edge>, bool, usize),
99-
Stole(bool),
100-
}
72+
impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::KV> {
73+
fn remove_internal_kv<F: FnOnce()>(
74+
self,
75+
handle_emptied_internal_root: F,
76+
) -> ((K, V), Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>) {
77+
// Remove an adjacent KV from its leaf and then put it back in place of
78+
// the element we were asked to remove. Prefer the left adjacent KV,
79+
// for the reasons listed in `choose_parent_kv`.
80+
let left_leaf_kv = self.left_edge().descend().last_leaf_edge().left_kv();
81+
let left_leaf_kv = unsafe { unwrap_unchecked(left_leaf_kv.ok()) };
82+
let (left_kv, left_hole) = left_leaf_kv.remove_leaf_kv(handle_emptied_internal_root);
10183

102-
fn handle_underfull_node<'a, K: 'a, V: 'a>(
103-
node: NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>,
104-
) -> UnderflowResult<'_, K, V> {
105-
let parent = match node.ascend() {
106-
Ok(parent) => parent,
107-
Err(_) => return UnderflowResult::AtRoot,
108-
};
84+
// The internal node may have been stolen from or merged. Go back right
85+
// to find where the original KV ended up.
86+
let mut internal = unsafe { unwrap_unchecked(left_hole.next_kv().ok()) };
87+
let old_key = mem::replace(internal.kv_mut().0, left_kv.0);
88+
let old_val = mem::replace(internal.kv_mut().1, left_kv.1);
89+
let pos = internal.next_leaf_edge();
90+
((old_key, old_val), pos)
91+
}
92+
}
10993

110-
// Prefer the left KV if it exists. Merging with the left side is faster,
111-
// since merging happens towards the left and `node` has fewer elements.
112-
// Stealing from the left side is faster, since we can pop from the end of
113-
// the KV arrays.
114-
let (is_left, mut handle) = match parent.left_kv() {
115-
Ok(left) => (true, left),
116-
Err(parent) => {
117-
let right = unsafe { unwrap_unchecked(parent.right_kv().ok()) };
118-
(false, right)
94+
impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
95+
/// Stocks up a possibly underfull internal node, recursively.
96+
/// Climbs up until it reaches an ancestor that has elements to spare or the root.
97+
fn handle_shrunk_node_recursively<F: FnOnce()>(mut self, handle_emptied_internal_root: F) {
98+
loop {
99+
self = match self.len() {
100+
0 => {
101+
// An empty node must be the root, because length is only
102+
// reduced by one, and non-root underfull nodes are stocked up,
103+
// so non-root nodes never have fewer than MIN_LEN - 1 elements.
104+
debug_assert!(self.ascend().is_err());
105+
handle_emptied_internal_root();
106+
return;
107+
}
108+
1..MIN_LEN => {
109+
if let Some(parent) = self.handle_underfull_node_locally() {
110+
parent
111+
} else {
112+
return;
113+
}
114+
}
115+
_ => return,
116+
}
119117
}
120-
};
118+
}
121119

122-
if handle.can_merge() {
123-
let offset = if is_left { handle.reborrow().left_edge().descend().len() + 1 } else { 0 };
124-
UnderflowResult::Merged(handle.merge(), is_left, offset)
125-
} else {
126-
if is_left {
127-
handle.steal_left();
128-
} else {
129-
handle.steal_right();
120+
/// Stocks up an underfull internal node, possibly at the cost of shrinking
121+
/// its parent instead, which is then returned.
122+
fn handle_underfull_node_locally(
123+
self,
124+
) -> Option<NodeRef<marker::Mut<'a>, K, V, marker::Internal>> {
125+
match self.forget_type().choose_parent_kv() {
126+
Ok(Left(left_parent_kv)) => {
127+
debug_assert!(left_parent_kv.right_child_len() == MIN_LEN - 1);
128+
if left_parent_kv.can_merge() {
129+
let pos = left_parent_kv.merge(None);
130+
let parent_edge = unsafe { unwrap_unchecked(pos.into_node().ascend().ok()) };
131+
Some(parent_edge.into_node())
132+
} else {
133+
debug_assert!(left_parent_kv.left_child_len() > MIN_LEN);
134+
left_parent_kv.steal_left(0);
135+
None
136+
}
137+
}
138+
Ok(Right(right_parent_kv)) => {
139+
debug_assert!(right_parent_kv.left_child_len() == MIN_LEN - 1);
140+
if right_parent_kv.can_merge() {
141+
let pos = right_parent_kv.merge(None);
142+
let parent_edge = unsafe { unwrap_unchecked(pos.into_node().ascend().ok()) };
143+
Some(parent_edge.into_node())
144+
} else {
145+
debug_assert!(right_parent_kv.right_child_len() > MIN_LEN);
146+
right_parent_kv.steal_right(0);
147+
None
148+
}
149+
}
150+
Err(_) => None,
130151
}
131-
UnderflowResult::Stole(is_left)
132152
}
133153
}

‎library/alloc/src/collections/btree/split.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,17 @@ impl<K, V> Root<K, V> {
6060
let mut cur_node = self.node_as_mut();
6161

6262
while let Internal(node) = cur_node.force() {
63-
let mut last_kv = node.last_kv();
63+
let mut last_kv = node.last_kv().consider_for_balancing();
6464

6565
if last_kv.can_merge() {
66-
cur_node = last_kv.merge().descend();
66+
cur_node = last_kv.merge(None).into_node();
6767
} else {
68-
let right_len = last_kv.reborrow().right_edge().descend().len();
68+
let right_len = last_kv.right_child_len();
6969
// `MIN_LEN + 1` to avoid readjust if merge happens on the next level.
7070
if right_len < MIN_LEN + 1 {
7171
last_kv.bulk_steal_left(MIN_LEN + 1 - right_len);
7272
}
73-
cur_node = last_kv.right_edge().descend();
73+
cur_node = last_kv.into_right_child();
7474
}
7575
}
7676
}
@@ -86,17 +86,17 @@ impl<K, V> Root<K, V> {
8686
let mut cur_node = self.node_as_mut();
8787

8888
while let Internal(node) = cur_node.force() {
89-
let mut first_kv = node.first_kv();
89+
let mut first_kv = node.first_kv().consider_for_balancing();
9090

9191
if first_kv.can_merge() {
92-
cur_node = first_kv.merge().descend();
92+
cur_node = first_kv.merge(None).into_node();
9393
} else {
94-
let left_len = first_kv.reborrow().left_edge().descend().len();
94+
let left_len = first_kv.left_child_len();
9595
// `MIN_LEN + 1` to avoid readjust if merge happens on the next level.
9696
if left_len < MIN_LEN + 1 {
9797
first_kv.bulk_steal_right(MIN_LEN + 1 - left_len);
9898
}
99-
cur_node = first_kv.left_edge().descend();
99+
cur_node = first_kv.into_left_child();
100100
}
101101
}
102102
}

0 commit comments

Comments
 (0)
This repository has been archived.