1#![forbid(unsafe_code)]
8#![no_std]
9
10use core::iter::Iterator;
11use core::iter::Peekable;
12use core::ops::Range;
13
14const PAGE_SIZE: u64 = 4096;
15const TWO_MB: u64 = 0x20_0000;
16const ONE_GB: u64 = 0x4000_0000;
17
18#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
24#[cfg_attr(
25 feature = "mesh",
26 derive(mesh_protobuf::Protobuf),
27 mesh(package = "topology")
28)]
29#[cfg_attr(feature = "inspect", derive(inspect::Inspect), inspect(display))]
30pub struct MemoryRange {
31 #[cfg_attr(feature = "mesh", mesh(1))]
32 start: u64,
33 #[cfg_attr(feature = "mesh", mesh(2))]
34 end: u64,
35}
36
37impl core::fmt::Display for MemoryRange {
38 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39 write!(f, "{:#x}-{:#x}", self.start(), self.end())
40 }
41}
42
43impl TryFrom<Range<u64>> for MemoryRange {
44 type Error = InvalidMemoryRange;
45
46 fn try_from(range: Range<u64>) -> Result<Self, Self::Error> {
47 Self::try_new(range)
48 }
49}
50
51impl TryFrom<Range<usize>> for MemoryRange {
52 type Error = InvalidMemoryRange;
53
54 fn try_from(range: Range<usize>) -> Result<Self, Self::Error> {
55 Self::try_new(range.start as u64..range.end as u64)
56 }
57}
58
59#[derive(Debug, thiserror::Error)]
61#[error("unaligned or invalid memory range: {start:#x}-{end:#x}")]
62pub struct InvalidMemoryRange {
63 start: u64,
64 end: u64,
65}
66
67impl MemoryRange {
68 pub const MAX_ADDRESS: u64 = !(PAGE_SIZE - 1);
70
71 #[track_caller]
76 pub const fn new(range: Range<u64>) -> Self {
77 assert!(range.start & (PAGE_SIZE - 1) == 0);
78 assert!(range.end & (PAGE_SIZE - 1) == 0);
79 assert!(range.start <= range.end);
80 Self {
81 start: range.start,
82 end: range.end,
83 }
84 }
85
86 pub const fn try_new(range: Range<u64>) -> Result<Self, InvalidMemoryRange> {
91 if range.start & (PAGE_SIZE - 1) != 0
92 || range.end & (PAGE_SIZE - 1) != 0
93 || range.start > range.end
94 {
95 return Err(InvalidMemoryRange {
96 start: range.start,
97 end: range.end,
98 });
99 }
100 Ok(Self {
101 start: range.start,
102 end: range.end,
103 })
104 }
105
106 pub fn bounding(range: Range<u64>) -> Self {
112 Self::bounding_aligned(range, PAGE_SIZE)
113 }
114
115 pub fn bounding_aligned(range: Range<u64>, alignment: u64) -> Self {
121 assert!(range.start <= range.end);
122 assert!(alignment.is_power_of_two());
123 assert!(range.end <= u64::MAX - (alignment - 1));
124 let start = range.start & !(alignment - 1);
125 let end = (range.end + (alignment - 1)) & !(alignment - 1);
126 Self::new(start..end)
127 }
128
129 pub fn from_4k_gpn_range(range: Range<u64>) -> Self {
134 const MAX: u64 = u64::MAX / PAGE_SIZE;
135 assert!(range.start <= MAX);
136 assert!(range.end <= MAX);
137 Self::new(range.start * PAGE_SIZE..range.end * PAGE_SIZE)
138 }
139
140 pub const EMPTY: Self = Self::new(0..0);
142
143 pub fn start(&self) -> u64 {
145 self.start
146 }
147
148 pub fn start_4k_gpn(&self) -> u64 {
150 self.start / PAGE_SIZE
151 }
152
153 pub fn end_4k_gpn(&self) -> u64 {
155 self.end / PAGE_SIZE
156 }
157
158 pub fn page_count_4k(&self) -> u64 {
160 (self.end - self.start) / PAGE_SIZE
161 }
162
163 pub fn page_count_2m(&self) -> u64 {
165 (self.end - self.start).div_ceil(TWO_MB)
166 }
167
168 pub fn end(&self) -> u64 {
170 self.end
171 }
172
173 pub fn len(&self) -> u64 {
175 self.end() - self.start()
176 }
177
178 pub fn is_empty(&self) -> bool {
180 self.start == self.end
181 }
182
183 pub fn alignment(&self, base: u64) -> u64 {
185 let order = ((base + self.start()) | (base + self.end())).trailing_zeros();
186 1 << order
187 }
188
189 pub fn aligned_subrange(&self, alignment: u64) -> Self {
194 assert!(alignment.is_power_of_two());
195 let start = (self.start + alignment - 1) & !(alignment - 1);
196 let end = self.end & !(alignment - 1);
197 if start <= end {
198 Self::new(start..end)
199 } else {
200 Self::EMPTY
201 }
202 }
203
204 pub fn overlaps(&self, other: &Self) -> bool {
206 self.end > other.start && self.start < other.end
207 }
208
209 pub fn contains(&self, other: &Self) -> bool {
211 self.start <= other.start && self.end >= other.end
212 }
213
214 pub fn contains_addr(&self, addr: u64) -> bool {
216 (self.start..self.end).contains(&addr)
217 }
218
219 pub fn offset_of(&self, addr: u64) -> Option<u64> {
221 if self.contains_addr(addr) {
222 Some(addr - self.start)
223 } else {
224 None
225 }
226 }
227
228 pub fn intersection(&self, other: &Self) -> Self {
230 let start = self.start.max(other.start);
231 let end = self.end.min(other.end);
232 if start <= end {
233 Self::new(start..end)
234 } else {
235 Self::EMPTY
236 }
237 }
238
239 #[track_caller]
243 pub fn split_at_offset(&self, offset: u64) -> (Self, Self) {
244 assert!(offset <= self.len());
245 assert!(offset.is_multiple_of(PAGE_SIZE));
246 (
247 Self {
248 start: self.start,
249 end: self.start + offset,
250 },
251 Self {
252 start: self.start + offset,
253 end: self.end,
254 },
255 )
256 }
257}
258
259impl From<MemoryRange> for Range<u64> {
260 fn from(range: MemoryRange) -> Self {
261 Range {
262 start: range.start(),
263 end: range.end(),
264 }
265 }
266}
267
268#[derive(Debug, Clone)]
274pub struct AlignedSubranges {
275 range: MemoryRange,
276 offset: u64,
277 max_len: u64,
278}
279
280impl AlignedSubranges {
281 pub fn new(range: MemoryRange) -> Self {
283 Self {
284 range,
285 offset: 0,
286 max_len: u64::MAX,
287 }
288 }
289
290 pub fn with_offset(self, offset: u64) -> Self {
293 Self { offset, ..self }
294 }
295
296 pub fn with_max_range_len(self, max_len: u64) -> Self {
298 Self { max_len, ..self }
299 }
300}
301
302impl Iterator for AlignedSubranges {
303 type Item = MemoryRange;
304
305 fn next(&mut self) -> Option<Self::Item> {
306 if self.range.is_empty() {
307 return None;
308 }
309
310 let start = self.range.start() + self.offset;
311 let mut end = self.range.end() + self.offset;
312 if end - start > self.max_len {
313 end = start + self.max_len;
314 }
315
316 let mut align = |page_size| {
317 let page_mask: u64 = page_size - 1;
318 if (start + page_mask) & !page_mask >= end & !page_mask {
319 return;
322 }
323 if start & page_mask != 0 {
324 end = end.min((start + page_mask) & !page_mask);
326 } else {
327 end &= !page_mask;
329 }
330 };
331
332 align(TWO_MB);
333 align(ONE_GB);
334 let start = start - self.offset;
335 let end = end - self.offset;
336 self.range = MemoryRange::new(end..self.range.end());
337 Some(MemoryRange::new(start..end))
338 }
339}
340
341pub fn overlapping_ranges(
348 left: impl IntoIterator<Item = MemoryRange>,
349 right: impl IntoIterator<Item = MemoryRange>,
350) -> impl Iterator<Item = MemoryRange> {
351 walk_ranges(
352 left.into_iter().map(|r| (r, ())),
353 right.into_iter().map(|r| (r, ())),
354 )
355 .filter_map(|(r, c)| match c {
356 RangeWalkResult::Both((), ()) => Some(r),
357 _ => None,
358 })
359}
360
361pub fn subtract_ranges(
369 left: impl IntoIterator<Item = MemoryRange>,
370 right: impl IntoIterator<Item = MemoryRange>,
371) -> impl Iterator<Item = MemoryRange> {
372 walk_ranges(
373 left.into_iter().map(|r| (r, ())),
374 right.into_iter().map(|r| (r, ())),
375 )
376 .filter_map(|(r, c)| match c {
377 RangeWalkResult::Left(()) => Some(r),
378 RangeWalkResult::Neither | RangeWalkResult::Right(()) | RangeWalkResult::Both((), ()) => {
379 None
380 }
381 })
382}
383
384pub fn walk_ranges<T: Clone, U: Clone>(
413 left: impl IntoIterator<Item = (MemoryRange, T)>,
414 right: impl IntoIterator<Item = (MemoryRange, U)>,
415) -> impl Iterator<Item = (MemoryRange, RangeWalkResult<T, U>)> {
416 RangeWalkIter {
417 pos: 0,
418 left: PeekableSorted::new(left),
419 right: PeekableSorted::new(right),
420 }
421}
422
423#[derive(Copy, Clone, Debug, PartialEq, Eq)]
425pub enum RangeWalkResult<T, U> {
426 Neither,
428 Left(T),
431 Right(U),
434 Both(T, U),
437}
438
439struct RangeWalkIter<I: Iterator, J: Iterator> {
440 pos: u64,
441 left: PeekableSorted<I>,
442 right: PeekableSorted<J>,
443}
444
445struct PeekableSorted<I: Iterator> {
446 iter: I,
447 #[expect(clippy::option_option)] item: Option<Option<I::Item>>,
449}
450
451impl<I: Iterator<Item = (MemoryRange, T)>, T> PeekableSorted<I> {
452 fn new(iter: impl IntoIterator<IntoIter = I>) -> Self {
453 Self {
454 iter: iter.into_iter(),
455 item: None,
456 }
457 }
458
459 fn peek_in_range_ensure_sorted(&mut self, pos: u64, msg: &str) -> Option<&(MemoryRange, T)> {
460 loop {
461 let r = self
462 .item
463 .get_or_insert_with(|| {
464 let r = self.iter.next()?;
465 assert!(r.0.start() >= pos, "{msg} not sorted");
466 Some(r)
467 })
468 .as_ref()?;
469 if !r.0.is_empty() && r.0.end() > pos {
470 return Some(self.item.as_ref().unwrap().as_ref().unwrap());
471 }
472 self.item = None;
473 }
474 }
475}
476
477impl<I: Iterator<Item = (MemoryRange, T)>, J: Iterator<Item = (MemoryRange, U)>, T: Clone, U: Clone>
478 Iterator for RangeWalkIter<I, J>
479{
480 type Item = (MemoryRange, RangeWalkResult<T, U>);
481
482 fn next(&mut self) -> Option<Self::Item> {
483 if self.pos == MemoryRange::MAX_ADDRESS {
484 return None;
485 }
486 let left = self.left.peek_in_range_ensure_sorted(self.pos, "left");
487 let right = self.right.peek_in_range_ensure_sorted(self.pos, "right");
488 let (end, c) = match (left, right) {
489 (Some(&(left, ref t)), Some(&(right, ref u))) => {
490 if self.pos < left.start() {
491 if self.pos < right.start() {
492 (left.start().min(right.start()), RangeWalkResult::Neither)
493 } else {
494 (
495 left.start().min(right.end()),
496 RangeWalkResult::Right(u.clone()),
497 )
498 }
499 } else if self.pos < right.start() {
500 (
501 right.start().min(left.end()),
502 RangeWalkResult::Left(t.clone()),
503 )
504 } else {
505 (
506 left.end().min(right.end()),
507 RangeWalkResult::Both(t.clone(), u.clone()),
508 )
509 }
510 }
511 (Some(&(left, ref t)), None) => {
512 if self.pos < left.start() {
513 (left.start, RangeWalkResult::Neither)
514 } else {
515 (left.end(), RangeWalkResult::Left(t.clone()))
516 }
517 }
518 (None, Some(&(right, ref u))) => {
519 if self.pos < right.start() {
520 (right.start, RangeWalkResult::Neither)
521 } else {
522 (right.end(), RangeWalkResult::Right(u.clone()))
523 }
524 }
525 (None, None) => (MemoryRange::MAX_ADDRESS, RangeWalkResult::Neither),
526 };
527 let r = MemoryRange::new(self.pos..end);
528 self.pos = end;
529 Some((r, c))
530 }
531}
532
533pub fn flatten_ranges(
556 ranges: impl IntoIterator<Item = MemoryRange>,
557) -> impl Iterator<Item = MemoryRange> {
558 FlattenIter {
559 iter: ranges.into_iter().peekable(),
560 }
561}
562
563struct FlattenIter<I: Iterator> {
564 iter: Peekable<I>,
565}
566
567impl<I: Iterator<Item = MemoryRange>> Iterator for FlattenIter<I> {
568 type Item = MemoryRange;
569
570 fn next(&mut self) -> Option<Self::Item> {
571 let first = self.iter.next()?;
572 let mut start = first.start();
573 let mut end = first.end();
574 while let Some(r) = self.iter.next_if(|r| {
575 assert!(r.start() >= start, "ranges are not sorted");
576 r.start() <= end
577 }) {
578 start = r.start();
579 end = end.max(r.end());
580 }
581 Some(MemoryRange::new(first.start()..end))
582 }
583}
584
585pub fn merge_adjacent_ranges<T: PartialEq>(
615 ranges: impl IntoIterator<Item = (MemoryRange, T)>,
616) -> impl Iterator<Item = (MemoryRange, T)> {
617 MergeAdjacentIter {
618 iter: ranges.into_iter().peekable(),
619 }
620}
621
622struct MergeAdjacentIter<I: Iterator> {
623 iter: Peekable<I>,
624}
625
626impl<I: Iterator<Item = (MemoryRange, T)>, T: PartialEq> Iterator for MergeAdjacentIter<I> {
627 type Item = (MemoryRange, T);
628
629 fn next(&mut self) -> Option<Self::Item> {
630 let (first, typ) = self.iter.next()?;
631 let mut start = first.start();
632 let mut end = first.end();
633 while let Some((r, _t)) = self.iter.next_if(|(r, t)| {
634 assert!(r.start() >= start, "ranges are not sorted");
635 assert!(r.start() >= end, "ranges overlap");
636 r.start() == end && &typ == t
637 }) {
638 start = r.start();
639 end = end.max(r.end());
640 }
641 Some((MemoryRange::new(first.start()..end), typ))
642 }
643}
644
645#[cfg(test)]
646mod tests {
647 extern crate alloc;
648 use super::MemoryRange;
649 use super::TWO_MB;
650 use crate::AlignedSubranges;
651 use crate::flatten_ranges;
652 use crate::merge_adjacent_ranges;
653 use crate::overlapping_ranges;
654 use crate::subtract_ranges;
655 use alloc::vec;
656 use alloc::vec::Vec;
657
658 const KB: u64 = 1024;
659 const MB: u64 = 1024 * KB;
660 const GB: u64 = 1024 * MB;
661
662 #[test]
663 fn test_align() {
664 #[derive(Clone, Debug, PartialEq, Copy)]
665 struct AlignedRangeResult {
666 range: MemoryRange,
667 page_size: u64,
668 }
669
670 let compare_with_base = |r1: Vec<MemoryRange>, base: u64, er: Vec<AlignedRangeResult>| {
671 let result: Vec<_> = r1
672 .iter()
673 .flat_map(|range| AlignedSubranges::new(*range).with_offset(base))
674 .collect();
675 assert_eq!(result.len(), er.len());
676 for (pos, range) in result.iter().enumerate() {
677 assert_eq!(*range, er[pos].range);
678 assert_eq!(range.alignment(base), er[pos].page_size);
679 }
680 };
681
682 let compare = |r1: Vec<MemoryRange>, er: Vec<AlignedRangeResult>| {
683 compare_with_base(r1, 0, er);
684 };
685
686 fn b_mr(start: u64, end: u64) -> MemoryRange {
688 MemoryRange::new(start..end)
689 }
690
691 fn b_arr(range: MemoryRange, page_size: u64) -> AlignedRangeResult {
693 AlignedRangeResult { range, page_size }
694 }
695
696 let ram: Vec<_> = vec![b_mr(0x0, 4 * KB)];
698 let expected_res: Vec<_> = vec![b_arr(ram[0], 1 << 12)];
699
700 compare(ram, expected_res);
701
702 let ram: Vec<_> = vec![b_mr(0x0, 2 * MB)];
704 let expected_res: Vec<_> = vec![b_arr(ram[0], 1 << 21)];
705 compare(ram, expected_res);
706
707 let ram: Vec<_> = vec![b_mr(0x0, GB)];
709 let expected_res: Vec<_> = vec![b_arr(ram[0], 1 << 30)];
710 compare(ram, expected_res);
711
712 let ram: Vec<_> = vec![b_mr(6 * MB, 12 * MB + 4 * KB)];
714 let expected_res: Vec<_> = vec![
715 b_arr(b_mr(6 * MB, 12 * MB), TWO_MB),
716 b_arr(b_mr(12 * MB, 12 * MB + 4 * KB), 1 << 12),
717 ];
718 compare(ram, expected_res);
719
720 let ram: Vec<_> = vec![b_mr(5 * MB + 400 * KB, 12 * MB + 400 * KB)];
722 let expected_res: Vec<_> = vec![
723 b_arr(b_mr(5 * MB + 400 * KB, 6 * MB), 1 << 14),
724 b_arr(b_mr(6 * MB, 12 * MB), TWO_MB),
725 b_arr(b_mr(12 * MB, 12 * MB + 400 * KB), 1 << 14),
726 ];
727 compare(ram, expected_res);
728
729 let ram: Vec<_> = vec![b_mr(GB + 501 * MB, 3 * GB + 503 * MB)];
731 let expected_res: Vec<_> = vec![
732 b_arr(b_mr(GB + 501 * MB, GB + 502 * MB), 1 << 20),
733 b_arr(b_mr(GB + 502 * MB, 2 * GB), 1 << 21),
734 b_arr(b_mr(2 * GB, 3 * GB), 1 << 30),
735 b_arr(b_mr(3 * GB, 3 * GB + 502 * MB), 1 << 21),
736 b_arr(b_mr(3 * GB + 502 * MB, 3 * GB + 503 * MB), 1 << 20),
737 ];
738 compare(ram, expected_res);
739
740 let ram: Vec<_> = vec![b_mr(4 * MB + 8 * KB, 6 * MB + 8 * KB)];
742 let base = 2 * MB - 8 * KB;
743 let expected_res: Vec<_> = vec![b_arr(ram[0], TWO_MB)];
744 compare_with_base(ram, base, expected_res);
745
746 let ram: Vec<_> = vec![b_mr(4 * MB + 8 * KB, 6 * MB + 8 * KB)];
748 let expected_res: Vec<_> = vec![b_arr(ram[0], 1 << 13)];
749 compare_with_base(ram, 0, expected_res);
750 }
751
752 #[test]
753 fn test_overlapping_ranges() {
754 let left = [
755 MemoryRange::new(0..4 * MB),
756 MemoryRange::new(8 * MB..12 * MB),
757 MemoryRange::new(12 * MB..12 * MB),
758 MemoryRange::new(16 * MB..20 * MB),
759 MemoryRange::new(24 * MB..32 * MB),
760 MemoryRange::new(40 * MB..48 * MB),
761 ];
762 let right = [
763 MemoryRange::new(2 * MB..6 * MB),
764 MemoryRange::new(10 * MB..11 * MB),
765 MemoryRange::new(11 * MB..11 * MB),
766 MemoryRange::new(11 * MB..13 * MB),
767 MemoryRange::new(15 * MB..22 * MB),
768 MemoryRange::new(26 * MB..30 * MB),
769 ];
770
771 let result: Vec<_> = overlapping_ranges(left, right).collect();
772 assert_eq!(
773 result.as_slice(),
774 &[
775 MemoryRange::new(2 * MB..4 * MB),
776 MemoryRange::new(10 * MB..11 * MB),
777 MemoryRange::new(11 * MB..12 * MB),
778 MemoryRange::new(16 * MB..20 * MB),
779 MemoryRange::new(26 * MB..30 * MB),
780 ]
781 );
782 }
783
784 #[test]
785 fn test_subtract_ranges() {
786 let left = [
787 MemoryRange::new(0..4 * MB),
788 MemoryRange::new(8 * MB..12 * MB),
789 MemoryRange::new(12 * MB..12 * MB),
790 MemoryRange::new(16 * MB..20 * MB),
791 MemoryRange::new(24 * MB..32 * MB),
792 MemoryRange::new(40 * MB..48 * MB),
793 ];
794 let right = [
795 MemoryRange::new(2 * MB..6 * MB),
796 MemoryRange::new(10 * MB..11 * MB),
797 MemoryRange::new(11 * MB..11 * MB),
798 MemoryRange::new(11 * MB..13 * MB),
799 MemoryRange::new(15 * MB..22 * MB),
800 MemoryRange::new(26 * MB..30 * MB),
801 ];
802
803 let result: Vec<_> = subtract_ranges(left, right).collect();
804 assert_eq!(
805 result.as_slice(),
806 &[
807 MemoryRange::new(0..2 * MB),
808 MemoryRange::new(8 * MB..10 * MB),
809 MemoryRange::new(24 * MB..26 * MB),
810 MemoryRange::new(30 * MB..32 * MB),
811 MemoryRange::new(40 * MB..48 * MB),
812 ]
813 );
814 }
815
816 #[test]
817 #[should_panic(expected = "left not sorted")]
818 fn test_panic_unsorted_overlapping_left() {
819 overlapping_ranges(
820 [MemoryRange::new(MB..2 * MB), MemoryRange::new(0..MB)],
821 [MemoryRange::new(3 * MB..4 * MB)],
822 )
823 .for_each(|_| ());
824 }
825
826 #[test]
827 #[should_panic(expected = "right not sorted")]
828 fn test_panic_unsorted_overlapping_right() {
829 overlapping_ranges(
830 [
831 MemoryRange::new(MB..2 * MB),
832 MemoryRange::new(3 * MB..4 * MB),
833 ],
834 [MemoryRange::new(0..MB), MemoryRange::new(0..MB)],
835 )
836 .for_each(|_| ());
837 }
838
839 #[test]
840 #[should_panic(expected = "left not sorted")]
841 fn test_panic_unsorted_subtract_left() {
842 subtract_ranges(
843 [MemoryRange::new(MB..2 * MB), MemoryRange::new(0..MB)],
844 [MemoryRange::new(MB..2 * MB)],
845 )
846 .for_each(|_| ());
847 }
848
849 #[test]
850 #[should_panic(expected = "right not sorted")]
851 fn test_panic_unsorted_subtract_right() {
852 subtract_ranges(
853 [
854 MemoryRange::new(MB..2 * MB),
855 MemoryRange::new(3 * MB..4 * MB),
856 ],
857 [MemoryRange::new(MB..2 * MB), MemoryRange::new(MB..2 * MB)],
858 )
859 .for_each(|_| ());
860 }
861
862 #[test]
863 fn test_aligned_subrange() {
864 let test_cases = &[
865 (0..0, MB, 0..0),
866 (0..MB, MB, 0..MB),
867 (4 * KB..MB + 4 * KB, MB, MB..MB),
868 (MB..5 * MB, 2 * MB, 2 * MB..4 * MB),
869 ];
870 for (range, alignment, expected_aligned_range) in test_cases.iter().cloned() {
871 assert_eq!(
872 MemoryRange::new(range).aligned_subrange(alignment),
873 MemoryRange::new(expected_aligned_range)
874 );
875 }
876 }
877
878 #[test]
879 fn test_flatten_ranges() {
880 let ranges =
881 [0..4, 5..7, 6..11, 13..20, 20..25, 22..24, 35..36].map(MemoryRange::from_4k_gpn_range);
882 let result = [0..4, 5..11, 13..25, 35..36].map(MemoryRange::from_4k_gpn_range);
883 assert!(flatten_ranges(ranges).eq(result));
884 }
885
886 #[test]
887 #[should_panic(expected = "ranges are not sorted")]
888 fn test_flatten_ranges_not_sorted() {
889 flatten_ranges([0..4, 5..7, 3..8].map(MemoryRange::from_4k_gpn_range)).for_each(|_| ());
890 }
891
892 #[test]
893 fn test_merge_adjacent_ranges() {
894 #[derive(Clone, Copy, PartialEq, Eq)]
895 enum Color {
896 Red,
897 Blue,
898 }
899
900 let ranges = [0..4, 5..7, 7..11, 11..12, 13..20, 20..25, 35..36]
901 .map(MemoryRange::from_4k_gpn_range)
902 .into_iter()
903 .zip([
904 Color::Red,
905 Color::Red,
906 Color::Red,
907 Color::Blue,
908 Color::Red,
909 Color::Red,
910 Color::Blue,
911 ]);
912 let result = [0..4, 5..11, 11..12, 13..25, 35..36]
913 .map(MemoryRange::from_4k_gpn_range)
914 .into_iter()
915 .zip([Color::Red, Color::Red, Color::Blue, Color::Red, Color::Blue]);
916 assert!(merge_adjacent_ranges(ranges).eq(result));
917 }
918
919 #[test]
920 #[should_panic(expected = "ranges are not sorted")]
921 fn test_merge_adjacent_ranges_not_sorted() {
922 merge_adjacent_ranges([0..4, 5..7, 3..8].map(|r| (MemoryRange::from_4k_gpn_range(r), ())))
923 .for_each(|_| ());
924 }
925
926 #[test]
927 #[should_panic(expected = "ranges overlap")]
928 fn test_merge_adjacent_ranges_overlap() {
929 merge_adjacent_ranges([0..6, 5..7, 9..12].map(|r| (MemoryRange::from_4k_gpn_range(r), ())))
930 .for_each(|_| ());
931 }
932}