radix_wasmi/memory/
mod.rs

1extern crate radix_wasmi_arena as wasmi_arena;
2
3mod byte_buffer;
4
5use self::byte_buffer::ByteBuffer;
6use super::{AsContext, AsContextMut, StoreContext, StoreContextMut, Stored};
7use core::{fmt, fmt::Display};
8use wasmi_arena::ArenaIndex;
9use wasmi_core::Pages;
10
11/// A raw index to a linear memory entity.
12#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
13pub struct MemoryIdx(u32);
14
15impl ArenaIndex for MemoryIdx {
16    fn into_usize(self) -> usize {
17        self.0 as usize
18    }
19
20    fn from_usize(value: usize) -> Self {
21        let value = value.try_into().unwrap_or_else(|error| {
22            panic!("index {value} is out of bounds as memory index: {error}")
23        });
24        Self(value)
25    }
26}
27
28/// An error that may occur upon operating with virtual or linear memory.
29#[derive(Debug)]
30#[non_exhaustive]
31pub enum MemoryError {
32    /// Tried to allocate more virtual memory than technically possible.
33    OutOfBoundsAllocation,
34    /// Tried to grow linear memory out of its set bounds.
35    OutOfBoundsGrowth,
36    /// Tried to access linear memory out of bounds.
37    OutOfBoundsAccess,
38    /// Tried to create an invalid linear memory type.
39    InvalidMemoryType,
40    /// Occurs when a memory type does not satisfy the constraints of another.
41    UnsatisfyingMemoryType {
42        /// The unsatisfying [`MemoryType`].
43        unsatisfying: MemoryType,
44        /// The required [`MemoryType`].
45        required: MemoryType,
46    },
47}
48
49impl Display for MemoryError {
50    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51        match self {
52            Self::OutOfBoundsAllocation => {
53                write!(f, "tried to allocate too much virtual memory")
54            }
55            Self::OutOfBoundsGrowth => {
56                write!(f, "tried to grow virtual memory out of bounds")
57            }
58            Self::OutOfBoundsAccess => {
59                write!(f, "tried to access virtual memory out of bounds")
60            }
61            Self::InvalidMemoryType => {
62                write!(f, "tried to create an invalid virtual memory type")
63            }
64            Self::UnsatisfyingMemoryType {
65                unsatisfying,
66                required,
67            } => {
68                write!(
69                    f,
70                    "memory type {unsatisfying:?} does not \
71                    satisfy requirements of {required:?}",
72                )
73            }
74        }
75    }
76}
77
78/// The memory type of a linear memory.
79#[derive(Debug, Copy, Clone, PartialEq, Eq)]
80pub struct MemoryType {
81    initial_pages: Pages,
82    maximum_pages: Option<Pages>,
83}
84
85impl MemoryType {
86    /// Creates a new memory type with initial and optional maximum pages.
87    ///
88    /// # Errors
89    ///
90    /// If the linear memory type initial or maximum size exceeds the
91    /// maximum limits of 2^16 pages.
92    pub fn new(initial: u32, maximum: Option<u32>) -> Result<Self, MemoryError> {
93        let initial_pages = Pages::new(initial).ok_or(MemoryError::InvalidMemoryType)?;
94        let maximum_pages = match maximum {
95            Some(maximum) => Pages::new(maximum)
96                .ok_or(MemoryError::InvalidMemoryType)?
97                .into(),
98            None => None,
99        };
100        Ok(Self {
101            initial_pages,
102            maximum_pages,
103        })
104    }
105
106    /// Returns the initial pages of the memory type.
107    pub fn initial_pages(self) -> Pages {
108        self.initial_pages
109    }
110
111    /// Returns the maximum pages of the memory type.
112    ///
113    /// # Note
114    ///
115    /// - Returns `None` if there is no limit set.
116    /// - Maximum memory size cannot exceed `65536` pages or 4GiB.
117    pub fn maximum_pages(self) -> Option<Pages> {
118        self.maximum_pages
119    }
120
121    /// Checks if `self` satisfies the given `MemoryType`.
122    ///
123    /// # Errors
124    ///
125    /// - If the initial limits of the `required` [`MemoryType`] are greater than `self`.
126    /// - If the maximum limits of the `required` [`MemoryType`] are greater than `self`.
127    pub(crate) fn satisfies(&self, required: &MemoryType) -> Result<(), MemoryError> {
128        if required.initial_pages() > self.initial_pages() {
129            return Err(MemoryError::UnsatisfyingMemoryType {
130                unsatisfying: *self,
131                required: *required,
132            });
133        }
134        match (required.maximum_pages(), self.maximum_pages()) {
135            (None, _) => (),
136            (Some(max_required), Some(max)) if max_required >= max => (),
137            _ => {
138                return Err(MemoryError::UnsatisfyingMemoryType {
139                    unsatisfying: *self,
140                    required: *required,
141                });
142            }
143        }
144        Ok(())
145    }
146}
147
148/// A linear memory entity.
149#[derive(Debug, Clone)]
150pub struct MemoryEntity {
151    bytes: ByteBuffer,
152    memory_type: MemoryType,
153    current_pages: Pages,
154}
155
156impl MemoryEntity {
157    /// Creates a new memory entity with the given memory type.
158    pub fn new(memory_type: MemoryType) -> Result<Self, MemoryError> {
159        let initial_pages = memory_type.initial_pages();
160        let initial_len = initial_pages
161            .to_bytes()
162            .ok_or(MemoryError::OutOfBoundsAllocation)?;
163        let memory = Self {
164            bytes: ByteBuffer::new(initial_len),
165            memory_type,
166            current_pages: initial_pages,
167        };
168        Ok(memory)
169    }
170
171    /// Returns the memory type of the linear memory.
172    pub fn ty(&self) -> MemoryType {
173        self.memory_type
174    }
175
176    /// Returns the amount of pages in use by the linear memory.
177    pub fn current_pages(&self) -> Pages {
178        self.current_pages
179    }
180
181    /// Grows the linear memory by the given amount of new pages.
182    ///
183    /// Returns the amount of pages before the operation upon success.
184    ///
185    /// # Errors
186    ///
187    /// If the linear memory would grow beyond its maximum limit after
188    /// the grow operation.
189    pub fn grow(&mut self, additional: Pages) -> Result<Pages, MemoryError> {
190        let current_pages = self.current_pages();
191        if additional == Pages::default() {
192            // Nothing to do in this case. Bail out early.
193            return Ok(current_pages);
194        }
195        let maximum_pages = self.ty().maximum_pages().unwrap_or_else(Pages::max);
196        let new_pages = current_pages
197            .checked_add(additional)
198            .filter(|&new_pages| new_pages <= maximum_pages)
199            .ok_or(MemoryError::OutOfBoundsGrowth)?;
200        let new_size = new_pages
201            .to_bytes()
202            .ok_or(MemoryError::OutOfBoundsAllocation)?;
203        // At this point it is okay to grow the underlying virtual memory
204        // by the given amount of additional pages.
205        self.bytes.grow(new_size);
206        self.current_pages = new_pages;
207        Ok(current_pages)
208    }
209
210    /// Returns a shared slice to the bytes underlying to the byte buffer.
211    pub fn data(&self) -> &[u8] {
212        self.bytes.data()
213    }
214
215    /// Returns an exclusive slice to the bytes underlying to the byte buffer.
216    pub fn data_mut(&mut self) -> &mut [u8] {
217        self.bytes.data_mut()
218    }
219
220    /// Reads `n` bytes from `memory[offset..offset+n]` into `buffer`
221    /// where `n` is the length of `buffer`.
222    ///
223    /// # Errors
224    ///
225    /// If this operation accesses out of bounds linear memory.
226    pub fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<(), MemoryError> {
227        let len_buffer = buffer.len();
228        let slice = self
229            .data()
230            .get(offset..(offset + len_buffer))
231            .ok_or(MemoryError::OutOfBoundsAccess)?;
232        buffer.copy_from_slice(slice);
233        Ok(())
234    }
235
236    /// Writes `n` bytes to `memory[offset..offset+n]` from `buffer`
237    /// where `n` if the length of `buffer`.
238    ///
239    /// # Errors
240    ///
241    /// If this operation accesses out of bounds linear memory.
242    pub fn write(&mut self, offset: usize, buffer: &[u8]) -> Result<(), MemoryError> {
243        let len_buffer = buffer.len();
244        let slice = self
245            .data_mut()
246            .get_mut(offset..(offset + len_buffer))
247            .ok_or(MemoryError::OutOfBoundsAccess)?;
248        slice.copy_from_slice(buffer);
249        Ok(())
250    }
251}
252
253/// A Wasm linear memory reference.
254#[derive(Debug, Copy, Clone)]
255#[repr(transparent)]
256pub struct Memory(Stored<MemoryIdx>);
257
258impl Memory {
259    /// Creates a new linear memory reference.
260    pub(super) fn from_inner(stored: Stored<MemoryIdx>) -> Self {
261        Self(stored)
262    }
263
264    /// Returns the underlying stored representation.
265    pub(super) fn into_inner(self) -> Stored<MemoryIdx> {
266        self.0
267    }
268
269    /// Creates a new linear memory to the store.
270    ///
271    /// # Errors
272    ///
273    /// If more than [`u32::MAX`] much linear memory is allocated.
274    pub fn new(mut ctx: impl AsContextMut, ty: MemoryType) -> Result<Self, MemoryError> {
275        let entity = MemoryEntity::new(ty)?;
276        let memory = ctx.as_context_mut().store.alloc_memory(entity);
277        Ok(memory)
278    }
279
280    /// Returns the memory type of the linear memory.
281    ///
282    /// # Panics
283    ///
284    /// Panics if `ctx` does not own this [`Memory`].
285    pub fn ty(&self, ctx: impl AsContext) -> MemoryType {
286        ctx.as_context().store.resolve_memory(*self).ty()
287    }
288
289    /// Returns the amount of pages in use by the linear memory.
290    ///
291    /// # Panics
292    ///
293    /// Panics if `ctx` does not own this [`Memory`].
294    pub fn current_pages(&self, ctx: impl AsContext) -> Pages {
295        ctx.as_context().store.resolve_memory(*self).current_pages()
296    }
297
298    /// Grows the linear memory by the given amount of new pages.
299    ///
300    /// Returns the amount of pages before the operation upon success.
301    ///
302    /// # Errors
303    ///
304    /// If the linear memory would grow beyond its maximum limit after
305    /// the grow operation.
306    ///
307    /// # Panics
308    ///
309    /// Panics if `ctx` does not own this [`Memory`].
310    pub fn grow(
311        &self,
312        mut ctx: impl AsContextMut,
313        additional: Pages,
314    ) -> Result<Pages, MemoryError> {
315        ctx.as_context_mut()
316            .store
317            .resolve_memory_mut(*self)
318            .grow(additional)
319    }
320
321    /// Returns a shared slice to the bytes underlying the [`Memory`].
322    ///
323    /// # Panics
324    ///
325    /// Panics if `ctx` does not own this [`Memory`].
326    pub fn data<'a, T: 'a>(&self, ctx: impl Into<StoreContext<'a, T>>) -> &'a [u8] {
327        ctx.into().store.resolve_memory(*self).data()
328    }
329
330    /// Returns an exclusive slice to the bytes underlying the [`Memory`].
331    ///
332    /// # Panics
333    ///
334    /// Panics if `ctx` does not own this [`Memory`].
335    pub fn data_mut<'a, T: 'a>(&self, ctx: impl Into<StoreContextMut<'a, T>>) -> &'a mut [u8] {
336        ctx.into().store.resolve_memory_mut(*self).data_mut()
337    }
338
339    /// Returns an exclusive slice to the bytes underlying the [`Memory`], and an exclusive
340    /// reference to the user provided state.
341    ///
342    /// # Panics
343    ///
344    /// Panics if `ctx` does not own this [`Memory`].
345    pub fn data_and_store_mut<'a, T: 'a>(
346        &self,
347        ctx: impl Into<StoreContextMut<'a, T>>,
348    ) -> (&'a mut [u8], &'a mut T) {
349        let (memory, store) = ctx.into().store.resolve_memory_and_state_mut(*self);
350        (memory.data_mut(), store)
351    }
352
353    /// Reads `n` bytes from `memory[offset..offset+n]` into `buffer`
354    /// where `n` is the length of `buffer`.
355    ///
356    /// # Errors
357    ///
358    /// If this operation accesses out of bounds linear memory.
359    ///
360    /// # Panics
361    ///
362    /// Panics if `ctx` does not own this [`Memory`].
363    pub fn read(
364        &self,
365        ctx: impl AsContext,
366        offset: usize,
367        buffer: &mut [u8],
368    ) -> Result<(), MemoryError> {
369        ctx.as_context()
370            .store
371            .resolve_memory(*self)
372            .read(offset, buffer)
373    }
374
375    /// Writes `n` bytes to `memory[offset..offset+n]` from `buffer`
376    /// where `n` if the length of `buffer`.
377    ///
378    /// # Errors
379    ///
380    /// If this operation accesses out of bounds linear memory.
381    ///
382    /// # Panics
383    ///
384    /// Panics if `ctx` does not own this [`Memory`].
385    pub fn write(
386        &self,
387        mut ctx: impl AsContextMut,
388        offset: usize,
389        buffer: &[u8],
390    ) -> Result<(), MemoryError> {
391        ctx.as_context_mut()
392            .store
393            .resolve_memory_mut(*self)
394            .write(offset, buffer)
395    }
396}