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