1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! Access to a store's linear [`Memory`].
use wasm3x_sys as ffi;
use crate::error::{Error, Result};
use crate::store::{AsContext, AsContextMut, Store, StoreId, assert_same_store};
/// A handle to the linear memory of a [`Store`].
///
/// Wasm3 may relocate the memory buffer when it grows, so a [`Memory`] never
/// hands out a slice that outlives the store borrow used to obtain it.
#[derive(Debug, Clone, Copy)]
pub struct Memory {
store_id: StoreId,
index: u32,
}
impl Memory {
pub(crate) fn new(store_id: StoreId, index: u32) -> Self {
Self { store_id, index }
}
/// Returns the current base pointer and size (in bytes) of the memory.
fn raw_parts(runtime: ffi::IM3Runtime, index: u32) -> (*mut u8, usize) {
let mut size = 0u32;
// SAFETY: valid runtime; `size` receives the byte length.
let ptr = unsafe { ffi::m3_GetMemory(runtime, &mut size, index) };
(ptr, size as usize)
}
/// Returns the size of the memory in bytes.
pub fn data_size(&self, store: impl AsContext) -> usize {
let ctx = store.as_context();
assert_same_store(ctx.store_id(), self.store_id);
Self::raw_parts(ctx.runtime(), self.index).1
}
/// Returns the size of the memory in 64 KiB Wasm pages.
pub fn size(&self, store: impl AsContext) -> usize {
self.data_size(store) / (64 * 1024)
}
/// Returns a shared view of the memory contents.
///
/// Unlike the other accessors this takes a `&Store<T>` directly: the
/// returned slice borrows the store, a relationship the by-value context
/// traits cannot express.
pub fn data<'a, T>(&self, store: &'a Store<T>) -> &'a [u8] {
store.ensure_owns(self.store_id);
let (ptr, size) = Self::raw_parts(store.raw(), self.index);
if ptr.is_null() || size == 0 {
return &[];
}
// SAFETY: `ptr`/`size` describe a valid buffer for as long as `store`
// is borrowed; the memory cannot grow (which needs `&mut Store`) while
// this shared borrow is alive.
unsafe { core::slice::from_raw_parts(ptr, size) }
}
/// Returns an exclusive view of the memory contents.
///
/// Takes a `&mut Store<T>` directly for the same reason as [`Memory::data`].
pub fn data_mut<'a, T>(&self, store: &'a mut Store<T>) -> &'a mut [u8] {
store.ensure_owns(self.store_id);
let (ptr, size) = Self::raw_parts(store.raw(), self.index);
if ptr.is_null() || size == 0 {
return &mut [];
}
// SAFETY: exclusive `store` borrow guarantees unique access; the buffer
// is valid for the borrow's duration.
unsafe { core::slice::from_raw_parts_mut(ptr, size) }
}
/// Reads `buffer.len()` bytes starting at `offset`.
pub fn read(&self, store: impl AsContext, offset: usize, buffer: &mut [u8]) -> Result<()> {
let ctx = store.as_context();
assert_same_store(ctx.store_id(), self.store_id);
let (ptr, size) = Self::raw_parts(ctx.runtime(), self.index);
// SAFETY: `ptr`/`size` describe the live memory; the shared context
// borrow keeps it from growing for the duration of this read.
let data: &[u8] = if ptr.is_null() || size == 0 {
&[]
} else {
unsafe { core::slice::from_raw_parts(ptr, size) }
};
let end = offset
.checked_add(buffer.len())
.filter(|end| *end <= data.len())
.ok_or_else(|| Error::new("out-of-bounds memory read"))?;
buffer.copy_from_slice(&data[offset..end]);
Ok(())
}
/// Writes `buffer` into memory starting at `offset`.
pub fn write(&self, mut store: impl AsContextMut, offset: usize, buffer: &[u8]) -> Result<()> {
let ctx = store.as_context_mut();
assert_same_store(ctx.store_id(), self.store_id);
let (ptr, size) = Self::raw_parts(ctx.runtime(), self.index);
// SAFETY: the exclusive context borrow guarantees unique access to the
// live memory for the duration of this write.
let data: &mut [u8] = if ptr.is_null() || size == 0 {
&mut []
} else {
unsafe { core::slice::from_raw_parts_mut(ptr, size) }
};
let end = offset
.checked_add(buffer.len())
.filter(|end| *end <= data.len())
.ok_or_else(|| Error::new("out-of-bounds memory write"))?;
data[offset..end].copy_from_slice(buffer);
Ok(())
}
}