use alloc::vec::Vec;
use bytes::Bytes;
use core::ops::Range;
use corevm_host::{
fs::{self, ReadBlock},
PageNum, PAGE_SIZE,
};
use jam_pvm_common::{ApiError, InvokeOutcome};
use jam_types::{SignedGas, SEGMENT_LEN};
pub trait InnerVm {
fn void(&mut self, page: PageNum, num_pages: u32) -> Result<(), ApiError>;
fn zero(&mut self, page: PageNum, num_pages: u32) -> Result<(), ApiError>;
fn poke(&mut self, outer_src: &[u8], inner_dst: u32) -> Result<(), ApiError>;
fn peek_into(&mut self, outer_dst: &mut [u8], inner_src: u32) -> Result<(), ApiError>;
fn invoke(
&mut self,
gas: SignedGas,
regs: [u64; 13],
) -> Result<(InvokeOutcome, SignedGas, [u64; 13]), ApiError>;
fn expunge(self) -> Result<u64, ApiError>;
fn zero_poke(&mut self, outer_src: &[u8], inner_dst: u32) -> Result<(), ApiError> {
let page = PageNum::from_address(inner_dst);
let num_pages = (outer_src.len() as u32).div_ceil(PAGE_SIZE);
self.zero(page, num_pages)?;
self.poke(outer_src, inner_dst)?;
Ok(())
}
fn poke_ro_rw_data_page(
&mut self,
data: &[u8],
address_range: &Range<u32>,
address: u32,
) -> Result<(), ApiError> {
debug_assert_eq!(0, address % PAGE_SIZE);
debug_assert!(address_range.contains(&address));
let data_start = address - address_range.start;
if data_start < data.len() as u32 {
let data_start = data_start as usize;
let data_end = (data_start + PAGE_SIZE as usize).min(data.len());
let data_range = data_start..data_end;
self.zero_poke(&data[data_range], address)?;
} else {
let page = PageNum::from_address(address);
self.zero(page, 1)?;
}
Ok(())
}
}
pub trait OuterVm {
type InnerVm: crate::InnerVm;
fn export(&mut self, segment: &[u8; SEGMENT_LEN]) -> Result<(), ApiError>;
fn read_file_block(&mut self, block_ref: &fs::BlockRef) -> Option<Bytes>;
fn machine(&mut self, code: &[u8], program_counter: u64) -> Result<Self::InnerVm, ApiError>;
fn read_file(&mut self, block_ref: &fs::BlockRef) -> Result<Vec<u8>, fs::Error> {
let mut block_reader = self.block_reader();
let buf = fs::read(block_ref, &mut block_reader)?;
Ok(buf)
}
fn block_reader(&mut self) -> Lookup<'_, Self> {
Lookup { outer_vm: self }
}
}
pub struct Lookup<'a, O: OuterVm + ?Sized> {
pub outer_vm: &'a mut O,
}
impl<O: OuterVm + ?Sized> ReadBlock for Lookup<'_, O> {
fn read_block(&mut self, block_ref: &fs::BlockRef) -> Result<Bytes, fs::IoError> {
self.outer_vm.read_file_block(block_ref).ok_or(fs::IoError)
}
}