Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement clone_from for BTreeMap and BTreeSet #66648

Merged
merged 7 commits into from
Jan 30, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 69 additions & 7 deletions src/liballoc/collections/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,60 @@ impl<K: Clone, V: Clone> Clone for BTreeMap<K, V> {
clone_subtree(self.root.as_ref())
}
}

fn clone_from(&mut self, other: &Self) {
Centril marked this conversation as resolved.
Show resolved Hide resolved
BTreeClone::clone_from(self, other);
}
}

trait BTreeClone {
fn clone_from(&mut self, other: &Self);
}

impl<K: Clone, V: Clone> BTreeClone for BTreeMap<K, V> {
default fn clone_from(&mut self, other: &Self) {
Centril marked this conversation as resolved.
Show resolved Hide resolved
*self = other.clone();
}
}

impl<K: Clone + Ord, V: Clone> BTreeClone for BTreeMap<K, V> {
fn clone_from(&mut self, other: &Self) {
// This truncates `self` to `other.len()` by calling `split_off` on
// the first key after `other.len()` elements if it exists
if let Some(key) = {
KodrAus marked this conversation as resolved.
Show resolved Hide resolved
if self.len() > other.len() {
let diff = self.len() - other.len();
if diff <= other.len() {
self.iter().nth_back(diff - 1).map(|pair| (*pair.0).clone())
} else {
self.iter().nth(other.len()).map(|pair| (*pair.0).clone())
}
} else {
None
}
} {
self.split_off(&key);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel that a lot of duplicate work being done here. We first find the nth key, the pass it to split_off so that it can find the position of that key (again!), after which it actually splits the tree. Would it be possible to avoid doing the key lookup twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that I can see, unfortunately! However, split_off doesn't exactly find the key and then split the tree. Because split_off has a target key, it can split the tree as it's searching for the key position on the edges it descends on. In theory it would be better to have a split_after function that splits off the first n keys, but in practice the BTree doesn't have any subtree size information available (and traverses the whole tree every time it needs to recalculate length as a result). Similarly, it would definitely be more efficient if nth was specialized for BTree iterators, but I don't see an easy way to do that. Please let me know if you have any ideas about how to avoid iterating through (up to) half the tree!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is that you are traversing the tree twice: once in the nth call in the iterator, and once in split_off. However fixing this is likely to be very complicated, so it's fine to leave it as it is for now.

}
let mut siter = self.range_mut(..);
let mut oiter = other.iter();
// After truncation, `self` is at most as long as `other` so this loop
// replaces every key-value pair in `self`. Since `oiter` is in sorted
// order and the structure of the `BTreeMap` stays the same,
// the BTree invariants are maintained at the end of the loop
while siter.front != siter.back {
KodrAus marked this conversation as resolved.
Show resolved Hide resolved
if let Some((ok, ov)) = oiter.next() {
// SAFETY: This is safe because the `siter.front != siter.back` check
// ensures that `siter` is nonempty
let (sk, sv) = unsafe { siter.next_unchecked() };
Centril marked this conversation as resolved.
Show resolved Hide resolved
sk.clone_from(ok);
sv.clone_from(ov);
} else {
break;
}
}
// If `other` is longer than `self`, the remaining elements are inserted
self.extend(oiter.map(|(k, v)| ((*k).clone(), (*v).clone())));
}
}

impl<K, Q: ?Sized> super::Recover<Q> for BTreeMap<K, ()>
Expand Down Expand Up @@ -1328,7 +1382,10 @@ impl<'a, K: 'a, V: 'a> Iterator for IterMut<'a, K, V> {
None
} else {
self.length -= 1;
unsafe { Some(self.range.next_unchecked()) }
unsafe {
let (k, v) = self.range.next_unchecked();
Some((k, v)) // coerce k from `&mut K` to `&K`
}
}
}

Expand Down Expand Up @@ -1707,7 +1764,14 @@ impl<'a, K, V> Iterator for RangeMut<'a, K, V> {
type Item = (&'a K, &'a mut V);

fn next(&mut self) -> Option<(&'a K, &'a mut V)> {
if self.front == self.back { None } else { unsafe { Some(self.next_unchecked()) } }
if self.front == self.back {
None
} else {
unsafe {
let (k, v) = self.next_unchecked();
Some((k, v)) // coerce k from `&mut K` to `&K`
}
}
}

fn last(mut self) -> Option<(&'a K, &'a mut V)> {
Expand All @@ -1716,16 +1780,15 @@ impl<'a, K, V> Iterator for RangeMut<'a, K, V> {
}

impl<'a, K, V> RangeMut<'a, K, V> {
unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) {
unsafe fn next_unchecked(&mut self) -> (&'a mut K, &'a mut V) {
KodrAus marked this conversation as resolved.
Show resolved Hide resolved
let handle = ptr::read(&self.front);

let mut cur_handle = match handle.right_kv() {
Ok(kv) => {
self.front = ptr::read(&kv).right_edge();
// Doing the descend invalidates the references returned by `into_kv_mut`,
// so we have to do this last.
let (k, v) = kv.into_kv_mut();
return (k, v); // coerce k from `&mut K` to `&K`
return kv.into_kv_mut();
}
Err(last_edge) => {
let next_level = last_edge.into_node().ascend().ok();
Expand All @@ -1739,8 +1802,7 @@ impl<'a, K, V> RangeMut<'a, K, V> {
self.front = first_leaf_edge(ptr::read(&kv).right_edge().descend());
// Doing the descend invalidates the references returned by `into_kv_mut`,
// so we have to do this last.
let (k, v) = kv.into_kv_mut();
return (k, v); // coerce k from `&mut K` to `&K`
return kv.into_kv_mut();
}
Err(last_edge) => {
let next_level = last_edge.into_node().ascend().ok();
Expand Down
13 changes: 12 additions & 1 deletion src/liballoc/collections/btree/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,23 @@ use crate::collections::btree_map::{self, BTreeMap, Keys};
/// println!("{}", book);
/// }
/// ```
#[derive(Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
#[derive(Hash, PartialEq, Eq, Ord, PartialOrd)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct BTreeSet<T> {
map: BTreeMap<T, ()>,
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Clone> Clone for BTreeSet<T> {
fn clone(&self) -> Self {
BTreeSet { map: self.map.clone() }
}

fn clone_from(&mut self, other: &Self) {
Centril marked this conversation as resolved.
Show resolved Hide resolved
self.map.clone_from(&other.map);
}
}

/// An iterator over the items of a `BTreeSet`.
///
/// This `struct` is created by the [`iter`] method on [`BTreeSet`].
Expand Down
20 changes: 20 additions & 0 deletions src/liballoc/tests/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,26 @@ fn test_clone() {
}
}

#[test]
fn test_clone_from() {
KodrAus marked this conversation as resolved.
Show resolved Hide resolved
let mut map1 = BTreeMap::new();
let size = 30;

for i in 0..size {
map1.insert(i, 10 * i);
let mut map2 = BTreeMap::new();
for j in 0..i {
map2.insert(100 * j + 1, 2 * j + 1);
let mut map1_copy = map2.clone();
map1_copy.clone_from(&map1);
assert_eq!(map1_copy, map1);
let mut map2_copy = map1.clone();
map2_copy.clone_from(&map2);
assert_eq!(map2_copy, map2);
}
}
}

#[test]
#[allow(dead_code)]
fn test_variance() {
Expand Down