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
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::types::{Pages, TrapCode};
use alloc::vec::Vec;
use bytes::BytesMut;
/// Shared linear memory backing store for a running module.
/// Tracks current size in Wasm pages and provides bounds-checked read/write helpers.
/// The buffer is pre-reserved and grown in page-sized steps.
pub struct GlobalMemory {
/// Underlying byte buffer for the linear memory.
pub shared_memory: BytesMut,
/// Current logical size of the linear memory in pages.
pub current_pages: Pages,
/// The maximum allowed size of the linear memory in pages.
pub max_allowed_memory_pages: Pages,
}
impl GlobalMemory {
pub fn new(initial_pages: Pages, max_allowed_memory_pages: Pages) -> Self {
let initial_len = initial_pages
.to_bytes()
.expect("rwasm: not supported target pointer width");
if initial_len > max_allowed_memory_pages.to_bytes().unwrap() {
unreachable!("rwasm: initial memory size is greater than the maximum");
}
let mut shared_memory = BytesMut::with_capacity(initial_len);
shared_memory.resize(initial_len, 0);
Self {
shared_memory,
current_pages: initial_pages,
max_allowed_memory_pages,
}
}
/// Returns the number of pages in use by the linear memory.
pub fn current_pages(&self) -> Pages {
self.current_pages
}
/// Grows the linear memory by the given number of new pages.
///
/// Returns the number of pages before the operation upon success.
///
/// # Errors
///
/// If the linear memory grows beyond its maximum limit after
/// the growth operation.
pub fn grow(&mut self, additional: Pages) -> Option<Pages> {
let current_pages = self.current_pages();
if additional == Pages::from(0) {
return Some(current_pages);
}
let desired_pages = current_pages.checked_add(additional)?;
if desired_pages > self.max_allowed_memory_pages {
return None;
}
// At this point, it is okay to grow the underlying virtual memory
// by the given number of additional pages.
let new_size = desired_pages
.to_bytes()
.expect("rwasm: not supported target pointer width");
assert!(new_size >= self.shared_memory.len());
self.shared_memory.resize(new_size, 0);
self.current_pages = desired_pages;
Some(current_pages)
}
/// Returns a shared slice to the bytes underlying to the byte buffer.
pub fn data(&self) -> &[u8] {
self.shared_memory.as_ref()
}
/// Returns an exclusive slice to the bytes underlying to the byte buffer.
pub fn data_mut(&mut self) -> &mut [u8] {
self.shared_memory.as_mut()
}
/// Reads `n` bytes from `memory[offset..offset+n]` into `buffer`
/// where `n` is the length of `buffer`.
///
/// # Errors
///
/// If this operation accesses out of bounds linear memory.
pub fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<(), TrapCode> {
let len_buffer = buffer.len();
let end = offset
.checked_add(len_buffer)
.ok_or(TrapCode::MemoryOutOfBounds)?;
let slice = self
.data()
.get(offset..end)
.ok_or(TrapCode::MemoryOutOfBounds)?;
buffer.copy_from_slice(slice);
Ok(())
}
/// Reads `n` bytes into vec
pub fn read_into_vec(&self, offset: usize, len_buffer: usize) -> Result<Vec<u8>, TrapCode> {
let end = offset
.checked_add(len_buffer)
.ok_or(TrapCode::MemoryOutOfBounds)?;
let slice = self
.data()
.get(offset..end)
.ok_or(TrapCode::MemoryOutOfBounds)?;
Ok(slice.to_vec())
}
/// Writes `n` bytes to `memory[offset..offset+n]` from `buffer`
/// where `n` if the length of `buffer`.
///
/// # Errors
///
/// If this operation accesses out of bounds linear memory.
pub fn write(&mut self, offset: usize, buffer: &[u8]) -> Result<(), TrapCode> {
let len_buffer = buffer.len();
let end = offset
.checked_add(len_buffer)
.ok_or(TrapCode::MemoryOutOfBounds)?;
let slice = self
.data_mut()
.get_mut(offset..end)
.ok_or(TrapCode::MemoryOutOfBounds)?;
slice.copy_from_slice(buffer);
Ok(())
}
}