about summary refs log tree commit diff
diff options
context:
space:
mode:
authorC Jones <code@calebjones.net>2018-04-30 17:34:14 -0400
committerC Jones <code@calebjones.net>2018-05-07 22:14:20 -0400
commitef6060c863c86e1422baa2cc85ae75af22feaf51 (patch)
tree4244bc3dd335bd172b97b11fe4e8c70eb238db78
parent669bd8223bf159d757d0c552a4c413a137bc6b10 (diff)
downloadrust-ef6060c863c86e1422baa2cc85ae75af22feaf51.tar.gz
rust-ef6060c863c86e1422baa2cc85ae75af22feaf51.zip
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.
-rw-r--r--src/liballoc/btree/map.rs15
-rw-r--r--src/liballoc/btree/node.rs30
2 files changed, 43 insertions, 2 deletions
diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs
index 3984379ea86..985a41722d7 100644
--- a/src/liballoc/btree/map.rs
+++ b/src/liballoc/btree/map.rs
@@ -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 {
diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs
index 3e27331aa38..6381006e7fc 100644
--- a/src/liballoc/btree/node.rs
+++ b/src/liballoc/btree/node.rs
@@ -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() })),