Skip to content

Commit b9d51ed

Browse files
authored
Rollup merge of rust-lang#53652 - oconnor663:copy_in_place, r=alexcrichton
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`. I've wanted this API a couple times in the past, and I figured I'd just whip up a PR to help discuss it. It's possible something like this exists elsewhere and I just missed it. It might also be a big enough addition to warrant an RFC, I'm not sure.
2 parents 1002e40 + d0e59f5 commit b9d51ed

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed

src/libcore/slice/mod.rs

+57
Original file line numberDiff line numberDiff line change
@@ -1618,6 +1618,63 @@ impl<T> [T] {
16181618
}
16191619
}
16201620

1621+
/// Copies elements from one part of the slice to another part of itself,
1622+
/// using a memmove.
1623+
///
1624+
/// `src` is the range within `self` to copy from. `dest` is the starting
1625+
/// index of the range within `self` to copy to, which will have the same
1626+
/// length as `src`. The two ranges may overlap. The ends of the two ranges
1627+
/// must be less than or equal to `self.len()`.
1628+
///
1629+
/// # Panics
1630+
///
1631+
/// This function will panic if either range exceeds the end of the slice,
1632+
/// or if the end of `src` is before the start.
1633+
///
1634+
/// # Examples
1635+
///
1636+
/// Copying four bytes within a slice:
1637+
///
1638+
/// ```
1639+
/// # #![feature(copy_within)]
1640+
/// let mut bytes = *b"Hello, World!";
1641+
///
1642+
/// bytes.copy_within(1..5, 8);
1643+
///
1644+
/// assert_eq!(&bytes, b"Hello, Wello!");
1645+
/// ```
1646+
#[unstable(feature = "copy_within", issue = "54236")]
1647+
pub fn copy_within<R: ops::RangeBounds<usize>>(&mut self, src: R, dest: usize)
1648+
where
1649+
T: Copy,
1650+
{
1651+
let src_start = match src.start_bound() {
1652+
ops::Bound::Included(&n) => n,
1653+
ops::Bound::Excluded(&n) => n
1654+
.checked_add(1)
1655+
.unwrap_or_else(|| slice_index_overflow_fail()),
1656+
ops::Bound::Unbounded => 0,
1657+
};
1658+
let src_end = match src.end_bound() {
1659+
ops::Bound::Included(&n) => n
1660+
.checked_add(1)
1661+
.unwrap_or_else(|| slice_index_overflow_fail()),
1662+
ops::Bound::Excluded(&n) => n,
1663+
ops::Bound::Unbounded => self.len(),
1664+
};
1665+
assert!(src_start <= src_end, "src end is before src start");
1666+
assert!(src_end <= self.len(), "src is out of bounds");
1667+
let count = src_end - src_start;
1668+
assert!(dest <= self.len() - count, "dest is out of bounds");
1669+
unsafe {
1670+
ptr::copy(
1671+
self.get_unchecked(src_start),
1672+
self.get_unchecked_mut(dest),
1673+
count,
1674+
);
1675+
}
1676+
}
1677+
16211678
/// Swaps all elements in `self` with those in `other`.
16221679
///
16231680
/// The length of `other` must be the same as `self`.

src/libcore/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#![feature(inner_deref)]
4040
#![feature(slice_internals)]
4141
#![feature(option_replace)]
42+
#![feature(copy_within)]
4243

4344
extern crate core;
4445
extern crate test;

src/libcore/tests/slice.rs

+46
Original file line numberDiff line numberDiff line change
@@ -1000,3 +1000,49 @@ fn test_align_to_empty_mid() {
10001000
assert_eq!(mid.as_ptr() as usize % mem::align_of::<Chunk>(), 0);
10011001
}
10021002
}
1003+
1004+
#[test]
1005+
fn test_copy_within() {
1006+
// Start to end, with a RangeTo.
1007+
let mut bytes = *b"Hello, World!";
1008+
bytes.copy_within(..3, 10);
1009+
assert_eq!(&bytes, b"Hello, WorHel");
1010+
1011+
// End to start, with a RangeFrom.
1012+
let mut bytes = *b"Hello, World!";
1013+
bytes.copy_within(10.., 0);
1014+
assert_eq!(&bytes, b"ld!lo, World!");
1015+
1016+
// Overlapping, with a RangeInclusive.
1017+
let mut bytes = *b"Hello, World!";
1018+
bytes.copy_within(0..=11, 1);
1019+
assert_eq!(&bytes, b"HHello, World");
1020+
1021+
// Whole slice, with a RangeFull.
1022+
let mut bytes = *b"Hello, World!";
1023+
bytes.copy_within(.., 0);
1024+
assert_eq!(&bytes, b"Hello, World!");
1025+
}
1026+
1027+
#[test]
1028+
#[should_panic(expected = "src is out of bounds")]
1029+
fn test_copy_within_panics_src_too_long() {
1030+
let mut bytes = *b"Hello, World!";
1031+
// The length is only 13, so 14 is out of bounds.
1032+
bytes.copy_within(10..14, 0);
1033+
}
1034+
1035+
#[test]
1036+
#[should_panic(expected = "dest is out of bounds")]
1037+
fn test_copy_within_panics_dest_too_long() {
1038+
let mut bytes = *b"Hello, World!";
1039+
// The length is only 13, so a slice of length 4 starting at index 10 is out of bounds.
1040+
bytes.copy_within(0..4, 10);
1041+
}
1042+
#[test]
1043+
#[should_panic(expected = "src end is before src start")]
1044+
fn test_copy_within_panics_src_inverted() {
1045+
let mut bytes = *b"Hello, World!";
1046+
// 2 is greater than 1, so this range is invalid.
1047+
bytes.copy_within(2..1, 0);
1048+
}

0 commit comments

Comments
 (0)