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:
C Jones 2018-04-30 17:34:14 -04:00
parent 669bd8223b
commit ef6060c863
2 changed files with 43 additions and 2 deletions

View file

@ -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 {

View file

@ -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() })),