holey-bytes/vm/src/value.rs

89 lines
2.4 KiB
Rust

//! HoleyBytes register value definition
use crate::utils::static_assert;
/// Define [`Value`] »union« (it's fake)
///
/// # Safety
/// Its variants have to be sound to byte-reinterpretate
/// between each other. Otherwise the behaviour is idk.
macro_rules! value_def {
($($ty:ident),* $(,)?) => {
/// HBVM register value
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Value(pub u64);
$(
impl From<$ty> for Value {
#[inline]
fn from(value: $ty) -> Self {
let mut new = core::mem::MaybeUninit::<u64>::zeroed();
unsafe {
new.as_mut_ptr().cast::<$ty>().write(value);
Self(new.assume_init())
}
}
}
static_assert!(core::mem::size_of::<$ty>() <= core::mem::size_of::<Value>());
impl private::Sealed for $ty {}
unsafe impl ValueVariant for $ty {}
)*
};
}
impl Value {
/// Byte reinterpret value to target variant
#[inline]
pub fn cast<V: ValueVariant>(self) -> V {
unsafe { core::mem::transmute_copy(&self.0) }
}
}
/// # Safety
/// - N/A, not to be implemented manually
pub unsafe trait ValueVariant: private::Sealed + Copy + Into<Value> {}
impl private::Sealed for Value {}
unsafe impl ValueVariant for Value {}
mod private {
pub trait Sealed {}
}
value_def!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
static_assert!(core::mem::size_of::<Value>() == 8);
impl core::fmt::Debug for Value {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
// Print formatted as hexadecimal, unsigned integer
write!(f, "{:x}", self.cast::<u64>())
}
}
pub(crate) trait CheckedDivRem {
fn checked_div(self, other: Self) -> Option<Self>
where
Self: Sized;
fn checked_rem(self, other: Self) -> Option<Self>
where
Self: Sized;
}
macro_rules! impl_checked_div_rem {
($($ty:ty),* $(,)?) => {
$(impl CheckedDivRem for $ty {
#[inline(always)]
fn checked_div(self, another: Self) -> Option<Self>
{ self.checked_div(another) }
#[inline(always)]
fn checked_rem(self, another: Self) -> Option<Self>
{ self.checked_rem(another) }
})*
};
}
impl_checked_div_rem!(u8, u16, u32, u64, i8, i16, i32, i64);