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
//! An implementation of a byte buffer based on virtual memory.
//!
//! This implementation uses `mmap` on POSIX systems (and should use `VirtualAlloc` on windows).
//! There are possibilities to improve the performance for the reallocating case by reserving
//! memory up to maximum. This might be a problem for systems that don't have a lot of virtual
//! memory (i.e. 32-bit platforms).

use core::{
    fmt,
    fmt::{Debug, Display},
    slice,
};
use region::{Allocation, Protection};

/// Dummy error for fallible `Vec`-based virtual memory operations.
#[derive(Debug)]
pub enum VirtualMemoryError {
    Region(region::Error),
    AllocationOutOfBounds,
}

impl From<region::Error> for VirtualMemoryError {
    #[inline]
    fn from(error: region::Error) -> Self {
        Self::Region(error)
    }
}

impl Display for VirtualMemoryError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Region(error) => write!(
                f,
                "encountered failure while operating with virtual memory: {}",
                error
            ),
            Self::AllocationOutOfBounds => write!(f, "virtual memory allocation is too big"),
        }
    }
}

/// A virtual memory buffer.
pub struct VirtualMemory {
    /// The virtual memory allocation.
    allocation: Allocation,
}

impl Debug for VirtualMemory {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("VirtualMemory")
            .field("len", &self.allocation.len())
            .finish()
    }
}

impl VirtualMemory {
    /// The maximum allocation size for a `wasmi` virtual memory on 16-bit.
    #[cfg(target_pointer_width = "16")]
    const MAX_ALLOCATION_SIZE: usize =
        compile_error!("16-bit architectures are current unsupported by wasmi");

    /// The maximum allocation size for a `wasmi` virtual memory on 32-bit.
    #[cfg(target_pointer_width = "32")]
    const MAX_ALLOCATION_SIZE: usize = i32::MAX as usize + 1; // 2GB

    /// The maximum allocation size for a `wasmi` virtual memory on 64-bit.
    #[cfg(target_pointer_width = "64")]
    const MAX_ALLOCATION_SIZE: usize = u32::MAX as usize + 1; // 4GB

    /// Create a new virtual memory allocation.
    ///
    /// # Note
    ///
    /// The allocated virtual memory allows for read and write operations.
    ///
    /// # Errors
    ///
    /// - If `len` should not exceed `isize::max_value()`
    /// - If `len` should be greater than 0.
    /// - If the operating system returns an error upon virtual memory allocation.
    pub fn new(len: usize) -> Result<Self, VirtualMemoryError> {
        assert_ne!(len, 0, "cannot allocate empty virtual memory");
        if len > Self::MAX_ALLOCATION_SIZE {
            return Err(VirtualMemoryError::AllocationOutOfBounds);
        }
        let allocation = region::alloc(len, Protection::READ_WRITE)?;
        Ok(Self { allocation })
    }

    /// Returns a shared slice over the bytes of the virtual memory allocation.
    #[inline]
    pub fn data(&self) -> &[u8] {
        // # SAFETY
        //
        // The operation is safe since we assume that the virtual memory allocation
        // has been successful and allocated exactly `self.allocation.len()` bytes.
        // Therefore creating a slice with `self.len` elements is valid.
        // Aliasing guarantees are not violated since `self` is the only owner
        // of the underlying virtual memory allocation.
        unsafe { slice::from_raw_parts(self.allocation.as_ptr(), self.allocation.len()) }
    }

    /// Returns an exclusive slice over the bytes of the virtual memory allocation.
    #[inline]
    pub fn data_mut(&mut self) -> &mut [u8] {
        // # SAFETY
        //
        // See safety proof of the `as_slice` method.
        // Additionally, it is not possible to obtain two mutable references for the same memory area.
        unsafe { slice::from_raw_parts_mut(self.allocation.as_mut_ptr(), self.allocation.len()) }
    }
}