use super::{PhysicalAddress, Pointer};
use crate::types::ByteSwap;
use core::convert::TryInto;
use std::default::Default;
use std::fmt;
use std::hash;
use std::ops;
#[cfg(feature = "64_bit_mem")]
#[allow(non_camel_case_types)]
pub type umem = u64;
#[cfg(all(feature = "128_bit_mem", not(feature = "64_bit_mem")))]
#[allow(non_camel_case_types)]
pub type umem = u128;
#[cfg(all(not(feature = "64_bit_mem"), not(feature = "128_bit_mem")))]
#[allow(non_camel_case_types)]
pub type umem = usize;
#[cfg(feature = "64_bit_mem")]
#[allow(non_camel_case_types)]
pub type imem = i64;
#[cfg(all(feature = "128_bit_mem", not(feature = "64_bit_mem")))]
#[allow(non_camel_case_types)]
pub type imem = i128;
#[cfg(all(not(feature = "64_bit_mem"), not(feature = "128_bit_mem")))]
#[allow(non_camel_case_types)]
pub type imem = isize;
pub const UMEM_BITS: u8 = core::mem::size_of::<umem>() as u8 * 8;
const _: [u8; (core::mem::size_of::<usize>() <= core::mem::size_of::<umem>()) as usize] = [0; 1];
pub const fn clamp_to_usize(val: umem) -> usize {
let max = core::usize::MAX as umem;
let ret = if max < val { max } else { val };
ret as usize
}
pub const fn clamp_to_isize(val: imem) -> isize {
let max = core::isize::MAX as imem;
let min = core::isize::MIN as imem;
let ret = if max < val {
max
} else if min > val {
min
} else {
val
};
ret as isize
}
pub trait PrimitiveAddress:
Copy
+ Eq
+ PartialEq
+ Ord
+ PartialOrd
+ hash::Hash
+ fmt::LowerHex
+ fmt::UpperHex
+ ByteSwap
+ ops::Add<Output = Self>
+ ops::Sub<Output = Self>
{
fn null() -> Self;
fn invalid() -> Self;
fn min() -> Self;
fn max() -> Self;
fn from_umem(frm: umem) -> Self;
fn from_imem(frm: imem) -> Self;
fn wrapping_add(self, rhs: Self) -> Self;
fn wrapping_sub(self, rhs: Self) -> Self;
fn saturating_sub(self, rhs: Self) -> Self;
fn overflowing_shr(self, rhs: u32) -> (Self, bool);
fn to_umem(self) -> umem;
fn to_imem(self) -> imem;
#[inline]
fn is_null(self) -> bool {
self.eq(&Self::null())
}
}
#[macro_export]
macro_rules! impl_primitive_address {
($type_name:ident) => {
impl PrimitiveAddress for $type_name {
#[inline]
fn null() -> Self {
0 as $type_name
}
#[inline]
fn invalid() -> Self {
!Self::null()
}
#[inline]
fn min() -> Self {
Self::MIN
}
#[inline]
fn max() -> Self {
Self::MAX
}
#[inline]
fn from_umem(frm: umem) -> Self {
frm as Self
}
#[inline]
fn from_imem(frm: imem) -> Self {
frm as Self
}
#[inline]
fn wrapping_add(self, rhs: Self) -> Self {
self.wrapping_add(rhs)
}
#[inline]
fn wrapping_sub(self, rhs: Self) -> Self {
self.wrapping_sub(rhs)
}
#[inline]
fn saturating_sub(self, rhs: Self) -> Self {
self.saturating_sub(rhs)
}
#[inline]
fn overflowing_shr(self, rhs: u32) -> (Self, bool) {
self.overflowing_shr(rhs)
}
#[inline]
fn to_umem(self) -> umem {
self as umem
}
#[inline]
fn to_imem(self) -> imem {
self as imem
}
}
};
}
impl_primitive_address!(u16);
impl_primitive_address!(u32);
impl_primitive_address!(u64);
#[cfg(all(feature = "128_bit_mem", not(feature = "64_bit_mem")))]
impl_primitive_address!(u128);
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
#[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))]
pub struct Address(umem);
impl Address {
pub const NULL: Address = Address(0);
pub const INVALID: Address = Address(!0);
#[inline]
pub const fn null() -> Self {
Address::NULL
}
pub fn bit_mask<T: TryInto<u8>>(bits: ops::RangeInclusive<T>) -> Address
where
T: TryInto<u8>,
T: Copy,
{
Address(
(!0 >> ((UMEM_BITS - 1) - (*bits.end()).try_into().ok().unwrap()))
& !((1 << (*bits.start()).try_into().ok().unwrap()) - 1),
)
}
pub const fn bit_mask_u8(bits: ops::RangeInclusive<u8>) -> Address {
Address((!0 >> (UMEM_BITS - 1 - *bits.end())) & !((1 << *bits.start()) - 1))
}
#[inline]
pub const fn is_null(self) -> bool {
self.0 == 0
}
#[inline]
pub fn non_null(self) -> Option<Address> {
if self.is_null() {
None
} else {
Some(self)
}
}
#[inline]
pub const fn invalid() -> Self {
Address::INVALID
}
#[inline]
pub const fn is_valid(self) -> bool {
self.0 != !0
}
#[inline]
pub const fn to_umem(self) -> umem {
self.0
}
pub const fn as_mem_aligned(self, mem_size: umem) -> Self {
Self(self.0 - self.0 % mem_size)
}
pub const fn as_page_aligned(self, page_size: usize) -> Self {
self.as_mem_aligned(page_size as umem)
}
pub const fn bit_at(self, idx: u8) -> bool {
(self.0 & (1 << idx)) != 0
}
pub fn extract_bits<T: TryInto<u8>>(self, bits: ops::RangeInclusive<T>) -> Address
where
T: Copy,
{
(self.0 & Address::bit_mask(bits).to_umem()).into()
}
pub const fn wrapping_add(self, other: Self) -> Self {
Self(self.0.wrapping_add(other.0))
}
pub const fn wrapping_sub(self, other: Self) -> Self {
Self(self.0.wrapping_sub(other.0))
}
}
impl Default for Address {
fn default() -> Self {
Self::null()
}
}
impl ByteSwap for Address {
fn byte_swap(&mut self) {
self.0.byte_swap();
}
}
#[macro_export]
macro_rules! impl_address_from {
($type_name:ident) => {
impl From<$type_name> for Address {
fn from(item: $type_name) -> Self {
Self { 0: item as umem }
}
}
impl<T: ?Sized> From<Pointer<$type_name, T>> for Address {
#[inline(always)]
fn from(ptr: Pointer<$type_name, T>) -> Self {
Self {
0: ptr.inner as umem,
}
}
}
};
}
impl_address_from!(i8);
impl_address_from!(u8);
impl_address_from!(i16);
impl_address_from!(i32);
impl_address_from!(i64);
impl_address_from!(usize);
#[cfg(all(feature = "128_bit_mem", not(feature = "64_bit_mem")))]
impl_address_from!(i128);
impl<U: PrimitiveAddress> From<U> for Address {
#[inline(always)]
fn from(val: U) -> Self {
Self(val.to_umem())
}
}
impl From<PhysicalAddress> for Address {
fn from(address: PhysicalAddress) -> Self {
address.address
}
}
impl<U: PrimitiveAddress, T: ?Sized> From<Pointer<U, T>> for Address {
#[inline(always)]
fn from(ptr: Pointer<U, T>) -> Self {
Self(ptr.inner.to_umem())
}
}
#[macro_export]
macro_rules! impl_address_arithmetic_unsigned {
($type_name:ident) => {
impl ops::Add<$type_name> for Address {
type Output = Self;
fn add(self, other: $type_name) -> Self {
Self {
0: self.0 + (other as umem),
}
}
}
impl ops::AddAssign<$type_name> for Address {
fn add_assign(&mut self, other: $type_name) {
*self = Self {
0: self.0 + (other as umem),
}
}
}
impl ops::Sub<$type_name> for Address {
type Output = Address;
fn sub(self, other: $type_name) -> Address {
Self {
0: self.0 - (other as umem),
}
}
}
impl ops::SubAssign<$type_name> for Address {
fn sub_assign(&mut self, other: $type_name) {
*self = Self {
0: self.0 - (other as umem),
}
}
}
};
}
#[macro_export]
macro_rules! impl_address_arithmetic_signed {
($type_name:ident) => {
impl ops::Add<$type_name> for Address {
type Output = Self;
fn add(self, other: $type_name) -> Self {
if other >= 0 {
Self {
0: self.0 + (other as umem),
}
} else {
Self {
0: self.0 - (-other as umem),
}
}
}
}
impl ops::AddAssign<$type_name> for Address {
fn add_assign(&mut self, other: $type_name) {
if other >= 0 {
*self = Self {
0: self.0 + (other as umem),
}
} else {
*self = Self {
0: self.0 - (-other as umem),
}
}
}
}
impl ops::Sub<$type_name> for Address {
type Output = Address;
fn sub(self, other: $type_name) -> Address {
if other >= 0 {
Self {
0: self.0 - (other as umem),
}
} else {
Self {
0: self.0 + (-other as umem),
}
}
}
}
impl ops::SubAssign<$type_name> for Address {
fn sub_assign(&mut self, other: $type_name) {
if other >= 0 {
*self = Self {
0: self.0 - (other as umem),
}
} else {
*self = Self {
0: self.0 + (-other as umem),
}
}
}
}
};
}
impl_address_arithmetic_signed!(i8);
impl_address_arithmetic_signed!(i16);
impl_address_arithmetic_signed!(i32);
impl_address_arithmetic_signed!(i64);
#[cfg(all(feature = "128_bit_mem", not(feature = "64_bit_mem")))]
impl_address_arithmetic_signed!(i128);
impl_address_arithmetic_signed!(isize);
impl_address_arithmetic_unsigned!(u8);
impl_address_arithmetic_unsigned!(u16);
impl_address_arithmetic_unsigned!(u32);
impl_address_arithmetic_unsigned!(u64);
#[cfg(all(feature = "128_bit_mem", not(feature = "64_bit_mem")))]
impl_address_arithmetic_unsigned!(u128);
impl_address_arithmetic_unsigned!(usize);
impl<'a, T: Into<umem> + Copy> ops::Add<&'a T> for Address {
type Output = Self;
fn add(self, other: &'a T) -> Self {
Self(self.0 + (*other).into())
}
}
impl<'a, T: Into<umem> + Copy> ops::Sub<&'a T> for Address {
type Output = Self;
fn sub(self, other: &'a T) -> Self {
Self(self.0 - (*other).into())
}
}
impl ops::Sub for Address {
type Output = imem;
fn sub(self, other: Self) -> imem {
if self.0 > other.0 {
(self.0 - other.0) as imem
} else {
-((other.0 - self.0) as imem)
}
}
}
impl fmt::Debug for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x}", self.0)
}
}
impl fmt::UpperHex for Address {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:X}", self.0)
}
}
impl fmt::LowerHex for Address {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x}", self.0)
}
}
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x}", self.0)
}
}
#[cfg(test)]
mod tests {
use super::super::size;
use super::*;
#[test]
fn test_null_valid() {
assert!(Address::null().is_null());
assert!(!Address::invalid().is_valid());
}
#[test]
fn test_from() {
assert_eq!(Address::from(1337_u32).to_umem(), 1337);
assert_eq!(Address::from(4321_u64).to_umem(), 4321);
}
#[test]
fn test_alignment() {
assert_eq!(
Address::from(0x1234_u64).as_page_aligned(size::kb(4)),
Address::from(0x1000_u64)
);
assert_eq!(
Address::from(0xFFF1_2345_u64).as_page_aligned(0x10000),
Address::from(0xFFF1_0000_u64)
);
}
#[test]
fn test_bits() {
assert!(Address::from(1_u64).bit_at(0));
assert!(!Address::from(1_u64).bit_at(1));
assert!(!Address::from(1_u64).bit_at(2));
assert!(!Address::from(1_u64).bit_at(3));
assert!(!Address::from(2_u64).bit_at(0));
assert!(Address::from(2_u64).bit_at(1));
assert!(!Address::from(2_u64).bit_at(2));
assert!(!Address::from(2_u64).bit_at(3));
assert!(Address::from(13_u64).bit_at(0));
assert!(!Address::from(13_u64).bit_at(1));
assert!(Address::from(13_u64).bit_at(2));
assert!(Address::from(13_u64).bit_at(3));
}
#[test]
fn test_bit_mask() {
assert_eq!(Address::bit_mask(0..=11).to_umem(), 0xfff);
assert_eq!(Address::bit_mask(12..=20).to_umem(), 0x001f_f000);
assert_eq!(Address::bit_mask(21..=29).to_umem(), 0x3fe0_0000);
assert_eq!(Address::bit_mask(30..=38).to_umem(), 0x007f_c000_0000);
assert_eq!(Address::bit_mask(39..=47).to_umem(), 0xff80_0000_0000);
assert_eq!(Address::bit_mask(12..=51).to_umem(), 0x000f_ffff_ffff_f000);
}
#[test]
fn test_bit_mask_u8() {
assert_eq!(Address::bit_mask_u8(0..=11).to_umem(), 0xfff);
assert_eq!(Address::bit_mask_u8(12..=20).to_umem(), 0x001f_f000);
assert_eq!(Address::bit_mask_u8(21..=29).to_umem(), 0x3fe0_0000);
assert_eq!(Address::bit_mask_u8(30..=38).to_umem(), 0x007f_c000_0000);
assert_eq!(Address::bit_mask_u8(39..=47).to_umem(), 0xff80_0000_0000);
assert_eq!(
Address::bit_mask_u8(12..=51).to_umem(),
0x000f_ffff_ffff_f000
);
}
#[test]
fn test_ops() {
assert_eq!(Address::from(10_u64) + 5usize, Address::from(15_u64));
assert_eq!(Address::from(10_u64) - Address::from(5_u64), 5);
assert_eq!(Address::from(100_u64) - 5usize, Address::from(95_u64));
}
}