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