use core::fmt::Debug;
use core::ops::RangeInclusive;
use amplify::confinement::SmallBlob;
use amplify::num::{u1, u2, u3, u4, u5, u6, u7};
use crate::core::SiteId;
pub trait Bytecode<Id: SiteId> {
fn op_range() -> RangeInclusive<u8>;
fn opcode_byte(&self) -> u8;
fn code_byte_len(&self) -> u16;
fn external_ref(&self) -> Option<Id>;
fn encode_instr<W>(&self, writer: &mut W) -> Result<(), W::Error>
where W: BytecodeWrite<Id> {
writer.write_byte(self.opcode_byte())?;
self.encode_operands(writer)?;
writer.check_aligned();
Ok(())
}
fn encode_operands<W>(&self, writer: &mut W) -> Result<(), W::Error>
where W: BytecodeWrite<Id>;
fn decode_instr<R>(reader: &mut R) -> Result<Self, CodeEofError>
where
Self: Sized,
R: BytecodeRead<Id>,
{
let opcode = reader.read_byte()?;
let instr = Self::decode_operands(reader, opcode)?;
reader.check_aligned();
Ok(instr)
}
fn decode_operands<R>(reader: &mut R, opcode: u8) -> Result<Self, CodeEofError>
where
Self: Sized,
R: BytecodeRead<Id>;
}
#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error)]
#[display("attempt to read or write outside of a code segment (i.e., at position > 0xFFFF)")]
pub struct CodeEofError;
pub trait BytecodeRead<Id: SiteId> {
fn pos(&self) -> u16;
fn seek(&mut self, byte_pos: u16) -> Result<u16, CodeEofError>;
fn is_eof(&self) -> bool;
fn peek_byte(&self) -> Result<u8, CodeEofError>;
fn read_bool(&mut self) -> Result<bool, CodeEofError> { Ok(self.read_1bit()? == u1::ONE) }
fn read_1bit(&mut self) -> Result<u1, CodeEofError>;
fn read_2bits(&mut self) -> Result<u2, CodeEofError>;
fn read_3bits(&mut self) -> Result<u3, CodeEofError>;
fn read_4bits(&mut self) -> Result<u4, CodeEofError>;
fn read_5bits(&mut self) -> Result<u5, CodeEofError>;
fn read_6bits(&mut self) -> Result<u6, CodeEofError>;
fn read_7bits(&mut self) -> Result<u7, CodeEofError>;
fn read_byte(&mut self) -> Result<u8, CodeEofError>;
fn read_word(&mut self) -> Result<u16, CodeEofError>;
fn read_fixed<N, const LEN: usize>(
&mut self,
f: impl FnOnce([u8; LEN]) -> N,
) -> Result<N, CodeEofError>;
fn read_bytes(&mut self) -> Result<(SmallBlob, bool), CodeEofError>;
fn read_ref(&mut self) -> Result<Id, CodeEofError>
where Id: Sized;
fn check_aligned(&self);
}
pub trait BytecodeWrite<Id: SiteId> {
type Error: Debug;
fn write_bool(&mut self, data: bool) -> Result<(), Self::Error> {
self.write_1bit(if data { u1::ONE } else { u1::ZERO })
}
fn write_1bit(&mut self, data: u1) -> Result<(), Self::Error>;
fn write_2bits(&mut self, data: u2) -> Result<(), Self::Error>;
fn write_3bits(&mut self, data: u3) -> Result<(), Self::Error>;
fn write_4bits(&mut self, data: u4) -> Result<(), Self::Error>;
fn write_5bits(&mut self, data: u5) -> Result<(), Self::Error>;
fn write_6bits(&mut self, data: u6) -> Result<(), Self::Error>;
fn write_7bits(&mut self, data: u7) -> Result<(), Self::Error>;
fn write_byte(&mut self, data: u8) -> Result<(), Self::Error>;
fn write_word(&mut self, data: u16) -> Result<(), Self::Error>;
fn write_fixed<const LEN: usize>(&mut self, data: [u8; LEN]) -> Result<(), Self::Error>;
fn write_bytes(&mut self, data: &[u8]) -> Result<(), Self::Error>;
fn write_ref(&mut self, id: Id) -> Result<(), Self::Error>;
fn check_aligned(&self);
}