-
Notifications
You must be signed in to change notification settings - Fork 12.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
BTreeMap: split off most code of remove and split_off
- Loading branch information
Showing
4 changed files
with
239 additions
and
222 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
use super::node::{self, marker, ForceResult, Handle, NodeRef}; | ||
use super::unwrap_unchecked; | ||
use core::mem; | ||
use core::ptr; | ||
|
||
impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::KV> { | ||
/// Removes a key/value-pair from the map, and returns that pair, as well as | ||
/// the leaf edge corresponding to that former pair. | ||
pub fn remove_kv_tracking<F: FnOnce()>( | ||
self, | ||
handle_emptied_internal_root: F, | ||
) -> ((K, V), Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>) { | ||
let (old_kv, mut pos, was_internal) = match self.force() { | ||
ForceResult::Leaf(leaf) => { | ||
let (old_kv, pos) = leaf.remove(); | ||
(old_kv, pos, false) | ||
} | ||
ForceResult::Internal(mut internal) => { | ||
// Replace the location freed in the internal node with an | ||
// adjacent KV, and remove that adjacent KV from its leaf. | ||
// Always choose the adjacent KV on the left side because | ||
// it is typically faster to pop an element from the end | ||
// of the KV arrays without needing to shift other elements. | ||
|
||
let key_loc = internal.kv_mut().0 as *mut K; | ||
let val_loc = internal.kv_mut().1 as *mut V; | ||
|
||
let to_remove = internal.left_edge().descend().last_leaf_edge().left_kv().ok(); | ||
let to_remove = unsafe { unwrap_unchecked(to_remove) }; | ||
|
||
let (kv, pos) = to_remove.remove(); | ||
|
||
let old_key = unsafe { mem::replace(&mut *key_loc, kv.0) }; | ||
let old_val = unsafe { mem::replace(&mut *val_loc, kv.1) }; | ||
|
||
((old_key, old_val), pos, true) | ||
} | ||
}; | ||
|
||
// Handle underflow | ||
let mut cur_node = unsafe { ptr::read(&pos).into_node().forget_type() }; | ||
let mut at_leaf = true; | ||
while cur_node.len() < node::MIN_LEN { | ||
match handle_underfull_node(cur_node) { | ||
UnderflowResult::AtRoot => break, | ||
UnderflowResult::Merged(edge, merged_with_left, offset) => { | ||
// If we merged with our right sibling then our tracked | ||
// position has not changed. However if we merged with our | ||
// left sibling then our tracked position is now dangling. | ||
if at_leaf && merged_with_left { | ||
let idx = pos.idx() + offset; | ||
let node = match unsafe { ptr::read(&edge).descend().force() } { | ||
ForceResult::Leaf(leaf) => leaf, | ||
ForceResult::Internal(_) => unreachable!(), | ||
}; | ||
pos = unsafe { Handle::new_edge(node, idx) }; | ||
} | ||
|
||
let parent = edge.into_node(); | ||
if parent.len() == 0 { | ||
// The parent that was just emptied must be the root, | ||
// because nodes on a lower level would not have been | ||
// left with a single child. | ||
handle_emptied_internal_root(); | ||
break; | ||
} else { | ||
cur_node = parent.forget_type(); | ||
at_leaf = false; | ||
} | ||
} | ||
UnderflowResult::Stole(stole_from_left) => { | ||
// Adjust the tracked position if we stole from a left sibling | ||
if stole_from_left && at_leaf { | ||
// SAFETY: This is safe since we just added an element to our node. | ||
unsafe { | ||
pos.move_next_unchecked(); | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
|
||
// If we deleted from an internal node then we need to compensate for | ||
// the earlier swap and adjust the tracked position to point to the | ||
// next element. | ||
if was_internal { | ||
pos = unsafe { unwrap_unchecked(pos.next_kv().ok()).next_leaf_edge() }; | ||
} | ||
|
||
(old_kv, pos) | ||
} | ||
} | ||
|
||
enum UnderflowResult<'a, K, V> { | ||
AtRoot, | ||
Merged(Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::Edge>, bool, usize), | ||
Stole(bool), | ||
} | ||
|
||
fn handle_underfull_node<'a, K: 'a, V: 'a>( | ||
node: NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, | ||
) -> UnderflowResult<'_, K, V> { | ||
let parent = match node.ascend() { | ||
Ok(parent) => parent, | ||
Err(_) => return UnderflowResult::AtRoot, | ||
}; | ||
|
||
// Prefer the left KV if it exists. Merging with the left side is faster, | ||
// since merging happens towards the left and `node` has fewer elements. | ||
// Stealing from the left side is faster, since we can pop from the end of | ||
// the KV arrays. | ||
let (is_left, mut handle) = match parent.left_kv() { | ||
Ok(left) => (true, left), | ||
Err(parent) => { | ||
let right = unsafe { unwrap_unchecked(parent.right_kv().ok()) }; | ||
(false, right) | ||
} | ||
}; | ||
|
||
if handle.can_merge() { | ||
let offset = if is_left { handle.reborrow().left_edge().descend().len() + 1 } else { 0 }; | ||
UnderflowResult::Merged(handle.merge(), is_left, offset) | ||
} else { | ||
if is_left { | ||
handle.steal_left(); | ||
} else { | ||
handle.steal_right(); | ||
} | ||
UnderflowResult::Stole(is_left) | ||
} | ||
} |
Oops, something went wrong.