From b3ffd3344e3ac5e2468c7255c2bbecac13daab82 Mon Sep 17 00:00:00 2001 From: Jack O'Connor Date: Thu, 23 Aug 2018 18:22:53 -0400 Subject: [PATCH 1/2] define copy_within on slices This is a safe wrapper around ptr::copy, for regions within a single slice. Previously, safe in-place copying was only available as a side effect of Vec::drain. --- src/libcore/slice/mod.rs | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index da4a56cfecd6e..e4ac79a26d860 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -1618,6 +1618,63 @@ impl [T] { } } + /// Copies elements from one part of the slice to another part of itself, + /// using a memmove. + /// + /// `src` is the range within `self` to copy from. `dest` is the starting + /// index of the range within `self` to copy to, which will have the same + /// length as `src`. The two ranges may overlap. The ends of the two ranges + /// must be less than or equal to `self.len()`. + /// + /// # Panics + /// + /// This function will panic if either range exceeds the end of the slice, + /// or if the end of `src` is before the start. + /// + /// # Examples + /// + /// Copying four bytes within a slice: + /// + /// ``` + /// # #![feature(copy_within)] + /// let mut bytes = *b"Hello, World!"; + /// + /// bytes.copy_within(1..5, 8); + /// + /// assert_eq!(&bytes, b"Hello, Wello!"); + /// ``` + #[unstable(feature = "copy_within", issue = "54236")] + pub fn copy_within>(&mut self, src: R, dest: usize) + where + T: Copy, + { + let src_start = match src.start_bound() { + ops::Bound::Included(&n) => n, + ops::Bound::Excluded(&n) => n + .checked_add(1) + .unwrap_or_else(|| slice_index_overflow_fail()), + ops::Bound::Unbounded => 0, + }; + let src_end = match src.end_bound() { + ops::Bound::Included(&n) => n + .checked_add(1) + .unwrap_or_else(|| slice_index_overflow_fail()), + ops::Bound::Excluded(&n) => n, + ops::Bound::Unbounded => self.len(), + }; + assert!(src_start <= src_end, "src end is before src start"); + assert!(src_end <= self.len(), "src is out of bounds"); + let count = src_end - src_start; + assert!(dest <= self.len() - count, "dest is out of bounds"); + unsafe { + ptr::copy( + self.get_unchecked(src_start), + self.get_unchecked_mut(dest), + count, + ); + } + } + /// Swaps all elements in `self` with those in `other`. /// /// The length of `other` must be the same as `self`. From d0e59f563d11a0d8efbe9a59e7b20526bc42adce Mon Sep 17 00:00:00 2001 From: Jack O'Connor Date: Wed, 19 Sep 2018 12:03:16 -0400 Subject: [PATCH 2/2] add tests for copy_within --- src/libcore/tests/lib.rs | 1 + src/libcore/tests/slice.rs | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 4f3086575c0cf..8fc32f40b9920 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -39,6 +39,7 @@ #![feature(inner_deref)] #![feature(slice_internals)] #![feature(option_replace)] +#![feature(copy_within)] extern crate core; extern crate test; diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 012dc9bf5e0b9..d46a35ab82cfc 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -1000,3 +1000,49 @@ fn test_align_to_empty_mid() { assert_eq!(mid.as_ptr() as usize % mem::align_of::(), 0); } } + +#[test] +fn test_copy_within() { + // Start to end, with a RangeTo. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(..3, 10); + assert_eq!(&bytes, b"Hello, WorHel"); + + // End to start, with a RangeFrom. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(10.., 0); + assert_eq!(&bytes, b"ld!lo, World!"); + + // Overlapping, with a RangeInclusive. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(0..=11, 1); + assert_eq!(&bytes, b"HHello, World"); + + // Whole slice, with a RangeFull. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(.., 0); + assert_eq!(&bytes, b"Hello, World!"); +} + +#[test] +#[should_panic(expected = "src is out of bounds")] +fn test_copy_within_panics_src_too_long() { + let mut bytes = *b"Hello, World!"; + // The length is only 13, so 14 is out of bounds. + bytes.copy_within(10..14, 0); +} + +#[test] +#[should_panic(expected = "dest is out of bounds")] +fn test_copy_within_panics_dest_too_long() { + let mut bytes = *b"Hello, World!"; + // The length is only 13, so a slice of length 4 starting at index 10 is out of bounds. + bytes.copy_within(0..4, 10); +} +#[test] +#[should_panic(expected = "src end is before src start")] +fn test_copy_within_panics_src_inverted() { + let mut bytes = *b"Hello, World!"; + // 2 is greater than 1, so this range is invalid. + bytes.copy_within(2..1, 0); +}