1use crate::WASM_PAGE_SIZE_BYTES as PAGE;
3use std::{cell::RefCell, rc::Rc};
4
5pub trait Memory {
14 fn size(&self) -> u64;
16 fn grow(&self, pages: u64) -> i64;
18 fn read(&self, offset: u64, bytes: &mut [u8]);
20 #[inline]
31 #[expect(
32 unsafe_code,
33 reason = "ICSS raw-read contract admits an exclusive destination and guarantees initialization"
34 )]
35 unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
36 unsafe {
39 std::ptr::write_bytes(dst, 0, count);
40 self.read(offset, std::slice::from_raw_parts_mut(dst, count));
41 }
42 }
43 fn write(&self, offset: u64, bytes: &[u8]);
45}
46
47impl<M: Memory + ?Sized> Memory for Rc<M> {
48 fn size(&self) -> u64 {
49 self.as_ref().size()
50 }
51 fn grow(&self, pages: u64) -> i64 {
52 self.as_ref().grow(pages)
53 }
54 fn read(&self, offset: u64, bytes: &mut [u8]) {
55 self.as_ref().read(offset, bytes);
56 }
57 #[expect(
58 unsafe_code,
59 reason = "Forward the caller's admitted destination and initialization contract"
60 )]
61 unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
62 unsafe { self.as_ref().read_unsafe(offset, dst, count) }
64 }
65 fn write(&self, offset: u64, bytes: &[u8]) {
66 self.as_ref().write(offset, bytes);
67 }
68}
69impl<M: Memory + ?Sized> Memory for &M {
70 fn size(&self) -> u64 {
71 (**self).size()
72 }
73 fn grow(&self, pages: u64) -> i64 {
74 (**self).grow(pages)
75 }
76 fn read(&self, offset: u64, bytes: &mut [u8]) {
77 (**self).read(offset, bytes);
78 }
79 #[expect(
80 unsafe_code,
81 reason = "Forward the caller's admitted destination and initialization contract"
82 )]
83 unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
84 unsafe { (**self).read_unsafe(offset, dst, count) }
86 }
87 fn write(&self, offset: u64, bytes: &[u8]) {
88 (**self).write(offset, bytes);
89 }
90}
91
92pub type VectorMemory = Rc<RefCell<Vec<u8>>>;
94impl Memory for RefCell<Vec<u8>> {
95 fn size(&self) -> u64 {
96 self.borrow().len() as u64 / PAGE
97 }
98 fn grow(&self, pages: u64) -> i64 {
99 let previous = self.size();
100 let Some(length) = previous
101 .checked_add(pages)
102 .and_then(|value| value.checked_mul(PAGE))
103 .and_then(|value| usize::try_from(value).ok())
104 else {
105 return -1;
106 };
107 let mut bytes = self.borrow_mut();
108 let additional = length - bytes.len();
109 if bytes.try_reserve(additional).is_err() {
110 return -1;
111 }
112 bytes.resize(length, 0);
113 i64::try_from(previous).expect("native page count")
114 }
115 fn read(&self, offset: u64, bytes: &mut [u8]) {
116 let start = usize::try_from(offset).expect("native offset");
117 let end = start
118 .checked_add(bytes.len())
119 .expect("native range overflow");
120 bytes.copy_from_slice(&self.borrow()[start..end]);
121 }
122 #[expect(
123 unsafe_code,
124 reason = "Copy a fully admitted source into the caller's exclusive non-overlapping destination"
125 )]
126 unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
127 let start = usize::try_from(offset).expect("native offset");
128 let end = start.checked_add(count).expect("native range overflow");
129 let backing = self.borrow();
130 let source = &backing[start..end];
131 unsafe { std::ptr::copy_nonoverlapping(source.as_ptr(), dst, count) }
134 }
135 fn write(&self, offset: u64, bytes: &[u8]) {
136 let start = usize::try_from(offset).expect("native offset");
137 let end = start
138 .checked_add(bytes.len())
139 .expect("native range overflow");
140 self.borrow_mut()[start..end].copy_from_slice(bytes);
141 }
142}
143
144#[cfg(not(target_arch = "wasm32"))]
146pub type DefaultMemoryImpl = VectorMemory;
147
148#[cfg(target_arch = "wasm32")]
154#[derive(Clone, Copy, Default)]
155pub struct DefaultMemoryImpl {
156 _private: (),
157}
158#[cfg(target_arch = "wasm32")]
159impl Memory for DefaultMemoryImpl {
160 fn size(&self) -> u64 {
161 ic_platform_runtime::ic0::stable64_size()
162 }
163 fn grow(&self, pages: u64) -> i64 {
164 let previous = ic_platform_runtime::ic0::stable64_grow(pages);
165 if previous == u64::MAX {
166 -1
167 } else {
168 i64::try_from(previous).expect("IC page count")
169 }
170 }
171 fn read(&self, offset: u64, bytes: &mut [u8]) {
172 ic_platform_runtime::ic0::stable64_read(bytes, offset);
173 }
174 #[expect(
175 unsafe_code,
176 reason = "Bridge the caller's admitted raw destination to Platform's safe initialization helper"
177 )]
178 unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
179 let bytes = unsafe {
183 std::slice::from_raw_parts_mut(dst.cast::<std::mem::MaybeUninit<u8>>(), count)
184 };
185 ic_platform_runtime::ic0::stable64_read_uninit(bytes, offset);
186 }
187 fn write(&self, offset: u64, bytes: &[u8]) {
188 ic_platform_runtime::ic0::stable64_write(bytes, offset);
189 }
190}
191
192#[cfg(test)]
193mod tests;