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, 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 #[inline(always)]
36 pub const fn max_value() -> Self {
37 Self(WASM_MAX_PAGES)
38 }
39
40 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 pub fn bytes(self) -> Bytes {
53 self.into()
54 }
55}
56
57impl fmt::Debug for Pages {
58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 write!(f, "{} pages", self.0)
60 }
61}
62
63impl From<u32> for Pages {
64 fn from(other: u32) -> Self {
65 Self(other)
66 }
67}
68
69#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
71#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
72pub struct Bytes(pub usize);
73
74impl fmt::Debug for Bytes {
75 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76 write!(f, "{} bytes", self.0)
77 }
78}
79
80impl From<Pages> for Bytes {
81 fn from(pages: Pages) -> Self {
82 Self((pages.0 as usize) * WASM_PAGE_SIZE)
83 }
84}
85
86impl From<usize> for Bytes {
87 fn from(other: usize) -> Self {
88 Self(other)
89 }
90}
91
92impl From<u32> for Bytes {
93 fn from(other: u32) -> Self {
94 Self(other.try_into().unwrap())
95 }
96}
97
98impl<T> Sub<T> for Pages
99where
100 T: Into<Self>,
101{
102 type Output = Self;
103 fn sub(self, rhs: T) -> Self {
104 Self(self.0 - rhs.into().0)
105 }
106}
107
108impl<T> Add<T> for Pages
109where
110 T: Into<Self>,
111{
112 type Output = Self;
113 fn add(self, rhs: T) -> Self {
114 Self(self.0 + rhs.into().0)
115 }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
120#[error("Number of pages exceeds uint32 range")]
121pub struct PageCountOutOfRange;
122
123impl TryFrom<Bytes> for Pages {
124 type Error = PageCountOutOfRange;
125
126 fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
127 let pages: u32 = (bytes.0 / WASM_PAGE_SIZE)
128 .try_into()
129 .or(Err(PageCountOutOfRange))?;
130 Ok(Self(pages))
131 }
132}
133
134impl<T> Sub<T> for Bytes
135where
136 T: Into<Self>,
137{
138 type Output = Self;
139 fn sub(self, rhs: T) -> Self {
140 Self(self.0 - rhs.into().0)
141 }
142}
143
144impl<T> Add<T> for Bytes
145where
146 T: Into<Self>,
147{
148 type Output = Self;
149 fn add(self, rhs: T) -> Self {
150 Self(self.0 + rhs.into().0)
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn convert_bytes_to_pages() {
160 let pages = Pages::try_from(Bytes(0)).unwrap();
162 assert_eq!(pages, Pages(0));
163 let pages = Pages::try_from(Bytes(1)).unwrap();
164 assert_eq!(pages, Pages(0));
165 let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE - 1)).unwrap();
166 assert_eq!(pages, Pages(0));
167 let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE)).unwrap();
168 assert_eq!(pages, Pages(1));
169 let pages = Pages::try_from(Bytes(WASM_PAGE_SIZE + 1)).unwrap();
170 assert_eq!(pages, Pages(1));
171 let pages = Pages::try_from(Bytes(28 * WASM_PAGE_SIZE + 42)).unwrap();
172 assert_eq!(pages, Pages(28));
173 let pages = Pages::try_from(Bytes((u32::MAX as usize) * WASM_PAGE_SIZE)).unwrap();
174 assert_eq!(pages, Pages(u32::MAX));
175 let pages = Pages::try_from(Bytes((u32::MAX as usize) * WASM_PAGE_SIZE + 1)).unwrap();
176 assert_eq!(pages, Pages(u32::MAX));
177
178 let result = Pages::try_from(Bytes((u32::MAX as usize + 1) * WASM_PAGE_SIZE));
180 assert_eq!(result.unwrap_err(), PageCountOutOfRange);
181 let result = Pages::try_from(Bytes(usize::MAX));
182 assert_eq!(result.unwrap_err(), PageCountOutOfRange);
183 }
184}