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)
132 .try_into()
133 .or(Err(PageCountOutOfRange))?;
134 Ok(Self(pages))
135 }
136}
137
138impl<T> Sub<T> for Bytes
139where
140 T: Into<Self>,
141{
142 type Output = Self;
143 fn sub(self, rhs: T) -> Self {
144 Self(self.0 - rhs.into().0)
145 }
146}
147
148impl<T> Add<T> for Bytes
149where
150 T: Into<Self>,
151{
152 type Output = Self;
153 fn add(self, rhs: T) -> Self {
154 Self(self.0 + rhs.into().0)
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn convert_bytes_to_pages() {
164 let pages = Pages::try_from(Bytes(0)).unwrap();
166 assert_eq!(pages, Pages(0));
167 let pages = Pages::try_from(Bytes(1)).unwrap();
168 assert_eq!(pages, Pages(0));
169 let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE - 1)).unwrap();
170 assert_eq!(pages, Pages(0));
171 let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE)).unwrap();
172 assert_eq!(pages, Pages(1));
173 let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE + 1)).unwrap();
174 assert_eq!(pages, Pages(1));
175 let pages = Pages::try_from(Bytes(28 * WASM_PAGE_SIZE + 42)).unwrap();
176 assert_eq!(pages, Pages(28));
177 let pages = Pages::try_from(Bytes((u32::MAX as usize) * WASM_PAGE_SIZE)).unwrap();
178 assert_eq!(pages, Pages(u32::MAX));
179 let pages = Pages::try_from(Bytes((u32::MAX as usize) * WASM_PAGE_SIZE + 1)).unwrap();
180 assert_eq!(pages, Pages(u32::MAX));
181
182 let result = Pages::try_from(Bytes((u32::MAX as usize + 1) * WASM_PAGE_SIZE));
184 assert_eq!(result.unwrap_err(), PageCountOutOfRange);
185 let result = Pages::try_from(Bytes(usize::MAX));
186 assert_eq!(result.unwrap_err(), PageCountOutOfRange);
187 }
188}