use crate::SIZE;
use crate::vm::{VmError, VmResult};
pub trait Mem: AsRef<[u8]> + AsMut<[u8]> {}
impl<M: AsRef<[u8]> + AsMut<[u8]>> Mem for M {}
pub struct Data<M: Mem> {
mem: M,
}
impl<M: Mem> Data<M> {
pub fn new(mem: M) -> Self {
Self { mem }
}
pub fn size(&self) -> usize {
self.mem.as_ref().len()
}
pub fn read(&self, addr: usize, len: usize) -> VmResult<&[u8]> {
let end = addr
.checked_add(len)
.ok_or(VmError::AddressOutOfRange(addr))?;
self.mem
.as_ref()
.get(addr..end)
.ok_or(VmError::AddressOutOfRange(addr))
}
pub fn read_cell(&self, addr: usize) -> VmResult<usize> {
if !addr.is_multiple_of(SIZE) {
return Err(VmError::AddressMisaligned(addr));
}
let bytes = self.read(addr, SIZE)?;
let mut buf = [0u8; SIZE];
buf.copy_from_slice(bytes);
Ok(usize::from_le_bytes(buf))
}
#[cfg(feature = "unsafe")]
pub unsafe fn read_cell_unchecked(&self, addr: usize) -> usize {
unsafe {
usize::from_le_bytes(*(self.mem.as_ref().as_ptr().add(addr) as *const [u8; SIZE]))
}
}
#[cfg(not(feature = "unsafe"))]
pub fn read_cell_unchecked(&self, addr: usize) -> usize {
let bytes = self.read(addr, SIZE).expect("unchecked read out of range");
let mut buf = [0u8; SIZE];
buf.copy_from_slice(bytes);
usize::from_le_bytes(buf)
}
pub fn read_char(&self, addr: usize) -> VmResult<u8> {
self.mem
.as_ref()
.get(addr)
.ok_or(VmError::AddressOutOfRange(addr))
.copied()
}
pub fn write(&mut self, addr: usize, bytes: &[u8]) -> VmResult<()> {
let end = addr
.checked_add(bytes.len())
.ok_or(VmError::AddressOutOfRange(addr))?;
let dst = self
.mem
.as_mut()
.get_mut(addr..end)
.ok_or(VmError::AddressOutOfRange(addr))?;
dst.copy_from_slice(bytes);
Ok(())
}
pub fn write_cell(&mut self, addr: usize, x: usize) -> VmResult<()> {
if !addr.is_multiple_of(SIZE) {
return Err(VmError::AddressMisaligned(addr));
}
self.write(addr, &x.to_le_bytes())
}
#[cfg(feature = "unsafe")]
pub unsafe fn write_cell_unchecked(&mut self, addr: usize, x: usize) {
unsafe { *(self.mem.as_mut().as_mut_ptr().add(addr) as *mut [u8; SIZE]) = x.to_le_bytes() };
}
#[cfg(not(feature = "unsafe"))]
pub fn write_cell_unchecked(&mut self, addr: usize, x: usize) {
self.write(addr, &x.to_le_bytes())
.expect("unchecked write out of range")
}
pub fn write_char(&mut self, addr: usize, c: u8) -> VmResult<()> {
*self
.mem
.as_mut()
.get_mut(addr)
.ok_or(VmError::AddressOutOfRange(addr))? = c;
Ok(())
}
pub fn slice_mut(&mut self, addr: usize, len: usize) -> VmResult<&mut [u8]> {
let end = addr
.checked_add(len)
.ok_or(VmError::AddressOutOfRange(addr))?;
self.mem
.as_mut()
.get_mut(addr..end)
.ok_or(VmError::AddressOutOfRange(addr))
}
pub fn copy_within(&mut self, src: usize, dest: usize, len: usize) -> VmResult<()> {
let src_end = src
.checked_add(len)
.ok_or(VmError::AddressOutOfRange(src))?;
let dest_end = dest
.checked_add(len)
.ok_or(VmError::AddressOutOfRange(dest))?;
let mem = self.mem.as_mut();
mem.get(src..src_end)
.ok_or(VmError::AddressOutOfRange(src))?;
mem.get(dest..dest_end)
.ok_or(VmError::AddressOutOfRange(dest))?;
mem.copy_within(src..src_end, dest);
Ok(())
}
}