use alloc::boxed::Box;
use alloc::vec::Vec;
use core::cell::RefCell;
use core::hint::cold_path;
use tinywasm_types::MemoryType;
use crate::{Error, MemoryBackend, Result};
use super::LinearMemory;
pub struct LazyLinearMemory {
ty: MemoryType,
initial_len: usize,
backend: MemoryBackend,
inner: RefCell<Option<Box<dyn LinearMemory>>>,
}
impl LazyLinearMemory {
pub fn try_new(ty: MemoryType, backend: MemoryBackend) -> Result<Self> {
let initial_len = usize::try_from(ty.initial_size())
.map_err(|_| Error::UnsupportedFeature("memory size exceeds the host address space"))?;
Ok(Self::new_with_initial_len(ty, initial_len, backend))
}
pub(crate) fn new_with_initial_len(ty: MemoryType, initial_len: usize, backend: MemoryBackend) -> Self {
Self { ty, initial_len, backend, inner: RefCell::new(None) }
}
fn with_inner<R>(&self, f: impl FnOnce(&dyn LinearMemory) -> R) -> R {
self.ensure_materialized();
let inner = self.inner.borrow();
f(inner.as_deref().expect("lazy memory should be materialized"))
}
fn try_with_inner<R>(&self, f: impl FnOnce(&dyn LinearMemory) -> R) -> core::result::Result<R, crate::Trap> {
self.try_ensure_materialized()?;
let inner = self.inner.borrow();
Ok(f(inner.as_deref().expect("lazy memory should be materialized")))
}
fn with_inner_mut<R>(&self, f: impl FnOnce(&mut dyn LinearMemory) -> R) -> R {
self.ensure_materialized();
let mut inner = self.inner.borrow_mut();
f(inner.as_deref_mut().expect("lazy memory should be materialized"))
}
fn try_with_inner_mut<R>(
&self,
f: impl FnOnce(&mut dyn LinearMemory) -> R,
) -> core::result::Result<R, crate::Trap> {
self.try_ensure_materialized()?;
let mut inner = self.inner.borrow_mut();
Ok(f(inner.as_deref_mut().expect("lazy memory should be materialized")))
}
fn ensure_materialized(&self) {
if self.inner.borrow().is_some() {
return;
}
let storage = self.backend.create(self.ty, self.initial_len).expect("lazy memory materialization failed");
*self.inner.borrow_mut() = Some(storage.0);
}
fn try_ensure_materialized(&self) -> core::result::Result<(), crate::Trap> {
if self.inner.borrow().is_some() {
return Ok(());
}
let storage = match self.backend.create(self.ty, self.initial_len) {
Ok(storage) => storage,
Err(Error::Trap(trap)) => {
cold_path();
return Err(trap);
}
Err(err) => panic!("lazy memory materialization failed: {err}"),
};
*self.inner.borrow_mut() = Some(storage.0);
Ok(())
}
}
impl LinearMemory for LazyLinearMemory {
fn len(&self) -> usize {
self.with_inner(|inner| inner.len())
}
fn grow_to(&mut self, new_len: usize) -> Result<(), crate::Trap> {
self.try_with_inner_mut(|inner| inner.grow_to(new_len))?
}
fn read(&self, addr: usize, dst: &mut [u8]) -> usize {
self.with_inner(|inner| inner.read(addr, dst))
}
fn write(&mut self, addr: usize, src: &[u8]) -> usize {
self.with_inner_mut(|inner| inner.write(addr, src))
}
fn write_all(&mut self, addr: usize, src: &[u8]) -> Option<()> {
self.with_inner_mut(|inner| inner.write_all(addr, src))
}
fn fill(&mut self, addr: usize, len: usize, val: u8) -> Option<()> {
self.with_inner_mut(|inner| inner.fill(addr, len, val))
}
fn copy_within(&mut self, dst: usize, src: usize, len: usize) -> Option<()> {
self.with_inner_mut(|inner| inner.copy_within(dst, src, len))
}
fn read_exact(&self, addr: usize, dst: &mut [u8]) -> Option<()> {
self.with_inner(|inner| inner.read_exact(addr, dst))
}
fn read_vec(&self, addr: usize, len: usize) -> Option<Vec<u8>> {
self.with_inner(|inner| inner.read_vec(addr, len))
}
fn read_8(&self, base: u64, offset: u64) -> core::result::Result<u8, crate::Trap> {
self.try_with_inner(|inner| inner.read_8(base, offset))?
}
fn read_16(&self, base: u64, offset: u64) -> core::result::Result<[u8; 2], crate::Trap> {
self.try_with_inner(|inner| inner.read_16(base, offset))?
}
fn read_32(&self, base: u64, offset: u64) -> core::result::Result<[u8; 4], crate::Trap> {
self.try_with_inner(|inner| inner.read_32(base, offset))?
}
fn read_64(&self, base: u64, offset: u64) -> core::result::Result<[u8; 8], crate::Trap> {
self.try_with_inner(|inner| inner.read_64(base, offset))?
}
fn read_128(&self, base: u64, offset: u64) -> core::result::Result<[u8; 16], crate::Trap> {
self.try_with_inner(|inner| inner.read_128(base, offset))?
}
fn write_8(&mut self, base: u64, offset: u64, byte: u8) -> core::result::Result<(), crate::Trap> {
self.try_with_inner_mut(|inner| inner.write_8(base, offset, byte))?
}
fn write_16(&mut self, base: u64, offset: u64, bytes: [u8; 2]) -> core::result::Result<(), crate::Trap> {
self.try_with_inner_mut(|inner| inner.write_16(base, offset, bytes))?
}
fn write_32(&mut self, base: u64, offset: u64, bytes: [u8; 4]) -> core::result::Result<(), crate::Trap> {
self.try_with_inner_mut(|inner| inner.write_32(base, offset, bytes))?
}
fn write_64(&mut self, base: u64, offset: u64, bytes: [u8; 8]) -> core::result::Result<(), crate::Trap> {
self.try_with_inner_mut(|inner| inner.write_64(base, offset, bytes))?
}
fn write_128(&mut self, base: u64, offset: u64, bytes: [u8; 16]) -> core::result::Result<(), crate::Trap> {
self.try_with_inner_mut(|inner| inner.write_128(base, offset, bytes))?
}
}
#[cfg(feature = "debug")]
impl core::fmt::Debug for LazyLinearMemory {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LazyLinearMemory")
.field("ty", &self.ty)
.field("materialized", &self.inner.borrow().is_some())
.finish()
}
}