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 serde::ser::Serialize for String {
59     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
60     where
61         S: serde::Serializer,
62     {
63         serializer.serialize_str(self)
64     }
65 }
66 
67 impl<'de> serde::de::Deserialize<'de> for String {
68     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
69     where
70         D: serde::Deserializer<'de>,
71     {
72         struct Visitor;
73 
74         impl<'de> serde::de::Visitor<'de> for Visitor {
75             type Value = String;
76 
77             fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
78                 f.write_str("a `wasmtime_core::alloc::String` str")
79             }
80 
81             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
82             where
83                 E: serde::de::Error,
84             {
85                 let mut s = String::new();
86                 s.reserve_exact(v.len()).map_err(|oom| E::custom(oom))?;
87                 s.push_str(v).expect("reserved capacity");
88                 Ok(s)
89             }
90         }
91 
92         // NB: do not use `deserialize_string` as that eagerly allocates the
93         // `String` and does not give us a chance to handle OOM. Instead, use
94         // `deserialize_str` which passes the visitor the borrowed `str`, giving
95         // us a chance to fallibly allocate space.
96         deserializer.deserialize_str(Visitor)
97     }
98 }
99 
100 impl String {
101     /// Same as [`std::string::String::new`].
102     #[inline]
103     pub fn new() -> Self {
104         Self {
105             inner: inner::String::new(),
106         }
107     }
108 
109     /// Same as [`std::string::String::with_capacity`] but returns an error on
110     /// allocation failure.
111     #[inline]
112     pub fn with_capacity(capacity: usize) -> Result<Self, OutOfMemory> {
113         let mut s = Self::new();
114         s.reserve(capacity)?;
115         Ok(s)
116     }
117 
118     /// Same as [`std::string::String::capacity`].
119     #[inline]
120     pub fn capacity(&self) -> usize {
121         self.inner.capacity()
122     }
123 
124     /// Same as [`std::string::String::reserve`] but returns an error on
125     /// allocation failure.
126     #[inline]
127     pub fn reserve(&mut self, additional: usize) -> Result<(), OutOfMemory> {
128         self.inner
129             .try_reserve(additional)
130             .map_err(|_| OutOfMemory::new(self.len().saturating_add(additional)))
131     }
132 
133     /// Same as [`std::string::String::reserve_exact`] but returns an error on
134     /// allocation failure.
135     #[inline]
136     pub fn reserve_exact(&mut self, additional: usize) -> Result<(), OutOfMemory> {
137         self.inner
138             .try_reserve_exact(additional)
139             .map_err(|_| OutOfMemory::new(self.len().saturating_add(additional)))
140     }
141 
142     /// Same as [`std::string::String::push`] but returns an error on allocation
143     /// failure.
144     #[inline]
145     pub fn push(&mut self, c: char) -> Result<(), OutOfMemory> {
146         self.reserve(c.len_utf8())?;
147         self.inner.push(c);
148         Ok(())
149     }
150 
151     /// Same as [`std::string::String::push_str`] but returns an error on
152     /// allocation failure.
153     #[inline]
154     pub fn push_str(&mut self, s: &str) -> Result<(), OutOfMemory> {
155         self.reserve(s.len())?;
156         self.inner.push_str(s);
157         Ok(())
158     }
159 
160     /// Same as [`std::string::String::into_raw_parts`].
161     pub fn into_raw_parts(mut self) -> (*mut u8, usize, usize) {
162         // NB: Can't use `String::into_raw_parts` until our MSRV is >= 1.93.
163         #[cfg(not(miri))]
164         {
165             let ptr = self.as_mut_ptr();
166             let len = self.len();
167             let cap = self.capacity();
168             mem::forget(self);
169             (ptr, len, cap)
170         }
171         // NB: Miri requires using `into_raw_parts`, but always run on nightly,
172         // so it's fine to use there.
173         #[cfg(miri)]
174         {
175             let _ = &mut self;
176             self.inner.into_raw_parts()
177         }
178     }
179 
180     /// Same as [`std::string::String::from_raw_parts`].
181     pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> Self {
182         Self {
183             // Safety: Same as our unsafe contract.
184             inner: unsafe { inner::String::from_raw_parts(buf, length, capacity) },
185         }
186     }
187 
188     /// Same as [`std::string::String::shrink_to_fit`] but returns an error on
189     /// allocation failure.
190     pub fn shrink_to_fit(&mut self) -> Result<(), OutOfMemory> {
191         // If our length is already equal to our capacity, then there is nothing
192         // to shrink.
193         if self.len() == self.capacity() {
194             return Ok(());
195         }
196 
197         // `realloc` requires a non-zero original layout as well as a non-zero
198         // destination layout, so this guard ensures that the sizes below are
199         // all nonzero. This handles a couple cases:
200         //
201         // * If `len == cap == 0` then no allocation has ever been made.
202         // * If `len == 0` and `cap != 0` then this function effectively frees
203         //   the memory.
204         //
205         // In both of these cases delegate to the standard library's
206         // `shrink_to_fit` which is guaranteed to not perform a `realloc`.
207         if self.is_empty() {
208             self.inner.shrink_to_fit();
209             return Ok(());
210         }
211 
212         let (ptr, len, cap) = mem::take(self).into_raw_parts();
213         debug_assert!(!ptr.is_null());
214         debug_assert!(len > 0);
215         debug_assert!(cap > len);
216         let old_layout = Layout::array::<u8>(cap).unwrap();
217         debug_assert_eq!(old_layout.size(), cap);
218         let new_layout = Layout::array::<u8>(len).unwrap();
219         debug_assert_eq!(old_layout.align(), new_layout.align());
220         debug_assert_eq!(new_layout.size(), len);
221 
222         // SAFETY: `ptr` was previously allocated in the global allocator,
223         // `layout` has a nonzero size and matches the current allocation of
224         // `ptr`, `len` is nonzero, and `len` is a valid array size
225         // for `len` elements given its constructor.
226         let result = unsafe { try_realloc(ptr, old_layout, len) };
227 
228         match result {
229             Ok(ptr) => {
230                 // SAFETY: `result` is allocated with the global allocator and
231                 // has room for exactly `[u8; len]`.
232                 *self = unsafe { Self::from_raw_parts(ptr.as_ptr(), len, len) };
233                 Ok(())
234             }
235             Err(oom) => {
236                 // SAFETY: If reallocation fails then it's guaranteed that the
237                 // original allocation is not tampered with, so it's safe to
238                 // reassemble the original vector.
239                 *self = unsafe { Self::from_raw_parts(ptr, len, cap) };
240                 Err(oom)
241             }
242         }
243     }
244 
245     /// Same as [`std::string::String::into_boxed_str`] but returns an error on
246     /// allocation failure.
247     pub fn into_boxed_str(mut self) -> Result<Box<str>, OutOfMemory> {
248         self.shrink_to_fit()?;
249 
250         let (ptr, len, cap) = self.into_raw_parts();
251         debug_assert_eq!(len, cap);
252         let ptr = str_ptr_from_raw_parts(ptr, len);
253 
254         // SAFETY: The `ptr` is allocated with the global allocator and points
255         // to a valid block of utf8.
256         let boxed = unsafe { Box::from_raw(ptr) };
257 
258         Ok(boxed)
259     }
260 }
261