wasmer_types/
units.rs

1use crate::lib::std::convert::TryFrom;
2use crate::lib::std::fmt;
3use crate::lib::std::ops::{Add, Sub};
4use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
5#[cfg(feature = "enable-serde")]
6use serde::{Deserialize, Serialize};
7use std::convert::TryInto;
8use thiserror::Error;
9
10/// WebAssembly page sizes are fixed to be 64KiB.
11/// Note: large page support may be added in an opt-in manner in the [future].
12///
13/// [future]: https://webassembly.org/docs/future-features/#large-page-support
14pub const WASM_PAGE_SIZE: usize = 0x10000;
15
16/// The number of pages we can have before we run out of byte index space.
17pub const WASM_MAX_PAGES: u32 = 0x10000;
18
19/// The minimum number of pages allowed.
20pub const WASM_MIN_PAGES: u32 = 0x100;
21
22/// Units of WebAssembly pages (as specified to be 65,536 bytes).
23#[derive(
24    Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RkyvSerialize, RkyvDeserialize, Archive,
25)]
26#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
27#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
28#[rkyv(derive(Debug), compare(PartialEq, PartialOrd))]
29pub struct Pages(pub u32);
30
31impl Pages {
32    /// Returns the largest value that can be represented by the Pages type.
33    ///
34    /// This is defined by the WebAssembly standard as 65,536 pages.
35    #[inline(always)]
36    pub const fn max_value() -> Self {
37        Self(WASM_MAX_PAGES)
38    }
39
40    /// Checked addition. Computes `self + rhs`,
41    /// returning `None` if overflow occurred.
42    pub fn checked_add(self, rhs: Self) -> Option<Self> {
43        let added = (self.0 as usize) + (rhs.0 as usize);
44        if added <= (WASM_MAX_PAGES as usize) {
45            Some(Self(added as u32))
46        } else {
47            None
48        }
49    }
50
51    /// Calculate number of bytes from pages.
52    pub fn bytes(self) -> Bytes {
53        self.into()
54    }
55
56    /// Calculate the number of pages needed to hold the given number of bytes,
57    /// rounding up to include any partial page,
58    /// returning `None` if overflow occurred.
59    pub fn from_bytes_rounded_up(bytes: u64) -> Option<Self> {
60        let pages = bytes.div_ceil(WASM_PAGE_SIZE as u64);
61        if pages <= (WASM_MAX_PAGES as u64) {
62            Some(Self(pages as u32))
63        } else {
64            None
65        }
66    }
67}
68
69impl fmt::Debug for Pages {
70    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71        write!(f, "{} pages", self.0)
72    }
73}
74
75impl From<u32> for Pages {
76    fn from(other: u32) -> Self {
77        Self(other)
78    }
79}
80
81/// Units of WebAssembly memory in terms of 8-bit bytes.
82#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
83#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
84pub struct Bytes(pub usize);
85
86impl fmt::Debug for Bytes {
87    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88        write!(f, "{} bytes", self.0)
89    }
90}
91
92impl From<Pages> for Bytes {
93    fn from(pages: Pages) -> Self {
94        Self((pages.0 as usize) * WASM_PAGE_SIZE)
95    }
96}
97
98impl From<usize> for Bytes {
99    fn from(other: usize) -> Self {
100        Self(other)
101    }
102}
103
104impl From<u32> for Bytes {
105    fn from(other: u32) -> Self {
106        Self(other.try_into().unwrap())
107    }
108}
109
110impl<T> Sub<T> for Pages
111where
112    T: Into<Self>,
113{
114    type Output = Self;
115    fn sub(self, rhs: T) -> Self {
116        Self(self.0 - rhs.into().0)
117    }
118}
119
120impl<T> Add<T> for Pages
121where
122    T: Into<Self>,
123{
124    type Output = Self;
125    fn add(self, rhs: T) -> Self {
126        Self(self.0 + rhs.into().0)
127    }
128}
129
130/// The only error that can happen when converting `Bytes` to `Pages`
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
132#[error("Number of pages exceeds uint32 range")]
133pub struct PageCountOutOfRange;
134
135impl TryFrom<Bytes> for Pages {
136    type Error = PageCountOutOfRange;
137
138    fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
139        let pages: u32 = (bytes.0 / WASM_PAGE_SIZE)
140            .try_into()
141            .or(Err(PageCountOutOfRange))?;
142        Ok(Self(pages))
143    }
144}
145
146impl<T> Sub<T> for Bytes
147where
148    T: Into<Self>,
149{
150    type Output = Self;
151    fn sub(self, rhs: T) -> Self {
152        Self(self.0 - rhs.into().0)
153    }
154}
155
156impl<T> Add<T> for Bytes
157where
158    T: Into<Self>,
159{
160    type Output = Self;
161    fn add(self, rhs: T) -> Self {
162        Self(self.0 + rhs.into().0)
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn convert_bytes_to_pages() {
172        // rounds down
173        let pages = Pages::try_from(Bytes(0)).unwrap();
174        assert_eq!(pages, Pages(0));
175        let pages = Pages::try_from(Bytes(1)).unwrap();
176        assert_eq!(pages, Pages(0));
177        let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE - 1)).unwrap();
178        assert_eq!(pages, Pages(0));
179        let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE)).unwrap();
180        assert_eq!(pages, Pages(1));
181        let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE + 1)).unwrap();
182        assert_eq!(pages, Pages(1));
183        let pages = Pages::try_from(Bytes(28 * WASM_PAGE_SIZE + 42)).unwrap();
184        assert_eq!(pages, Pages(28));
185        let pages = Pages::try_from(Bytes((u32::MAX as usize) * WASM_PAGE_SIZE)).unwrap();
186        assert_eq!(pages, Pages(u32::MAX));
187        let pages = Pages::try_from(Bytes((u32::MAX as usize) * WASM_PAGE_SIZE + 1)).unwrap();
188        assert_eq!(pages, Pages(u32::MAX));
189
190        // Errors when page count cannot be represented as u32
191        let result = Pages::try_from(Bytes((u32::MAX as usize + 1) * WASM_PAGE_SIZE));
192        assert_eq!(result.unwrap_err(), PageCountOutOfRange);
193        let result = Pages::try_from(Bytes(usize::MAX));
194        assert_eq!(result.unwrap_err(), PageCountOutOfRange);
195    }
196}