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.
This commit is contained in:
Matthew Leach
2026-05-13 17:10:29 +01:00
parent 64c8b02904
commit fee153d23c

View File

@@ -59,6 +59,11 @@ impl<T: MemKind> MemoryRegion<T> {
}
}
/// 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<T: MemKind> MemoryRegion<T> {
}
}
/// 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<T, ()>) -> 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)