use core::cmp::Ord;
pub trait MemoryAddr:
Copy
+ From<usize>
+ Into<usize>
+ Ord
{
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn align_down<U>(self, align: U) -> Self
where
U: Into<usize>,
{
Self::from(crate::align_down(self.into(), align.into()))
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn align_up<U>(self, align: U) -> Self
where
U: Into<usize>,
{
Self::from(crate::align_up(self.into(), align.into()))
}
#[inline]
#[must_use = "this function has no side effects, so it can be removed if the return value is not used"]
fn align_offset<U>(self, align: U) -> usize
where
U: Into<usize>,
{
crate::align_offset(self.into(), align.into())
}
#[inline]
#[must_use = "this function has no side effects, so it can be removed if the return value is not used"]
fn is_aligned<U>(self, align: U) -> bool
where
U: Into<usize>,
{
crate::is_aligned(self.into(), align.into())
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn align_down_4k(self) -> Self {
Self::from(crate::align_down(self.into(), crate::PAGE_SIZE_4K))
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn align_up_4k(self) -> Self {
Self::from(crate::align_up(self.into(), crate::PAGE_SIZE_4K))
}
#[inline]
#[must_use = "this function has no side effects, so it can be removed if the return value is not used"]
fn align_offset_4k(self) -> usize {
crate::align_offset(self.into(), crate::PAGE_SIZE_4K)
}
#[inline]
#[must_use = "this function has no side effects, so it can be removed if the return value is not used"]
fn is_aligned_4k(self) -> bool {
crate::is_aligned(self.into(), crate::PAGE_SIZE_4K)
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn offset(self, offset: isize) -> Self {
Self::from(usize::checked_add_signed(self.into(), offset).expect("overflow in `MemoryAddr::offset`"))
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn wrapping_offset(self, offset: isize) -> Self {
Self::from(usize::wrapping_add_signed(self.into(), offset))
}
#[inline]
#[must_use = "this function has no side effects, so it can be removed if the return value is not used"]
fn offset_from(self, base: Self) -> isize {
let result = usize::wrapping_sub(self.into(), base.into()) as isize;
if (result > 0) ^ (base < self) {
panic!("overflow in `MemoryAddr::offset_from`");
} else {
result
}
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn add(self, rhs: usize) -> Self {
Self::from(usize::checked_add(self.into(), rhs).expect("overflow in `MemoryAddr::add`"))
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn wrapping_add(self, rhs: usize) -> Self {
Self::from(usize::wrapping_add(self.into(), rhs))
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn overflowing_add(self, rhs: usize) -> (Self, bool) {
let (result, overflow) = self.into().overflowing_add(rhs);
(Self::from(result), overflow)
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn checked_add(self, rhs: usize) -> Option<Self> {
usize::checked_add(self.into(), rhs).map(Self::from)
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn sub(self, rhs: usize) -> Self {
Self::from(usize::checked_sub(self.into(), rhs).expect("overflow in `MemoryAddr::sub`"))
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn wrapping_sub(self, rhs: usize) -> Self {
Self::from(usize::wrapping_sub(self.into(), rhs))
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn overflowing_sub(self, rhs: usize) -> (Self, bool) {
let (result, overflow) = self.into().overflowing_sub(rhs);
(Self::from(result), overflow)
}
#[inline]
#[must_use = "this returns a new address, without modifying the original"]
fn checked_sub(self, rhs: usize) -> Option<Self> {
usize::checked_sub(self.into(), rhs).map(Self::from)
}
#[inline]
#[must_use = "this function has no side effects, so it can be removed if the return value is not used"]
fn sub_addr(self, rhs: Self) -> usize {
usize::checked_sub(self.into(), rhs.into()).expect("overflow in `MemoryAddr::sub_addr`")
}
#[inline]
#[must_use = "this function has no side effects, so it can be removed if the return value is not used"]
fn wrapping_sub_addr(self, rhs: Self) -> usize {
usize::wrapping_sub(self.into(), rhs.into())
}
#[inline]
#[must_use = "this function has no side effects, so it can be removed if the return value is not used"]
fn overflowing_sub_addr(self, rhs: Self) -> (usize, bool) {
usize::overflowing_sub(self.into(), rhs.into())
}
#[inline]
#[must_use = "this function has no side effects, so it can be removed if the return value is not used"]
fn checked_sub_addr(self, rhs: Self) -> Option<usize> {
usize::checked_sub(self.into(), rhs.into())
}
}
impl<T> MemoryAddr for T where T: Copy + From<usize> + Into<usize> + Ord {}
#[macro_export]
macro_rules! def_usize_addr {
(
$(#[$meta:meta])*
$vis:vis type $name:ident;
$($tt:tt)*
) => {
#[repr(transparent)]
#[derive(Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq)]
$(#[$meta])*
pub struct $name(usize);
impl $name {
#[doc = concat!("Converts an `usize` to an [`", stringify!($name), "`].")]
#[inline]
pub const fn from_usize(addr: usize) -> Self {
Self(addr)
}
#[doc = concat!("Converts an [`", stringify!($name), "`] to an `usize`.")]
#[inline]
pub const fn as_usize(self) -> usize {
self.0
}
}
impl From<usize> for $name {
#[inline]
fn from(addr: usize) -> Self {
Self(addr)
}
}
impl From<$name> for usize {
#[inline]
fn from(addr: $name) -> usize {
addr.0
}
}
impl core::ops::Add<usize> for $name {
type Output = Self;
#[inline]
fn add(self, rhs: usize) -> Self {
Self(self.0 + rhs)
}
}
impl core::ops::AddAssign<usize> for $name {
#[inline]
fn add_assign(&mut self, rhs: usize) {
self.0 += rhs;
}
}
impl core::ops::Sub<usize> for $name {
type Output = Self;
#[inline]
fn sub(self, rhs: usize) -> Self {
Self(self.0 - rhs)
}
}
impl core::ops::SubAssign<usize> for $name {
#[inline]
fn sub_assign(&mut self, rhs: usize) {
self.0 -= rhs;
}
}
impl core::ops::Sub<$name> for $name {
type Output = usize;
#[inline]
fn sub(self, rhs: $name) -> usize {
self.0 - rhs.0
}
}
$crate::def_usize_addr!($($tt)*);
};
() => {};
}
#[macro_export]
macro_rules! def_usize_addr_formatter {
(
$name:ident = $format:literal;
$($tt:tt)*
) => {
impl core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.write_fmt(format_args!($format, format_args!("{:#x}", self.0)))
}
}
impl core::fmt::LowerHex for $name {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.write_fmt(format_args!($format, format_args!("{:#x}", self.0)))
}
}
impl core::fmt::UpperHex for $name {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.write_fmt(format_args!($format, format_args!("{:#X}", self.0)))
}
}
$crate::def_usize_addr_formatter!($($tt)*);
};
() => {};
}
def_usize_addr! {
pub type PhysAddr;
pub type VirtAddr;
}
def_usize_addr_formatter! {
PhysAddr = "PA:{}";
VirtAddr = "VA:{}";
}
impl VirtAddr {
#[inline]
pub fn from_ptr_of<T>(ptr: *const T) -> Self {
Self(ptr as usize)
}
#[inline]
pub fn from_mut_ptr_of<T>(ptr: *mut T) -> Self {
Self(ptr as usize)
}
#[inline]
pub const fn as_ptr(self) -> *const u8 {
self.0 as *const u8
}
#[inline]
pub const fn as_ptr_of<T>(self) -> *const T {
self.0 as *const T
}
#[inline]
pub const fn as_mut_ptr(self) -> *mut u8 {
self.0 as *mut u8
}
#[inline]
pub const fn as_mut_ptr_of<T>(self) -> *mut T {
self.0 as *mut T
}
}
#[macro_export]
macro_rules! pa {
($addr:expr) => {
$crate::PhysAddr::from_usize($addr)
};
}
#[macro_export]
macro_rules! va {
($addr:expr) => {
$crate::VirtAddr::from_usize($addr)
};
}
#[cfg(test)]
mod test {
use super::*;
def_usize_addr! {
pub type ExampleAddr;
pub type AnotherAddr;
}
def_usize_addr_formatter! {
ExampleAddr = "EA:{}";
AnotherAddr = "AA:{}";
}
#[test]
pub fn test_alignment() {
let alignment = 0x1000usize;
let base = alignment * 2;
let offset = 0x123usize;
let addr = ExampleAddr::from_usize(base + offset);
assert_eq!(addr.align_down(alignment), ExampleAddr::from_usize(base));
assert_eq!(
addr.align_up(alignment),
ExampleAddr::from_usize(base + alignment)
);
assert_eq!(addr.align_offset(alignment), offset);
assert!(!addr.is_aligned(alignment));
assert!(ExampleAddr::from_usize(base).is_aligned(alignment));
assert_eq!(
ExampleAddr::from_usize(base).align_up(alignment),
ExampleAddr::from_usize(base)
);
}
#[test]
pub fn test_addr_wrapping_arithmetic() {
let base = usize::MAX - 0x100usize;
let offset = 0x200usize;
let with_offset = base.wrapping_add(offset);
let addr = ExampleAddr::from_usize(base);
let offset_addr = ExampleAddr::from_usize(with_offset);
assert_eq!(addr.wrapping_offset(offset as isize), offset_addr);
assert_eq!(offset_addr.wrapping_offset(-(offset as isize)), addr);
assert_eq!(addr.wrapping_add(offset), offset_addr);
assert_eq!(offset_addr.wrapping_sub(offset), addr);
assert_eq!(offset_addr.wrapping_sub_addr(addr), offset);
}
#[test]
pub fn test_addr_checked_arithmetic() {
let low_addr = ExampleAddr::from_usize(0x100usize);
let high_addr = ExampleAddr::from_usize(usize::MAX - 0x100usize);
let small_offset = 0x50usize;
let large_offset = 0x200usize;
assert_eq!(
low_addr.checked_sub(small_offset),
Some(low_addr.wrapping_sub(small_offset))
);
assert_eq!(low_addr.checked_sub(large_offset), None);
assert_eq!(
high_addr.checked_add(small_offset),
Some(high_addr.wrapping_add(small_offset))
);
assert_eq!(high_addr.checked_add(large_offset), None);
assert_eq!(
high_addr.checked_sub_addr(low_addr),
Some(usize::MAX - 0x200usize)
);
assert_eq!(low_addr.checked_sub_addr(high_addr), None);
}
#[test]
pub fn test_addr_overflowing_arithmetic() {
let low_addr = ExampleAddr::from_usize(0x100usize);
let high_addr = ExampleAddr::from_usize(usize::MAX - 0x100usize);
let small_offset = 0x50usize;
let large_offset = 0x200usize;
assert_eq!(
low_addr.overflowing_sub(small_offset),
(low_addr.wrapping_sub(small_offset), false)
);
assert_eq!(
low_addr.overflowing_sub(large_offset),
(low_addr.wrapping_sub(large_offset), true)
);
assert_eq!(
high_addr.overflowing_add(small_offset),
(high_addr.wrapping_add(small_offset), false)
);
assert_eq!(
high_addr.overflowing_add(large_offset),
(high_addr.wrapping_add(large_offset), true)
);
assert_eq!(
high_addr.overflowing_sub_addr(low_addr),
(high_addr.wrapping_sub_addr(low_addr), false)
);
assert_eq!(
low_addr.overflowing_sub_addr(high_addr),
(low_addr.wrapping_sub_addr(high_addr), true)
);
}
#[test]
#[should_panic]
pub fn test_addr_offset_overflow() {
let addr = ExampleAddr::from_usize(usize::MAX);
let _ = addr.offset(1);
}
#[test]
#[should_panic]
pub fn test_addr_offset_from_overflow() {
let addr = ExampleAddr::from_usize(usize::MAX);
let _ = addr.offset_from(ExampleAddr::from_usize(0));
}
#[test]
#[should_panic]
pub fn test_addr_offset_from_underflow() {
let addr = ExampleAddr::from_usize(0);
let _ = addr.offset_from(ExampleAddr::from_usize(usize::MAX));
}
#[test]
#[should_panic]
pub fn test_addr_add_overflow() {
let addr = ExampleAddr::from_usize(usize::MAX);
let _ = addr.add(1);
}
#[test]
#[should_panic]
pub fn test_addr_sub_underflow() {
let addr = ExampleAddr::from_usize(0);
let _ = addr.sub(1);
}
#[test]
#[should_panic]
pub fn test_addr_sub_addr_overflow() {
let addr = ExampleAddr::from_usize(0);
let _ = addr.sub_addr(ExampleAddr::from_usize(1));
}
}