oasis_runtime_sdk_contracts/abi/oasis/
memory.rs1use std::convert::TryInto;
3
4use oasis_runtime_sdk::context::Context;
5
6use super::OasisV1;
7use crate::{abi::ExecutionContext, Config};
8
9pub const EXPORT_ALLOCATE: &str = "allocate";
11pub const EXPORT_DEALLOCATE: &str = "deallocate";
13
14#[derive(Debug)]
16pub struct Region {
17 pub offset: usize,
18 pub length: usize,
19}
20
21#[derive(Debug, thiserror::Error)]
22pub enum RegionError {
23 #[error("region too big")]
24 RegionTooBig,
25 #[error("bad allocation function: {0}")]
26 BadAllocationFunction(#[source] anyhow::Error),
27 #[error("region allocation failed: {0}")]
28 AllocationFailed(#[source] anyhow::Error),
29 #[error("region size mismatch")]
30 SizeMismatch,
31 #[error("bad region pointer")]
32 BadPointer,
33}
34
35impl Region {
36 pub fn to_arg(&self) -> (u32, u32) {
38 (self.offset as u32, self.length as u32)
39 }
40
41 pub fn from_arg(arg: (u32, u32)) -> Self {
43 Region {
44 offset: arg.0 as usize,
45 length: arg.1 as usize,
46 }
47 }
48
49 pub fn deref(memory: &wasm3::Memory<'_>, arg: u32) -> Result<Self, RegionError> {
51 let arg = arg as usize;
52
53 if arg + 8 > memory.size() {
55 return Err(RegionError::BadPointer);
56 }
57
58 let dst = memory.as_slice();
60 let offset = u32::from_le_bytes(dst[arg..arg + 4].try_into().unwrap()) as usize;
61 let length = u32::from_le_bytes(dst[arg + 4..arg + 8].try_into().unwrap()) as usize;
62
63 if offset + length > memory.size() {
65 return Err(RegionError::BadPointer);
66 }
67
68 Ok(Region { offset, length })
69 }
70
71 pub fn copy_from_slice(
73 &self,
74 memory: &mut wasm3::Memory<'_>,
75 src: &[u8],
76 ) -> Result<(), RegionError> {
77 if src.len() != self.length {
79 return Err(RegionError::SizeMismatch);
80 }
81
82 if (self.offset + self.length) > memory.size() {
84 return Err(RegionError::BadPointer);
85 }
86
87 let dst = &mut memory.as_slice_mut()[self.offset..self.offset + self.length];
88 dst.copy_from_slice(src);
89
90 Ok(())
91 }
92
93 pub fn as_slice<'mem>(
95 &self,
96 memory: &'mem wasm3::Memory<'_>,
97 ) -> Result<&'mem [u8], RegionError> {
98 if (self.offset + self.length) > memory.size() {
100 return Err(RegionError::BadPointer);
101 }
102
103 Ok(&memory.as_slice()[self.offset..self.offset + self.length])
104 }
105
106 pub fn as_slice_mut<'mem>(
108 &self,
109 memory: &'mem mut wasm3::Memory<'_>,
110 ) -> Result<&'mem mut [u8], RegionError> {
111 if (self.offset + self.length) > memory.size() {
113 return Err(RegionError::BadPointer);
114 }
115
116 Ok(&mut memory.as_slice_mut()[self.offset..self.offset + self.length])
117 }
118
119 pub fn serialize(&self) -> [u8; 8] {
121 let mut data = [0u8; 8];
122 data[..4].copy_from_slice(&(self.offset as u32).to_le_bytes());
123 data[4..].copy_from_slice(&(self.length as u32).to_le_bytes());
124 data
125 }
126}
127
128impl<Cfg: Config> OasisV1<Cfg> {
129 pub fn allocate<C: Context>(
131 instance: &wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
132 length: usize,
133 ) -> Result<Region, RegionError> {
134 let length: u32 = length.try_into().map_err(|_| RegionError::RegionTooBig)?;
135
136 let func = instance
138 .find_function::<u32, u32>(EXPORT_ALLOCATE)
139 .map_err(|err| RegionError::BadAllocationFunction(err.into()))?;
140 let offset = func
141 .call(length) .map_err(|err| RegionError::AllocationFailed(err.into()))?;
143
144 let region = Region {
146 offset: offset as usize,
147 length: length as usize,
148 };
149
150 instance
152 .runtime()
153 .try_with_memory(|memory| -> Result<(), RegionError> {
154 if (region.offset + region.length) > memory.size() {
156 return Err(RegionError::BadPointer);
157 }
158 Ok(())
159 })
160 .unwrap()?;
161
162 Ok(region)
163 }
164
165 pub fn allocate_and_copy<C: Context>(
167 instance: &wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
168 data: &[u8],
169 ) -> Result<Region, RegionError> {
170 let dst = Self::allocate(instance, data.len())?;
172 instance
174 .runtime()
175 .try_with_memory(|mut memory| -> Result<(), RegionError> {
176 dst.copy_from_slice(&mut memory, data)?;
177 Ok(())
178 })
179 .unwrap()?;
180
181 Ok(dst)
182 }
183
184 pub fn serialize_and_allocate<C, T>(
187 instance: &wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
188 data: T,
189 ) -> Result<Region, RegionError>
190 where
191 C: Context,
192 T: cbor::Encode,
193 {
194 let data = cbor::to_vec(data);
195 Self::allocate_and_copy(instance, &data)
196 }
197
198 pub fn serialize_and_allocate_as_ptr<C, T>(
206 instance: &wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
207 data: T,
208 ) -> Result<u32, RegionError>
209 where
210 C: Context,
211 T: cbor::Encode,
212 {
213 let data = cbor::to_vec(data);
214 let outer = Self::allocate(instance, data.len() + 8)?;
216 let inner = Region {
219 offset: outer.offset + 8,
220 length: outer.length - 8,
221 };
222
223 instance
224 .runtime()
225 .try_with_memory(|mut memory| -> Result<(), RegionError> {
226 inner.copy_from_slice(&mut memory, &data)?;
227
228 let dst = &mut memory.as_slice_mut()[outer.offset..outer.offset + 8];
229 dst.copy_from_slice(&inner.serialize());
230
231 Ok(())
232 })
233 .unwrap()?;
234
235 Ok(outer.offset as u32)
236 }
237
238 pub fn allocate_region<C: Context>(
240 instance: &wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
241 region: Region,
242 ) -> Result<u32, RegionError> {
243 let data = region.serialize();
244
245 let dst = Self::allocate(instance, data.len())?;
247 instance
248 .runtime()
249 .try_with_memory(|mut memory| -> Result<(), RegionError> {
250 dst.copy_from_slice(&mut memory, &data)?;
251 Ok(())
252 })
253 .unwrap()?;
254
255 Ok(dst.offset as u32)
256 }
257}