Skip to content

Commit

Permalink
btree: add {Entry,VacantEntry}::insert_entry
Browse files Browse the repository at this point in the history
This matches the recently-stabilized methods on `HashMap` entries. I've
reused tracking issue #65225 for now, but we may want to split it.
  • Loading branch information
cuviper committed Nov 14, 2024
1 parent a00df61 commit 6f682b6
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 31 deletions.
105 changes: 75 additions & 30 deletions library/alloc/src/collections/btree/map/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,31 @@ impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> {
Vacant(entry) => Vacant(entry),
}
}

/// Sets the value of the entry, and returns an `OccupiedEntry`.
///
/// # Examples
///
/// ```
/// #![feature(btree_entry_insert)]
/// use std::collections::BTreeMap;
///
/// let mut map: BTreeMap<&str, String> = BTreeMap::new();
/// let entry = map.entry("poneyland").insert_entry("hoho".to_string());
///
/// assert_eq!(entry.key(), &"poneyland");
/// ```
#[inline]
#[unstable(feature = "btree_entry_insert", issue = "65225")]
pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> {
match self {
Occupied(mut entry) => {
entry.insert(value);
entry
}
Vacant(entry) => entry.insert_entry(value),
}
}
}

impl<'a, K: Ord, V: Default, A: Allocator + Clone> Entry<'a, K, V, A> {
Expand Down Expand Up @@ -348,41 +373,61 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_confusables("push", "put")]
pub fn insert(mut self, value: V) -> &'a mut V {
let out_ptr = match self.handle {
pub fn insert(self, value: V) -> &'a mut V {
self.insert_entry(value).into_mut()
}

/// Sets the value of the entry with the `VacantEntry`'s key,
/// and returns an `OccupiedEntry`.
///
/// # Examples
///
/// ```
/// #![feature(btree_entry_insert)]
/// use std::collections::BTreeMap;
/// use std::collections::btree_map::Entry;
///
/// let mut map: BTreeMap<&str, u32> = BTreeMap::new();
///
/// if let Entry::Vacant(o) = map.entry("poneyland") {
/// let entry = o.insert_entry(37);
/// assert_eq!(entry.get(), &37);
/// }
/// assert_eq!(map["poneyland"], 37);
/// ```
#[unstable(feature = "btree_entry_insert", issue = "65225")]
pub fn insert_entry(mut self, value: V) -> OccupiedEntry<'a, K, V, A> {
let handle = match self.handle {
None => {
// SAFETY: There is no tree yet so no reference to it exists.
let map = unsafe { self.dormant_map.awaken() };
let mut root = NodeRef::new_leaf(self.alloc.clone());
let val_ptr = root.borrow_mut().push(self.key, value);
map.root = Some(root.forget_type());
map.length = 1;
val_ptr
}
Some(handle) => {
let new_handle =
handle.insert_recursing(self.key, value, self.alloc.clone(), |ins| {
drop(ins.left);
// SAFETY: Pushing a new root node doesn't invalidate
// handles to existing nodes.
let map = unsafe { self.dormant_map.reborrow() };
let root = map.root.as_mut().unwrap(); // same as ins.left
root.push_internal_level(self.alloc).push(ins.kv.0, ins.kv.1, ins.right)
});

// Get the pointer to the value
let val_ptr = new_handle.into_val_mut();

// SAFETY: We have consumed self.handle.
let map = unsafe { self.dormant_map.awaken() };
map.length += 1;
val_ptr
let map = unsafe { self.dormant_map.reborrow() };
let root = map.root.insert(NodeRef::new_leaf(self.alloc.clone()).forget_type());
// SAFETY: We *just* created the root as a leaf, and we're
// stacking the new handle on the original borrow lifetime.
unsafe {
let mut leaf = root.borrow_mut().cast_to_leaf_unchecked();
leaf.push_with_handle(self.key, value)
}
}
Some(handle) => handle.insert_recursing(self.key, value, self.alloc.clone(), |ins| {
drop(ins.left);
// SAFETY: Pushing a new root node doesn't invalidate
// handles to existing nodes.
let map = unsafe { self.dormant_map.reborrow() };
let root = map.root.as_mut().unwrap(); // same as ins.left
root.push_internal_level(self.alloc.clone()).push(ins.kv.0, ins.kv.1, ins.right)
}),
};

// Now that we have finished growing the tree using borrowed references,
// dereference the pointer to a part of it, that we picked up along the way.
unsafe { &mut *out_ptr }
// SAFETY: modifying the length doesn't invalidate handles to existing nodes.
unsafe { self.dormant_map.reborrow().length += 1 };

OccupiedEntry {
handle: handle.forget_node_type(),
dormant_map: self.dormant_map,
alloc: self.alloc,
_marker: PhantomData,
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion library/alloc/src/collections/btree/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {

impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
/// Unsafely asserts to the compiler the static information that this node is a `Leaf`.
unsafe fn cast_to_leaf_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
pub unsafe fn cast_to_leaf_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> {
debug_assert!(self.height == 0);
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
}
Expand Down

0 comments on commit 6f682b6

Please sign in to comment.