1use std::mem::{self, MaybeUninit};
2use std::slice;
3
4use crate::{
5 MemoryAccessError,
6 utils::mem::{WasmRef, WasmSlice},
7};
8
9pub(crate) enum SliceCow<'a, T> {
10 #[allow(dead_code)]
11 Borrowed(&'a mut [T]),
12 #[allow(dead_code)]
13 Owned(Vec<T>, bool),
14}
15
16impl<T> AsRef<[T]> for SliceCow<'_, T> {
17 fn as_ref(&self) -> &[T] {
18 match self {
19 Self::Borrowed(buf) => buf,
20 Self::Owned(buf, _) => buf,
21 }
22 }
23}
24
25impl<T> AsMut<[T]> for SliceCow<'_, T> {
26 fn as_mut(&mut self) -> &mut [T] {
27 match self {
31 Self::Borrowed(buf) => buf,
32 Self::Owned(buf, modified) => {
33 *modified = true;
34 buf.as_mut()
35 }
36 }
37 }
38}
39
40pub struct WasmSliceAccess<'a, T>
43where
44 T: wasmer_types::ValueType,
45{
46 pub(crate) slice: WasmSlice<'a, T>,
47 pub(crate) buf: SliceCow<'a, T>,
48}
49
50impl<T> AsRef<[T]> for WasmSliceAccess<'_, T>
51where
52 T: wasmer_types::ValueType,
53{
54 fn as_ref(&self) -> &[T] {
55 self.buf.as_ref()
56 }
57}
58
59impl<T> AsMut<[T]> for WasmSliceAccess<'_, T>
60where
61 T: wasmer_types::ValueType,
62{
63 fn as_mut(&mut self) -> &mut [T] {
64 self.buf.as_mut()
65 }
66}
67
68impl<'a, T> WasmSliceAccess<'a, T>
69where
70 T: wasmer_types::ValueType,
71{
72 pub fn iter(&'a self) -> std::slice::Iter<'a, T> {
74 self.as_ref().iter()
75 }
76
77 pub fn iter_mut(&'a mut self) -> std::slice::IterMut<'a, T> {
79 self.buf.as_mut().iter_mut()
80 }
81
82 pub fn len(&self) -> usize {
84 self.buf.as_ref().len()
85 }
86
87 pub fn is_empty(&self) -> bool {
89 self.buf.as_ref().is_empty()
90 }
91}
92
93impl WasmSliceAccess<'_, u8> {
94 #[inline]
96 pub fn copy_from_slice(&mut self, src: &[u8]) {
97 let dst = self.buf.as_mut();
98 dst.copy_from_slice(src);
99 }
100
101 #[inline]
108 pub fn copy_from_slice_min(&mut self, src: &[u8]) -> usize {
109 let dst = self.buf.as_mut();
110 let amt = dst.len().min(src.len());
111 dst[..amt].copy_from_slice(&src[..amt]);
112 amt
113 }
114}
115
116impl<T> Drop for WasmSliceAccess<'_, T>
117where
118 T: wasmer_types::ValueType,
119{
120 fn drop(&mut self) {
121 if let SliceCow::Owned(buf, modified) = &self.buf
122 && *modified
123 {
124 self.slice.write_slice(buf.as_ref()).ok();
125 }
126 }
127}
128
129pub(crate) enum RefCow<'a, T> {
130 #[allow(dead_code)]
131 Borrowed(&'a mut T),
132 #[allow(dead_code)]
133 Owned(T, bool),
134}
135
136impl<T> AsRef<T> for RefCow<'_, T> {
137 fn as_ref(&self) -> &T {
138 match self {
139 Self::Borrowed(val) => val,
140 Self::Owned(val, _) => val,
141 }
142 }
143}
144
145impl<T> AsMut<T> for RefCow<'_, T> {
146 fn as_mut(&mut self) -> &mut T {
147 match self {
151 Self::Borrowed(val) => val,
152 Self::Owned(val, modified) => {
153 *modified = true;
154 val
155 }
156 }
157 }
158}
159
160pub struct WasmRefAccess<'a, T>
163where
164 T: wasmer_types::ValueType,
165{
166 pub(crate) is_owned: bool,
167 pub(crate) ptr: WasmRef<'a, T>,
168 pub(crate) buf: RefCow<'a, T>,
169}
170
171impl<T> AsRef<T> for WasmRefAccess<'_, T>
172where
173 T: wasmer_types::ValueType,
174{
175 fn as_ref(&self) -> &T {
176 self.buf.as_ref()
177 }
178}
179
180impl<T> AsMut<T> for WasmRefAccess<'_, T>
181where
182 T: wasmer_types::ValueType,
183{
184 fn as_mut(&mut self) -> &mut T {
185 self.buf.as_mut()
186 }
187}
188
189impl<T> Drop for WasmRefAccess<'_, T>
190where
191 T: wasmer_types::ValueType,
192{
193 fn drop(&mut self) {
194 if let RefCow::Owned(val, modified) = &self.buf
195 && *modified
196 {
197 self.ptr.write(*val).ok();
198 }
199 }
200}
201
202impl<T> WasmSliceAccess<'_, T>
203where
204 T: wasmer_types::ValueType,
205{
206 pub fn as_mut_uninit(&mut self) -> &mut [MaybeUninit<T>] {
208 let ret: &mut [T] = self.buf.as_mut();
209 let ret: &mut [MaybeUninit<T>] = unsafe { std::mem::transmute(ret) };
210 ret
211 }
212}
213
214impl<T> WasmRefAccess<'_, T>
215where
216 T: wasmer_types::ValueType,
217{
218 pub fn as_mut_uninit(&mut self) -> &mut MaybeUninit<T> {
220 let ret: &mut T = self.buf.as_mut();
221 let ret: &mut MaybeUninit<T> = unsafe { std::mem::transmute(ret) };
222 ret
223 }
224}
225
226impl<'a, T> WasmSliceAccess<'a, T>
227where
228 T: wasmer_types::ValueType,
229{
230 pub(crate) fn new(slice: WasmSlice<'a, T>, is_owned: bool) -> Result<Self, MemoryAccessError> {
231 if is_owned {
232 Self::new_owned(slice)
233 } else {
234 Self::new_borrowed(slice)
235 }
236 }
237
238 pub(crate) fn new_borrowed(slice: WasmSlice<'a, T>) -> Result<Self, MemoryAccessError> {
239 let total_len = slice
240 .len
241 .checked_mul(std::mem::size_of::<T>() as u64)
242 .ok_or(MemoryAccessError::Overflow)?;
243 let end = slice
244 .offset
245 .checked_add(total_len)
246 .ok_or(MemoryAccessError::Overflow)?;
247 if end > slice.buffer.len() as u64 {
248 tracing::warn!(
249 "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})",
250 total_len,
251 end,
252 slice.buffer.len()
253 );
254 return Err(MemoryAccessError::HeapOutOfBounds);
255 }
256 let buf = unsafe {
257 let buf_ptr: *mut u8 = slice.buffer.base().add(slice.offset as usize);
258 let buf_ptr: *mut T = std::mem::transmute(buf_ptr);
259 if !buf_ptr.is_aligned() {
260 return Err(MemoryAccessError::UnalignedPointerRead);
261 }
262 std::slice::from_raw_parts_mut(buf_ptr, slice.len as usize)
263 };
264 Ok(Self {
265 slice,
266 buf: SliceCow::Borrowed(buf),
267 })
268 }
269
270 pub(crate) fn new_owned(slice: WasmSlice<'a, T>) -> Result<Self, MemoryAccessError> {
271 let buf = slice.read_to_vec()?;
272 Ok(Self {
273 slice,
274 buf: SliceCow::Owned(buf, false),
275 })
276 }
277}
278
279impl<'a, T> WasmRefAccess<'a, T>
280where
281 T: wasmer_types::ValueType,
282{
283 pub(crate) fn new(ptr: WasmRef<'a, T>, is_owned: bool) -> Result<Self, MemoryAccessError> {
284 if is_owned {
285 Self::new_owned(ptr)
286 } else {
287 Self::new_borrowed(ptr)
288 }
289 }
290
291 pub(crate) fn new_borrowed(ptr: WasmRef<'a, T>) -> Result<Self, MemoryAccessError> {
292 let total_len = std::mem::size_of::<T>() as u64;
293 let end = ptr
294 .offset
295 .checked_add(total_len)
296 .ok_or(MemoryAccessError::Overflow)?;
297 if end > ptr.buffer.len() as u64 {
298 tracing::warn!(
299 "attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})",
300 total_len,
301 end,
302 ptr.buffer.len()
303 );
304 return Err(MemoryAccessError::HeapOutOfBounds);
305 }
306 let val = unsafe {
307 let val_ptr: *mut u8 = ptr.buffer.base().add(ptr.offset as usize);
308 let val_ptr: *mut T = std::mem::transmute(val_ptr);
309 &mut *val_ptr
310 };
311 Ok(Self {
312 is_owned: false,
313 ptr,
314 buf: RefCow::Borrowed(val),
315 })
316 }
317
318 pub(crate) fn new_owned(ptr: WasmRef<'a, T>) -> Result<Self, MemoryAccessError> {
319 let mut out = MaybeUninit::uninit();
320 let buf =
321 unsafe { slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, mem::size_of::<T>()) };
322 ptr.buffer.read(ptr.offset, buf)?;
323 let val = unsafe { out.assume_init() };
324
325 Ok(Self {
326 is_owned: true,
327 ptr,
328 buf: RefCow::Owned(val, false),
329 })
330 }
331}
332
333impl<T> WasmRefAccess<'_, T>
334where
335 T: wasmer_types::ValueType,
336{
337 #[inline]
339 #[allow(clippy::clone_on_copy)]
340 pub fn read(&self) -> T
341 where
342 T: Clone,
343 {
344 self.as_ref().clone()
345 }
346
347 #[inline]
349 pub fn write(&mut self, val: T) {
350 if self.is_owned {
351 let mut data = MaybeUninit::new(val);
352 let data = unsafe {
353 slice::from_raw_parts_mut(
354 data.as_mut_ptr() as *mut MaybeUninit<u8>,
355 mem::size_of::<T>(),
356 )
357 };
358 val.zero_padding_bytes(data);
359 let data = unsafe { slice::from_raw_parts(data.as_ptr() as *const _, data.len()) };
360 self.ptr.buffer.write(self.ptr.offset, data).unwrap()
361 } else {
362 *(self.as_mut()) = val;
366 }
367 }
368}