1 use crate::{ 2 alloc::{TryClone, str_ptr_from_raw_parts, try_realloc}, 3 error::OutOfMemory, 4 }; 5 use core::{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)] 11 pub struct String { 12 inner: inner::String, 13 } 14 15 impl TryClone for String { 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 String { 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 String { 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 String { 36 type Target = str; 37 38 #[inline] 39 fn deref(&self) -> &Self::Target { 40 &self.inner 41 } 42 } 43 44 impl ops::DerefMut for String { 45 #[inline] 46 fn deref_mut(&mut self) -> &mut Self::Target { 47 &mut self.inner 48 } 49 } 50 51 impl From<inner::String> for String { 52 #[inline] 53 fn from(inner: inner::String) -> Self { 54 Self { inner } 55 } 56 } 57 58 impl String { 59 /// Same as [`std::string::String::new`]. 60 #[inline] 61 pub fn new() -> Self { 62 Self { 63 inner: inner::String::new(), 64 } 65 } 66 67 /// Same as [`std::string::String::with_capacity`] but returns an error on 68 /// allocation failure. 69 #[inline] 70 pub fn with_capacity(capacity: usize) -> Result<Self, OutOfMemory> { 71 let mut s = Self::new(); 72 s.reserve(capacity)?; 73 Ok(s) 74 } 75 76 /// Same as [`std::string::String::capacity`]. 77 #[inline] 78 pub fn capacity(&self) -> usize { 79 self.inner.capacity() 80 } 81 82 /// Same as [`std::string::String::reserve`] but returns an error on 83 /// allocation failure. 84 #[inline] 85 pub fn reserve(&mut self, additional: usize) -> Result<(), OutOfMemory> { 86 self.inner 87 .try_reserve(additional) 88 .map_err(|_| OutOfMemory::new(self.len().saturating_add(additional))) 89 } 90 91 /// Same as [`std::string::String::reserve_exact`] but returns an error on 92 /// allocation failure. 93 #[inline] 94 pub fn reserve_exact(&mut self, additional: usize) -> Result<(), OutOfMemory> { 95 self.inner 96 .try_reserve_exact(additional) 97 .map_err(|_| OutOfMemory::new(self.len().saturating_add(additional))) 98 } 99 100 /// Same as [`std::string::String::push`] but returns an error on allocation 101 /// failure. 102 #[inline] 103 pub fn push(&mut self, c: char) -> Result<(), OutOfMemory> { 104 self.reserve(c.len_utf8())?; 105 self.inner.push(c); 106 Ok(()) 107 } 108 109 /// Same as [`std::string::String::push_str`] but returns an error on 110 /// allocation failure. 111 #[inline] 112 pub fn push_str(&mut self, s: &str) -> Result<(), OutOfMemory> { 113 self.reserve(s.len())?; 114 self.inner.push_str(s); 115 Ok(()) 116 } 117 118 /// Same as [`std::string::String::into_raw_parts`]. 119 pub fn into_raw_parts(mut self) -> (*mut u8, usize, usize) { 120 // NB: Can't use `String::into_raw_parts` until our MSRV is >= 1.93. 121 #[cfg(not(miri))] 122 { 123 let ptr = self.as_mut_ptr(); 124 let len = self.len(); 125 let cap = self.capacity(); 126 mem::forget(self); 127 (ptr, len, cap) 128 } 129 // NB: Miri requires using `into_raw_parts`, but always run on nightly, 130 // so it's fine to use there. 131 #[cfg(miri)] 132 { 133 let _ = &mut self; 134 self.inner.into_raw_parts() 135 } 136 } 137 138 /// Same as [`std::string::String::from_raw_parts`]. 139 pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> Self { 140 Self { 141 // Safety: Same as our unsafe contract. 142 inner: unsafe { inner::String::from_raw_parts(buf, length, capacity) }, 143 } 144 } 145 146 /// Same as [`std::string::String::shrink_to_fit`] but returns an error on 147 /// allocation failure. 148 pub fn shrink_to_fit(&mut self) -> Result<(), OutOfMemory> { 149 // If our length is already equal to our capacity, then there is nothing 150 // to shrink. 151 if self.len() == self.capacity() { 152 return Ok(()); 153 } 154 155 // `realloc` requires a non-zero original layout as well as a non-zero 156 // destination layout, so this guard ensures that the sizes below are 157 // all nonzero. This handles a couple cases: 158 // 159 // * If `len == cap == 0` then no allocation has ever been made. 160 // * If `len == 0` and `cap != 0` then this function effectively frees 161 // the memory. 162 // 163 // In both of these cases delegate to the standard library's 164 // `shrink_to_fit` which is guaranteed to not perform a `realloc`. 165 if self.is_empty() { 166 self.inner.shrink_to_fit(); 167 return Ok(()); 168 } 169 170 let (ptr, len, cap) = mem::take(self).into_raw_parts(); 171 debug_assert!(!ptr.is_null()); 172 debug_assert!(len > 0); 173 debug_assert!(cap > len); 174 let old_layout = Layout::array::<u8>(cap).unwrap(); 175 debug_assert_eq!(old_layout.size(), cap); 176 let new_layout = Layout::array::<u8>(len).unwrap(); 177 debug_assert_eq!(old_layout.align(), new_layout.align()); 178 debug_assert_eq!(new_layout.size(), len); 179 180 // SAFETY: `ptr` was previously allocated in the global allocator, 181 // `layout` has a nonzero size and matches the current allocation of 182 // `ptr`, `len` is nonzero, and `len` is a valid array size 183 // for `len` elements given its constructor. 184 let result = unsafe { try_realloc(ptr, old_layout, len) }; 185 186 match result { 187 Ok(ptr) => { 188 // SAFETY: `result` is allocated with the global allocator and 189 // has room for exactly `[u8; len]`. 190 *self = unsafe { Self::from_raw_parts(ptr.as_ptr(), len, len) }; 191 Ok(()) 192 } 193 Err(oom) => { 194 // SAFETY: If reallocation fails then it's guaranteed that the 195 // original allocation is not tampered with, so it's safe to 196 // reassemble the original vector. 197 *self = unsafe { Self::from_raw_parts(ptr, len, cap) }; 198 Err(oom) 199 } 200 } 201 } 202 203 /// Same as [`std::string::String::into_boxed_str`] but returns an error on 204 /// allocation failure. 205 pub fn into_boxed_str(mut self) -> Result<Box<str>, OutOfMemory> { 206 self.shrink_to_fit()?; 207 208 let (ptr, len, cap) = self.into_raw_parts(); 209 debug_assert_eq!(len, cap); 210 let ptr = str_ptr_from_raw_parts(ptr, len); 211 212 // SAFETY: The `ptr` is allocated with the global allocator and points 213 // to a valid block of utf8. 214 let boxed = unsafe { Box::from_raw(ptr) }; 215 216 Ok(boxed) 217 } 218 } 219