1use core::cmp::Ordering;
2use core::fmt;
3use core::num::NonZeroUsize;
4use core::ops::{Add, Range, Sub};
5
6use align_address::{usize_align_down, usize_checked_align_up, usize_is_aligned_to};
7
8use crate::{PAGE_SIZE, PageLayout};
9
10#[non_exhaustive]
12#[derive(Clone, PartialEq, Eq, Debug)]
13pub struct PageRangeError;
14
15impl fmt::Display for PageRangeError {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 f.write_str("invalid parameters to PageLayout::new")
18 }
19}
20
21#[derive(Copy, Clone, PartialEq, Eq, Hash)]
23pub struct PageRange {
24 start: usize,
25 end: NonZeroUsize,
26}
27
28impl PageRange {
29 pub const fn new(start: usize, end: usize) -> Result<Self, PageRangeError> {
46 if start >= end {
47 return Err(PageRangeError);
48 }
49
50 if !usize_is_aligned_to(start, PAGE_SIZE) {
51 return Err(PageRangeError);
52 }
53
54 if !usize_is_aligned_to(end, PAGE_SIZE) {
55 return Err(PageRangeError);
56 }
57
58 let end = match NonZeroUsize::new(end) {
59 Some(end) => end,
60 None => unreachable!(),
61 };
62
63 Ok(Self { start, end })
64 }
65
66 pub const fn from_start_len(start: usize, len: usize) -> Result<Self, PageRangeError> {
85 let end = match start.checked_add(len) {
86 Some(end) => end,
87 None => return Err(PageRangeError),
88 };
89 Self::new(start, end)
90 }
91
92 pub const fn start(self) -> usize {
103 self.start
104 }
105
106 pub const fn end(self) -> usize {
117 self.end.get()
118 }
119
120 pub const fn len(self) -> NonZeroUsize {
131 match NonZeroUsize::new(self.end() - self.start) {
132 Some(len) => len,
133 None => unreachable!(),
134 }
135 }
136
137 pub const fn pages(self) -> NonZeroUsize {
148 match NonZeroUsize::new(self.len().get() / PAGE_SIZE) {
149 Some(pages) => pages,
150 None => unreachable!(),
151 }
152 }
153
154 pub const fn overlaps(self, other: Self) -> bool {
168 self.end() > other.start && self.start < other.end()
169 }
170
171 pub const fn touches(self, other: Self) -> bool {
185 self.end() == other.start || self.start == other.end()
186 }
187
188 pub const fn contains(self, other: Self) -> bool {
200 self.start <= other.start && self.end() >= other.end()
201 }
202
203 pub const fn fit(self, layout: PageLayout) -> Option<PageRange> {
216 let Some(start) = usize_checked_align_up(self.start, layout.align()) else {
218 return None;
219 };
220 let Ok(range) = Self::from_start_len(start, layout.size()) else {
221 return None;
222 };
223 if self.contains(range) {
224 Some(range)
225 } else {
226 None
227 }
228 }
229
230 pub const fn containing(start: usize, end: usize) -> Result<Self, PageRangeError> {
242 let start = usize_align_down(start, PAGE_SIZE);
243 let Some(end) = usize_checked_align_up(end, PAGE_SIZE) else {
244 return Err(PageRangeError);
245 };
246 Self::new(start, end)
247 }
248
249 pub const fn and(self, rhs: Self) -> Option<Self> {
262 let start = if rhs.start() < self.start() {
263 self.start()
264 } else {
265 rhs.start()
266 };
267 let end = if rhs.end() < self.end() {
268 rhs.end()
269 } else {
270 self.end()
271 };
272 match Self::new(start, end) {
273 Ok(page_range) => Some(page_range),
274 Err(_) => None,
275 }
276 }
277}
278
279impl TryFrom<Range<usize>> for PageRange {
280 type Error = PageRangeError;
281
282 fn try_from(value: Range<usize>) -> Result<Self, Self::Error> {
283 Self::new(value.start, value.end)
284 }
285}
286
287impl fmt::Debug for PageRange {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 self.start.fmt(f)?;
290 f.write_str("..")?;
291 self.end.fmt(f)?;
292 Ok(())
293 }
294}
295
296impl fmt::Display for PageRange {
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 let Self { start, end } = self;
299 let len = self.len();
300 let pages = self.pages();
301 if f.alternate() {
302 write!(
303 f,
304 "{start:#18x}..{end:#18x} (len = {len:#18x}, pages = {pages:16})"
305 )
306 } else {
307 write!(f, "{start:#x}..{end:#x} (len = {len:#x}, pages = {pages})")
308 }
309 }
310}
311
312impl PartialOrd for PageRange {
313 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
314 if self.end() <= other.start {
315 Some(Ordering::Less)
316 } else if self.start >= other.end() {
317 Some(Ordering::Greater)
318 } else if self == other {
319 Some(Ordering::Equal)
320 } else {
321 None
322 }
323 }
324}
325
326impl Add for PageRange {
327 type Output = Option<Self>;
328
329 fn add(self, rhs: Self) -> Self::Output {
330 if !(self.touches(rhs) || self.overlaps(rhs)) {
331 return None;
332 }
333
334 Some(PageRange::new(self.start.min(rhs.start), self.end().max(rhs.end())).unwrap())
335 }
336}
337
338impl Sub for PageRange {
339 type Output = PageRangeSub;
340
341 fn sub(self, rhs: Self) -> Self::Output {
342 if !self.overlaps(rhs) {
343 return PageRangeSub::One(self);
344 }
345
346 let before = PageRange::new(self.start, rhs.start).ok();
347 let after = PageRange::new(rhs.end(), self.end()).ok();
348
349 match (before, after) {
350 (None, None) => PageRangeSub::None,
351 (None, Some(range)) | (Some(range), None) => PageRangeSub::One(range),
352 (Some(a), Some(b)) => PageRangeSub::Two(a, b),
353 }
354 }
355}
356
357pub enum PageRangeSub {
359 None,
361
362 One(PageRange),
364
365 Two(PageRange, PageRange),
367}
368
369#[cfg(test)]
370mod tests {
371 use core::alloc::Layout;
372
373 use super::*;
374
375 #[test]
376 fn niche() {
377 assert_eq!(
378 Layout::new::<Option<PageRange>>(),
379 Layout::new::<PageRange>()
380 );
381 }
382
383 #[test]
384 fn overlaps() {
385 let assert = |a, b, c: bool| {
386 let a = PageRange::try_from(a).unwrap();
387 let b = PageRange::try_from(b).unwrap();
388 assert_eq!(c, a.overlaps(b), "{a}.overlaps({b})");
389 };
390
391 assert(0x2000..0x5000, 0x0000..0x1000, false);
392 assert(0x2000..0x5000, 0x0000..0x2000, false);
393 assert(0x2000..0x5000, 0x0000..0x3000, true);
394 assert(0x2000..0x5000, 0x0000..0x4000, true);
395 assert(0x2000..0x5000, 0x0000..0x5000, true);
396 assert(0x2000..0x5000, 0x0000..0x6000, true);
397 assert(0x2000..0x5000, 0x0000..0x7000, true);
398 assert(0x2000..0x5000, 0x0000..0x7000, true);
399 assert(0x2000..0x5000, 0x1000..0x7000, true);
400 assert(0x2000..0x5000, 0x2000..0x7000, true);
401 assert(0x2000..0x5000, 0x3000..0x7000, true);
402 assert(0x2000..0x5000, 0x4000..0x7000, true);
403 assert(0x2000..0x5000, 0x5000..0x7000, false);
404 assert(0x2000..0x5000, 0x6000..0x7000, false);
405 }
406
407 #[test]
408 fn add() {
409 let assert = |a, b, c: Option<Range<usize>>| {
410 let a = PageRange::try_from(a).unwrap();
411 let b = PageRange::try_from(b).unwrap();
412 let c = c.map(|range| PageRange::try_from(range).unwrap());
413 assert_eq!(c, a + b, "{a} + {b}");
414 };
415
416 assert(0x2000..0x5000, 0x0000..0x1000, None);
417 assert(0x2000..0x5000, 0x0000..0x2000, Some(0x0000..0x5000));
418 assert(0x2000..0x5000, 0x0000..0x3000, Some(0x0000..0x5000));
419 assert(0x2000..0x5000, 0x0000..0x4000, Some(0x0000..0x5000));
420 assert(0x2000..0x5000, 0x0000..0x5000, Some(0x0000..0x5000));
421 assert(0x2000..0x5000, 0x0000..0x6000, Some(0x0000..0x6000));
422 assert(0x2000..0x5000, 0x0000..0x7000, Some(0x0000..0x7000));
423 assert(0x2000..0x5000, 0x1000..0x7000, Some(0x1000..0x7000));
424 assert(0x2000..0x5000, 0x2000..0x7000, Some(0x2000..0x7000));
425 assert(0x2000..0x5000, 0x3000..0x7000, Some(0x2000..0x7000));
426 assert(0x2000..0x5000, 0x4000..0x7000, Some(0x2000..0x7000));
427 assert(0x2000..0x5000, 0x5000..0x7000, Some(0x2000..0x7000));
428 assert(0x2000..0x5000, 0x6000..0x7000, None);
429
430 assert(0x2000..0x5000, 0x2000..0x3000, Some(0x2000..0x5000));
431 assert(0x2000..0x5000, 0x2000..0x4000, Some(0x2000..0x5000));
432 assert(0x2000..0x5000, 0x2000..0x5000, Some(0x2000..0x5000));
433 assert(0x2000..0x5000, 0x3000..0x5000, Some(0x2000..0x5000));
434 assert(0x2000..0x5000, 0x4000..0x5000, Some(0x2000..0x5000));
435
436 assert(0x2000..0x5000, 0x3000..0x4000, Some(0x2000..0x5000));
437 }
438
439 #[allow(clippy::single_range_in_vec_init)]
440 #[test]
441 fn sub() {
442 let assert = |a, b, c: &[Range<usize>]| {
443 let a = PageRange::try_from(a).unwrap();
444 let b = PageRange::try_from(b).unwrap();
445 let c = c
446 .iter()
447 .cloned()
448 .map(|range| PageRange::try_from(range).unwrap())
449 .collect::<Vec<_>>();
450 let v = match a - b {
451 PageRangeSub::None => vec![],
452 PageRangeSub::One(a) => vec![a],
453 PageRangeSub::Two(a, b) => vec![a, b],
454 };
455 assert_eq!(c, v, "{a} - {b}");
456 };
457
458 assert(0x2000..0x5000, 0x0000..0x1000, &[0x2000..0x5000]);
459 assert(0x2000..0x5000, 0x0000..0x2000, &[0x2000..0x5000]);
460 assert(0x2000..0x5000, 0x0000..0x3000, &[0x3000..0x5000]);
461 assert(0x2000..0x5000, 0x0000..0x4000, &[0x4000..0x5000]);
462 assert(0x2000..0x5000, 0x0000..0x5000, &[]);
463 assert(0x2000..0x5000, 0x0000..0x6000, &[]);
464 assert(0x2000..0x5000, 0x0000..0x7000, &[]);
465 assert(0x2000..0x5000, 0x1000..0x7000, &[]);
466 assert(0x2000..0x5000, 0x2000..0x7000, &[]);
467 assert(0x2000..0x5000, 0x3000..0x7000, &[0x2000..0x3000]);
468 assert(0x2000..0x5000, 0x4000..0x7000, &[0x2000..0x4000]);
469 assert(0x2000..0x5000, 0x5000..0x7000, &[0x2000..0x5000]);
470 assert(0x2000..0x5000, 0x6000..0x7000, &[0x2000..0x5000]);
471
472 assert(0x2000..0x5000, 0x2000..0x3000, &[0x3000..0x5000]);
473 assert(0x2000..0x5000, 0x2000..0x4000, &[0x4000..0x5000]);
474 assert(0x2000..0x5000, 0x2000..0x5000, &[]);
475 assert(0x2000..0x5000, 0x3000..0x5000, &[0x2000..0x3000]);
476 assert(0x2000..0x5000, 0x4000..0x5000, &[0x2000..0x4000]);
477
478 assert(
479 0x2000..0x5000,
480 0x3000..0x4000,
481 &[0x2000..0x3000, 0x4000..0x5000],
482 );
483 }
484
485 #[test]
486 fn fit() {
487 let range = PageRange::new(0x1000, 0x5000).unwrap();
488 let layout = PageLayout::from_size_align(0x3000, 0x2000).unwrap();
489 let expected = PageRange::new(0x2000, 0x5000).unwrap();
490 assert_eq!(range.fit(layout), Some(expected));
491
492 let range = PageRange::new(0x1000, 0x5000).unwrap();
493 let layout = PageLayout::from_size_align(0, 0x2000).unwrap();
494 assert_eq!(range.fit(layout), None);
495
496 let range = PageRange::new(0xffff_ffff_ffff_0000, 0xffff_ffff_ffff_f000).unwrap();
497 let layout = PageLayout::from_size_align(0x1000, 0x10_0000).unwrap();
498 assert_eq!(range.fit(layout), None);
499 }
500}