Auto merge of #74827 - ssomers:btree_cleanup_insert, r=Mark-Simulacrum

Move bulk of BTreeMap::insert method down to new method on handle

Adjust the boundary between the map and node layers for insertion: do more in the node layer, keep root manipulation and pointer dereferencing separate. No change in undefined behaviour or performance.

r? @Mark-Simulacrum
This commit is contained in:
bors 2020-08-03 15:46:02 +00:00
commit 829d69b9c6
2 changed files with 70 additions and 37 deletions

View File

@ -2465,40 +2465,17 @@ impl<'a, K: Ord, V> VacantEntry<'a, K, V> {
pub fn insert(self, value: V) -> &'a mut V { pub fn insert(self, value: V) -> &'a mut V {
*self.length += 1; *self.length += 1;
let out_ptr; let out_ptr = match self.handle.insert_recursing(self.key, value) {
(Fit(_), val_ptr) => val_ptr,
let mut ins_k; (Split(ins), val_ptr) => {
let mut ins_v; let root = ins.left.into_root_mut();
let mut ins_edge; root.push_internal_level().push(ins.k, ins.v, ins.right);
val_ptr
let mut cur_parent = match self.handle.insert(self.key, value) {
(Fit(handle), _) => return handle.into_kv_mut().1,
(Split(left, k, v, right), ptr) => {
ins_k = k;
ins_v = v;
ins_edge = right;
out_ptr = ptr;
left.ascend().map_err(|n| n.into_root_mut())
} }
}; };
// Now that we have finished growing the tree using borrowed references,
loop { // dereference the pointer to a part of it, that we picked up along the way.
match cur_parent { unsafe { &mut *out_ptr }
Ok(parent) => match parent.insert(ins_k, ins_v, ins_edge) {
Fit(_) => return unsafe { &mut *out_ptr },
Split(left, k, v, right) => {
ins_k = k;
ins_v = v;
ins_edge = right;
cur_parent = left.ascend().map_err(|n| n.into_root_mut());
}
},
Err(root) => {
root.push_internal_level().push(ins_k, ins_v, ins_edge);
return unsafe { &mut *out_ptr };
}
}
}
} }
} }

View File

@ -843,7 +843,7 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge
/// this edge. This method splits the node if there isn't enough room. /// this edge. This method splits the node if there isn't enough room.
/// ///
/// The returned pointer points to the inserted value. /// The returned pointer points to the inserted value.
pub fn insert(mut self, key: K, val: V) -> (InsertResult<'a, K, V, marker::Leaf>, *mut V) { fn insert(mut self, key: K, val: V) -> (InsertResult<'a, K, V, marker::Leaf>, *mut V) {
if self.node.len() < CAPACITY { if self.node.len() < CAPACITY {
let ptr = self.insert_fit(key, val); let ptr = self.insert_fit(key, val);
let kv = unsafe { Handle::new_kv(self.node, self.idx) }; let kv = unsafe { Handle::new_kv(self.node, self.idx) };
@ -862,7 +862,7 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge
.insert_fit(key, val) .insert_fit(key, val)
} }
}; };
(InsertResult::Split(left, k, v, right), ptr) (InsertResult::Split(SplitResult { left: left.forget_type(), k, v, right }), ptr)
} }
} }
} }
@ -918,7 +918,7 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::
/// Inserts a new key/value pair and an edge that will go to the right of that new pair /// Inserts a new key/value pair and an edge that will go to the right of that new pair
/// between this edge and the key/value pair to the right of this edge. This method splits /// between this edge and the key/value pair to the right of this edge. This method splits
/// the node if there isn't enough room. /// the node if there isn't enough room.
pub fn insert( fn insert(
mut self, mut self,
key: K, key: K,
val: V, val: V,
@ -946,7 +946,43 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::
.insert_fit(key, val, edge); .insert_fit(key, val, edge);
} }
} }
InsertResult::Split(left, k, v, right) InsertResult::Split(SplitResult { left: left.forget_type(), k, v, right })
}
}
}
impl<'a, K: 'a, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge> {
/// Inserts a new key/value pair between the key/value pairs to the right and left of
/// this edge. This method splits the node if there isn't enough room, and tries to
/// insert the split off portion into the parent node recursively, until the root is reached.
///
/// If the returned result is a `Fit`, its handle's node can be this edge's node or an ancestor.
/// If the returned result is a `Split`, the `left` field will be the root node.
/// The returned pointer points to the inserted value.
pub fn insert_recursing(
self,
key: K,
value: V,
) -> (InsertResult<'a, K, V, marker::LeafOrInternal>, *mut V) {
let (mut split, val_ptr) = match self.insert(key, value) {
(InsertResult::Fit(handle), ptr) => {
return (InsertResult::Fit(handle.forget_node_type()), ptr);
}
(InsertResult::Split(split), val_ptr) => (split, val_ptr),
};
loop {
split = match split.left.ascend() {
Ok(parent) => match parent.insert(split.k, split.v, split.right) {
InsertResult::Fit(handle) => {
return (InsertResult::Fit(handle.forget_node_type()), val_ptr);
}
InsertResult::Split(split) => split,
},
Err(root) => {
return (InsertResult::Split(SplitResult { left: root, ..split }), val_ptr);
}
};
} }
} }
} }
@ -1389,6 +1425,14 @@ impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::K
} }
} }
impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Internal>, marker::KV> {
pub fn forget_node_type(
self,
) -> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, marker::KV> {
unsafe { Handle::new_kv(self.node.forget_type(), self.idx) }
}
}
impl<BorrowType, K, V, HandleType> impl<BorrowType, K, V, HandleType>
Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, HandleType> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, HandleType>
{ {
@ -1455,9 +1499,21 @@ pub enum ForceResult<Leaf, Internal> {
Internal(Internal), Internal(Internal),
} }
/// Result of insertion, when a node needed to expand beyond its capacity.
/// Does not distinguish between `Leaf` and `Internal` because `Root` doesn't.
pub struct SplitResult<'a, K, V> {
// Altered node in existing tree with elements and edges that belong to the left of `k`.
pub left: NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>,
// Some key and value split off, to be inserted elsewhere.
pub k: K,
pub v: V,
// Owned, unattached, new node with elements and edges that belong to the right of `k`.
pub right: Root<K, V>,
}
pub enum InsertResult<'a, K, V, Type> { pub enum InsertResult<'a, K, V, Type> {
Fit(Handle<NodeRef<marker::Mut<'a>, K, V, Type>, marker::KV>), Fit(Handle<NodeRef<marker::Mut<'a>, K, V, Type>, marker::KV>),
Split(NodeRef<marker::Mut<'a>, K, V, Type>, K, V, Root<K, V>), Split(SplitResult<'a, K, V>),
} }
pub mod marker { pub mod marker {