bus/read.rs: Improve template code somewhat

This commit is contained in:
John 2023-04-23 12:14:04 -05:00
parent e9f8d917a4
commit 59ba8ac20b

View File

@ -10,7 +10,7 @@ use std::{fmt::Debug, slice::SliceIndex};
/// Gets a `&[T]` at [SliceIndex] `I`. /// Gets a `&[T]` at [SliceIndex] `I`.
/// ///
/// This is similar to the [SliceIndex] method `.get(...)`, however implementing this trait /// This is similar to the [SliceIndex] method `.get(...)`, however implementing this trait
/// for [u8] will auto-impl [Read]<(i8, u8, i16, u16 ... i128, u128)> /// for [u8] will auto-impl [ReadWrite]<([i8], [u8], [i16], [u16] ... [i128], [u128])>
pub trait Get<T> { pub trait Get<T> {
/// Gets the slice of Self at [SliceIndex] I /// Gets the slice of Self at [SliceIndex] I
fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output> fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
@ -23,7 +23,7 @@ pub trait Get<T> {
I: SliceIndex<[T]>; I: SliceIndex<[T]>;
} }
/// Read a T from address `addr` /// Read or Write a T at address `addr`
pub trait ReadWrite<T>: FallibleReadWrite<T> { pub trait ReadWrite<T>: FallibleReadWrite<T> {
/// Reads a T from address `addr` /// Reads a T from address `addr`
/// ///
@ -42,7 +42,7 @@ pub trait ReadWrite<T>: FallibleReadWrite<T> {
/// Read a T from address `addr`, and return the value as a [Result] /// Read a T from address `addr`, and return the value as a [Result]
pub trait FallibleReadWrite<T>: Get<u8> { pub trait FallibleReadWrite<T>: Get<u8> {
/// The [Err] type returned by [read_fallible] /// The [Err] type
type Error: Debug; type Error: Debug;
/// Read a T from address `addr`, returning the value as a [Result] /// Read a T from address `addr`, returning the value as a [Result]
fn read_fallible(&self, addr: impl Into<usize>) -> Result<T, Self::Error>; fn read_fallible(&self, addr: impl Into<usize>) -> Result<T, Self::Error>;
@ -55,45 +55,44 @@ pub trait FallibleReadWrite<T>: Get<u8> {
/// Relies on inherent methods of Rust numeric types: /// Relies on inherent methods of Rust numeric types:
/// - `Self::from_be_bytes` /// - `Self::from_be_bytes`
/// - `Self::to_be_bytes` /// - `Self::to_be_bytes`
macro_rules! impl_rw { macro_rules! impl_rw {($($t:ty) ,* $(,)?) =>{
($($t:ty) ,* $(,)?) => { $(
$( #[doc = concat!("Read or Write [", stringify!($t), "] at address `addr`")]
impl<T: Get<u8> + FallibleReadWrite<$t>> ReadWrite<$t> for T { impl<T: Get<u8> + FallibleReadWrite<$t>> ReadWrite<$t> for T {
#[inline(always)] #[inline(always)]
fn read(&self, addr: impl Into<usize>) -> $t { fn read(&self, addr: impl Into<usize>) -> $t {
self.read_fallible(addr).ok().unwrap_or_default() self.read_fallible(addr).ok().unwrap_or_default()
} }
#[inline(always)] #[inline(always)]
fn write(&mut self, addr: impl Into<usize>, data: $t) { fn write(&mut self, addr: impl Into<usize>, data: $t) {
self.write_fallible(addr, data).ok(); self.write_fallible(addr, data).ok();
}
}
impl<T: Get<u8>> FallibleReadWrite<$t> for T {
type Error = $crate::error::Error;
#[inline(always)]
fn read_fallible(&self, addr: impl Into<usize>) -> $crate::error::Result<$t> {
let addr: usize = addr.into();
let range = addr..addr + core::mem::size_of::<$t>();
if let Some(bytes) = self.get(range.clone()) {
// Chip-8 is a big-endian system
Ok(<$t>::from_be_bytes(bytes.try_into()?))
} else {
Err($crate::error::Error::InvalidAddressRange{range: range.into()})
} }
} }
impl<T: Get<u8>> FallibleReadWrite<$t> for T { #[inline(always)]
type Error = $crate::error::Error; fn write_fallible(&mut self, addr: impl Into<usize>, data: $t) -> std::result::Result<(), Self::Error> {
#[inline(always)] let addr: usize = addr.into();
fn read_fallible(&self, addr: impl Into<usize>) -> $crate::error::Result<$t> { if let Some(slice) = self.get_mut(addr..addr + core::mem::size_of::<$t>()) {
let addr: usize = addr.into(); // Chip-8 is a big-endian system
let range = addr..addr + core::mem::size_of::<$t>(); data.to_be_bytes().as_mut().swap_with_slice(slice);
if let Some(bytes) = self.get(range.clone()) {
// Chip-8 is a big-endian system
Ok(<$t>::from_be_bytes(bytes.try_into()?))
} else {
Err($crate::error::Error::InvalidAddressRange{range})
}
}
#[inline(always)]
fn write_fallible(&mut self, addr: impl Into<usize>, data: $t) -> std::result::Result<(), Self::Error> {
let addr: usize = addr.into();
if let Some(slice) = self.get_mut(addr..addr + core::mem::size_of::<$t>()) {
// Chip-8 is a big-endian system
data.to_be_bytes().as_mut().swap_with_slice(slice);
}
Ok(())
} }
Ok(())
} }
)* }
}; )*
} }}
impl_rw!(i8, i16, i32, i64, i128); impl_rw!(i8, i16, i32, i64, i128);
impl_rw!(u8, u16, u32, u64, u128); impl_rw!(u8, u16, u32, u64, u128);