From fee153d23cb48101ad984bdb822676b5f9c63796 Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Wed, 13 May 2026 17:10:29 +0100 Subject: [PATCH] libkernel: region: add new utility functions Add the following to all memory region variants: - `is_empty()`: Return true if the region has zero size. - `cap_size()`: Reduces the size if the given size < current. - `shrink_start()`: Move start address forward, while shrinking the region. --- libkernel/src/memory/region.rs | 103 +++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/libkernel/src/memory/region.rs b/libkernel/src/memory/region.rs index 0d51215..b2ddc86 100644 --- a/libkernel/src/memory/region.rs +++ b/libkernel/src/memory/region.rs @@ -59,6 +59,11 @@ impl MemoryRegion { } } + /// Returns `true` if this memory region is empty. + pub fn is_empty(self) -> bool { + self.size == 0 + } + /// Create a memory region from a start and end address. /// /// The size is calculated as `end - start`. No alignment is enforced. @@ -71,6 +76,27 @@ impl MemoryRegion { } } + /// Cap the size of the region. If `max_size < self.size`, the region is + /// shrunk. + pub fn cap_size(self, max_size: usize) -> Self { + if max_size < self.size { + Self::new(self.start_address(), max_size) + } else { + self + } + } + + /// Shrink this region by moving the start address 'forward' by `x` bytes. + /// + /// If `x` moves the start address beyond the end of the region, it + /// saturates on the boundary and becomes empty. + pub fn shrink_start(self, x: usize) -> Self { + Self { + address: self.start_address().add_bytes(x), + size: self.size.saturating_sub(x), + } + } + /// Return a new region with the same size but a different start address. pub fn with_start_address(mut self, new_start: Address) -> Self { self.address = new_start; @@ -676,6 +702,83 @@ mod tests { assert_eq!(main.punch_hole(hole), (Some(main), None)); } + #[test] + fn is_empty_zero_size() { + let r = region(0x1000, 0); + assert!(r.is_empty()); + } + + #[test] + fn is_empty_nonzero_size() { + let r = region(0x1000, 0x10); + assert!(!r.is_empty()); + } + + #[test] + fn is_empty_empty_constructor() { + assert!(PhysMemoryRegion::empty().is_empty()); + } + + #[test] + fn cap_size_below_current() { + let r = region(0x1000, 0x100); + let capped = r.cap_size(0x50); + assert_eq!(capped.start_address().value(), 0x1000); + assert_eq!(capped.size(), 0x50); + } + + #[test] + fn cap_size_equal_to_current() { + let r = region(0x1000, 0x100); + let capped = r.cap_size(0x100); + assert_eq!(capped, r); + } + + #[test] + fn cap_size_above_current() { + let r = region(0x1000, 0x100); + let capped = r.cap_size(0x200); + assert_eq!(capped, r); + } + + #[test] + fn cap_size_to_zero() { + let r = region(0x1000, 0x100); + let capped = r.cap_size(0); + assert_eq!(capped.start_address().value(), 0x1000); + assert!(capped.is_empty()); + } + + #[test] + fn shrink_start_within_bounds() { + let r = region(0x1000, 0x100); + let shrunk = r.shrink_start(0x40); + assert_eq!(shrunk.start_address().value(), 0x1040); + assert_eq!(shrunk.size(), 0xC0); + } + + #[test] + fn shrink_start_exact_size() { + let r = region(0x1000, 0x100); + let shrunk = r.shrink_start(0x100); + assert_eq!(shrunk.start_address().value(), 0x1100); + assert!(shrunk.is_empty()); + } + + #[test] + fn shrink_start_beyond_end_saturates() { + let r = region(0x1000, 0x100); + let shrunk = r.shrink_start(0x200); + assert_eq!(shrunk.start_address().value(), 0x1200); + assert!(shrunk.is_empty()); + } + + #[test] + fn shrink_start_zero() { + let r = region(0x1000, 0x100); + assert_eq!(r.shrink_start(0), r); + } + #[test] fn test_punch_hole_disjoint() { // Hole is completely far away (after)