diff options
| author | C Jones <code@calebjones.net> | 2018-04-30 17:34:14 -0400 |
|---|---|---|
| committer | C Jones <code@calebjones.net> | 2018-05-07 22:14:20 -0400 |
| commit | ef6060c863c86e1422baa2cc85ae75af22feaf51 (patch) | |
| tree | 4244bc3dd335bd172b97b11fe4e8c70eb238db78 | |
| parent | 669bd8223bf159d757d0c552a4c413a137bc6b10 (diff) | |
| download | rust-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.rs | 15 | ||||
| -rw-r--r-- | src/liballoc/btree/node.rs | 30 |
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() })), |
