use fuel_asm::{
PanicReason,
RegId,
};
use fuel_types::{
bytes::{
self,
SizedBytes,
},
mem_layout,
AssetId,
ContractId,
MemLayout,
MemLocType,
Word,
};
use crate::consts::{
WORD_SIZE,
*,
};
use std::io::{
self,
Write,
};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Call {
to: ContractId,
a: Word,
b: Word,
}
mem_layout!(
CallLayout for Call
to: ContractId = {ContractId::LEN},
a: Word = WORD_SIZE,
b: Word = WORD_SIZE
);
impl Call {
pub const LEN: usize = <Call as MemLayout>::LEN;
pub const fn new(to: ContractId, a: Word, b: Word) -> Self {
Self { to, a, b }
}
pub const fn to(&self) -> &ContractId {
&self.to
}
pub const fn a(&self) -> Word {
self.a
}
pub const fn b(&self) -> Word {
self.b
}
pub const fn into_inner(self) -> (ContractId, Word, Word) {
(self.to, self.a, self.b)
}
}
impl From<Call> for [u8; Call::LEN] {
fn from(val: Call) -> [u8; Call::LEN] {
let mut buf = [0u8; Call::LEN];
bytes::store_at(&mut buf, Call::layout(Call::LAYOUT.to), &val.to);
bytes::store_number_at(&mut buf, Call::layout(Call::LAYOUT.a), val.a);
bytes::store_number_at(&mut buf, Call::layout(Call::LAYOUT.b), val.b);
buf
}
}
impl From<[u8; Self::LEN]> for Call {
fn from(buf: [u8; Self::LEN]) -> Self {
let to = bytes::restore_at(&buf, Self::layout(Self::LAYOUT.to));
let a = bytes::restore_number_at(&buf, Self::layout(Self::LAYOUT.a));
let b = bytes::restore_number_at(&buf, Self::layout(Self::LAYOUT.b));
Self {
to: to.into(),
a,
b,
}
}
}
impl SizedBytes for Call {
fn serialized_size(&self) -> usize {
Self::LEN
}
}
impl io::Read for Call {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let buf: &mut [_; Self::LEN] = buf
.get_mut(..Self::LEN)
.and_then(|slice| slice.try_into().ok())
.ok_or(bytes::eof())?;
let bytes: [u8; Self::LEN] = (*self).into();
buf.copy_from_slice(&bytes);
Ok(Self::LEN)
}
}
impl io::Write for Call {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let buf: &[_; Self::LEN] = buf
.get(..Self::LEN)
.and_then(|slice| slice.try_into().ok())
.ok_or(bytes::eof())?;
*self = Self::from(*buf);
Ok(Self::LEN)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl TryFrom<&[u8]> for Call {
type Error = PanicReason;
fn try_from(bytes: &[u8]) -> Result<Self, PanicReason> {
let mut call = Self::default();
call.write(bytes)
.map_err(|_| PanicReason::MalformedCallStructure)?;
Ok(call)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CallFrame {
to: ContractId,
asset_id: AssetId,
registers: [Word; VM_REGISTER_COUNT],
code_size: Word,
a: Word,
b: Word,
}
mem_layout!(
CallFrameLayout for CallFrame
to: ContractId = {ContractId::LEN},
asset_id: AssetId = {AssetId::LEN},
registers: [u8; WORD_SIZE * VM_REGISTER_COUNT] = {WORD_SIZE * VM_REGISTER_COUNT},
code_size: Word = WORD_SIZE,
a: Word = WORD_SIZE,
b: Word = WORD_SIZE
);
#[cfg(test)]
impl Default for CallFrame {
fn default() -> Self {
Self {
to: ContractId::default(),
asset_id: AssetId::default(),
registers: [0; VM_REGISTER_COUNT],
code_size: 0,
a: 0,
b: 0,
}
}
}
impl CallFrame {
pub const fn new(
to: ContractId,
asset_id: AssetId,
registers: [Word; VM_REGISTER_COUNT],
code_size: Word,
a: Word,
b: Word,
) -> Self {
Self {
to,
asset_id,
registers,
code_size,
a,
b,
}
}
pub const fn contract_id_offset() -> usize {
0
}
pub const fn asset_id_offset() -> usize {
Self::contract_id_offset() + ContractId::LEN
}
pub const fn registers_offset() -> usize {
Self::asset_id_offset() + AssetId::LEN
}
pub const fn code_size_offset() -> usize {
Self::registers_offset() + WORD_SIZE * (VM_REGISTER_COUNT)
}
pub const fn a_offset() -> usize {
Self::code_size_offset() + WORD_SIZE
}
pub const fn b_offset() -> usize {
Self::a_offset() + WORD_SIZE
}
pub const fn serialized_size() -> usize {
Self::b_offset() + WORD_SIZE
}
pub const fn registers(&self) -> &[Word] {
&self.registers
}
pub const fn to(&self) -> &ContractId {
&self.to
}
pub fn code_size(&self) -> Word {
self.code_size
}
pub fn code_size_padding(&self) -> Word {
const WORD_SIZE: Word = crate::consts::WORD_SIZE as Word;
(WORD_SIZE - self.code_size() % WORD_SIZE) % WORD_SIZE
}
pub fn total_code_size(&self) -> Word {
self.code_size() + self.code_size_padding()
}
pub const fn a(&self) -> Word {
self.a
}
pub const fn b(&self) -> Word {
self.b
}
pub fn context_gas(&self) -> Word {
self.registers[RegId::CGAS]
}
pub const fn asset_id(&self) -> &AssetId {
&self.asset_id
}
pub fn context_gas_mut(&mut self) -> &mut Word {
&mut self.registers[RegId::CGAS]
}
pub fn global_gas_mut(&mut self) -> &mut Word {
&mut self.registers[RegId::GGAS]
}
}
impl SizedBytes for CallFrame {
fn serialized_size(&self) -> usize {
Self::serialized_size()
}
}
impl io::Read for CallFrame {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let buf: &mut [_; Self::LEN] = buf
.get_mut(..Self::LEN)
.and_then(|slice| slice.try_into().ok())
.ok_or(bytes::eof())?;
bytes::store_at(buf, Self::layout(Self::LAYOUT.to), &self.to);
bytes::store_at(buf, Self::layout(Self::LAYOUT.asset_id), &self.asset_id);
let mut registers = [0u8; Self::LAYOUT.registers.size()];
for (reg, out) in self
.registers
.iter()
.zip(registers.chunks_exact_mut(WORD_SIZE))
{
bytes::store_number(
out.try_into().expect("Can't fail as chunks are exact"),
*reg,
);
}
bytes::store_at(buf, Self::layout(Self::LAYOUT.registers), ®isters);
bytes::store_number_at(buf, Self::layout(Self::LAYOUT.code_size), self.code_size);
bytes::store_number_at(buf, Self::layout(Self::LAYOUT.a), self.a);
bytes::store_number_at(buf, Self::layout(Self::LAYOUT.b), self.b);
Ok(Self::LEN)
}
}
impl io::Write for CallFrame {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let buf: &[_; Self::LEN] = buf
.get(..Self::LEN)
.and_then(|slice| slice.try_into().ok())
.ok_or(bytes::eof())?;
let to = bytes::restore_at(buf, Self::layout(Self::LAYOUT.to));
let asset_id = bytes::restore_at(buf, Self::layout(Self::LAYOUT.asset_id));
let registers = bytes::restore_at(buf, Self::layout(Self::LAYOUT.registers));
let code_size =
bytes::restore_number_at(buf, Self::layout(Self::LAYOUT.code_size));
let a = bytes::restore_number_at(buf, Self::layout(Self::LAYOUT.a));
let b = bytes::restore_number_at(buf, Self::layout(Self::LAYOUT.b));
for (reg, word) in self
.registers
.iter_mut()
.zip(registers.chunks_exact(WORD_SIZE))
{
*reg = bytes::restore_number(
word.try_into().expect("Can't fail as chunks are exact"),
);
}
self.to = to.into();
self.asset_id = asset_id.into();
self.code_size = code_size;
self.a = a;
self.b = b;
Ok(Self::LEN)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[cfg(test)]
impl From<Call> for Vec<u8> {
fn from(mut call: Call) -> Self {
use io::Read;
let mut buf = [0; Call::LEN];
call.read_exact(&mut buf[..]).unwrap();
buf.to_vec()
}
}
#[cfg(test)]
impl From<CallFrame> for Vec<u8> {
fn from(mut call: CallFrame) -> Self {
use io::Read;
let mut buf = [0; CallFrame::serialized_size()];
call.read_exact(&mut buf[..]).unwrap();
buf.to_vec()
}
}