use crate::prelude::*;
use crate::StackAddress;
use crate::bytecode::{VMFunc, Program, ConstEndianness, ConstDescriptor, Constructor};
use std::cell::{Cell, RefCell, RefMut};
#[derive(Debug)]
pub struct Writer<T> {
pub(crate) program: RefCell<Program<T>>,
position: Cell<StackAddress>,
}
impl<T> Writer<T> where T: VMFunc<T> {
pub fn new() -> Self {
Writer {
program: RefCell::new(Program::<T>::new()),
position: Cell::new(0),
}
}
pub(crate) fn program(self: &Self) -> RefMut<Program<T>> {
self.program.borrow_mut()
}
pub fn len(self: &Self) -> StackAddress {
self.program().instructions.len() as StackAddress
}
pub fn position(self: &Self) -> StackAddress {
self.position.get()
}
pub fn set_position(self: &Self, new_position: StackAddress) {
self.position.set(new_position);
}
pub fn into_program(self: Self) -> Program<T> {
self.program.into_inner()
}
pub fn overwrite(self: &Self, position: StackAddress, mut write_fn: impl FnMut(&Self) -> StackAddress) -> StackAddress {
let original_position = self.position();
self.position.set(position);
let result = write_fn(self);
self.position.set(original_position);
result
}
pub(crate) fn write(self: &Self, buf: &[u8]) {
let buf_len = buf.len() as StackAddress;
if self.position() == self.len() {
self.program().instructions.extend_from_slice(buf);
} else {
let position = self.position();
let end = min(position + buf_len, self.len());
self.program().instructions.splice(position as usize .. end as usize, buf.iter().cloned());
}
self.position.set(self.position.get() + buf_len);
}
pub fn const_len(self: &Self) -> StackAddress {
self.program().consts.len() as StackAddress
}
pub fn reserve_const_data(self: &Self, size: StackAddress) -> StackAddress {
let position = self.program().consts.len() as StackAddress;
self.program().const_descriptors.push(ConstDescriptor { position, size, endianness: ConstEndianness::None });
let pool_size = self.program().consts.len();
self.program().consts.resize(pool_size + size as usize, 0);
position
}
}
#[allow(unused_macros)]
macro_rules! impl_store_const {
($type:tt, $endianess:path) => {
impl<P> StoreConst<$type> for Writer<P> where P: VMFunc<P> {
fn store_const(self: &Self, value: $type) -> StackAddress {
let position = self.program().consts.len() as StackAddress;
let size = size_of::<$type>() as StackAddress;
let mut program = self.program();
program.const_descriptors.push(ConstDescriptor { position, size, endianness: $endianess });
program.consts.extend_from_slice(&value.to_le_bytes());
position
}
fn update_const(self: &Self, position: StackAddress, value: $type) {
let mut program = self.program();
let position = position as usize;
for (index, &byte) in value.to_le_bytes().iter().enumerate() {
program.consts[position + index] = byte;
}
}
}
};
}
pub trait StoreConst<T> {
fn store_const(self: &Self, value: T) -> StackAddress;
fn update_const(self: &Self, position: StackAddress, value: T);
}
impl_store_const!(u8, ConstEndianness::None);
impl_store_const!(i8, ConstEndianness::None);
impl_store_const!(u16, ConstEndianness::Integer);
impl_store_const!(i16, ConstEndianness::Integer);
impl_store_const!(u32, ConstEndianness::Integer);
impl_store_const!(i32, ConstEndianness::Integer);
impl_store_const!(f32, ConstEndianness::Float);
impl_store_const!(u64, ConstEndianness::Integer);
impl_store_const!(i64, ConstEndianness::Integer);
impl_store_const!(f64, ConstEndianness::Float);
impl_store_const!(usize, ConstEndianness::Integer);
impl_store_const!(isize, ConstEndianness::Integer);
impl<P> StoreConst<&str> for Writer<P> where P: VMFunc<P> {
fn store_const(self: &Self, value: &str) -> StackAddress {
let raw_bytes = &value.as_bytes();
let size = raw_bytes.len() as StackAddress;
self.store_const(size);
let position = self.program().consts.len() as StackAddress;
let mut program = self.program();
program.const_descriptors.push(ConstDescriptor { position, size, endianness: ConstEndianness::None });
program.consts.extend_from_slice(raw_bytes);
position
}
fn update_const(self: &Self, _position: StackAddress, _value: &str) {
unimplemented!("Cannot update constant string");
}
}
impl<P> StoreConst<Constructor> for Writer<P> where P: VMFunc<P> {
fn store_const(self: &Self, value: Constructor) -> StackAddress {
let position = self.program().consts.len() as StackAddress;
let size = size_of::<Constructor>() as StackAddress;
let mut program = self.program();
program.const_descriptors.push(ConstDescriptor { position, size, endianness: ConstEndianness::None });
program.consts.push(value.to_u8());
position
}
fn update_const(self: &Self, position: StackAddress, value: Constructor) {
let mut program = self.program();
program.consts[position as usize] = value.to_u8();
}
}