Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/core/src/alloc/string.rs
3073 views
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
220