Skip to content

Commit f57a6db

Browse files
committed
libkernel: region: add punch_hole
Add a function for calculating the set difference when punching one region out of another.
1 parent 3306b54 commit f57a6db

File tree

1 file changed

+173
-15
lines changed

1 file changed

+173
-15
lines changed

libkernel/src/memory/region.rs

Lines changed: 173 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,52 @@ impl<T: MemKind> MemoryRegion<T> {
163163
}
164164
}
165165

166+
/// Punches a hole (`other`) into the region.
167+
///
168+
/// # Returns
169+
/// * `(None, None)` if `other` fully contains `self`.
170+
/// * `(Some(Left), None)` if `other` overlaps the tail (end) of `self`,
171+
/// or if `other` is strictly after `self` (no overlap).
172+
/// * `(None, Some(Right))` if `other` overlaps the head (start) of `self`,
173+
/// or if `other` is strictly before `self` (no overlap).
174+
/// * `(Some(Left), Some(Right))` if `other` is strictly inside `self`.
175+
pub fn punch_hole(self, other: Self) -> (Option<Self>, Option<Self>) {
176+
let s_start = self.start_address();
177+
let s_end = self.end_address();
178+
let o_start = other.start_address();
179+
let o_end = other.end_address();
180+
181+
// Calculate the "Left" remainder:
182+
// Exists if self starts before other starts.
183+
// The region is [self.start, min(self.end, other.start)).
184+
let left = if s_start < o_start {
185+
let end = core::cmp::min(s_end, o_start);
186+
if end > s_start {
187+
Some(Self::from_start_end_address(s_start, end))
188+
} else {
189+
None
190+
}
191+
} else {
192+
None
193+
};
194+
195+
// Calculate the "Right" remainder:
196+
// Exists if self ends after other ends.
197+
// The region is [max(self.start, other.end), self.end).
198+
let right = if s_end > o_end {
199+
let start = core::cmp::max(s_start, o_end);
200+
if start < s_end {
201+
Some(Self::from_start_end_address(start, s_end))
202+
} else {
203+
None
204+
}
205+
} else {
206+
None
207+
};
208+
209+
(left, right)
210+
}
211+
166212
/// Returns `true` if this region fully contains `other`.
167213
pub fn contains(self, other: Self) -> bool {
168214
self.start_address().value() <= other.start_address().value()
@@ -403,10 +449,14 @@ mod tests {
403449
region::VirtMemoryRegion,
404450
};
405451

452+
fn region(start: usize, size: usize) -> PhysMemoryRegion {
453+
PhysMemoryRegion::new(PA::from_value(start), size)
454+
}
455+
406456
#[test]
407457
fn merge_adjacent() {
408-
let a = PhysMemoryRegion::new(PA::from_value(0x100), 0x10);
409-
let b = PhysMemoryRegion::new(PA::from_value(0x110), 0x10);
458+
let a = region(0x100, 0x10);
459+
let b = region(0x110, 0x10);
410460
let merged = a.merge(b).unwrap();
411461

412462
assert_eq!(merged.address.value(), 0x100);
@@ -415,8 +465,8 @@ mod tests {
415465

416466
#[test]
417467
fn merge_overlap() {
418-
let a = PhysMemoryRegion::new(PA::from_value(0x100), 0x20);
419-
let b = PhysMemoryRegion::new(PA::from_value(0x110), 0x20);
468+
let a = region(0x100, 0x20);
469+
let b = region(0x110, 0x20);
420470
let merged = a.merge(b).unwrap();
421471

422472
assert_eq!(merged.address.value(), 0x100);
@@ -425,8 +475,8 @@ mod tests {
425475

426476
#[test]
427477
fn merge_identical() {
428-
let a = PhysMemoryRegion::new(PA::from_value(0x100), 0x20);
429-
let b = PhysMemoryRegion::new(PA::from_value(0x100), 0x20);
478+
let a = region(0x100, 0x20);
479+
let b = region(0x100, 0x20);
430480
let merged = a.merge(b).unwrap();
431481

432482
assert_eq!(merged.address.value(), 0x100);
@@ -435,15 +485,15 @@ mod tests {
435485

436486
#[test]
437487
fn merge_non_touching() {
438-
let a = PhysMemoryRegion::new(PA::from_value(0x100), 0x10);
439-
let b = PhysMemoryRegion::new(PA::from_value(0x200), 0x10);
488+
let a = region(0x100, 0x10);
489+
let b = region(0x200, 0x10);
440490
assert!(a.merge(b).is_none());
441491
}
442492

443493
#[test]
444494
fn merge_reverse_order() {
445-
let a = PhysMemoryRegion::new(PA::from_value(0x200), 0x20);
446-
let b = PhysMemoryRegion::new(PA::from_value(0x100), 0x100);
495+
let a = region(0x200, 0x20);
496+
let b = region(0x100, 0x100);
447497
let merged = a.merge(b).unwrap();
448498

449499
assert_eq!(merged.address.value(), 0x100);
@@ -452,8 +502,8 @@ mod tests {
452502

453503
#[test]
454504
fn merge_partial_overlap() {
455-
let a = PhysMemoryRegion::new(PA::from_value(0x100), 0x30); // [0x100, 0x130)
456-
let b = PhysMemoryRegion::new(PA::from_value(0x120), 0x20); // [0x120, 0x140)
505+
let a = region(0x100, 0x30); // [0x100, 0x130)
506+
let b = region(0x120, 0x20); // [0x120, 0x140)
457507
let merged = a.merge(b).unwrap(); // should be [0x100, 0x140)
458508

459509
assert_eq!(merged.address.value(), 0x100);
@@ -462,15 +512,15 @@ mod tests {
462512

463513
#[test]
464514
fn test_contains_region() {
465-
let a = PhysMemoryRegion::new(PA::from_value(0x1000), 0x300);
466-
let b = PhysMemoryRegion::new(PA::from_value(0x1100), 0x100);
515+
let a = region(0x1000, 0x300);
516+
let b = region(0x1100, 0x100);
467517
assert!(a.contains(b));
468518
assert!(!b.contains(a));
469519
}
470520

471521
#[test]
472522
fn test_contains_address() {
473-
let region = PhysMemoryRegion::new(PA::from_value(0x1000), 0x100);
523+
let region = region(0x1000, 0x100);
474524
assert!(region.contains_address(PA::from_value(0x1000)));
475525
assert!(region.contains_address(PA::from_value(0x10FF)));
476526
assert!(!region.contains_address(PA::from_value(0x1100)));
@@ -534,4 +584,112 @@ mod tests {
534584

535585
assert_eq!(region.iter_pages().count(), num_pages);
536586
}
587+
588+
#[test]
589+
fn test_punch_hole_middle() {
590+
// Region: [0x1000 ... 0x5000) (size 0x4000)
591+
// Hole: [0x2000 ... 0x3000)
592+
// Expect: Left [0x1000, 0x2000), Right [0x3000, 0x5000)
593+
let main = region(0x1000, 0x4000);
594+
let hole = region(0x2000, 0x1000);
595+
596+
let (left, right) = main.punch_hole(hole);
597+
598+
assert_eq!(left, Some(region(0x1000, 0x1000)));
599+
assert_eq!(right, Some(region(0x3000, 0x2000)));
600+
}
601+
602+
#[test]
603+
fn test_punch_hole_consumes_start() {
604+
// Region: [0x2000 ... 0x4000)
605+
// Hole: [0x1000 ... 0x3000) (Overlaps the beginning)
606+
// Expect: Left None, Right [0x3000, 0x4000)
607+
let main = region(0x2000, 0x2000);
608+
let hole = region(0x1000, 0x2000);
609+
610+
let (left, right) = main.punch_hole(hole);
611+
612+
assert_eq!(left, None);
613+
assert_eq!(right, Some(region(0x3000, 0x1000)));
614+
}
615+
616+
#[test]
617+
fn test_punch_hole_consumes_end() {
618+
// Region: [0x1000 ... 0x3000)
619+
// Hole: [0x2000 ... 0x4000) (Overlaps the end)
620+
// Expect: Left [0x1000, 0x2000), Right None
621+
let main = region(0x1000, 0x2000);
622+
let hole = region(0x2000, 0x2000);
623+
624+
let (left, right) = main.punch_hole(hole);
625+
626+
assert_eq!(left, Some(region(0x1000, 0x1000)));
627+
assert_eq!(right, None);
628+
}
629+
630+
#[test]
631+
fn test_punch_hole_exact_match() {
632+
// Region: [0x1000 ... 0x2000)
633+
// Hole: [0x1000 ... 0x2000)
634+
// Expect: None, None
635+
let main = region(0x1000, 0x1000);
636+
let hole = region(0x1000, 0x1000);
637+
638+
let (left, right) = main.punch_hole(hole);
639+
640+
assert_eq!(left, None);
641+
assert_eq!(right, None);
642+
}
643+
644+
#[test]
645+
fn test_punch_hole_fully_contained() {
646+
// Region: [0x2000 ... 0x3000)
647+
// Hole: [0x1000 ... 0x4000) (Swallows the region entirely)
648+
// Expect: None, None
649+
let main = region(0x2000, 0x1000);
650+
let hole = region(0x1000, 0x3000);
651+
652+
let (left, right) = main.punch_hole(hole);
653+
654+
assert_eq!(left, None);
655+
assert_eq!(right, None);
656+
}
657+
658+
#[test]
659+
fn test_punch_hole_touching_edges() {
660+
// Case A: Hole ends exactly where region starts
661+
// Region: [0x2000 ... 0x3000)
662+
// Hole: [0x1000 ... 0x2000)
663+
// Result: Region is to the right of the hole.
664+
let main = region(0x2000, 0x1000);
665+
let hole = region(0x1000, 0x1000);
666+
assert_eq!(main.punch_hole(hole), (None, Some(main)));
667+
668+
// Case B: Hole starts exactly where region ends
669+
// Region: [0x1000 ... 0x2000)
670+
// Hole: [0x2000 ... 0x3000)
671+
// Result: Region is to the left of the hole.
672+
let main = region(0x1000, 0x1000);
673+
let hole = region(0x2000, 0x1000);
674+
assert_eq!(main.punch_hole(hole), (Some(main), None));
675+
}
676+
677+
#[test]
678+
fn test_punch_hole_disjoint() {
679+
// Hole is completely far away (after)
680+
// Region: [0x1000 ... 0x2000)
681+
// Hole: [0x5000 ... 0x6000)
682+
// Result: Region is to the left of the hole.
683+
let main = region(0x1000, 0x1000);
684+
let hole = region(0x5000, 0x1000);
685+
assert_eq!(main.punch_hole(hole), (Some(main), None));
686+
687+
// Hole is completely far away (before)
688+
// Region: [0x5000 ... 0x6000)
689+
// Hole: [0x1000 ... 0x2000)
690+
// Result: Region is to the right of the hole.
691+
let main = region(0x5000, 0x1000);
692+
let hole = region(0x1000, 0x1000);
693+
assert_eq!(main.punch_hole(hole), (None, Some(main)));
694+
}
537695
}

0 commit comments

Comments
 (0)