use crate::{Result, V128, bail_bug};
use core::mem;
use core::ops::Range;
pub trait PodValType<const SIZE: usize>: Copy {
fn read_le(le_bytes: &[u8; SIZE]) -> Self;
fn write_le(&self, into: &mut [u8; SIZE]);
}
macro_rules! impl_pod_val_type {
( $( $t:ty , )* ) => {
$(
impl PodValType<{mem::size_of::<$t>()}> for $t {
fn read_le(le_bytes: &[u8; mem::size_of::<$t>()]) -> Self {
<$t>::from_le_bytes(*le_bytes)
}
fn write_le(&self, into: &mut [u8; mem::size_of::<$t>()]) {
*into = self.to_le_bytes();
}
}
)*
};
}
impl_pod_val_type! {
u8,
u16,
u32,
u64,
i8,
i16,
i32,
i64,
}
impl PodValType<{ mem::size_of::<V128>() }> for V128 {
fn read_le(le_bytes: &[u8; mem::size_of::<V128>()]) -> Self {
u128::from_le_bytes(*le_bytes).into()
}
fn write_le(&self, into: &mut [u8; mem::size_of::<V128>()]) {
*into = self.as_u128().to_le_bytes();
}
}
#[repr(transparent)]
pub struct VMGcObjectData {
data: [u8],
}
macro_rules! impl_pod_methods {
( $( $T:ty, $read:ident, $write:ident; )* ) => {
$(
#[doc = stringify!($T)]
#[inline]
pub fn $read(&self, offset: u32) -> Result<$T>
{
self.read_pod::<{ mem::size_of::<$T>() }, $T>(offset)
}
#[doc = stringify!($T)]
#[inline]
pub fn $write(&mut self, offset: u32, val: $T) -> Result<()>
{
self.write_pod::<{ mem::size_of::<$T>() }, $T>(offset, val)
}
)*
};
}
impl<'a> From<&'a [u8]> for &'a VMGcObjectData {
#[inline]
fn from(data: &'a [u8]) -> Self {
&VMGcObjectData::from_slice(data)
}
}
impl<'a> From<&'a mut [u8]> for &'a mut VMGcObjectData {
#[inline]
fn from(data: &'a mut [u8]) -> Self {
VMGcObjectData::from_slice_mut(data)
}
}
impl VMGcObjectData {
#[inline]
pub fn from_slice(data: &[u8]) -> &Self {
unsafe { mem::transmute(data) }
}
#[inline]
pub fn from_slice_mut(data: &mut [u8]) -> &mut Self {
unsafe { mem::transmute(data) }
}
#[inline]
fn read_pod<const N: usize, T>(&self, offset: u32) -> Result<T>
where
T: PodValType<N>,
{
assert_eq!(N, mem::size_of::<T>());
let offset = usize::try_from(offset)?;
let bytes = match self.data.get(offset..).and_then(|s| s.first_chunk()) {
Some(data) => data,
None => bail_bug!("out of bounds field"),
};
Ok(T::read_le(bytes))
}
#[inline]
fn write_pod<const N: usize, T>(&mut self, offset: u32, val: T) -> Result<()>
where
T: PodValType<N>,
{
assert_eq!(N, mem::size_of::<T>());
let offset = usize::try_from(offset)?;
let into = match self
.data
.get_mut(offset..)
.and_then(|s| s.first_chunk_mut())
{
Some(into) => into,
None => bail_bug!("out of bounds field"),
};
Ok(val.write_le(into))
}
#[inline]
pub fn slice(&self, offset: u32, len: u32) -> Result<&[u8]> {
let start = usize::try_from(offset)?;
let len = usize::try_from(len)?;
match self.data.get(start..).and_then(|s| s.get(..len)) {
Some(slice) => Ok(slice),
None => bail_bug!("out of bounds slice"),
}
}
#[inline]
pub fn slice_mut(&mut self, offset: u32, len: u32) -> Result<&mut [u8]> {
let start = usize::try_from(offset)?;
let len = usize::try_from(len)?;
match self.data.get_mut(start..).and_then(|s| s.get_mut(..len)) {
Some(slice) => Ok(slice),
None => bail_bug!("out of bounds slice"),
}
}
#[inline]
pub fn copy_from_slice(&mut self, offset: u32, src: &[u8]) -> Result<()> {
let offset = usize::try_from(offset)?;
let into = match self
.data
.get_mut(offset..)
.and_then(|s| s.get_mut(..src.len()))
{
Some(into) => into,
None => bail_bug!("out of bounds copy"),
};
into.copy_from_slice(src);
Ok(())
}
#[inline]
pub fn copy_within(&mut self, src: Range<u32>, dest: u32) -> Result<()> {
let start = usize::try_from(src.start)?;
let end = usize::try_from(src.end)?;
let dest = usize::try_from(dest)?;
if self.data.get(start..end).is_none()
|| self
.data
.get(dest..)
.and_then(|s| s.get(..src.len()))
.is_none()
{
bail_bug!("invalid copy range");
}
self.data.copy_within(start..end, dest);
Ok(())
}
impl_pod_methods! {
u8, read_u8, write_u8;
u16, read_u16, write_u16;
u32, read_u32, write_u32;
u64, read_u64, write_u64;
i8, read_i8, write_i8;
i16, read_i16, write_i16;
i32, read_i32, write_i32;
i64, read_i64, write_i64;
V128, read_v128, write_v128;
}
}