From 0f3d1b805a27710d6ef23e2e2d873e9420951266 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 28 May 2022 12:40:26 +0200 Subject: [PATCH] Add `as_ptr` and `as_mut_ptr` inherent method to `String` Before, they went through `&str` and `&mut str`, which created intermediary references, shrinking provenance to only the initialized parts. `Vec` already has such inherent methods added in #61114. --- library/alloc/src/string.rs | 75 +++++++++++++++++++++++++++++++++++ library/alloc/tests/string.rs | 19 +++++++++ 2 files changed, 94 insertions(+) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 668af60611b86..9cc6f5d755776 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1612,6 +1612,81 @@ impl String { &mut self.vec } + /// Returns a raw pointer to the string's buffer. + /// + /// The caller must ensure that the string outlives the pointer this + /// function returns, or else it will end up pointing to garbage. + /// Modifying the string may cause its buffer to be reallocated, + /// which would also make any pointers to it invalid. + /// + /// The caller must also ensure that the memory the pointer (non-transitively) points to + /// is never written to (except inside an `UnsafeCell`) using this pointer or any pointer + /// derived from it. If you need to mutate the contents of the slice, use [`as_mut_ptr`]. + /// + /// # Examples + /// + /// ``` + /// let x = "012".to_string(); + /// let x_ptr = x.as_ptr(); + /// + /// unsafe { + /// for i in 0..x.len() { + /// assert_eq!(*x_ptr.add(i), b'0' + u8::try_from(i).unwrap()); + /// } + /// } + /// ``` + /// + /// [`as_mut_ptr`]: String::as_mut_ptr + #[stable(feature = "string_as_ptr", since = "1.63.0")] + #[inline] + pub fn as_ptr(&self) -> *const u8 { + // We shadow the str method of the same name to avoid going through + // `deref`, which creates an intermediate reference. + self.vec.as_ptr() + } + + /// Returns an unsafe mutable pointer to the string's buffer. + /// + /// The caller must ensure that the string outlives the pointer this + /// function returns, or else it will end up pointing to garbage. + /// Modifying the string may cause its buffer to be reallocated, + /// which would also make any pointers to it invalid. + /// + /// The caller must also guarantee to not write invalid UTF-8 to the + /// initialized part of the buffer. + /// + /// # Examples + /// + /// ``` + /// # use core::mem::ManuallyDrop; + /// + /// // Allocate String big enough for 4 elements. + /// let size = 4; + /// // use a ManuallyDrop to avoid a double free + /// let mut x = ManuallyDrop::new(String::with_capacity(size)); + /// let x_ptr = x.as_mut_ptr(); + /// + /// // Initialize elements via raw pointer writes. + /// unsafe { + /// for i in 0..size { + /// *x_ptr.add(i) = b'A'; + /// } + /// } + /// + /// // Create a new String from the ptr + /// unsafe { + /// let y = String::from_utf8_unchecked(Vec::from_raw_parts(x_ptr, size, size)); + /// assert_eq!(y.as_str(), "AAAA"); + /// } + /// ``` + #[stable(feature = "string_as_ptr", since = "1.63.0")] + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut u8 { + // We shadow the str method of the same name to avoid going through + // `deref_mut`, which creates an intermediate reference. + self.vec.as_mut_ptr() + } + /// Returns the length of this `String`, in bytes, not [`char`]s or /// graphemes. In other words, it might not be what a human considers the /// length of the string. diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index b6836fdc88ee8..aae31a30f9dfd 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -874,3 +874,22 @@ fn test_str_concat() { let s: String = format!("{a}{b}"); assert_eq!(s.as_bytes()[9], 'd' as u8); } + +#[test] +fn test_string_as_mut_ptr_roundtrip() { + // Allocate String big enough for 4 elements. + let cap = 4; + // use a ManuallyDrop to avoid a double free + let mut x = std::mem::ManuallyDrop::new(String::with_capacity(cap)); + let x_ptr = x.as_mut_ptr(); + + // Create a new String from the ptr + // Because as_mut_ptr goes through Vec::as_mut_ptr, it has provenance for the entire allocation + let mut y = unsafe { String::from_utf8_unchecked(Vec::from_raw_parts(x_ptr, 0, cap)) }; + // We have provenance to write to the empty capacity + y.push_str("uwu"); + assert_eq!(y.as_str(), "uwu"); + + y.push('!'); + assert_eq!(y.as_str(), "uwu!"); +}