1 use crate::{ 2 alloc::{TryClone, str_ptr_from_raw_parts, try_realloc}, 3 error::OutOfMemory, 4 }; 5 use core::{borrow::Borrow, fmt, mem, ops}; 6 use std_alloc::{alloc::Layout, boxed::Box, string as inner}; 7 8 /// A newtype wrapper around [`std::string::String`] that only exposes 9 /// fallible-allocation methods. 10 #[derive(Default, Hash, PartialEq, Eq, PartialOrd, Ord)] 11 pub struct TryString { 12 inner: inner::String, 13 } 14 15 impl TryClone for TryString { 16 fn try_clone(&self) -> Result<Self, OutOfMemory> { 17 let mut s = Self::new(); 18 s.push_str(self)?; 19 Ok(s) 20 } 21 } 22 23 impl fmt::Debug for TryString { 24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 25 fmt::Debug::fmt(&self.inner, f) 26 } 27 } 28 29 impl fmt::Display for TryString { 30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 fmt::Display::fmt(&self.inner, f) 32 } 33 } 34 35 impl ops::Deref for TryString { 36 type Target = str; 37 38 #[inline] 39 fn deref(&self) -> &Self::Target { 40 &self.inner 41 } 42 } 43 44 impl ops::DerefMut for TryString { 45 #[inline] 46 fn deref_mut(&mut self) -> &mut Self::Target { 47 &mut self.inner 48 } 49 } 50 51 impl AsRef<str> for TryString { 52 fn as_ref(&self) -> &str { 53 self 54 } 55 } 56 57 impl Borrow<str> for TryString { 58 fn borrow(&self) -> &str { 59 self 60 } 61 } 62 63 impl From<inner::String> for TryString { 64 #[inline] 65 fn from(inner: inner::String) -> Self { 66 Self { inner } 67 } 68 } 69 70 #[cfg(feature = "serde")] 71 impl serde::ser::Serialize for TryString { 72 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 73 where 74 S: serde::Serializer, 75 { 76 serializer.serialize_str(self) 77 } 78 } 79 80 #[cfg(feature = "serde")] 81 impl<'de> serde::de::Deserialize<'de> for TryString { 82 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 83 where 84 D: serde::Deserializer<'de>, 85 { 86 struct Visitor; 87 88 impl<'de> serde::de::Visitor<'de> for Visitor { 89 type Value = TryString; 90 91 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { 92 f.write_str("a `wasmtime_core::alloc::String` str") 93 } 94 95 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 96 where 97 E: serde::de::Error, 98 { 99 let mut s = TryString::new(); 100 s.reserve_exact(v.len()).map_err(|oom| E::custom(oom))?; 101 s.push_str(v).expect("reserved capacity"); 102 Ok(s) 103 } 104 } 105 106 // NB: do not use `deserialize_string` as that eagerly allocates the 107 // `String` and does not give us a chance to handle OOM. Instead, use 108 // `deserialize_str` which passes the visitor the borrowed `str`, giving 109 // us a chance to fallibly allocate space. 110 deserializer.deserialize_str(Visitor) 111 } 112 } 113 114 impl TryString { 115 /// Same as [`std::string::String::new`]. 116 #[inline] 117 pub fn new() -> Self { 118 Self { 119 inner: inner::String::new(), 120 } 121 } 122 123 /// Same as [`std::string::String::with_capacity`] but returns an error on 124 /// allocation failure. 125 #[inline] 126 pub fn with_capacity(capacity: usize) -> Result<Self, OutOfMemory> { 127 let mut s = Self::new(); 128 s.reserve(capacity)?; 129 Ok(s) 130 } 131 132 /// Same as [`std::string::String::capacity`]. 133 #[inline] 134 pub fn capacity(&self) -> usize { 135 self.inner.capacity() 136 } 137 138 /// Same as [`std::string::String::as_str`]. 139 #[inline] 140 pub const fn as_str(&self) -> &str { 141 self.inner.as_str() 142 } 143 144 /// Same as [`std::string::String::reserve`] but returns an error on 145 /// allocation failure. 146 #[inline] 147 pub fn reserve(&mut self, additional: usize) -> Result<(), OutOfMemory> { 148 self.inner 149 .try_reserve(additional) 150 .map_err(|_| OutOfMemory::new(self.len().saturating_add(additional))) 151 } 152 153 /// Same as [`std::string::String::reserve_exact`] but returns an error on 154 /// allocation failure. 155 #[inline] 156 pub fn reserve_exact(&mut self, additional: usize) -> Result<(), OutOfMemory> { 157 self.inner 158 .try_reserve_exact(additional) 159 .map_err(|_| OutOfMemory::new(self.len().saturating_add(additional))) 160 } 161 162 /// Same as [`std::string::String::push`] but returns an error on allocation 163 /// failure. 164 #[inline] 165 pub fn push(&mut self, c: char) -> Result<(), OutOfMemory> { 166 self.reserve(c.len_utf8())?; 167 self.inner.push(c); 168 Ok(()) 169 } 170 171 /// Same as [`std::string::String::push_str`] but returns an error on 172 /// allocation failure. 173 #[inline] 174 pub fn push_str(&mut self, s: &str) -> Result<(), OutOfMemory> { 175 self.reserve(s.len())?; 176 self.inner.push_str(s); 177 Ok(()) 178 } 179 180 /// Same as [`std::string::String::into_raw_parts`]. 181 pub fn into_raw_parts(mut self) -> (*mut u8, usize, usize) { 182 // NB: Can't use `String::into_raw_parts` until our MSRV is >= 1.93. 183 #[cfg(not(miri))] 184 { 185 let ptr = self.as_mut_ptr(); 186 let len = self.len(); 187 let cap = self.capacity(); 188 mem::forget(self); 189 (ptr, len, cap) 190 } 191 // NB: Miri requires using `into_raw_parts`, but always run on nightly, 192 // so it's fine to use there. 193 #[cfg(miri)] 194 { 195 let _ = &mut self; 196 self.inner.into_raw_parts() 197 } 198 } 199 200 /// Same as [`std::string::String::from_raw_parts`]. 201 pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> Self { 202 Self { 203 // Safety: Same as our unsafe contract. 204 inner: unsafe { inner::String::from_raw_parts(buf, length, capacity) }, 205 } 206 } 207 208 /// Same as [`std::string::String::shrink_to_fit`] but returns an error on 209 /// allocation failure. 210 pub fn shrink_to_fit(&mut self) -> Result<(), OutOfMemory> { 211 // If our length is already equal to our capacity, then there is nothing 212 // to shrink. 213 if self.len() == self.capacity() { 214 return Ok(()); 215 } 216 217 // `realloc` requires a non-zero original layout as well as a non-zero 218 // destination layout, so this guard ensures that the sizes below are 219 // all nonzero. This handles a couple cases: 220 // 221 // * If `len == cap == 0` then no allocation has ever been made. 222 // * If `len == 0` and `cap != 0` then this function effectively frees 223 // the memory. 224 // 225 // In both of these cases delegate to the standard library's 226 // `shrink_to_fit` which is guaranteed to not perform a `realloc`. 227 if self.is_empty() { 228 self.inner.shrink_to_fit(); 229 return Ok(()); 230 } 231 232 let (ptr, len, cap) = mem::take(self).into_raw_parts(); 233 debug_assert!(!ptr.is_null()); 234 debug_assert!(len > 0); 235 debug_assert!(cap > len); 236 let old_layout = Layout::array::<u8>(cap).unwrap(); 237 debug_assert_eq!(old_layout.size(), cap); 238 let new_layout = Layout::array::<u8>(len).unwrap(); 239 debug_assert_eq!(old_layout.align(), new_layout.align()); 240 debug_assert_eq!(new_layout.size(), len); 241 242 // SAFETY: `ptr` was previously allocated in the global allocator, 243 // `layout` has a nonzero size and matches the current allocation of 244 // `ptr`, `len` is nonzero, and `len` is a valid array size 245 // for `len` elements given its constructor. 246 let result = unsafe { try_realloc(ptr, old_layout, len) }; 247 248 match result { 249 Ok(ptr) => { 250 // SAFETY: `result` is allocated with the global allocator and 251 // has room for exactly `[u8; len]`. 252 *self = unsafe { Self::from_raw_parts(ptr.as_ptr(), len, len) }; 253 Ok(()) 254 } 255 Err(oom) => { 256 // SAFETY: If reallocation fails then it's guaranteed that the 257 // original allocation is not tampered with, so it's safe to 258 // reassemble the original vector. 259 *self = unsafe { Self::from_raw_parts(ptr, len, cap) }; 260 Err(oom) 261 } 262 } 263 } 264 265 /// Same as [`std::string::String::into_boxed_str`] but returns an error on 266 /// allocation failure. 267 pub fn into_boxed_str(mut self) -> Result<Box<str>, OutOfMemory> { 268 self.shrink_to_fit()?; 269 270 let (ptr, len, cap) = self.into_raw_parts(); 271 debug_assert_eq!(len, cap); 272 let ptr = str_ptr_from_raw_parts(ptr, len); 273 274 // SAFETY: The `ptr` is allocated with the global allocator and points 275 // to a valid block of utf8. 276 let boxed = unsafe { Box::from_raw(ptr) }; 277 278 Ok(boxed) 279 } 280 } 281