Skip to content

Commit 7365748

Browse files
committed
Keep track of position when deleting from a BTreeMap
This improves the performance of drain_filter and is needed for future Cursor support for BTreeMap.
1 parent 537ccdf commit 7365748

File tree

2 files changed

+59
-39
lines changed

2 files changed

+59
-39
lines changed

src/liballoc/collections/btree/map.rs

+54-39
Original file line numberDiff line numberDiff line change
@@ -1780,18 +1780,12 @@ where
17801780
where
17811781
F: FnMut(&K, &mut V) -> bool,
17821782
{
1783-
while let Some(kv) = unsafe { self.next_kv() } {
1784-
let (k, v) = unsafe { ptr::read(&kv) }.into_kv_mut();
1783+
while let Some(mut kv) = unsafe { self.next_kv() } {
1784+
let (k, v) = kv.kv_mut();
17851785
if pred(k, v) {
17861786
*self.length -= 1;
17871787
let (k, v, leaf_edge_location) = kv.remove_kv_tracking();
1788-
// `remove_kv_tracking` has either preserved or invalidated `self.cur_leaf_edge`
1789-
if let Some(node) = leaf_edge_location {
1790-
match search::search_tree(node, &k) {
1791-
search::SearchResult::Found(_) => unreachable!(),
1792-
search::SearchResult::GoDown(leaf) => self.cur_leaf_edge = Some(leaf),
1793-
}
1794-
};
1788+
self.cur_leaf_edge = Some(leaf_edge_location);
17951789
return Some((k, v));
17961790
}
17971791
self.cur_leaf_edge = Some(kv.next_leaf_edge());
@@ -2698,79 +2692,99 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> {
26982692

26992693
impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::KV> {
27002694
/// Removes a key/value-pair from the map, and returns that pair, as well as
2701-
/// the whereabouts of the leaf edge corresponding to that former pair:
2702-
/// if None is returned, the leaf edge is still the left leaf edge of the KV handle;
2703-
/// if a node is returned, it heads the subtree where the leaf edge may be found.
2695+
/// the leaf edge corresponding to that former pair.
27042696
fn remove_kv_tracking(
27052697
self,
2706-
) -> (K, V, Option<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>>) {
2707-
let mut levels_down_handled: isize;
2708-
let (small_leaf, old_key, old_val) = match self.force() {
2698+
) -> (K, V, Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>) {
2699+
let (mut pos, old_key, old_val, was_internal) = match self.force() {
27092700
Leaf(leaf) => {
2710-
levels_down_handled = 1; // handled at same level, but affects only the right side
27112701
let (hole, old_key, old_val) = leaf.remove();
2712-
(hole.into_node(), old_key, old_val)
2702+
(hole, old_key, old_val, false)
27132703
}
27142704
Internal(mut internal) => {
27152705
// Replace the location freed in the internal node with the next KV,
27162706
// and remove that next KV from its leaf.
2717-
levels_down_handled = unsafe { ptr::read(&internal).into_node().height() } as isize;
27182707

27192708
let key_loc = internal.kv_mut().0 as *mut K;
27202709
let val_loc = internal.kv_mut().1 as *mut V;
27212710

2722-
let to_remove = internal.right_edge().descend().first_leaf_edge().right_kv().ok();
2711+
// Deleting from the left side is typically faster since we can
2712+
// just pop an element from the end of the KV array without
2713+
// needing to shift the other values.
2714+
let to_remove = internal.left_edge().descend().last_leaf_edge().left_kv().ok();
27232715
let to_remove = unsafe { unwrap_unchecked(to_remove) };
27242716

27252717
let (hole, key, val) = to_remove.remove();
27262718

27272719
let old_key = unsafe { mem::replace(&mut *key_loc, key) };
27282720
let old_val = unsafe { mem::replace(&mut *val_loc, val) };
27292721

2730-
(hole.into_node(), old_key, old_val)
2722+
(hole, old_key, old_val, true)
27312723
}
27322724
};
27332725

27342726
// Handle underflow
2735-
let mut cur_node = small_leaf.forget_type();
2727+
let mut cur_node = unsafe { ptr::read(&pos).into_node().forget_type() };
2728+
let mut at_leaf = true;
27362729
while cur_node.len() < node::MIN_LEN {
27372730
match handle_underfull_node(cur_node) {
2738-
AtRoot(root) => {
2739-
cur_node = root;
2740-
break;
2741-
}
2731+
AtRoot(_) => break,
27422732
EmptyParent(_) => unreachable!(),
2743-
Merged(parent) => {
2744-
levels_down_handled -= 1;
2733+
Merged(edge, merged_with_left, offset) => {
2734+
// If we merged with our right sibling then our tracked
2735+
// position has not changed. However if we merged with our
2736+
// left sibling then our tracked position is now dangling.
2737+
if at_leaf && merged_with_left {
2738+
let idx = pos.idx() + offset;
2739+
let node = match unsafe { ptr::read(&edge).descend().force() } {
2740+
Leaf(leaf) => leaf,
2741+
Internal(_) => unreachable!(),
2742+
};
2743+
debug_assert!(idx <= node.len());
2744+
pos = unsafe { Handle::new_edge(node, idx) };
2745+
}
2746+
2747+
let parent = edge.into_node();
27452748
if parent.len() == 0 {
27462749
// We must be at the root
2747-
let root = parent.into_root_mut();
2748-
root.pop_level();
2749-
cur_node = root.as_mut();
2750+
parent.into_root_mut().pop_level();
27502751
break;
27512752
} else {
27522753
cur_node = parent.forget_type();
2754+
at_leaf = false;
27532755
}
27542756
}
2755-
Stole(internal_node) => {
2756-
levels_down_handled -= 1;
2757-
cur_node = internal_node.forget_type();
2757+
Stole(_, stole_from_left) => {
2758+
// Adjust the tracked position if we stole from a left sibling
2759+
if stole_from_left && at_leaf {
2760+
// SAFETY: This is safe since we just added an element to our node.
2761+
unsafe {
2762+
pos.next_unchecked();
2763+
}
2764+
}
2765+
27582766
// This internal node might be underfull, but only if it's the root.
27592767
break;
27602768
}
27612769
}
27622770
}
27632771

2764-
let leaf_edge_location = if levels_down_handled > 0 { None } else { Some(cur_node) };
2765-
(old_key, old_val, leaf_edge_location)
2772+
// If we deleted from an internal node then we need to compensate for
2773+
// the earlier swap and adjust the tracked position to point to the
2774+
// next element.
2775+
if was_internal {
2776+
pos = unsafe { unwrap_unchecked(pos.next_kv().ok()).next_leaf_edge() };
2777+
}
2778+
2779+
(old_key, old_val, pos)
27662780
}
27672781
}
27682782

27692783
enum UnderflowResult<'a, K, V> {
27702784
AtRoot(NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>),
27712785
EmptyParent(NodeRef<marker::Mut<'a>, K, V, marker::Internal>),
2772-
Merged(NodeRef<marker::Mut<'a>, K, V, marker::Internal>),
2773-
Stole(NodeRef<marker::Mut<'a>, K, V, marker::Internal>),
2786+
Merged(Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::Edge>, bool, usize),
2787+
Stole(NodeRef<marker::Mut<'a>, K, V, marker::Internal>, bool),
27742788
}
27752789

27762790
fn handle_underfull_node<K, V>(
@@ -2792,14 +2806,15 @@ fn handle_underfull_node<K, V>(
27922806
};
27932807

27942808
if handle.can_merge() {
2795-
Merged(handle.merge().into_node())
2809+
let offset = if is_left { handle.reborrow().left_edge().descend().len() + 1 } else { 0 };
2810+
Merged(handle.merge(), is_left, offset)
27962811
} else {
27972812
if is_left {
27982813
handle.steal_left();
27992814
} else {
28002815
handle.steal_right();
28012816
}
2802-
Stole(handle.into_node())
2817+
Stole(handle.into_node(), is_left)
28032818
}
28042819
}
28052820

src/liballoc/collections/btree/node.rs

+5
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,11 @@ impl<Node, Type> Handle<Node, Type> {
723723
pub fn into_node(self) -> Node {
724724
self.node
725725
}
726+
727+
/// Returns the position of this handle in the node.
728+
pub fn idx(&self) -> usize {
729+
self.idx
730+
}
726731
}
727732

728733
impl<BorrowType, K, V, NodeType> Handle<NodeRef<BorrowType, K, V, NodeType>, marker::KV> {

0 commit comments

Comments
 (0)