1#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
3#[repr(transparent)]
4pub struct Pages(u32);
5
6impl Pages {
7 pub const fn max() -> Self {
16 Self(65536) }
18}
19
20impl From<u16> for Pages {
21 fn from(amount: u16) -> Self {
30 Self(u32::from(amount))
31 }
32}
33
34impl Pages {
35 pub fn new(amount: u32) -> Option<Self> {
41 if amount > u32::from(Self::max()) {
42 return None;
43 }
44 Some(Self(amount))
45 }
46
47 pub fn checked_add<T>(self, rhs: T) -> Option<Self>
51 where
52 T: Into<u32>,
53 {
54 let lhs: u32 = self.into();
55 let rhs: u32 = rhs.into();
56 lhs.checked_add(rhs).and_then(Self::new)
57 }
58
59 pub fn checked_sub<T>(self, rhs: T) -> Option<Self>
63 where
64 T: Into<u32>,
65 {
66 let lhs: u32 = self.into();
67 let rhs: u32 = rhs.into();
68 lhs.checked_sub(rhs).and_then(Self::new)
69 }
70
71 pub fn to_bytes(self) -> Option<usize> {
76 Bytes::new(self).map(Into::into)
77 }
78}
79
80impl From<Pages> for u32 {
81 fn from(pages: Pages) -> Self {
82 pages.0
83 }
84}
85
86#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
88#[repr(transparent)]
89pub struct Bytes(usize);
90
91impl Bytes {
92 const fn max16() -> u64 {
94 i16::MAX as u64 + 1
95 }
96
97 const fn max32() -> u64 {
99 i32::MAX as u64 + 1
100 }
101
102 const fn max64() -> u64 {
104 u32::MAX as u64 + 1
105 }
106
107 const fn per_page() -> Self {
114 Self(65536) }
116
117 fn new(pages: Pages) -> Option<Bytes> {
124 if cfg!(target_pointer_width = "16") {
125 Self::new16(pages)
126 } else if cfg!(target_pointer_width = "32") {
127 Self::new32(pages)
128 } else if cfg!(target_pointer_width = "64") {
129 Self::new64(pages)
130 } else {
131 None
132 }
133 }
134
135 fn new16(pages: Pages) -> Option<Bytes> {
144 Self::new_impl(pages, Bytes::max16())
145 }
146
147 fn new32(pages: Pages) -> Option<Bytes> {
156 Self::new_impl(pages, Bytes::max32())
157 }
158
159 fn new64(pages: Pages) -> Option<Bytes> {
168 Self::new_impl(pages, Bytes::max64())
169 }
170
171 fn new_impl(pages: Pages, max: u64) -> Option<Bytes> {
173 let pages = u64::from(u32::from(pages));
174 let bytes_per_page = usize::from(Self::per_page()) as u64;
175 let bytes = pages
176 .checked_mul(bytes_per_page)
177 .filter(|&amount| amount <= max)?;
178 Some(Self(bytes as usize))
179 }
180}
181
182impl From<Bytes> for usize {
183 #[inline]
184 fn from(bytes: Bytes) -> Self {
185 bytes.0
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 fn pages(amount: u32) -> Pages {
194 Pages::new(amount).unwrap()
195 }
196
197 fn bytes(amount: usize) -> Bytes {
198 Bytes(amount)
199 }
200
201 #[test]
202 fn pages_max() {
203 assert_eq!(Pages::max(), pages(u32::from(u16::MAX) + 1));
204 }
205
206 #[test]
207 fn pages_new() {
208 assert_eq!(Pages::new(0), Some(Pages(0)));
209 assert_eq!(Pages::new(1), Some(Pages(1)));
210 assert_eq!(Pages::new(1000), Some(Pages(1000)));
211 assert_eq!(
212 Pages::new(u32::from(u16::MAX)),
213 Some(Pages(u32::from(u16::MAX)))
214 );
215 assert_eq!(Pages::new(u32::from(u16::MAX) + 1), Some(Pages::max()));
216 assert_eq!(Pages::new(u32::from(u16::MAX) + 2), None);
217 assert_eq!(Pages::new(u32::MAX), None);
218 }
219
220 #[test]
221 fn pages_checked_add() {
222 let max_pages = u32::from(Pages::max());
223
224 assert_eq!(pages(0).checked_add(0u32), Some(pages(0)));
225 assert_eq!(pages(0).checked_add(1u32), Some(pages(1)));
226 assert_eq!(pages(1).checked_add(0u32), Some(pages(1)));
227
228 assert_eq!(pages(0).checked_add(max_pages), Some(Pages::max()));
229 assert_eq!(pages(0).checked_add(Pages::max()), Some(Pages::max()));
230 assert_eq!(pages(1).checked_add(max_pages), None);
231 assert_eq!(pages(1).checked_add(Pages::max()), None);
232
233 assert_eq!(Pages::max().checked_add(0u32), Some(Pages::max()));
234 assert_eq!(Pages::max().checked_add(1u32), None);
235 assert_eq!(pages(0).checked_add(u32::MAX), None);
236
237 for i in 0..100 {
238 for j in 0..100 {
239 assert_eq!(pages(i).checked_add(pages(j)), Some(pages(i + j)));
240 }
241 }
242 }
243
244 #[test]
245 fn pages_checked_sub() {
246 let max_pages = u32::from(Pages::max());
247
248 assert_eq!(pages(0).checked_sub(0u32), Some(pages(0)));
249 assert_eq!(pages(0).checked_sub(1u32), None);
250 assert_eq!(pages(1).checked_sub(0u32), Some(pages(1)));
251 assert_eq!(pages(1).checked_sub(1u32), Some(pages(0)));
252
253 assert_eq!(Pages::max().checked_sub(Pages::max()), Some(pages(0)));
254 assert_eq!(Pages::max().checked_sub(u32::MAX), None);
255 assert_eq!(Pages::max().checked_sub(1u32), Some(pages(max_pages - 1)));
256
257 for i in 0..100 {
258 for j in 0..100 {
259 assert_eq!(pages(i).checked_sub(pages(j)), i.checked_sub(j).map(pages));
260 }
261 }
262 }
263
264 #[test]
265 fn pages_to_bytes() {
266 assert_eq!(pages(0).to_bytes(), Some(0));
267 if cfg!(target_pointer_width = "16") {
268 assert_eq!(pages(1).to_bytes(), None);
269 }
270 if cfg!(target_pointer_width = "32") || cfg!(target_pointer_width = "64") {
271 let bytes_per_page = usize::from(Bytes::per_page());
272 for n in 1..10 {
273 assert_eq!(pages(n as u32).to_bytes(), Some(n * bytes_per_page));
274 }
275 }
276 }
277
278 #[test]
279 fn bytes_new16() {
280 assert_eq!(Bytes::new16(pages(0)), Some(bytes(0)));
281 assert_eq!(Bytes::new16(pages(1)), None);
282 assert!(Bytes::new16(Pages::max()).is_none());
283 }
284
285 #[test]
286 fn bytes_new32() {
287 assert_eq!(Bytes::new32(pages(0)), Some(bytes(0)));
288 assert_eq!(Bytes::new32(pages(1)), Some(Bytes::per_page()));
289 let bytes_per_page = usize::from(Bytes::per_page());
290 for n in 2..10 {
291 assert_eq!(
292 Bytes::new32(pages(n as u32)),
293 Some(bytes(n * bytes_per_page))
294 );
295 }
296 assert!(Bytes::new32(pages(i16::MAX as u32 + 1)).is_some());
297 assert!(Bytes::new32(pages(i16::MAX as u32 + 2)).is_none());
298 assert!(Bytes::new32(Pages::max()).is_none());
299 }
300
301 #[test]
302 fn bytes_new64() {
303 assert_eq!(Bytes::new64(pages(0)), Some(bytes(0)));
304 assert_eq!(Bytes::new64(pages(1)), Some(Bytes::per_page()));
305 let bytes_per_page = usize::from(Bytes::per_page());
306 for n in 2..10 {
307 assert_eq!(
308 Bytes::new64(pages(n as u32)),
309 Some(bytes(n * bytes_per_page))
310 );
311 }
312 assert!(Bytes::new64(Pages(u32::from(u16::MAX) + 1)).is_some());
313 assert!(Bytes::new64(Pages(u32::from(u16::MAX) + 2)).is_none());
314 assert!(Bytes::new64(Pages::max()).is_some());
315 }
316}