Skip to content

Commit

Permalink
Add address range types
Browse files Browse the repository at this point in the history
  • Loading branch information
equation314 committed Jul 16, 2024
1 parent 3c9a7fe commit abe1670
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 85 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Wrappers and helper functions for physical and virtual memory addresses.
## Examples

```rust
use memory_addr::{pa, va, PhysAddr, VirtAddr};
use memory_addr::{pa, va, va_range, PhysAddr, VirtAddr};

let phys_addr = PhysAddr::from(0x12345678);
let virt_addr = VirtAddr::from(0x87654321);
Expand All @@ -17,4 +17,11 @@ assert_eq!(phys_addr.align_offset_4k(), 0x678);
assert_eq!(virt_addr.align_up_4k(), va!(0x87655000));
assert!(!virt_addr.is_aligned_4k());
assert!(va!(0xabcedf0).is_aligned(16usize));

let va_range = va_range!(0x87654000..0x87655000);
assert_eq!(va_range.start, va!(0x87654000));
assert_eq!(va_range.size(), 0x1000);
assert!(va_range.contains(virt_addr));
assert!(va_range.contains_range(va_range!(virt_addr..virt_addr + 0x100)));
assert!(!va_range.contains_range(va_range!(virt_addr..virt_addr + 0x1000)));
```
47 changes: 46 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
#![doc = include_str!("../README.md")]

mod addr;
mod range;

pub use self::addr::{PhysAddr, VirtAddr};
pub use self::range::{PhysAddrRange, VirtAddrRange};

/// The size of a 4K page (4096 bytes).
pub const PAGE_SIZE_4K: usize = 0x1000;
Expand Down Expand Up @@ -70,7 +72,7 @@ pub const fn is_aligned_4k(addr: usize) -> bool {

#[cfg(test)]
mod tests {
use crate::va;
use crate::{va, va_range, VirtAddrRange};

#[test]
fn test_addr() {
Expand All @@ -95,4 +97,47 @@ mod tests {
assert_eq!(addr.align_down(align), va!(align * 5));
assert_eq!(addr.align_up(align), va!(align * 6));
}

#[test]
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((0xfff..0x1fff).into()));
assert!(!range.contains_range((0xfff..0x2000).into()));
assert!(!range.contains_range((0xfff..0x2001).into()));
assert!(range.contains_range((0x1000..0x1fff).into()));
assert!(range.contains_range((0x1000..0x2000).into()));
assert!(!range.contains_range((0x1000..0x2001).into()));
assert!(range.contains_range((0x1001..0x1fff).into()));
assert!(range.contains_range((0x1001..0x2000).into()));
assert!(!range.contains_range((0x1001..0x2001).into()));
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((0xfff..0x2000).into()));
assert!(range.contained_in((0x1000..0x2000).into()));
assert!(range.contained_in((0x1000..0x2001).into()));

assert!(!range.overlaps((0x800..0x1000).into()));
assert!(range.overlaps((0x800..0x1001).into()));
assert!(range.overlaps((0x1800..0x2000).into()));
assert!(range.overlaps((0x1800..0x2001).into()));
assert!(!range.overlaps((0x2000..0x2800).into()));
assert!(range.overlaps((0xfff..0x2001).into()));
}
}
179 changes: 96 additions & 83 deletions src/range.rs
Original file line number Diff line number Diff line change
@@ -1,97 +1,110 @@
use core::{fmt, ops::Range};

/// A generic range of addresses.
///
/// The range is inclusive on the start and exclusive on the end.
/// It is empty if `start >= end`.
#[derive(Clone, Copy, Default, PartialEq, Eq)]
pub struct AddrRange<A>
where
A: From<usize> + Into<usize> + Copy,
{
/// The lower bound of the range (inclusive).
pub start: A,
/// The upper bound of the range (exclusive).
pub end: A,
}
use crate::{PhysAddr, VirtAddr};

macro_rules! usize {
($addr:expr) => {
Into::<usize>::into($addr)
($addr).as_usize()
};
}

impl<A> AddrRange<A>
where
A: From<usize> + Into<usize> + Copy,
{
/// Creates a new address range.
#[inline]
pub const fn new(start: A, end: A) -> Self {
Self { start, end }
}

/// Creates a new address range from the start address and the size.
#[inline]
pub const fn from_start_size(start: A, size: usize) -> Self {
Self {
start,
end: A::from(usize!(start) + size),
macro_rules! def_range {
($name:ident, $addr_type:ty) => {
/// A generic range of addresses.
///
/// The range is inclusive on the start and exclusive on the end.
/// It is empty if `start >= end`.
#[derive(Clone, Copy, Default, PartialEq, Eq)]
pub struct $name {
/// The lower bound of the range (inclusive).
pub start: $addr_type,
/// The upper bound of the range (exclusive).
pub end: $addr_type,
}

impl $name {
/// Creates a new address range.
#[inline]
pub const fn new(start: $addr_type, end: $addr_type) -> Self {
Self { start, end }
}

/// Creates a new address range from the start address and the size.
#[inline]
pub const fn from_start_size(start: $addr_type, size: usize) -> Self {
Self {
start,
end: <$addr_type>::from(usize!(start) + size),
}
}

/// Returns `true` if the range is empty (`start >= end`).
#[inline]
pub const fn is_empty(self) -> bool {
usize!(self.start) >= usize!(self.end)
}

/// Returns the size of the range.
#[inline]
pub const fn size(self) -> usize {
usize!(self.end) - usize!(self.start)
}

/// Checks if the range contains the given address.
#[inline]
pub const fn contains(self, addr: $addr_type) -> bool {
usize!(self.start) <= usize!(addr) && usize!(addr) < usize!(self.end)
}

/// Checks if the range contains the given address range.
#[inline]
pub const fn contains_range(self, other: Self) -> bool {
usize!(self.start) <= usize!(other.start) && usize!(other.end) <= usize!(self.end)
}

/// Checks if the range is contained in the given address range.
#[inline]
pub const fn contained_in(self, other: Self) -> bool {
other.contains_range(self)
}

/// Checks if the range overlaps with the given address range.
#[inline]
pub const fn overlaps(self, other: Self) -> bool {
usize!(self.start) < usize!(other.end) && usize!(other.start) < usize!(self.end)
}
}

impl<A> From<Range<A>> for $name
where
A: From<usize> + Into<usize>,
{
fn from(range: Range<A>) -> Self {
Self::new(range.start.into().into(), range.end.into().into())
}
}

impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x?}..{:#x?}", usize!(self.start), usize!(self.end))
}
}
}

/// Returns `true` if the range is empty (`start >= end`).
#[inline]
pub const fn is_empty(self) -> bool {
usize!(self.start) >= usize!(self.end)
}

/// Returns the size of the range.
#[inline]
pub const fn size(self) -> usize {
self.end.into() - self.start.into()
}

/// Checks if the range contains the given address.
#[inline]
pub const fn contains(self, addr: A) -> bool {
usize!(self.start) <= usize!(addr) && usize!(addr) < usize!(self.end)
}

/// Checks if the range contains the given address range.
#[inline]
pub const fn contains_range(self, other: Self) -> bool {
usize!(self.start) <= usize!(other.start) && usize!(other.end) <= usize!(self.end)
}

/// Checks if the range is contained in the given address range.
#[inline]
pub const fn contained_in(self, other: Self) -> bool {
other.contains_range(self)
}

/// Checks if the range overlaps with the given address range.
#[inline]
pub const fn overlaps(self, other: Self) -> bool {
usize!(self.start) < usize!(other.end) && usize!(other.start) < usize!(self.end)
}
};
}

impl<A, B> From<Range<B>> for AddrRange<A>
where
A: From<usize> + Into<usize> + Copy,
B: Into<A>,
{
fn from(range: Range<B>) -> Self {
Self::new(range.start.into(), range.end.into())
}
def_range!(VirtAddrRange, VirtAddr);
def_range!(PhysAddrRange, PhysAddr);

#[macro_export]
macro_rules! va_range {
($range:expr) => {
$crate::VirtAddrRange::from($range)
};
}

impl<A> fmt::Debug for AddrRange<A>
where
A: From<usize> + Into<usize> + Copy,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x?}..{:#x?}", usize!(self.start), usize!(self.end))
}
#[macro_export]
macro_rules! pa_range {
($range:expr) => {
$crate::PhysAddrRange::from($range)
};
}

0 comments on commit abe1670

Please sign in to comment.