use crate::asm::{opcode::ParseOp, ToBytes, ToOpcode, TryFromBytes};
#[derive(Clone, Debug, PartialEq)]
pub struct BytecodeMapped<Op, Bytes = Vec<u8>> {
bytecode: Bytes,
op_indices: Vec<usize>,
_op_ty: core::marker::PhantomData<Op>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BytecodeMappedSlice<'a, Op> {
bytecode: &'a [u8],
op_indices: &'a [usize],
_op_ty: core::marker::PhantomData<Op>,
}
impl<Op> BytecodeMapped<Op, Vec<u8>> {
pub fn push_op(&mut self, op: Op)
where
Op: ToBytes,
{
self.op_indices.push(self.bytecode.len());
self.bytecode.extend(op.to_bytes());
}
}
impl<Op, Bytes> BytecodeMapped<Op, Bytes>
where
Bytes: core::ops::Deref<Target = [u8]>,
{
pub fn try_from_bytes(bytes: Bytes) -> Result<BytecodeMapped<Op, Bytes>, Op::Error>
where
Op: ToOpcode + TryFromBytes,
Op::Opcode: ParseOp<Op = Op> + TryFrom<u8>,
Op::Error: From<<Op::Opcode as TryFrom<u8>>::Error> + From<<Op::Opcode as ParseOp>::Error>,
{
let bytecode = bytes.deref();
let mut op_indices = Vec::with_capacity(bytecode.len() / std::mem::size_of::<Op>());
let mut iter_enum = bytecode.iter().enumerate();
while let Some((ix, &opcode_byte)) = iter_enum.next() {
let opcode = Op::Opcode::try_from(opcode_byte)?;
let mut op_bytes = iter_enum.by_ref().map(|(_, &byte)| byte);
let _op = opcode.parse_op(&mut op_bytes)?;
op_indices.push(ix);
}
Ok(BytecodeMapped {
bytecode: bytes,
op_indices,
_op_ty: core::marker::PhantomData,
})
}
pub fn as_slice(&self) -> BytecodeMappedSlice<Op> {
BytecodeMappedSlice {
bytecode: self.bytecode(),
op_indices: self.op_indices(),
_op_ty: self._op_ty,
}
}
pub fn bytecode(&self) -> &[u8] {
self.bytecode.deref()
}
pub fn ops_from(&self, start: usize) -> Option<BytecodeMappedSlice<Op>> {
Some(BytecodeMappedSlice {
bytecode: self.bytecode(),
op_indices: self.op_indices.get(start..)?,
_op_ty: self._op_ty,
})
}
pub fn op(&self, ix: usize) -> Option<Op>
where
Op: TryFromBytes,
{
let slice = self.ops_from(ix)?;
slice.ops().next()
}
pub fn ops(&self) -> impl '_ + Iterator<Item = Op>
where
Op: TryFromBytes,
{
expect_ops_from_indices(self.bytecode(), self.op_indices.iter().copied())
}
}
impl<Op, Bytes> BytecodeMapped<Op, Bytes> {
pub fn op_indices(&self) -> &[usize] {
&self.op_indices
}
}
impl<'a, Op> BytecodeMappedSlice<'a, Op> {
pub fn op_indices(self) -> &'a [usize] {
self.op_indices
}
pub fn ops(self) -> impl 'a + Iterator<Item = Op>
where
Op: TryFromBytes,
{
expect_ops_from_indices(self.bytecode, self.op_indices.iter().copied())
}
}
impl<Op> Default for BytecodeMapped<Op> {
fn default() -> Self {
BytecodeMapped {
bytecode: Default::default(),
op_indices: Default::default(),
_op_ty: Default::default(),
}
}
}
impl<Op> FromIterator<Op> for BytecodeMapped<Op>
where
Op: ToBytes,
{
fn from_iter<T: IntoIterator<Item = Op>>(iter: T) -> Self {
let iter = iter.into_iter();
let (min, _) = iter.size_hint();
let mut mapped = BytecodeMapped {
bytecode: Vec::with_capacity(min),
op_indices: Vec::with_capacity(min),
_op_ty: core::marker::PhantomData,
};
iter.for_each(|op| mapped.push_op(op));
mapped
}
}
impl<Op> TryFrom<Vec<u8>> for BytecodeMapped<Op>
where
Op: ToOpcode + TryFromBytes,
Op::Opcode: ParseOp<Op = Op> + TryFrom<u8>,
Op::Error: From<<Op::Opcode as TryFrom<u8>>::Error> + From<<Op::Opcode as ParseOp>::Error>,
{
type Error = Op::Error;
fn try_from(bytecode: Vec<u8>) -> Result<Self, Self::Error> {
Self::try_from_bytes(bytecode)
}
}
impl<'a, Op> TryFrom<&'a [u8]> for BytecodeMapped<Op, &'a [u8]>
where
Op: ToOpcode + TryFromBytes,
Op::Opcode: ParseOp<Op = Op> + TryFrom<u8>,
Op::Error: From<<Op::Opcode as TryFrom<u8>>::Error> + From<<Op::Opcode as ParseOp>::Error>,
{
type Error = Op::Error;
fn try_from(bytecode: &'a [u8]) -> Result<Self, Self::Error> {
Self::try_from_bytes(bytecode)
}
}
fn expect_ops_from_indices<'a, Op>(
bytecode: &'a [u8],
op_indices: impl 'a + IntoIterator<Item = usize>,
) -> impl 'a + Iterator<Item = Op>
where
Op: TryFromBytes,
{
const EXPECT_MSG: &str = "validated upon construction";
op_indices.into_iter().map(|ix| {
let mut bytes = bytecode[ix..].iter().copied();
Op::try_from_bytes(&mut bytes)
.expect(EXPECT_MSG)
.expect(EXPECT_MSG)
})
}