Rollup merge of #78417 - ssomers:btree_chop_up_2, r=Mark-Simulacrum
BTreeMap: split off most code of append To complete #78056, move the last single-purpose pieces of code out of map.rs into a separate module. Also, tweaked documentation and safeness - I doubt think this code would be safe if the iterators passed in wouldn't be as sorted as the method says they should be - and bounds on MergeIterInner. r? ```@Mark-Simulacrum```
This commit is contained in:
commit
56e0806a1a
5 changed files with 176 additions and 114 deletions
124
library/alloc/src/collections/btree/append.rs
Normal file
124
library/alloc/src/collections/btree/append.rs
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
use super::map::MIN_LEN;
|
||||||
|
use super::merge_iter::MergeIterInner;
|
||||||
|
use super::node::{self, ForceResult::*, Root};
|
||||||
|
use core::iter::FusedIterator;
|
||||||
|
|
||||||
|
impl<K, V> Root<K, V> {
|
||||||
|
/// Appends all key-value pairs from the union of two ascending iterators,
|
||||||
|
/// incrementing a `length` variable along the way. The latter makes it
|
||||||
|
/// easier for the caller to avoid a leak when a drop handler panicks.
|
||||||
|
///
|
||||||
|
/// If both iterators produce the same key, this method drops the pair from
|
||||||
|
/// the left iterator and appends the pair from the right iterator.
|
||||||
|
///
|
||||||
|
/// If you want the tree to end up in a strictly ascending order, like for
|
||||||
|
/// a `BTreeMap`, both iterators should produce keys in strictly ascending
|
||||||
|
/// order, each greater than all keys in the tree, including any keys
|
||||||
|
/// already in the tree upon entry.
|
||||||
|
pub fn append_from_sorted_iters<I>(&mut self, left: I, right: I, length: &mut usize)
|
||||||
|
where
|
||||||
|
K: Ord,
|
||||||
|
I: Iterator<Item = (K, V)> + FusedIterator,
|
||||||
|
{
|
||||||
|
// We prepare to merge `left` and `right` into a sorted sequence in linear time.
|
||||||
|
let iter = MergeIter(MergeIterInner::new(left, right));
|
||||||
|
|
||||||
|
// Meanwhile, we build a tree from the sorted sequence in linear time.
|
||||||
|
self.bulk_push(iter, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes all key-value pairs to the end of the tree, incrementing a
|
||||||
|
/// `length` variable along the way. The latter makes it easier for the
|
||||||
|
/// caller to avoid a leak when the iterator panicks.
|
||||||
|
fn bulk_push<I>(&mut self, iter: I, length: &mut usize)
|
||||||
|
where
|
||||||
|
I: Iterator<Item = (K, V)>,
|
||||||
|
{
|
||||||
|
let mut cur_node = self.node_as_mut().last_leaf_edge().into_node();
|
||||||
|
// Iterate through all key-value pairs, pushing them into nodes at the right level.
|
||||||
|
for (key, value) in iter {
|
||||||
|
// Try to push key-value pair into the current leaf node.
|
||||||
|
if cur_node.len() < node::CAPACITY {
|
||||||
|
cur_node.push(key, value);
|
||||||
|
} else {
|
||||||
|
// No space left, go up and push there.
|
||||||
|
let mut open_node;
|
||||||
|
let mut test_node = cur_node.forget_type();
|
||||||
|
loop {
|
||||||
|
match test_node.ascend() {
|
||||||
|
Ok(parent) => {
|
||||||
|
let parent = parent.into_node();
|
||||||
|
if parent.len() < node::CAPACITY {
|
||||||
|
// Found a node with space left, push here.
|
||||||
|
open_node = parent;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// Go up again.
|
||||||
|
test_node = parent.forget_type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// We are at the top, create a new root node and push there.
|
||||||
|
open_node = self.push_internal_level();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push key-value pair and new right subtree.
|
||||||
|
let tree_height = open_node.height() - 1;
|
||||||
|
let mut right_tree = Root::new_leaf();
|
||||||
|
for _ in 0..tree_height {
|
||||||
|
right_tree.push_internal_level();
|
||||||
|
}
|
||||||
|
open_node.push(key, value, right_tree);
|
||||||
|
|
||||||
|
// Go down to the right-most leaf again.
|
||||||
|
cur_node = open_node.forget_type().last_leaf_edge().into_node();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment length every iteration, to make sure the map drops
|
||||||
|
// the appended elements even if advancing the iterator panicks.
|
||||||
|
*length += 1;
|
||||||
|
}
|
||||||
|
self.fix_right_edge();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fix_right_edge(&mut self) {
|
||||||
|
// Handle underfull nodes, start from the top.
|
||||||
|
let mut cur_node = self.node_as_mut();
|
||||||
|
while let Internal(internal) = cur_node.force() {
|
||||||
|
// Check if right-most child is underfull.
|
||||||
|
let mut last_edge = internal.last_edge();
|
||||||
|
let right_child_len = last_edge.reborrow().descend().len();
|
||||||
|
if right_child_len < MIN_LEN {
|
||||||
|
// We need to steal.
|
||||||
|
let mut last_kv = match last_edge.left_kv() {
|
||||||
|
Ok(left) => left,
|
||||||
|
Err(_) => unreachable!(),
|
||||||
|
};
|
||||||
|
last_kv.bulk_steal_left(MIN_LEN - right_child_len);
|
||||||
|
last_edge = last_kv.right_edge();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go further down.
|
||||||
|
cur_node = last_edge.descend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An iterator for merging two sorted sequences into one
|
||||||
|
struct MergeIter<K, V, I: Iterator<Item = (K, V)>>(MergeIterInner<I>);
|
||||||
|
|
||||||
|
impl<K: Ord, V, I> Iterator for MergeIter<K, V, I>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = (K, V)> + FusedIterator,
|
||||||
|
{
|
||||||
|
type Item = (K, V);
|
||||||
|
|
||||||
|
/// If two keys are equal, returns the key-value pair from the right source.
|
||||||
|
fn next(&mut self) -> Option<(K, V)> {
|
||||||
|
let (a_next, b_next) = self.0.nexts(|a: &(K, V), b: &(K, V)| K::cmp(&a.0, &b.0));
|
||||||
|
b_next.or(a_next)
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,6 @@ use core::ops::{Index, RangeBounds};
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
use super::borrow::DormantMutRef;
|
use super::borrow::DormantMutRef;
|
||||||
use super::merge_iter::MergeIterInner;
|
|
||||||
use super::node::{self, marker, ForceResult::*, Handle, NodeRef};
|
use super::node::{self, marker, ForceResult::*, Handle, NodeRef};
|
||||||
use super::search::{self, SearchResult::*};
|
use super::search::{self, SearchResult::*};
|
||||||
use super::unwrap_unchecked;
|
use super::unwrap_unchecked;
|
||||||
|
@ -458,9 +457,6 @@ impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for RangeMut<'_, K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// An iterator for merging two sorted sequences into one
|
|
||||||
struct MergeIter<K, V, I: Iterator<Item = (K, V)>>(MergeIterInner<I>);
|
|
||||||
|
|
||||||
impl<K: Ord, V> BTreeMap<K, V> {
|
impl<K: Ord, V> BTreeMap<K, V> {
|
||||||
/// Makes a new empty BTreeMap.
|
/// Makes a new empty BTreeMap.
|
||||||
///
|
///
|
||||||
|
@ -908,13 +904,10 @@ impl<K: Ord, V> BTreeMap<K, V> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, we merge `self` and `other` into a sorted sequence in linear time.
|
|
||||||
let self_iter = mem::take(self).into_iter();
|
let self_iter = mem::take(self).into_iter();
|
||||||
let other_iter = mem::take(other).into_iter();
|
let other_iter = mem::take(other).into_iter();
|
||||||
let iter = MergeIter(MergeIterInner::new(self_iter, other_iter));
|
let root = BTreeMap::ensure_is_owned(&mut self.root);
|
||||||
|
root.append_from_sorted_iters(self_iter, other_iter, &mut self.length)
|
||||||
// Second, we build a tree from the sorted sequence in linear time.
|
|
||||||
self.from_sorted_iter(iter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a double-ended iterator over a sub-range of elements in the map.
|
/// Constructs a double-ended iterator over a sub-range of elements in the map.
|
||||||
|
@ -1039,78 +1032,6 @@ impl<K: Ord, V> BTreeMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_sorted_iter<I: Iterator<Item = (K, V)>>(&mut self, iter: I) {
|
|
||||||
let root = Self::ensure_is_owned(&mut self.root);
|
|
||||||
let mut cur_node = root.node_as_mut().last_leaf_edge().into_node();
|
|
||||||
// Iterate through all key-value pairs, pushing them into nodes at the right level.
|
|
||||||
for (key, value) in iter {
|
|
||||||
// Try to push key-value pair into the current leaf node.
|
|
||||||
if cur_node.len() < node::CAPACITY {
|
|
||||||
cur_node.push(key, value);
|
|
||||||
} else {
|
|
||||||
// No space left, go up and push there.
|
|
||||||
let mut open_node;
|
|
||||||
let mut test_node = cur_node.forget_type();
|
|
||||||
loop {
|
|
||||||
match test_node.ascend() {
|
|
||||||
Ok(parent) => {
|
|
||||||
let parent = parent.into_node();
|
|
||||||
if parent.len() < node::CAPACITY {
|
|
||||||
// Found a node with space left, push here.
|
|
||||||
open_node = parent;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
// Go up again.
|
|
||||||
test_node = parent.forget_type();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
// We are at the top, create a new root node and push there.
|
|
||||||
open_node = root.push_internal_level();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push key-value pair and new right subtree.
|
|
||||||
let tree_height = open_node.height() - 1;
|
|
||||||
let mut right_tree = node::Root::new_leaf();
|
|
||||||
for _ in 0..tree_height {
|
|
||||||
right_tree.push_internal_level();
|
|
||||||
}
|
|
||||||
open_node.push(key, value, right_tree);
|
|
||||||
|
|
||||||
// Go down to the right-most leaf again.
|
|
||||||
cur_node = open_node.forget_type().last_leaf_edge().into_node();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.length += 1;
|
|
||||||
}
|
|
||||||
Self::fix_right_edge(root)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fix_right_edge(root: &mut node::Root<K, V>) {
|
|
||||||
// Handle underfull nodes, start from the top.
|
|
||||||
let mut cur_node = root.node_as_mut();
|
|
||||||
while let Internal(internal) = cur_node.force() {
|
|
||||||
// Check if right-most child is underfull.
|
|
||||||
let mut last_edge = internal.last_edge();
|
|
||||||
let right_child_len = last_edge.reborrow().descend().len();
|
|
||||||
if right_child_len < MIN_LEN {
|
|
||||||
// We need to steal.
|
|
||||||
let mut last_kv = match last_edge.left_kv() {
|
|
||||||
Ok(left) => left,
|
|
||||||
Err(_) => unreachable!(),
|
|
||||||
};
|
|
||||||
last_kv.bulk_steal_left(MIN_LEN - right_child_len);
|
|
||||||
last_edge = last_kv.right_edge();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go further down.
|
|
||||||
cur_node = last_edge.descend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Splits the collection into two at the given key. Returns everything after the given key,
|
/// Splits the collection into two at the given key. Returns everything after the given key,
|
||||||
/// including the key.
|
/// including the key.
|
||||||
///
|
///
|
||||||
|
@ -2220,18 +2141,5 @@ impl<K, V> BTreeMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Ord, V, I> Iterator for MergeIter<K, V, I>
|
|
||||||
where
|
|
||||||
I: Iterator<Item = (K, V)> + ExactSizeIterator + FusedIterator,
|
|
||||||
{
|
|
||||||
type Item = (K, V);
|
|
||||||
|
|
||||||
/// If two keys are equal, returns the key/value-pair from the right source.
|
|
||||||
fn next(&mut self) -> Option<(K, V)> {
|
|
||||||
let (a_next, b_next) = self.0.nexts(|a: &(K, V), b: &(K, V)| K::cmp(&a.0, &b.0));
|
|
||||||
b_next.or(a_next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
|
@ -1685,6 +1685,33 @@ create_append_test!(test_append_239, 239);
|
||||||
#[cfg(not(miri))] // Miri is too slow
|
#[cfg(not(miri))] // Miri is too slow
|
||||||
create_append_test!(test_append_1700, 1700);
|
create_append_test!(test_append_1700, 1700);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_append_drop_leak() {
|
||||||
|
static DROPS: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
struct D;
|
||||||
|
|
||||||
|
impl Drop for D {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if DROPS.fetch_add(1, Ordering::SeqCst) == 0 {
|
||||||
|
panic!("panic in `drop`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut left = BTreeMap::new();
|
||||||
|
let mut right = BTreeMap::new();
|
||||||
|
left.insert(0, D);
|
||||||
|
left.insert(1, D); // first to be dropped during append
|
||||||
|
left.insert(2, D);
|
||||||
|
right.insert(1, D);
|
||||||
|
right.insert(2, D);
|
||||||
|
|
||||||
|
catch_unwind(move || left.append(&mut right)).unwrap_err();
|
||||||
|
|
||||||
|
assert_eq!(DROPS.load(Ordering::SeqCst), 4); // Rust issue #47949 ate one little piggy
|
||||||
|
}
|
||||||
|
|
||||||
fn rand_data(len: usize) -> Vec<(u32, u32)> {
|
fn rand_data(len: usize) -> Vec<(u32, u32)> {
|
||||||
assert!(len * 2 <= 70029); // from that point on numbers repeat
|
assert!(len * 2 <= 70029); // from that point on numbers repeat
|
||||||
let mut rng = DeterministicRng::new();
|
let mut rng = DeterministicRng::new();
|
||||||
|
|
|
@ -2,27 +2,25 @@ use core::cmp::Ordering;
|
||||||
use core::fmt::{self, Debug};
|
use core::fmt::{self, Debug};
|
||||||
use core::iter::FusedIterator;
|
use core::iter::FusedIterator;
|
||||||
|
|
||||||
/// Core of an iterator that merges the output of two ascending iterators,
|
/// Core of an iterator that merges the output of two strictly ascending iterators,
|
||||||
/// for instance a union or a symmetric difference.
|
/// for instance a union or a symmetric difference.
|
||||||
pub struct MergeIterInner<I>
|
pub struct MergeIterInner<I: Iterator> {
|
||||||
where
|
|
||||||
I: Iterator,
|
|
||||||
{
|
|
||||||
a: I,
|
a: I,
|
||||||
b: I,
|
b: I,
|
||||||
peeked: Option<Peeked<I>>,
|
peeked: Option<Peeked<I>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Benchmarks faster than wrapping both iterators in a Peekable.
|
/// Benchmarks faster than wrapping both iterators in a Peekable,
|
||||||
|
/// probably because we can afford to impose a FusedIterator bound.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum Peeked<I: Iterator> {
|
enum Peeked<I: Iterator> {
|
||||||
A(I::Item),
|
A(I::Item),
|
||||||
B(I::Item),
|
B(I::Item),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> Clone for MergeIterInner<I>
|
impl<I: Iterator> Clone for MergeIterInner<I>
|
||||||
where
|
where
|
||||||
I: Clone + Iterator,
|
I: Clone,
|
||||||
I::Item: Clone,
|
I::Item: Clone,
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
|
@ -30,20 +28,17 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> Debug for MergeIterInner<I>
|
impl<I: Iterator> Debug for MergeIterInner<I>
|
||||||
where
|
where
|
||||||
I: Iterator + Debug,
|
I: Debug,
|
||||||
I::Item: Debug,
|
I::Item: Debug,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_tuple("MergeIterInner").field(&self.a).field(&self.b).finish()
|
f.debug_tuple("MergeIterInner").field(&self.a).field(&self.b).field(&self.peeked).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> MergeIterInner<I>
|
impl<I: Iterator> MergeIterInner<I> {
|
||||||
where
|
|
||||||
I: ExactSizeIterator + FusedIterator,
|
|
||||||
{
|
|
||||||
/// Creates a new core for an iterator merging a pair of sources.
|
/// Creates a new core for an iterator merging a pair of sources.
|
||||||
pub fn new(a: I, b: I) -> Self {
|
pub fn new(a: I, b: I) -> Self {
|
||||||
MergeIterInner { a, b, peeked: None }
|
MergeIterInner { a, b, peeked: None }
|
||||||
|
@ -52,13 +47,17 @@ where
|
||||||
/// Returns the next pair of items stemming from the pair of sources
|
/// Returns the next pair of items stemming from the pair of sources
|
||||||
/// being merged. If both returned options contain a value, that value
|
/// being merged. If both returned options contain a value, that value
|
||||||
/// is equal and occurs in both sources. If one of the returned options
|
/// is equal and occurs in both sources. If one of the returned options
|
||||||
/// contains a value, that value doesn't occur in the other source.
|
/// contains a value, that value doesn't occur in the other source (or
|
||||||
/// If neither returned option contains a value, iteration has finished
|
/// the sources are not strictly ascending). If neither returned option
|
||||||
/// and subsequent calls will return the same empty pair.
|
/// contains a value, iteration has finished and subsequent calls will
|
||||||
|
/// return the same empty pair.
|
||||||
pub fn nexts<Cmp: Fn(&I::Item, &I::Item) -> Ordering>(
|
pub fn nexts<Cmp: Fn(&I::Item, &I::Item) -> Ordering>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cmp: Cmp,
|
cmp: Cmp,
|
||||||
) -> (Option<I::Item>, Option<I::Item>) {
|
) -> (Option<I::Item>, Option<I::Item>)
|
||||||
|
where
|
||||||
|
I: FusedIterator,
|
||||||
|
{
|
||||||
let mut a_next;
|
let mut a_next;
|
||||||
let mut b_next;
|
let mut b_next;
|
||||||
match self.peeked.take() {
|
match self.peeked.take() {
|
||||||
|
@ -86,7 +85,10 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a pair of upper bounds for the `size_hint` of the final iterator.
|
/// Returns a pair of upper bounds for the `size_hint` of the final iterator.
|
||||||
pub fn lens(&self) -> (usize, usize) {
|
pub fn lens(&self) -> (usize, usize)
|
||||||
|
where
|
||||||
|
I: ExactSizeIterator,
|
||||||
|
{
|
||||||
match self.peeked {
|
match self.peeked {
|
||||||
Some(Peeked::A(_)) => (1 + self.a.len(), self.b.len()),
|
Some(Peeked::A(_)) => (1 + self.a.len(), self.b.len()),
|
||||||
Some(Peeked::B(_)) => (self.a.len(), 1 + self.b.len()),
|
Some(Peeked::B(_)) => (self.a.len(), 1 + self.b.len()),
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod append;
|
||||||
mod borrow;
|
mod borrow;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
mod mem;
|
mod mem;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue