Add a statically allocated empty node for empty maps
This gives a pointer to that static empty node instead of allocating a new node, and then whenever inserting makes sure that the root isn't that empty node.
This commit is contained in:
parent
669bd8223b
commit
ef6060c863
2 changed files with 43 additions and 2 deletions
|
|
@ -523,7 +523,7 @@ impl<K: Ord, V> BTreeMap<K, V> {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new() -> BTreeMap<K, V> {
|
||||
BTreeMap {
|
||||
root: node::Root::new_leaf(),
|
||||
root: node::Root::shared_empty_root(),
|
||||
length: 0,
|
||||
}
|
||||
}
|
||||
|
|
@ -544,7 +544,6 @@ impl<K: Ord, V> BTreeMap<K, V> {
|
|||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn clear(&mut self) {
|
||||
// FIXME(gereeter) .clear() allocates
|
||||
*self = BTreeMap::new();
|
||||
}
|
||||
|
||||
|
|
@ -687,6 +686,10 @@ impl<K: Ord, V> BTreeMap<K, V> {
|
|||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
|
||||
if self.root.is_shared_root() {
|
||||
self.root = node::Root::new_leaf();
|
||||
}
|
||||
|
||||
match self.entry(key) {
|
||||
Occupied(mut entry) => Some(entry.insert(value)),
|
||||
Vacant(entry) => {
|
||||
|
|
@ -890,6 +893,10 @@ impl<K: Ord, V> BTreeMap<K, V> {
|
|||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn entry(&mut self, key: K) -> Entry<K, V> {
|
||||
if self.root.is_shared_root() {
|
||||
self.root = node::Root::new_leaf();
|
||||
}
|
||||
|
||||
match search::search_tree(self.root.as_mut(), &key) {
|
||||
Found(handle) => {
|
||||
Occupied(OccupiedEntry {
|
||||
|
|
@ -910,6 +917,10 @@ impl<K: Ord, V> BTreeMap<K, V> {
|
|||
}
|
||||
|
||||
fn from_sorted_iter<I: Iterator<Item = (K, V)>>(&mut self, iter: I) {
|
||||
if self.root.is_shared_root() {
|
||||
self.root = node::Root::new_leaf();
|
||||
}
|
||||
|
||||
let mut cur_node = last_leaf_edge(self.root.as_mut()).into_node();
|
||||
// Iterate through all key-value pairs, pushing them into nodes at the right level.
|
||||
for (key, value) in iter {
|
||||
|
|
|
|||
|
|
@ -103,6 +103,18 @@ impl<K, V> LeafNode<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
// We need to implement Sync here in order to make a static instance
|
||||
unsafe impl Sync for LeafNode<(), ()> {}
|
||||
|
||||
// An empty node used as a placeholder for the root node, to avoid allocations
|
||||
static EMPTY_ROOT_NODE: LeafNode<(), ()> = LeafNode {
|
||||
parent: ptr::null(),
|
||||
parent_idx: 0,
|
||||
len: 0,
|
||||
keys: [(); CAPACITY],
|
||||
vals: [(); CAPACITY],
|
||||
};
|
||||
|
||||
/// The underlying representation of internal nodes. As with `LeafNode`s, these should be hidden
|
||||
/// behind `BoxedNode`s to prevent dropping uninitialized keys and values. Any pointer to an
|
||||
/// `InternalNode` can be directly casted to a pointer to the underlying `LeafNode` portion of the
|
||||
|
|
@ -172,6 +184,24 @@ unsafe impl<K: Sync, V: Sync> Sync for Root<K, V> { }
|
|||
unsafe impl<K: Send, V: Send> Send for Root<K, V> { }
|
||||
|
||||
impl<K, V> Root<K, V> {
|
||||
pub fn is_shared_root(&self) -> bool {
|
||||
ptr::eq(
|
||||
self.node.as_ptr().as_ptr(),
|
||||
&EMPTY_ROOT_NODE as *const _ as *const LeafNode<K, V>,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn shared_empty_root() -> Self {
|
||||
Root {
|
||||
node: unsafe {
|
||||
BoxedNode::from_ptr(NonNull::new_unchecked(
|
||||
&EMPTY_ROOT_NODE as *const _ as *const LeafNode<K, V> as *mut _
|
||||
))
|
||||
},
|
||||
height: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_leaf() -> Self {
|
||||
Root {
|
||||
node: BoxedNode::from_leaf(Box::new(unsafe { LeafNode::new() })),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue