Skip to content

Commit c09edaf

Browse files
Rollup merge of rust-lang#153107 - asder8215:btreemap_append_optimized, r=Mark-Simulacrum
Optimize BTreeMap::append() using CursorMut Since [`BTreeMap::merge`](rust-lang#152418 (comment)) uses `CursorMut` to avoid reconstructing the map from scratch and instead inserting other `BTreeMap` at the right places or overwriting the value in self `BTreeMap` on conflict, we might as well do the same for `BTreeMap::append`. This also means that some of the code in `append.rs` can be removed; `bulk_push()` however is used by `bulk_build_from_sorted_iterator()`, which is used by the `From`/`FromIterator` trait impl on `BTreeMap`. Feels like we should rename the file or place the `bulk_push()` in an existing file. The same additional optimization consideration that `BTreeMap::merge` has is also applied to `BTreeMap::append`. r? @Mark-Simulacrum since Mark has seen the `BTreeMap::merge` code already (only diff is the `Ordering::Equal` case and now one of the test assertions on a panic case has the correct value now).
2 parents 562dee4 + e2a870f commit c09edaf

File tree

3 files changed

+3
-72
lines changed

3 files changed

+3
-72
lines changed

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

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,8 @@
11
use core::alloc::Allocator;
2-
use core::iter::FusedIterator;
32

4-
use super::merge_iter::MergeIterInner;
53
use super::node::{self, Root};
64

75
impl<K, V> Root<K, V> {
8-
/// Appends all key-value pairs from the union of two ascending iterators,
9-
/// incrementing a `length` variable along the way. The latter makes it
10-
/// easier for the caller to avoid a leak when a drop handler panicks.
11-
///
12-
/// If both iterators produce the same key, this method drops the pair from
13-
/// the left iterator and appends the pair from the right iterator.
14-
///
15-
/// If you want the tree to end up in a strictly ascending order, like for
16-
/// a `BTreeMap`, both iterators should produce keys in strictly ascending
17-
/// order, each greater than all keys in the tree, including any keys
18-
/// already in the tree upon entry.
19-
pub(super) fn append_from_sorted_iters<I, A: Allocator + Clone>(
20-
&mut self,
21-
left: I,
22-
right: I,
23-
length: &mut usize,
24-
alloc: A,
25-
) where
26-
K: Ord,
27-
I: Iterator<Item = (K, V)> + FusedIterator,
28-
{
29-
// We prepare to merge `left` and `right` into a sorted sequence in linear time.
30-
let iter = MergeIter(MergeIterInner::new(left, right));
31-
32-
// Meanwhile, we build a tree from the sorted sequence in linear time.
33-
self.bulk_push(iter, length, alloc)
34-
}
35-
366
/// Pushes all key-value pairs to the end of the tree, incrementing a
377
/// `length` variable along the way. The latter makes it easier for the
388
/// caller to avoid a leak when the iterator panicks.
@@ -94,24 +64,3 @@ impl<K, V> Root<K, V> {
9464
self.fix_right_border_of_plentiful();
9565
}
9666
}
97-
98-
// An iterator for merging two sorted sequences into one
99-
struct MergeIter<K, V, I: Iterator<Item = (K, V)>>(MergeIterInner<I>);
100-
101-
impl<K: Ord, V, I> Iterator for MergeIter<K, V, I>
102-
where
103-
I: Iterator<Item = (K, V)> + FusedIterator,
104-
{
105-
type Item = (K, V);
106-
107-
/// If two keys are equal, returns the key from the left and the value from the right.
108-
fn next(&mut self) -> Option<(K, V)> {
109-
let (a_next, b_next) = self.0.nexts(|a: &(K, V), b: &(K, V)| K::cmp(&a.0, &b.0));
110-
match (a_next, b_next) {
111-
(Some((a_k, _)), Some((_, b_v))) => Some((a_k, b_v)),
112-
(Some(a), None) => Some(a),
113-
(None, Some(b)) => Some(b),
114-
(None, None) => None,
115-
}
116-
}
117-
}

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

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,26 +1219,8 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
12191219
K: Ord,
12201220
A: Clone,
12211221
{
1222-
// Do we have to append anything at all?
1223-
if other.is_empty() {
1224-
return;
1225-
}
1226-
1227-
// We can just swap `self` and `other` if `self` is empty.
1228-
if self.is_empty() {
1229-
mem::swap(self, other);
1230-
return;
1231-
}
1232-
1233-
let self_iter = mem::replace(self, Self::new_in((*self.alloc).clone())).into_iter();
1234-
let other_iter = mem::replace(other, Self::new_in((*self.alloc).clone())).into_iter();
1235-
let root = self.root.get_or_insert_with(|| Root::new((*self.alloc).clone()));
1236-
root.append_from_sorted_iters(
1237-
self_iter,
1238-
other_iter,
1239-
&mut self.length,
1240-
(*self.alloc).clone(),
1241-
)
1222+
let other = mem::replace(other, Self::new_in((*self.alloc).clone()));
1223+
self.merge(other, |_key, _self_val, other_val| other_val);
12421224
}
12431225

12441226
/// Moves all elements from `other` into `self`, leaving `other` empty.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2224,7 +2224,7 @@ fn test_append_drop_leak() {
22242224

22252225
catch_unwind(move || left.append(&mut right)).unwrap_err();
22262226
assert_eq!(a.dropped(), 1);
2227-
assert_eq!(b.dropped(), 1); // should be 2 were it not for Rust issue #47949
2227+
assert_eq!(b.dropped(), 2);
22282228
assert_eq!(c.dropped(), 2);
22292229
}
22302230

0 commit comments

Comments
 (0)