Skip to main content

wasmedge_sys/instance/
memory.rs

1//! Defines WasmEdge Memory and MemType structs.
2//!
3//! A WasmEdge `Memory` defines a linear memory as described by `MemType`.
4//! `MemType` specifies the limits on the size of a memory by a range. The start of
5//! the limit range specifies min size (initial size) of that memory, while the end
6//! restricts the size to which the memory can grow later.
7
8use crate::{ffi, types::WasmEdgeLimit, utils::check, WasmEdgeResult};
9use wasmedge_types::error::{MemError, WasmEdgeError};
10
11/// Defines a WebAssembly memory instance, which is a linear memory described by its [type](crate::MemType). Each memory instance consists of a vector of bytes and an optional maximum size, and its size is a multiple of the WebAssembly page size (*64KiB* of each page).
12#[derive(Debug)]
13pub struct Memory {
14    pub(crate) inner: InnerMemory,
15}
16impl Memory {
17    /// Create a new [Memory] to be associated with the given capacity limit.
18    ///
19    /// # Arguments
20    ///
21    /// * `ty` - The type of the new [Memory] instance.
22    ///
23    /// # Errors
24    ///
25    /// * If fail to create the memory instance, then [WasmEdgeError::Mem(MemError::Create)](wasmedge_types::error::MemError) is returned.
26    ///
27    pub fn create(ty: &wasmedge_types::MemoryType) -> WasmEdgeResult<Self> {
28        let ty: MemType = ty.into();
29        let ctx = unsafe { ffi::WasmEdge_MemoryInstanceCreate(ty.inner.0 as *const _) };
30
31        match ctx.is_null() {
32            true => Err(Box::new(WasmEdgeError::Mem(MemError::Create))),
33            false => Ok(Memory {
34                inner: InnerMemory(ctx),
35            }),
36        }
37    }
38
39    /// Returns the type of the [Memory].
40    ///
41    /// # Errors
42    ///
43    /// If fail to get the type from the [Memory], then an error is returned.
44    ///
45    pub fn ty(&self) -> WasmEdgeResult<wasmedge_types::MemoryType> {
46        let ty_ctx = unsafe { ffi::WasmEdge_MemoryInstanceGetMemoryType(self.inner.0) };
47        match ty_ctx.is_null() {
48            true => Err(Box::new(WasmEdgeError::Mem(MemError::Type))),
49            false => {
50                let ty = std::mem::ManuallyDrop::new(MemType {
51                    inner: InnerMemType(ty_ctx as *mut _),
52                });
53                Ok((&*ty).into())
54            }
55        }
56    }
57
58    /// Copies the data from the [Memory] to the output buffer.
59    ///
60    /// # Arguments
61    ///
62    /// * `offset` - The data start offset in the [Memory].
63    ///
64    /// * `len` - The requested data length.
65    ///
66    /// # Errors
67    ///
68    /// If the `offset + len` is larger than the data size in the [Memory], then an error is returned.
69    ///
70    pub fn get_data(&self, offset: u32, len: u32) -> WasmEdgeResult<Vec<u8>> {
71        let mut data = Vec::with_capacity(len as usize);
72        unsafe {
73            check(ffi::WasmEdge_MemoryInstanceGetData(
74                self.inner.0,
75                data.as_mut_ptr(),
76                offset.into(),
77                len.into(),
78            ))?;
79            data.set_len(len as usize);
80        }
81
82        Ok(data.into_iter().collect())
83    }
84
85    /// Copies the data from the given input buffer into the [Memory].
86    ///
87    /// # Arguments
88    ///
89    /// * `data` - The data buffer to copy.
90    ///
91    /// * `offset` - The data start offset in the [Memory].
92    ///
93    /// # Errors
94    ///
95    /// If the sum of the `offset` and the data length is larger than the size of the [Memory],
96    /// then an error is returned.
97    ///
98    pub fn set_data(&mut self, data: impl AsRef<[u8]>, offset: u32) -> WasmEdgeResult<()> {
99        unsafe {
100            check(ffi::WasmEdge_MemoryInstanceSetData(
101                self.inner.0,
102                data.as_ref().as_ptr(),
103                offset.into(),
104                data.as_ref().len() as u64,
105            ))
106        }
107    }
108
109    /// Returns the const data pointer to the [Memory].
110    ///
111    /// # Arguments
112    ///
113    /// * `offset` - The data start offset in the [Memory].
114    ///
115    /// * `len` - The requested data length. If the size of `offset` + `len` is larger than the data size in the [Memory]
116    ///
117    ///
118    /// # Errors
119    ///
120    /// If fail to get the data pointer, then an error is returned.
121    ///
122    /// # Safety
123    ///
124    /// The lifetime of the returned pointer must not exceed that of the object itself.
125    ///
126    pub unsafe fn data_pointer(&self, offset: u32, len: u32) -> WasmEdgeResult<*const u8> {
127        let ptr = unsafe {
128            ffi::WasmEdge_MemoryInstanceGetPointerConst(self.inner.0, offset.into(), len.into())
129        };
130        match ptr.is_null() {
131            true => Err(Box::new(WasmEdgeError::Mem(MemError::ConstPtr))),
132            false => Ok(ptr),
133        }
134    }
135
136    /// Returns the data pointer to the [Memory].
137    ///
138    /// # Arguments
139    ///
140    /// * `offset` - The data start offset in the [Memory].
141    ///
142    /// * `len` - The requested data length. If the size of `offset` + `len` is larger than the data size in the [Memory]
143    ///
144    /// # Errors
145    ///
146    /// If fail to get the data pointer, then an error is returned.
147    ///
148    /// # Safety
149    ///
150    /// The lifetime of the returned pointer must not exceed that of the object itself.
151    ///
152    pub unsafe fn data_pointer_mut(&mut self, offset: u32, len: u32) -> WasmEdgeResult<*mut u8> {
153        let ptr = unsafe {
154            ffi::WasmEdge_MemoryInstanceGetPointer(self.inner.0, offset.into(), len.into())
155        };
156        match ptr.is_null() {
157            true => Err(Box::new(WasmEdgeError::Mem(MemError::MutPtr))),
158            false => Ok(ptr),
159        }
160    }
161
162    /// Returns the size, in WebAssembly pages (64 KiB of each page), of this wasm memory.
163    pub fn size(&self) -> u32 {
164        unsafe { ffi::WasmEdge_MemoryInstanceGetPageSize(self.inner.0) as u32 }
165    }
166
167    /// Grows this WebAssembly memory by `count` pages.
168    ///
169    /// # Arguments
170    ///
171    /// * `count` - The page counts to be extended to the [Memory].
172    ///
173    /// # Errors
174    ///
175    /// If fail to grow the page count, then an error is returned.
176    ///
177    pub fn grow(&mut self, count: u32) -> WasmEdgeResult<()> {
178        unsafe {
179            check(ffi::WasmEdge_MemoryInstanceGrowPage(
180                self.inner.0,
181                count.into(),
182            ))
183        }
184    }
185
186    /// # Safety
187    ///
188    /// Provides a raw pointer to the inner memory context.
189    /// The lifetime of the returned pointer must not exceed that of the object itself.
190    pub unsafe fn as_ptr(&self) -> *const ffi::WasmEdge_MemoryInstanceContext {
191        self.inner.0
192    }
193}
194impl Drop for Memory {
195    fn drop(&mut self) {
196        unsafe { ffi::WasmEdge_MemoryInstanceDelete(self.inner.0) };
197    }
198}
199
200impl Memory {
201    pub fn get_ref<T: Sized>(&self, offset: usize) -> Option<&T> {
202        unsafe {
203            let r = std::mem::size_of::<T>();
204            let ptr = self.data_pointer(offset as u32, r as u32).ok()?;
205            ptr.cast::<T>().as_ref()
206        }
207    }
208
209    pub fn slice<T: Sized>(&self, offset: usize, len: usize) -> Option<&[T]> {
210        unsafe {
211            let r = std::mem::size_of::<T>() * len;
212            let ptr = self.data_pointer(offset as u32, r as u32).ok()? as *const T;
213            Some(std::slice::from_raw_parts(ptr, len))
214        }
215    }
216
217    pub fn get_ref_mut<T: Sized>(&mut self, offset: usize) -> Option<&mut T> {
218        unsafe {
219            let r = std::mem::size_of::<T>();
220            let ptr = self.data_pointer_mut(offset as u32, r as u32).ok()?;
221            ptr.cast::<T>().as_mut()
222        }
223    }
224
225    #[allow(clippy::mut_from_ref)]
226    pub fn mut_slice<T: Sized>(&self, offset: usize, len: usize) -> Option<&mut [T]> {
227        unsafe {
228            let r = std::mem::size_of::<T>() * len;
229            let ptr = self.data_pointer(offset as u32, r as u32).ok()? as *mut T;
230            Some(std::slice::from_raw_parts_mut(ptr, len))
231        }
232    }
233
234    pub fn write<T: Sized>(&mut self, offset: usize, data: T) -> Option<()> {
235        let p = self.get_ref_mut(offset)?;
236        *p = data;
237        Some(())
238    }
239}
240
241#[derive(Debug)]
242pub(crate) struct InnerMemory(pub(crate) *mut ffi::WasmEdge_MemoryInstanceContext);
243unsafe impl Send for InnerMemory {}
244unsafe impl Sync for InnerMemory {}
245
246/// Defines the type of a wasm memory instance
247#[derive(Debug)]
248pub(crate) struct MemType {
249    pub(crate) inner: InnerMemType,
250}
251impl MemType {
252    /// Create a new [MemType] to be associated with the given limit range for the capacity.
253    ///
254    /// # Arguments
255    ///
256    /// * 'min' - The initial size of the linear memory.
257    ///
258    /// * 'max' - The upper bound of the linear memory size allowed to grow. If 'max' is set 'None', then the maximum size will be set `u32::MAX`.
259    ///
260    /// * `shared` - Whether the memory is shared or not. Reference [Threading proposal for WebAssembly](https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md#shared-linear-memory) for details about shared memory. If `shared` is set `true`, then `max` MUST not be `None`.
261    ///
262    /// # Errors
263    ///
264    /// If fail to create a [MemType], then an error is returned.
265    ///
266    /// # Example
267    ///
268    /// ```ignore
269    /// let ty = MemType::create(0, Some(u32::MAX), false);
270    /// ```
271    ///
272    pub(crate) fn create(min: u32, max: Option<u32>, shared: bool) -> WasmEdgeResult<Self> {
273        if shared && max.is_none() {
274            return Err(Box::new(WasmEdgeError::Mem(MemError::CreateSharedType)));
275        }
276        // WasmEdge_MemoryTypeCreate borrows the limit (declared `const` in
277        // the 0.17.0 C API), so the caller owns and must delete the context.
278        let limit_ctx = WasmEdgeLimit::new(min, max, shared).to_context();
279        let ctx = unsafe { ffi::WasmEdge_MemoryTypeCreate(limit_ctx) };
280        unsafe { ffi::WasmEdge_LimitDelete(limit_ctx) };
281        match ctx.is_null() {
282            true => Err(Box::new(WasmEdgeError::MemTypeCreate)),
283            false => Ok(Self {
284                inner: InnerMemType(ctx),
285            }),
286        }
287    }
288
289    /// Returns the initial size of a [Memory].
290    pub(crate) fn min(&self) -> u32 {
291        let limit = unsafe { ffi::WasmEdge_MemoryTypeGetLimit(self.inner.0) };
292        let limit = WasmEdgeLimit::from_context(limit);
293        limit.min()
294    }
295
296    /// Returns the maximum size of a [Memory] allowed to grow.
297    pub(crate) fn max(&self) -> Option<u32> {
298        let limit = unsafe { ffi::WasmEdge_MemoryTypeGetLimit(self.inner.0) };
299        let limit = WasmEdgeLimit::from_context(limit);
300        limit.max()
301    }
302
303    /// Returns whether the memory is shared or not.
304    pub(crate) fn shared(&self) -> bool {
305        let limit = unsafe { ffi::WasmEdge_MemoryTypeGetLimit(self.inner.0) };
306        let limit = WasmEdgeLimit::from_context(limit);
307        limit.shared()
308    }
309}
310impl Drop for MemType {
311    fn drop(&mut self) {
312        unsafe { ffi::WasmEdge_MemoryTypeDelete(self.inner.0) }
313    }
314}
315impl From<&wasmedge_types::MemoryType> for MemType {
316    fn from(ty: &wasmedge_types::MemoryType) -> Self {
317        MemType::create(ty.minimum(), ty.maximum(), ty.shared()).expect(
318            "[wasmedge-sys] Failed to convert wasmedge_types::MemoryType into wasmedge_sys::MemType.",
319        )
320    }
321}
322impl From<wasmedge_types::MemoryType> for MemType {
323    fn from(ty: wasmedge_types::MemoryType) -> Self {
324        (&ty).into()
325    }
326}
327impl From<&MemType> for wasmedge_types::MemoryType {
328    fn from(ty: &MemType) -> Self {
329        wasmedge_types::MemoryType::new(ty.min(), ty.max(), ty.shared()).expect(
330            "[wasmedge-sys] Failed to convert wasmedge_sys::MemType into wasmedge_types::MemoryType."
331        )
332    }
333}
334
335#[derive(Debug)]
336pub(crate) struct InnerMemType(pub(crate) *mut ffi::WasmEdge_MemoryTypeContext);
337unsafe impl Send for InnerMemType {}
338unsafe impl Sync for InnerMemType {}
339
340#[cfg(test)]
341mod tests {
342    use super::*;
343    use std::thread;
344    use wasmedge_types::error::{CoreError, CoreExecutionError, WasmEdgeError};
345
346    #[test]
347    fn test_memory_type() {
348        // case 1
349        let result = MemType::create(0, Some(u32::MAX), false);
350        assert!(result.is_ok());
351        let ty = result.unwrap();
352        assert!(!ty.inner.0.is_null());
353        assert_eq!(ty.min(), 0);
354        assert_eq!(ty.max(), Some(u32::MAX));
355
356        // case 2
357        let result = MemType::create(10, Some(101), false);
358        assert!(result.is_ok());
359        let ty = result.unwrap();
360        assert!(!ty.inner.0.is_null());
361        assert_eq!(ty.min(), 10);
362        assert_eq!(ty.max(), Some(101));
363    }
364
365    #[test]
366    #[allow(clippy::assertions_on_result_states)]
367    fn test_memory_grow() {
368        // create a Memory with a limit range [10, 20]
369        let result = wasmedge_types::MemoryType::new(10, Some(20), false);
370        assert!(result.is_ok());
371        let ty = result.unwrap();
372        let result = Memory::create(&ty);
373        assert!(result.is_ok());
374        let mut mem = result.unwrap();
375        assert!(!mem.inner.0.is_null());
376
377        // get type
378        let result = mem.ty();
379        assert!(result.is_ok());
380        let ty = result.unwrap();
381        // check limit
382        assert_eq!(ty.minimum(), 10);
383        assert_eq!(ty.maximum(), Some(20));
384
385        // check page count
386        let count = mem.size();
387        assert_eq!(count, 10);
388
389        // grow 5 pages
390        let result = mem.grow(10);
391        assert!(result.is_ok());
392        assert_eq!(mem.size(), 20);
393
394        // grow additional  pages, which causes a failure
395        let result = mem.grow(1);
396        assert!(result.is_err());
397    }
398
399    #[test]
400    #[allow(clippy::assertions_on_result_states)]
401    fn test_memory_data() {
402        // create a Memory: the min size 1 and the max size 2
403        let result = wasmedge_types::MemoryType::new(1, Some(2), false);
404        assert!(result.is_ok());
405        let ty = result.unwrap();
406        let result = Memory::create(&ty);
407        assert!(result.is_ok());
408        let mut mem = result.unwrap();
409        assert!(!mem.inner.0.is_null());
410
411        // check page count
412        let count = mem.size();
413        assert_eq!(count, 1);
414
415        // get data before set data
416        let result = mem.get_data(0, 10);
417        assert!(result.is_ok());
418        let data = result.unwrap();
419        assert_eq!(data, vec![0; 10]);
420
421        // set data
422        let result = mem.set_data(vec![1; 10], 10);
423        assert!(result.is_ok());
424        // get data after set data
425        let result = mem.get_data(10, 10);
426        assert!(result.is_ok());
427        let data = result.unwrap();
428        assert_eq!(data, vec![1; 10]);
429
430        // set data and the data length is larger than the data size in the memory
431        let result = mem.set_data(vec![1; 10], u32::pow(2, 16) - 9);
432        assert!(result.is_err());
433        assert_eq!(
434            result.unwrap_err(),
435            Box::new(WasmEdgeError::Core(CoreError::Execution(
436                CoreExecutionError::MemoryOutOfBounds
437            )))
438        );
439
440        // grow the memory size
441        let result = mem.grow(1);
442        assert!(result.is_ok());
443        assert_eq!(mem.size(), 2);
444        let result = mem.set_data(vec![1; 10], u32::pow(2, 16) - 9);
445        assert!(result.is_ok());
446    }
447
448    #[test]
449    fn test_memory_send() {
450        {
451            let result = wasmedge_types::MemoryType::new(10, Some(101), false);
452            assert!(result.is_ok());
453            let ty = result.unwrap();
454
455            let handle = thread::spawn(move || {
456                assert_eq!(ty.minimum(), 10);
457                assert_eq!(ty.maximum(), Some(101));
458            });
459
460            handle.join().unwrap()
461        }
462
463        {
464            // create a Memory with a limit range [10, 20]
465            let result = wasmedge_types::MemoryType::new(10, Some(20), false);
466            assert!(result.is_ok());
467            let ty = result.unwrap();
468            let result = Memory::create(&ty);
469            assert!(result.is_ok());
470            let mem = result.unwrap();
471            assert!(!mem.inner.0.is_null());
472
473            let handle = thread::spawn(move || {
474                // get type
475                let result = mem.ty();
476                assert!(result.is_ok());
477                let ty = result.unwrap();
478                // check limit
479                assert_eq!(ty.minimum(), 10);
480                assert_eq!(ty.maximum(), Some(20));
481
482                // check page count
483                let count = mem.size();
484                assert_eq!(count, 10);
485            });
486
487            handle.join().unwrap()
488        }
489    }
490
491    #[test]
492    fn test_memory_sync() {
493        // create a Memory with a limit range [10, 20]
494        let result = wasmedge_types::MemoryType::new(10, Some(20), false);
495        assert!(result.is_ok());
496        let ty = result.unwrap();
497        let result = Memory::create(&ty);
498        assert!(result.is_ok());
499        let mem = result.unwrap();
500        assert!(!mem.inner.0.is_null());
501        let mem = &mem;
502
503        std::thread::scope(|s| {
504            let _ = s
505                .spawn(|| {
506                    // get type
507                    let result = mem.ty();
508                    assert!(result.is_ok());
509                    let ty = result.unwrap();
510                    // check limit
511                    assert_eq!(ty.minimum(), 10);
512                    assert_eq!(ty.maximum(), Some(20));
513
514                    // check page count
515                    let count = mem.size();
516                    assert_eq!(count, 10);
517                })
518                .join();
519        })
520    }
521}