Skip to content

Commit e189d70

Browse files
committed
Implement clone_from for HashMap
1 parent 6f8703d commit e189d70

File tree

3 files changed

+120
-32
lines changed

3 files changed

+120
-32
lines changed

src/map.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,29 @@ pub enum DefaultHashBuilder {}
188188
/// .iter().cloned().collect();
189189
/// // use the values stored in map
190190
/// ```
191-
#[derive(Clone)]
192191
pub struct HashMap<K, V, S = DefaultHashBuilder> {
193192
pub(crate) hash_builder: S,
194193
pub(crate) table: RawTable<(K, V)>,
195194
}
196195

196+
impl<K: Clone, V: Clone, S: Clone> Clone for HashMap<K, V, S> {
197+
fn clone(&self) -> Self {
198+
HashMap {
199+
hash_builder: self.hash_builder.clone(),
200+
table: self.table.clone(),
201+
}
202+
}
203+
204+
fn clone_from(&mut self, source: &Self) {
205+
// We clone the hash_builder first since this might panic and we don't
206+
// want the table to have elements hashed with the wrong hash_builder.
207+
let hash_builder = source.hash_builder.clone();
208+
209+
self.table.clone_from(&source.table);
210+
self.hash_builder = hash_builder;
211+
}
212+
}
213+
197214
#[cfg_attr(feature = "inline-more", inline)]
198215
pub(crate) fn make_hash<K: Hash + ?Sized>(hash_builder: &impl BuildHasher, val: &K) -> u64 {
199216
let mut state = hash_builder.build_hasher();
@@ -2820,6 +2837,21 @@ mod test_map {
28202837
assert_eq!(m2.len(), 2);
28212838
}
28222839

2840+
#[test]
2841+
fn test_clone_from() {
2842+
let mut m = HashMap::new();
2843+
let mut m2 = HashMap::new();
2844+
assert_eq!(m.len(), 0);
2845+
assert!(m.insert(1, 2).is_none());
2846+
assert_eq!(m.len(), 1);
2847+
assert!(m.insert(2, 4).is_none());
2848+
assert_eq!(m.len(), 2);
2849+
m2.clone_from(&m);
2850+
assert_eq!(*m2.get(&1).unwrap(), 2);
2851+
assert_eq!(*m2.get(&2).unwrap(), 4);
2852+
assert_eq!(m2.len(), 2);
2853+
}
2854+
28232855
thread_local! { static DROP_VECTOR: RefCell<Vec<i32>> = RefCell::new(Vec::new()) }
28242856

28252857
#[derive(Hash, PartialEq, Eq)]

src/raw/mod.rs

Lines changed: 75 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -987,47 +987,92 @@ impl<T: Clone> Clone for RawTable<T> {
987987
.unwrap_or_else(|_| hint::unreachable_unchecked()),
988988
);
989989

990-
// Copy the control bytes unchanged. We do this in a single pass
991-
self.ctrl(0)
992-
.copy_to_nonoverlapping(new_table.ctrl(0), self.num_ctrl_bytes());
993-
994-
{
995-
// The cloning of elements may panic, in which case we need
996-
// to make sure we drop only the elements that have been
997-
// cloned so far.
998-
let mut guard = guard((0, &mut new_table), |(index, new_table)| {
999-
if mem::needs_drop::<T>() {
1000-
for i in 0..=*index {
1001-
if is_full(*new_table.ctrl(i)) {
1002-
new_table.bucket(i).drop();
1003-
}
1004-
}
1005-
}
1006-
new_table.free_buckets();
1007-
});
990+
new_table.clone_from_impl(self, |new_table| {
991+
// We need to free the memory allocated for the new table.
992+
new_table.free_buckets();
993+
});
1008994

1009-
for from in self.iter() {
1010-
let index = self.bucket_index(&from);
1011-
let to = guard.1.bucket(index);
1012-
to.write(from.as_ref().clone());
995+
// Return the newly created table.
996+
ManuallyDrop::into_inner(new_table)
997+
}
998+
}
999+
}
10131000

1014-
// Update the index in case we need to unwind.
1015-
guard.0 = index;
1001+
fn clone_from(&mut self, source: &Self) {
1002+
if source.is_empty_singleton() {
1003+
*self = Self::new();
1004+
} else {
1005+
unsafe {
1006+
// First, drop all our elements without clearing the control bytes.
1007+
if mem::needs_drop::<T>() {
1008+
for item in self.iter() {
1009+
item.drop();
10161010
}
1011+
}
10171012

1018-
// Successfully cloned all items, no need to clean up.
1019-
mem::forget(guard);
1013+
// If necessary, resize our table to match the source.
1014+
if self.buckets() != source.buckets() {
1015+
// Skip our drop by using ptr::write.
1016+
self.free_buckets();
1017+
(self as *mut Self).write(
1018+
Self::new_uninitialized(source.buckets(), Fallibility::Infallible)
1019+
.unwrap_or_else(|_| hint::unreachable_unchecked()),
1020+
);
10201021
}
10211022

1022-
// Return the newly created table.
1023-
new_table.items = self.items;
1024-
new_table.growth_left = self.growth_left;
1025-
ManuallyDrop::into_inner(new_table)
1023+
self.clone_from_impl(source, |self_| {
1024+
// We need to leave the table in an empty state.
1025+
self_.clear_no_drop()
1026+
});
10261027
}
10271028
}
10281029
}
10291030
}
10301031

1032+
impl<T: Clone> RawTable<T> {
1033+
/// Common code for clone and clone_from. Assumes `self.buckets() == source.buckets()`.
1034+
#[cfg_attr(feature = "inline-more", inline)]
1035+
unsafe fn clone_from_impl(&mut self, source: &Self, mut on_panic: impl FnMut(&mut Self)) {
1036+
// Copy the control bytes unchanged. We do this in a single pass
1037+
source
1038+
.ctrl(0)
1039+
.copy_to_nonoverlapping(self.ctrl(0), self.num_ctrl_bytes());
1040+
1041+
// The cloning of elements may panic, in which case we need
1042+
// to make sure we drop only the elements that have been
1043+
// cloned so far.
1044+
let mut guard = guard((0, &mut *self), |(index, self_)| {
1045+
if mem::needs_drop::<T>() {
1046+
for i in 0..=*index {
1047+
if is_full(*self_.ctrl(i)) {
1048+
self_.bucket(i).drop();
1049+
}
1050+
}
1051+
}
1052+
1053+
// Depending on whether we were called from clone or clone_from, we
1054+
// either need to free the memory for the destination table or just
1055+
// clear the control bytes.
1056+
on_panic(self_);
1057+
});
1058+
1059+
for from in source.iter() {
1060+
let index = source.bucket_index(&from);
1061+
let to = guard.1.bucket(index);
1062+
to.write(from.as_ref().clone());
1063+
1064+
// Update the index in case we need to unwind.
1065+
guard.0 = index;
1066+
}
1067+
1068+
// Successfully cloned all items, no need to clean up.
1069+
mem::forget(guard);
1070+
1071+
self.items = source.items;
1072+
self.growth_left = source.growth_left;
1073+
}
1074+
}
1075+
10311076
#[cfg(feature = "nightly")]
10321077
unsafe impl<#[may_dangle] T> Drop for RawTable<T> {
10331078
#[cfg_attr(feature = "inline-more", inline)]

src/set.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,22 @@ use super::map::{self, DefaultHashBuilder, HashMap, Keys};
111111
/// [`HashMap`]: struct.HashMap.html
112112
/// [`PartialEq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html
113113
/// [`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
114-
#[derive(Clone)]
115114
pub struct HashSet<T, S = DefaultHashBuilder> {
116115
pub(crate) map: HashMap<T, (), S>,
117116
}
118117

118+
impl<T: Clone, S: Clone> Clone for HashSet<T, S> {
119+
fn clone(&self) -> Self {
120+
HashSet {
121+
map: self.map.clone(),
122+
}
123+
}
124+
125+
fn clone_from(&mut self, source: &Self) {
126+
self.map.clone_from(&source.map);
127+
}
128+
}
129+
119130
#[cfg(feature = "ahash")]
120131
impl<T: Hash + Eq> HashSet<T, DefaultHashBuilder> {
121132
/// Creates an empty `HashSet`.

0 commit comments

Comments
 (0)