about summary refs log tree commit diff
path: root/src/liballoc
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2020-05-30 13:45:06 +0200
committerGitHub <noreply@github.com>2020-05-30 13:45:06 +0200
commit8d64fd80adef2c80325f7a5a545108035198daba (patch)
treed4c7cc359f8fe829430ca74ed89883f1b0aa72e2 /src/liballoc
parent49ca99de93f8ed26b16ac2ef70c61c7ed2267754 (diff)
parentdbf32e2270f82601bd2816da270ce70269cc59ba (diff)
downloadrust-8d64fd80adef2c80325f7a5a545108035198daba.tar.gz
rust-8d64fd80adef2c80325f7a5a545108035198daba.zip
Rollup merge of #72499 - mendess:master, r=dtolnay
Override Box::<[T]>::clone_from

Avoid dropping and reallocating when cloning to an existing box if the lengths are the same.

It would be nice if this could also be specialized for `Copy` but I don't know how that works since it's not on stable. Will gladly look into it if it's deemed as a good idea.

This is my first PR with code, hope I did everything right :smile:
Diffstat (limited to 'src/liballoc')
-rw-r--r--src/liballoc/boxed.rs8
-rw-r--r--src/liballoc/tests/boxed.rs33
2 files changed, 41 insertions, 0 deletions
diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs
index 8cc6f04c065..22c344323a2 100644
--- a/src/liballoc/boxed.rs
+++ b/src/liballoc/boxed.rs
@@ -1109,6 +1109,14 @@ impl<T: Clone> Clone for Box<[T]> {
     fn clone(&self) -> Self {
         self.to_vec().into_boxed_slice()
     }
+
+    fn clone_from(&mut self, other: &Self) {
+        if self.len() == other.len() {
+            self.clone_from_slice(&other);
+        } else {
+            *self = other.clone();
+        }
+    }
 }
 
 #[stable(feature = "box_borrow", since = "1.1.0")]
diff --git a/src/liballoc/tests/boxed.rs b/src/liballoc/tests/boxed.rs
index 66782ecbeb7..5377485da8f 100644
--- a/src/liballoc/tests/boxed.rs
+++ b/src/liballoc/tests/boxed.rs
@@ -16,3 +16,36 @@ fn unitialized_zero_size_box() {
         NonNull::<MaybeUninit<String>>::dangling().as_ptr(),
     );
 }
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+struct Dummy {
+    _data: u8,
+}
+
+#[test]
+fn box_clone_and_clone_from_equivalence() {
+    for size in (0..8).map(|i| 2usize.pow(i)) {
+        let control = vec![Dummy { _data: 42 }; size].into_boxed_slice();
+        let clone = control.clone();
+        let mut copy = vec![Dummy { _data: 84 }; size].into_boxed_slice();
+        copy.clone_from(&control);
+        assert_eq!(control, clone);
+        assert_eq!(control, copy);
+    }
+}
+
+/// This test might give a false positive in case the box realocates, but the alocator keeps the
+/// original pointer.
+///
+/// On the other hand it won't give a false negative, if it fails than the memory was definitly not
+/// reused
+#[test]
+fn box_clone_from_ptr_stability() {
+    for size in (0..8).map(|i| 2usize.pow(i)) {
+        let control = vec![Dummy { _data: 42 }; size].into_boxed_slice();
+        let mut copy = vec![Dummy { _data: 84 }; size].into_boxed_slice();
+        let copy_raw = copy.as_ptr() as usize;
+        copy.clone_from(&control);
+        assert_eq!(copy.as_ptr() as usize, copy_raw);
+    }
+}