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
10pub const WASM_PAGE_SIZE: usize = 0x10000;
15
16pub const WASM_MAX_PAGES: u32 = 0x10000;
18
19pub const WASM_MIN_PAGES: u32 = 0x100;
21
22#[derive(
24 Copy,
25 Clone,
26 PartialEq,
27 Eq,
28 PartialOrd,
29 Ord,
30 Hash,
31 RkyvSerialize,
32 RkyvDeserialize,
33 Archive,
34 rkyv::CheckBytes,
35)]
36#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
37#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
38#[archive(as = "Self")]
39pub struct Pages(pub u32);
40
41impl Pages {
42 #[inline(always)]
46 pub const fn max_value() -> Self {
47 Self(WASM_MAX_PAGES)
48 }
49
50 pub fn checked_add(self, rhs: Self) -> Option<Self> {
53 let added = (self.0 as usize) + (rhs.0 as usize);
54 if added <= (WASM_MAX_PAGES as usize) {
55 Some(Self(added as u32))
56 } else {
57 None
58 }
59 }
60
61 pub fn bytes(self) -> Bytes {
63 self.into()
64 }
65}
66
67impl fmt::Debug for Pages {
68 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69 write!(f, "{} pages", self.0)
70 }
71}
72
73impl From<u32> for Pages {
74 fn from(other: u32) -> Self {
75 Self(other)
76 }
77}
78
79#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
81#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
82pub struct Bytes(pub usize);
83
84impl fmt::Debug for Bytes {
85 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86 write!(f, "{} bytes", self.0)
87 }
88}
89
90impl From<Pages> for Bytes {
91 fn from(pages: Pages) -> Self {
92 Self((pages.0 as usize) * WASM_PAGE_SIZE)
93 }
94}
95
96impl From<usize> for Bytes {
97 fn from(other: usize) -> Self {
98 Self(other)
99 }
100}
101
102impl From<u32> for Bytes {
103 fn from(other: u32) -> Self {
104 Self(other.try_into().unwrap())
105 }
106}
107
108impl<T> Sub<T> for Pages
109where
110 T: Into<Self>,
111{
112 type Output = Self;
113 fn sub(self, rhs: T) -> Self {
114 Self(self.0 - rhs.into().0)
115 }
116}
117
118impl<T> Add<T> for Pages
119where
120 T: Into<Self>,
121{
122 type Output = Self;
123 fn add(self, rhs: T) -> Self {
124 Self(self.0 + rhs.into().0)
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
130#[error("Number of pages exceeds uint32 range")]
131pub struct PageCountOutOfRange;
132
133impl TryFrom<Bytes> for Pages {
134 type Error = PageCountOutOfRange;
135
136 fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
137 let pages: u32 = (bytes.0 / WASM_PAGE_SIZE)
138 .try_into()
139 .or(Err(PageCountOutOfRange))?;
140 Ok(Self(pages))
141 }
142}
143
144impl<T> Sub<T> for Bytes
145where
146 T: Into<Self>,
147{
148 type Output = Self;
149 fn sub(self, rhs: T) -> Self {
150 Self(self.0 - rhs.into().0)
151 }
152}
153
154impl<T> Add<T> for Bytes
155where
156 T: Into<Self>,
157{
158 type Output = Self;
159 fn add(self, rhs: T) -> Self {
160 Self(self.0 + rhs.into().0)
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn convert_bytes_to_pages() {
170 let pages = Pages::try_from(Bytes(0)).unwrap();
172 assert_eq!(pages, Pages(0));
173 let pages = Pages::try_from(Bytes(1)).unwrap();
174 assert_eq!(pages, Pages(0));
175 let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE - 1)).unwrap();
176 assert_eq!(pages, Pages(0));
177 let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE)).unwrap();
178 assert_eq!(pages, Pages(1));
179 let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE + 1)).unwrap();
180 assert_eq!(pages, Pages(1));
181 let pages = Pages::try_from(Bytes(28 * WASM_PAGE_SIZE + 42)).unwrap();
182 assert_eq!(pages, Pages(28));
183 let pages = Pages::try_from(Bytes((u32::MAX as usize) * WASM_PAGE_SIZE)).unwrap();
184 assert_eq!(pages, Pages(u32::MAX));
185 let pages = Pages::try_from(Bytes((u32::MAX as usize) * WASM_PAGE_SIZE + 1)).unwrap();
186 assert_eq!(pages, Pages(u32::MAX));
187
188 let result = Pages::try_from(Bytes((u32::MAX as usize + 1) * WASM_PAGE_SIZE));
190 assert_eq!(result.unwrap_err(), PageCountOutOfRange);
191 let result = Pages::try_from(Bytes(usize::MAX));
192 assert_eq!(result.unwrap_err(), PageCountOutOfRange);
193 }
194}