use core::ptr::NonNull;
use zerocopy::{FromBytes, Immutable, IntoBytes};
fn convert<T: Immutable + IntoBytes, U: FromBytes>(value: T) -> U {
U::read_from_bytes(value.as_bytes()).unwrap()
}
pub trait MmioOps {
unsafe fn read_u8(src: *const u8) -> u8;
unsafe fn read_u16(src: *const u16) -> u16;
unsafe fn read_u32(src: *const u32) -> u32;
unsafe fn read_u64(src: *const u64) -> u64;
unsafe fn write_u8(dst: *mut u8, value: u8);
unsafe fn write_u16(dst: *mut u16, value: u16);
unsafe fn write_u32(dst: *mut u32, value: u32);
unsafe fn write_u64(dst: *mut u64, value: u64);
unsafe fn read<T: FromBytes + IntoBytes>(ptr: NonNull<T>) -> T {
unsafe {
match size_of::<T>() {
1 => convert(Self::read_u8(ptr.cast().as_ptr())),
2 => convert(Self::read_u16(ptr.cast().as_ptr())),
4 => convert(Self::read_u32(ptr.cast().as_ptr())),
8 => convert(Self::read_u64(ptr.cast().as_ptr())),
_ => {
let mut value = T::new_zeroed();
Self::read_slice(ptr.cast(), value.as_mut_bytes());
value
}
}
}
}
unsafe fn read_slice(ptr: NonNull<u8>, slice: &mut [u8]) {
if let Some((first, rest)) = slice.split_at_mut_checked(8)
&& ptr.cast::<u64>().is_aligned()
{
unsafe {
Self::read_u64(ptr.cast().as_ptr()).write_to(first).unwrap();
Self::read_slice(ptr.add(8), rest);
}
} else if let Some((first, rest)) = slice.split_at_mut_checked(4)
&& ptr.cast::<u32>().is_aligned()
{
unsafe {
Self::read_u32(ptr.cast().as_ptr()).write_to(first).unwrap();
Self::read_slice(ptr.add(4), rest);
}
} else if let Some((first, rest)) = slice.split_at_mut_checked(2)
&& ptr.cast::<u16>().is_aligned()
{
unsafe {
Self::read_u16(ptr.cast().as_ptr()).write_to(first).unwrap();
Self::read_slice(ptr.add(2), rest);
}
} else if let [first, rest @ ..] = slice {
unsafe {
*first = Self::read_u8(ptr.as_ptr());
Self::read_slice(ptr.add(1), rest);
}
}
}
unsafe fn write_slice(ptr: NonNull<u8>, slice: &[u8]) {
if let Some((first, rest)) = slice.split_at_checked(8)
&& ptr.cast::<u64>().is_aligned()
{
unsafe {
Self::write_u64(ptr.cast().as_ptr(), u64::read_from_bytes(first).unwrap());
Self::write_slice(ptr.add(8), rest);
}
} else if let Some((first, rest)) = slice.split_at_checked(4)
&& ptr.cast::<u32>().is_aligned()
{
unsafe {
Self::write_u32(ptr.cast().as_ptr(), u32::read_from_bytes(first).unwrap());
Self::write_slice(ptr.add(4), rest);
}
} else if let Some((first, rest)) = slice.split_at_checked(2)
&& ptr.cast::<u16>().is_aligned()
{
unsafe {
Self::write_u16(ptr.cast().as_ptr(), u16::read_from_bytes(first).unwrap());
Self::write_slice(ptr.add(2), rest);
}
} else if let [first, rest @ ..] = slice {
unsafe {
Self::write_u8(ptr.as_ptr(), *first);
Self::write_slice(ptr.add(1), rest);
}
}
}
unsafe fn write<T: Immutable + IntoBytes>(ptr: NonNull<T>, value: T) {
unsafe {
match size_of::<T>() {
1 => Self::write_u8(ptr.cast().as_ptr(), value.as_bytes()[0]),
2 => Self::write_u16(ptr.cast().as_ptr(), convert(value)),
4 => Self::write_u32(ptr.cast().as_ptr(), convert(value)),
8 => Self::write_u64(ptr.cast().as_ptr(), convert(value)),
_ => Self::write_slice(ptr.cast(), value.as_bytes()),
}
}
}
}