use crate::WASM_PAGE_SIZE_BYTES as PAGE;
use std::{cell::RefCell, rc::Rc};
pub trait Memory {
fn size(&self) -> u64;
fn grow(&self, pages: u64) -> i64;
fn read(&self, offset: u64, bytes: &mut [u8]);
fn write(&self, offset: u64, bytes: &[u8]);
}
impl<M: Memory + ?Sized> Memory for Rc<M> {
fn size(&self) -> u64 {
self.as_ref().size()
}
fn grow(&self, pages: u64) -> i64 {
self.as_ref().grow(pages)
}
fn read(&self, offset: u64, bytes: &mut [u8]) {
self.as_ref().read(offset, bytes);
}
fn write(&self, offset: u64, bytes: &[u8]) {
self.as_ref().write(offset, bytes);
}
}
impl<M: Memory + ?Sized> Memory for &M {
fn size(&self) -> u64 {
(**self).size()
}
fn grow(&self, pages: u64) -> i64 {
(**self).grow(pages)
}
fn read(&self, offset: u64, bytes: &mut [u8]) {
(**self).read(offset, bytes);
}
fn write(&self, offset: u64, bytes: &[u8]) {
(**self).write(offset, bytes);
}
}
pub type VectorMemory = Rc<RefCell<Vec<u8>>>;
impl Memory for RefCell<Vec<u8>> {
fn size(&self) -> u64 {
self.borrow().len() as u64 / PAGE
}
fn grow(&self, pages: u64) -> i64 {
let previous = self.size();
let Some(length) = previous
.checked_add(pages)
.and_then(|value| value.checked_mul(PAGE))
.and_then(|value| usize::try_from(value).ok())
else {
return -1;
};
let mut bytes = self.borrow_mut();
let additional = length - bytes.len();
if bytes.try_reserve(additional).is_err() {
return -1;
}
bytes.resize(length, 0);
i64::try_from(previous).expect("native page count")
}
fn read(&self, offset: u64, bytes: &mut [u8]) {
let start = usize::try_from(offset).expect("native offset");
let end = start
.checked_add(bytes.len())
.expect("native range overflow");
bytes.copy_from_slice(&self.borrow()[start..end]);
}
fn write(&self, offset: u64, bytes: &[u8]) {
let start = usize::try_from(offset).expect("native offset");
let end = start
.checked_add(bytes.len())
.expect("native range overflow");
self.borrow_mut()[start..end].copy_from_slice(bytes);
}
}
#[cfg(not(target_arch = "wasm32"))]
pub type DefaultMemoryImpl = VectorMemory;
#[cfg(target_arch = "wasm32")]
#[derive(Clone, Copy, Default)]
pub struct DefaultMemoryImpl {
_private: (),
}
#[cfg(target_arch = "wasm32")]
impl Memory for DefaultMemoryImpl {
fn size(&self) -> u64 {
ic_platform_runtime::ic0::stable64_size()
}
fn grow(&self, pages: u64) -> i64 {
let previous = ic_platform_runtime::ic0::stable64_grow(pages);
if previous == u64::MAX {
-1
} else {
i64::try_from(previous).expect("IC page count")
}
}
fn read(&self, offset: u64, bytes: &mut [u8]) {
ic_platform_runtime::ic0::stable64_read(bytes, offset);
}
fn write(&self, offset: u64, bytes: &[u8]) {
ic_platform_runtime::ic0::stable64_write(bytes, offset);
}
}