Skip to content

Commit 5c56f40

Browse files
committed
BTreeMap: disentangle Drop implementation from IntoIter
1 parent 440d869 commit 5c56f40

File tree

2 files changed

+97
-61
lines changed

2 files changed

+97
-61
lines changed

library/alloc/src/collections/btree/map.rs

+32-24
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ pub struct BTreeMap<K, V> {
138138
#[stable(feature = "btree_drop", since = "1.7.0")]
139139
unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for BTreeMap<K, V> {
140140
fn drop(&mut self) {
141-
unsafe {
142-
drop(ptr::read(self).into_iter());
141+
if let Some(root) = self.root.take() {
142+
Dropper { front: root.into_dying().first_leaf_edge(), remaining_length: self.length };
143143
}
144144
}
145145
}
@@ -325,6 +325,14 @@ impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for IntoIter<K, V> {
325325
}
326326
}
327327

328+
/// A simplified version of `IntoIter` that is not double-ended and has only one
329+
/// purpose: to drop the remainder of an `IntoIter`. Therefore it also serves to
330+
/// drop an entire tree without the need to look up a `back` leaf edge.
331+
struct Dropper<K, V> {
332+
front: Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge>,
333+
remaining_length: usize,
334+
}
335+
328336
/// An iterator over the keys of a `BTreeMap`.
329337
///
330338
/// This `struct` is created by the [`keys`] method on [`BTreeMap`]. See its
@@ -1373,42 +1381,42 @@ impl<K, V> IntoIterator for BTreeMap<K, V> {
13731381
}
13741382
}
13751383

1376-
#[stable(feature = "btree_drop", since = "1.7.0")]
1377-
impl<K, V> Drop for IntoIter<K, V> {
1384+
impl<K, V> Drop for Dropper<K, V> {
13781385
fn drop(&mut self) {
1379-
struct DropGuard<'a, K, V>(&'a mut IntoIter<K, V>);
1386+
// Similar to advancing a non-fusing iterator.
1387+
fn next_or_end<K, V>(this: &mut Dropper<K, V>) -> Option<(K, V)> {
1388+
if this.remaining_length == 0 {
1389+
unsafe { ptr::read(&this.front).deallocating_end() }
1390+
None
1391+
} else {
1392+
this.remaining_length -= 1;
1393+
Some(unsafe { this.front.next_unchecked() })
1394+
}
1395+
}
1396+
1397+
struct DropGuard<'a, K, V>(&'a mut Dropper<K, V>);
13801398

13811399
impl<'a, K, V> Drop for DropGuard<'a, K, V> {
13821400
fn drop(&mut self) {
13831401
// Continue the same loop we perform below. This only runs when unwinding, so we
13841402
// don't have to care about panics this time (they'll abort).
1385-
while let Some(_) = self.0.next() {}
1386-
1387-
unsafe {
1388-
let mut node =
1389-
unwrap_unchecked(ptr::read(&self.0.front)).into_node().forget_type();
1390-
while let Some(parent) = node.deallocate_and_ascend() {
1391-
node = parent.into_node().forget_type();
1392-
}
1393-
}
1403+
while let Some(_pair) = next_or_end(&mut self.0) {}
13941404
}
13951405
}
13961406

1397-
while let Some(pair) = self.next() {
1407+
while let Some(pair) = next_or_end(self) {
13981408
let guard = DropGuard(self);
13991409
drop(pair);
14001410
mem::forget(guard);
14011411
}
1412+
}
1413+
}
14021414

1403-
unsafe {
1404-
if let Some(front) = ptr::read(&self.front) {
1405-
let mut node = front.into_node().forget_type();
1406-
// Most of the nodes have been deallocated while traversing
1407-
// but one pile from a leaf up to the root is left standing.
1408-
while let Some(parent) = node.deallocate_and_ascend() {
1409-
node = parent.into_node().forget_type();
1410-
}
1411-
}
1415+
#[stable(feature = "btree_drop", since = "1.7.0")]
1416+
impl<K, V> Drop for IntoIter<K, V> {
1417+
fn drop(&mut self) {
1418+
if let Some(front) = self.front.take() {
1419+
Dropper { front, remaining_length: self.length };
14121420
}
14131421
}
14141422
}

library/alloc/src/collections/btree/navigate.rs

+65-37
Original file line numberDiff line numberDiff line change
@@ -290,37 +290,71 @@ impl<BorrowType: marker::BorrowType, K, V>
290290
}
291291
}
292292

293-
macro_rules! def_next_kv_uncheched_dealloc {
294-
{ unsafe fn $name:ident : $adjacent_kv:ident } => {
295-
/// Given a leaf edge handle into an owned tree, returns a handle to the next KV,
296-
/// while deallocating any node left behind yet leaving the corresponding edge
297-
/// in its parent node dangling.
298-
///
299-
/// # Safety
300-
/// - The leaf edge must not be the last one in the direction travelled.
301-
/// - The node carrying the next KV returned must not have been deallocated by a
302-
/// previous call on any handle obtained for this tree.
303-
unsafe fn $name <K, V>(
304-
leaf_edge: Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge>,
305-
) -> Handle<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>, marker::KV> {
306-
let mut edge = leaf_edge.forget_node_type();
307-
loop {
308-
edge = match edge.$adjacent_kv() {
309-
Ok(internal_kv) => return internal_kv,
310-
Err(last_edge) => {
311-
unsafe {
312-
let parent_edge = last_edge.into_node().deallocate_and_ascend();
313-
unwrap_unchecked(parent_edge).forget_node_type()
314-
}
315-
}
293+
impl<K, V> Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge> {
294+
/// Given a leaf edge handle into a dying tree, returns the next leaf edge
295+
/// on the right side, and the key-value pair in between, which is either
296+
/// in the same leaf node, in an ancestor node, or non-existent.
297+
///
298+
/// This method also deallocates any node(s) it reaches the end of. This
299+
/// implies that if no more key-value pair exists, the entire remainder of
300+
/// the tree will have been deallocated and there is nothing left to return.
301+
///
302+
/// # Safety
303+
/// The next KV must not have been previously returned by counterpart `deallocating_next_back`.
304+
unsafe fn deallocating_next(self) -> Option<(Self, (K, V))> {
305+
let mut edge = self.forget_node_type();
306+
loop {
307+
edge = match edge.right_kv() {
308+
Ok(kv) => {
309+
let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
310+
let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
311+
return Some((kv.next_leaf_edge(), (k, v)));
316312
}
313+
Err(last_edge) => match unsafe { last_edge.into_node().deallocate_and_ascend() } {
314+
Some(parent_edge) => parent_edge.forget_node_type(),
315+
None => return None,
316+
},
317317
}
318318
}
319-
};
320-
}
319+
}
321320

322-
def_next_kv_uncheched_dealloc! {unsafe fn next_kv_unchecked_dealloc: right_kv}
323-
def_next_kv_uncheched_dealloc! {unsafe fn next_back_kv_unchecked_dealloc: left_kv}
321+
/// Given a leaf edge handle into a dying tree, returns the next leaf edge
322+
/// on the left side, and the key-value pair in between, which is either
323+
/// in the same leaf node, in an ancestor node, or non-existent.
324+
///
325+
/// This method also deallocates any node(s) it reaches the end of. This
326+
/// implies that if no more key-value pair exists, the entire remainder of
327+
/// the tree will have been deallocated and there is nothing left to return.
328+
///
329+
/// # Safety
330+
/// The next KV must not have been previously returned by counterpart `deallocating_next`.
331+
unsafe fn deallocating_next_back(self) -> Option<(Self, (K, V))> {
332+
let mut edge = self.forget_node_type();
333+
loop {
334+
edge = match edge.left_kv() {
335+
Ok(kv) => {
336+
let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
337+
let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
338+
return Some((kv.next_back_leaf_edge(), (k, v)));
339+
}
340+
Err(last_edge) => match unsafe { last_edge.into_node().deallocate_and_ascend() } {
341+
Some(parent_edge) => parent_edge.forget_node_type(),
342+
None => return None,
343+
},
344+
}
345+
}
346+
}
347+
348+
/// Deallocates a pile of nodes from the leaf up to the root.
349+
/// This is useful when `deallocating_next` and `deallocating_next_back`
350+
/// have been nibbling at both sides of the same tree.
351+
pub fn deallocating_end(self) {
352+
let mut edge = self.forget_node_type();
353+
while let Some(parent_edge) = unsafe { edge.into_node().deallocate_and_ascend() } {
354+
edge = parent_edge.forget_node_type();
355+
}
356+
}
357+
}
324358

325359
impl<'a, K, V> Handle<NodeRef<marker::Immut<'a>, K, V, marker::Leaf>, marker::Edge> {
326360
/// Moves the leaf edge handle to the next leaf edge and returns references to the
@@ -396,11 +430,8 @@ impl<K, V> Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge> {
396430
/// call this method again subject to its safety conditions, or call counterpart
397431
/// `next_back_unchecked` subject to its safety conditions.
398432
pub unsafe fn next_unchecked(&mut self) -> (K, V) {
399-
super::mem::replace(self, |leaf_edge| {
400-
let kv = unsafe { next_kv_unchecked_dealloc(leaf_edge) };
401-
let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
402-
let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
403-
(kv.next_leaf_edge(), (k, v))
433+
super::mem::replace(self, |leaf_edge| unsafe {
434+
unwrap_unchecked(leaf_edge.deallocating_next())
404435
})
405436
}
406437

@@ -417,11 +448,8 @@ impl<K, V> Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge> {
417448
/// call this method again subject to its safety conditions, or call counterpart
418449
/// `next_unchecked` subject to its safety conditions.
419450
pub unsafe fn next_back_unchecked(&mut self) -> (K, V) {
420-
super::mem::replace(self, |leaf_edge| {
421-
let kv = unsafe { next_back_kv_unchecked_dealloc(leaf_edge) };
422-
let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
423-
let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
424-
(kv.next_back_leaf_edge(), (k, v))
451+
super::mem::replace(self, |leaf_edge| unsafe {
452+
unwrap_unchecked(leaf_edge.deallocating_next_back())
425453
})
426454
}
427455
}

0 commit comments

Comments
 (0)