use core::ptr::NonNull;
use crate::{ByteRepr, Result, os::ProtFlags, sync::Arc, try_cast_bytes};
use super::{HostRegion, traits::RegionAccess};
#[inline]
pub(crate) fn roundup(value: usize, align: usize) -> usize {
if align == 0 {
return value;
}
(value + align - 1) & !(align - 1)
}
#[inline]
pub(crate) fn rounddown(value: usize, align: usize) -> usize {
value & !(align - 1)
}
#[inline]
pub(crate) fn align_up(value: usize, align: usize) -> usize {
let align = align.max(1);
let remainder = value % align;
if remainder == 0 {
return value;
}
value
.checked_add(align - remainder)
.expect("alignment overflowed while rounding up value")
}
#[must_use = "address arithmetic returns a new value"]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct VmAddr(usize);
#[must_use = "offset arithmetic returns a new value"]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct VmOffset(usize);
impl VmOffset {
#[inline]
pub const fn new(offset: usize) -> Self {
Self(offset)
}
#[inline]
pub const fn get(self) -> usize {
self.0
}
#[inline]
pub fn checked_add(self, bytes: usize) -> Option<Self> {
self.0.checked_add(bytes).map(Self)
}
#[inline]
pub fn checked_offset_from(self, base: Self) -> Option<Self> {
self.0.checked_sub(base.0).map(Self)
}
}
impl core::fmt::Display for VmOffset {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "0x{:x}", self.0)
}
}
impl VmAddr {
#[inline]
pub const fn new(addr: usize) -> Self {
Self(addr)
}
#[inline]
pub const fn get(self) -> usize {
self.0
}
#[inline]
pub fn from_ptr<T>(ptr: *const T) -> Self {
Self(ptr as usize)
}
#[inline]
pub const fn null() -> Self {
Self(0)
}
#[inline]
pub const fn as_ptr<T>(self) -> *const T {
self.0 as *const T
}
#[inline]
pub const fn as_mut_ptr<T>(self) -> *mut T {
self.0 as *mut T
}
#[inline]
pub fn roundup(self, align: usize) -> Self {
Self(roundup(self.0, align))
}
#[inline]
pub fn rounddown(self, align: usize) -> Self {
Self(rounddown(self.0, align))
}
#[inline]
pub fn checked_add(self, offset: VmOffset) -> Option<Self> {
self.0.checked_add(offset.0).map(Self)
}
#[inline]
pub fn checked_offset_from(self, base: Self) -> Option<VmOffset> {
self.0.checked_sub(base.0).map(VmOffset)
}
#[inline]
pub fn wrapping_add(self, offset: VmOffset) -> Self {
Self(self.0.wrapping_add(offset.0))
}
#[inline]
pub fn wrapping_sub(self, offset: VmOffset) -> Self {
Self(self.0.wrapping_sub(offset.0))
}
#[inline]
pub const fn wrapping_offset_from(self, base: Self) -> VmOffset {
VmOffset(self.0.wrapping_sub(base.0))
}
#[inline]
pub const fn wrapping_add_signed(self, rhs: isize) -> Self {
Self(self.0.wrapping_add_signed(rhs))
}
}
impl core::ops::Add<VmOffset> for VmAddr {
type Output = Self;
#[inline]
fn add(self, offset: VmOffset) -> Self::Output {
self.wrapping_add(offset)
}
}
impl core::ops::Sub<VmOffset> for VmAddr {
type Output = Self;
#[inline]
fn sub(self, offset: VmOffset) -> Self::Output {
self.wrapping_sub(offset)
}
}
impl core::fmt::Display for VmAddr {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "0x{:x}", self.0)
}
}
pub struct MappedRegion<R: RegionAccess = HostRegion>(Arc<R>);
pub(crate) struct MappedView<T: 'static> {
slice: &'static [T],
}
impl<R: RegionAccess> Clone for MappedRegion<R> {
#[inline]
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl<T: 'static> Clone for MappedView<T> {
#[inline]
fn clone(&self) -> Self {
Self { slice: self.slice }
}
}
impl<R: RegionAccess> MappedRegion<R> {
#[inline]
pub fn new(region: R) -> Self {
Self(Arc::new(region))
}
#[inline]
pub fn addr(&self) -> VmAddr {
self.0.addr()
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub(crate) unsafe fn read_bytes(&self, offset: usize, dst: &mut [u8]) -> Result<()> {
unsafe { self.0.read_bytes(offset, dst) }
}
#[inline]
pub(crate) unsafe fn write_bytes(&self, offset: usize, src: &[u8]) -> Result<()> {
unsafe { self.0.write_bytes(offset, src) }
}
#[inline]
pub(crate) unsafe fn zero_bytes(&self, offset: usize, len: usize) -> Result<()> {
unsafe { self.0.zero_bytes(offset, len) }
}
#[inline]
pub(crate) unsafe fn borrow_bytes(&self, offset: usize, len: usize) -> Option<&'static [u8]> {
unsafe { self.0.borrow_bytes(offset, len) }
}
pub(crate) fn read_view<T: ByteRepr + 'static>(
&self,
offset: usize,
byte_len: usize,
) -> Option<MappedView<T>> {
if core::mem::size_of::<T>() == 0 {
return None;
}
if byte_len == 0 {
return Some(MappedView { slice: &[] });
}
let bytes = (unsafe { self.borrow_bytes(offset, byte_len) })?;
Some(MappedView {
slice: try_cast_bytes(bytes)?,
})
}
#[inline]
pub(crate) unsafe fn host_ptr(&self, offset: usize) -> Option<NonNull<u8>> {
unsafe { self.0.host_ptr(offset) }
}
#[inline]
pub(crate) unsafe fn mprotect(&self, offset: usize, len: usize, prot: ProtFlags) -> Result<()> {
unsafe { self.0.mprotect(offset, len, prot) }
}
}
impl<T: 'static> MappedView<T> {
#[inline]
pub(crate) const fn from_slice(slice: &'static [T]) -> Self {
Self { slice }
}
#[inline]
pub(crate) const fn empty() -> Self {
Self { slice: &[] }
}
#[inline]
pub(crate) fn as_slice(&self) -> &'static [T] {
self.slice
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.as_slice().len()
}
#[inline]
pub(crate) fn is_empty(&self) -> bool {
self.as_slice().is_empty()
}
#[inline]
pub(crate) fn split_at(&self, mid: usize) -> Option<(Self, Self)> {
if mid > self.len() {
return None;
}
let (head, tail) = self.slice.split_at(mid);
Some((Self::from_slice(head), Self::from_slice(tail)))
}
}
impl<T: 'static> AsRef<[T]> for MappedView<T> {
#[inline]
fn as_ref(&self) -> &[T] {
self.as_slice()
}
}