From e8339e820b0c4e7c9a26a3e3495b7655e60bd102 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 20 Mar 2020 15:28:26 -0700 Subject: [PATCH 1/3] Use split_at in slice's ToOwned::clone_into It appears to codegen slightly more efficiently with `split_at` taking two slices at once, rather than slicing across different calls. --- src/liballoc/slice.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index a3d9c78b7f5..cd750d25580 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -733,14 +733,14 @@ fn to_owned(&self) -> Vec { fn clone_into(&self, target: &mut Vec) { // drop anything in target that will not be overwritten target.truncate(self.len()); - let len = target.len(); - - // reuse the contained values' allocations/resources. - target.clone_from_slice(&self[..len]); // target.len <= self.len due to the truncate above, so the - // slice here is always in-bounds. - target.extend_from_slice(&self[len..]); + // slices here are always in-bounds. + let (init, tail) = self.split_at(target.len()); + + // reuse the contained values' allocations/resources. + target.clone_from_slice(init); + target.extend_from_slice(tail); } } From b80fa76ee0a160fffa04e337b8e33ef655a80649 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 20 Mar 2020 15:42:56 -0700 Subject: [PATCH 2/3] Implement ToOwned::clone_into for CStr It can try to keep its allocation by converting the inner `Box` to `Vec`, using `clone_into` on the bytes, then convert back to `Box`. --- src/libstd/ffi/c_str.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 04eaba515ff..0a4802fb2c8 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -1329,6 +1329,12 @@ impl ToOwned for CStr { fn to_owned(&self) -> CString { CString { inner: self.to_bytes_with_nul().into() } } + + fn clone_into(&self, target: &mut CString) { + let mut b = Vec::from(mem::take(&mut target.inner)); + self.to_bytes_with_nul().clone_into(&mut b); + target.inner = b.into_boxed_slice(); + } } #[stable(feature = "cstring_asref", since = "1.7.0")] @@ -1510,6 +1516,17 @@ fn boxed_default() { assert_eq!(boxed.to_bytes_with_nul(), &[0]); } + #[test] + fn test_c_str_clone_into() { + let mut c_string = CString::new("lorem").unwrap(); + let c_ptr = c_string.as_ptr(); + let c_str = CStr::from_bytes_with_nul(b"ipsum\0").unwrap(); + c_str.clone_into(&mut c_string); + assert_eq!(c_str, c_string.as_c_str()); + // The exact same size shouldn't have needed to move its allocation + assert_eq!(c_ptr, c_string.as_ptr()); + } + #[test] fn into_rc() { let orig: &[u8] = b"Hello, world!\0"; From f854070bb820501d88d1b029660bfde663595530 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 20 Mar 2020 15:46:40 -0700 Subject: [PATCH 3/3] Forward OsStr::clone_into to the inner Vec Despite OS differences, they're all just `Vec` inside, so we can just forward `clone_into` calls to that optimized implementation. --- src/libstd/ffi/os_str.rs | 3 +-- src/libstd/sys/windows/os_str.rs | 4 ++++ src/libstd/sys_common/os_str_bytes.rs | 4 ++++ src/libstd/sys_common/wtf8.rs | 4 ++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index 5e686946f8e..4fde3316973 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -1120,8 +1120,7 @@ fn to_owned(&self) -> OsString { self.to_os_string() } fn clone_into(&self, target: &mut OsString) { - target.clear(); - target.push(self); + self.inner.clone_into(&mut target.inner) } } diff --git a/src/libstd/sys/windows/os_str.rs b/src/libstd/sys/windows/os_str.rs index 6aab028873e..2f5fc72ab44 100644 --- a/src/libstd/sys/windows/os_str.rs +++ b/src/libstd/sys/windows/os_str.rs @@ -159,6 +159,10 @@ pub fn to_owned(&self) -> Buf { Buf { inner: buf } } + pub fn clone_into(&self, buf: &mut Buf) { + self.inner.clone_into(&mut buf.inner) + } + #[inline] pub fn into_box(&self) -> Box { unsafe { mem::transmute(self.inner.into_box()) } diff --git a/src/libstd/sys_common/os_str_bytes.rs b/src/libstd/sys_common/os_str_bytes.rs index aa6cc33d831..984c032e2a3 100644 --- a/src/libstd/sys_common/os_str_bytes.rs +++ b/src/libstd/sys_common/os_str_bytes.rs @@ -173,6 +173,10 @@ pub fn to_owned(&self) -> Buf { Buf { inner: self.inner.to_vec() } } + pub fn clone_into(&self, buf: &mut Buf) { + self.inner.clone_into(&mut buf.inner) + } + #[inline] pub fn into_box(&self) -> Box { let boxed: Box<[u8]> = self.inner.into(); diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index fc6614552a9..a98407da448 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -613,6 +613,10 @@ fn initial_trail_surrogate(&self) -> Option { } } + pub fn clone_into(&self, buf: &mut Wtf8Buf) { + self.bytes.clone_into(&mut buf.bytes) + } + /// Boxes this `Wtf8`. #[inline] pub fn into_box(&self) -> Box {