Skip to content

BTreeMap: lightly refactor the split_off implementation #81361

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions library/alloc/src/collections/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ use Entry::*;
/// We might temporarily have fewer elements during methods.
pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT;

// A tree in a `BTreeMap` is a tree in the `node` module with addtional invariants:
// - Keys must appear in ascending order (according to the key's type).
// - If the root node is internal, it must contain at least 1 element.
// - Every non-root node contains at least MIN_LEN elements.
//
// An empty map may be represented both by the absense of a root node or by a
// root node that is an empty leaf.

/// A map based on a B-Tree.
///
/// B-Trees represent a fundamental compromise between cache-efficiency and actually minimizing
Expand Down Expand Up @@ -1100,20 +1108,12 @@ impl<K: Ord, V> BTreeMap<K, V> {
let total_num = self.len();
let left_root = self.root.as_mut().unwrap(); // unwrap succeeds because not empty

let mut right = Self::new();
let right_root = Self::ensure_is_owned(&mut right.root);

left_root.split_off(right_root, key);
let right_root = left_root.split_off(key);

if left_root.height() < right_root.height() {
self.length = left_root.reborrow().calc_length();
right.length = total_num - self.len();
} else {
right.length = right_root.reborrow().calc_length();
self.length = total_num - right.len();
}
let (new_left_len, right_len) = Root::calc_split_length(total_num, &left_root, &right_root);
self.length = new_left_len;

right
BTreeMap { root: Some(right_root), length: right_len }
}

/// Creates an iterator which uses a closure to determine if an element should be removed.
Expand Down
85 changes: 55 additions & 30 deletions library/alloc/src/collections/btree/split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,71 @@ use super::search::SearchResult::*;
use core::borrow::Borrow;

impl<K, V> Root<K, V> {
pub fn split_off<Q: ?Sized + Ord>(&mut self, right_root: &mut Self, key: &Q)
/// Calculates the length of both trees that result from splitting up
/// a given number of distinct key-value pairs.
pub fn calc_split_length(
total_num: usize,
root_a: &Root<K, V>,
root_b: &Root<K, V>,
) -> (usize, usize) {
let (length_a, length_b);
if root_a.height() < root_b.height() {
length_a = root_a.reborrow().calc_length();
length_b = total_num - length_a;
debug_assert_eq!(length_b, root_b.reborrow().calc_length());
} else {
length_b = root_b.reborrow().calc_length();
length_a = total_num - length_b;
debug_assert_eq!(length_a, root_a.reborrow().calc_length());
}
(length_a, length_b)
}

/// Split off a tree with key-value pairs at and after the given key.
/// The result is meaningful only if the tree is ordered by key,
/// and if the ordering of `Q` corresponds to that of `K`.
/// If `self` respects all `BTreeMap` tree invariants, then both
/// `self` and the returned tree will respect those invariants.
pub fn split_off<Q: ?Sized + Ord>(&mut self, key: &Q) -> Self
where
K: Borrow<Q>,
{
debug_assert!(right_root.height() == 0);
debug_assert!(right_root.len() == 0);

let left_root = self;
for _ in 0..left_root.height() {
right_root.push_internal_level();
}

{
let mut left_node = left_root.borrow_mut();
let mut right_node = right_root.borrow_mut();

loop {
let mut split_edge = match left_node.search_node(key) {
// key is going to the right tree
Found(kv) => kv.left_edge(),
GoDown(edge) => edge,
};

split_edge.move_suffix(&mut right_node);

match (split_edge.force(), right_node.force()) {
(Internal(edge), Internal(node)) => {
left_node = edge.descend();
right_node = node.first_edge().descend();
}
(Leaf(_), Leaf(_)) => {
break;
}
_ => unreachable!(),
let mut right_root = Root::new_pillar(left_root.height());
let mut left_node = left_root.borrow_mut();
let mut right_node = right_root.borrow_mut();

loop {
let mut split_edge = match left_node.search_node(key) {
// key is going to the right tree
Found(kv) => kv.left_edge(),
GoDown(edge) => edge,
};

split_edge.move_suffix(&mut right_node);

match (split_edge.force(), right_node.force()) {
(Internal(edge), Internal(node)) => {
left_node = edge.descend();
right_node = node.first_edge().descend();
}
(Leaf(_), Leaf(_)) => break,
_ => unreachable!(),
}
}

left_root.fix_right_border();
right_root.fix_left_border();
right_root
}

/// Creates a tree consisting of empty nodes.
fn new_pillar(height: usize) -> Self {
let mut root = Root::new();
for _ in 0..height {
root.push_internal_level();
}
root
}

/// Removes empty levels on the top, but keeps an empty leaf if the entire tree is empty.
Expand Down