use core::{fmt, ops::Range};
use crate::{MemoryAddr, PhysAddr, VirtAddr};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct AddrRange<A: MemoryAddr> {
pub start: A,
pub end: A,
}
impl<A> AddrRange<A>
where
A: MemoryAddr,
{
#[inline]
pub fn new(start: A, end: A) -> Self {
assert!(
start <= end,
"invalid `AddrRange`: {}..{}",
start.into(),
end.into()
);
Self { start, end }
}
#[inline]
pub fn try_new(start: A, end: A) -> Option<Self> {
if start <= end {
Some(Self { start, end })
} else {
None
}
}
#[inline]
pub const unsafe fn new_unchecked(start: A, end: A) -> Self {
Self { start, end }
}
#[inline]
pub fn from_start_size(start: A, size: usize) -> Self {
if let Some(end) = start.checked_add(size) {
Self { start, end }
} else {
panic!(
"size too large for `AddrRange`: {} + {}",
start.into(),
size
);
}
}
#[inline]
pub fn try_from_start_size(start: A, size: usize) -> Option<Self> {
start.checked_add(size).map(|end| Self { start, end })
}
#[inline]
pub unsafe fn from_start_size_unchecked(start: A, size: usize) -> Self {
Self {
start,
end: start.wrapping_add(size),
}
}
#[inline]
pub fn is_empty(self) -> bool {
self.start >= self.end
}
#[inline]
pub fn size(self) -> usize {
self.end.wrapping_sub_addr(self.start)
}
#[inline]
pub fn contains(self, addr: A) -> bool {
self.start <= addr && addr < self.end
}
#[inline]
pub fn contains_range(self, other: Self) -> bool {
self.start <= other.start && other.end <= self.end
}
#[inline]
pub fn contained_in(self, other: Self) -> bool {
other.contains_range(self)
}
#[inline]
pub fn overlaps(self, other: Self) -> bool {
self.start < other.end && other.start < self.end
}
}
impl<A, T> TryFrom<Range<T>> for AddrRange<A>
where
A: MemoryAddr + From<T>,
{
type Error = ();
#[inline]
fn try_from(range: Range<T>) -> Result<Self, Self::Error> {
Self::try_new(range.start.into(), range.end.into()).ok_or(())
}
}
impl<A> Default for AddrRange<A>
where
A: MemoryAddr,
{
#[inline]
fn default() -> Self {
Self {
start: 0.into(),
end: 0.into(),
}
}
}
impl<A> fmt::Debug for AddrRange<A>
where
A: MemoryAddr + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}..{:?}", self.start, self.end)
}
}
impl<A> fmt::LowerHex for AddrRange<A>
where
A: MemoryAddr + fmt::LowerHex,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x}..{:x}", self.start, self.end)
}
}
impl<A> fmt::UpperHex for AddrRange<A>
where
A: MemoryAddr + fmt::UpperHex,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:X}..{:X}", self.start, self.end)
}
}
pub type VirtAddrRange = AddrRange<VirtAddr>;
pub type PhysAddrRange = AddrRange<PhysAddr>;
#[macro_export]
macro_rules! addr_range {
($range:expr) => {
$crate::AddrRange::try_from($range).expect("invalid address range in `addr_range!`")
};
}
#[macro_export]
macro_rules! va_range {
($range:expr) => {
$crate::VirtAddrRange::try_from($range).expect("invalid address range in `va_range!`")
};
}
#[macro_export]
macro_rules! pa_range {
($range:expr) => {
$crate::PhysAddrRange::try_from($range).expect("invalid address range in `pa_range!`")
};
}
#[cfg(test)]
mod test {
use crate::{va, va_range, VirtAddrRange};
#[test]
fn test_range_format() {
let range = va_range!(0xfec000..0xfff000usize);
assert_eq!(format!("{:?}", range), "VA:0xfec000..VA:0xfff000");
assert_eq!(format!("{:x}", range), "VA:0xfec000..VA:0xfff000");
assert_eq!(format!("{:X}", range), "VA:0xFEC000..VA:0xFFF000");
}
#[test]
#[allow(clippy::reversed_empty_ranges)]
fn test_range() {
let start = va!(0x1000);
let end = va!(0x2000);
let range = va_range!(start..end);
println!("range: {:?}", range);
assert!((0x1000..0x1000).is_empty());
assert!((0x1000..0xfff).is_empty());
assert!(!range.is_empty());
assert_eq!(range.start, start);
assert_eq!(range.end, end);
assert_eq!(range.size(), 0x1000);
assert!(range.contains(va!(0x1000)));
assert!(range.contains(va!(0x1080)));
assert!(!range.contains(va!(0x2000)));
assert!(!range.contains_range(addr_range!(0xfff..0x1fff)));
assert!(!range.contains_range(addr_range!(0xfff..0x2000)));
assert!(!range.contains_range(va_range!(0xfff..0x2001))); assert!(range.contains_range(va_range!(0x1000..0x1fff)));
assert!(range.contains_range(addr_range!(0x1000..0x2000)));
assert!(!range.contains_range(addr_range!(0x1000..0x2001)));
assert!(range.contains_range(va_range!(0x1001..0x1fff)));
assert!(range.contains_range(va_range!(0x1001..0x2000)));
assert!(!range.contains_range(va_range!(0x1001..0x2001)));
assert!(!range.contains_range(VirtAddrRange::from_start_size(0xfff.into(), 0x1)));
assert!(!range.contains_range(VirtAddrRange::from_start_size(0x2000.into(), 0x1)));
assert!(range.contained_in(addr_range!(0xfff..0x2000)));
assert!(range.contained_in(addr_range!(0x1000..0x2000)));
assert!(range.contained_in(va_range!(0x1000..0x2001)));
assert!(!range.overlaps(addr_range!(0x800..0x1000)));
assert!(range.overlaps(addr_range!(0x800..0x1001)));
assert!(range.overlaps(addr_range!(0x1800..0x2000)));
assert!(range.overlaps(va_range!(0x1800..0x2001)));
assert!(!range.overlaps(va_range!(0x2000..0x2800)));
assert!(range.overlaps(va_range!(0xfff..0x2001)));
let default_range: VirtAddrRange = Default::default();
assert!(default_range.is_empty());
assert_eq!(default_range.size(), 0);
assert_eq!(default_range.start, va!(0));
assert_eq!(default_range.end, va!(0));
}
}