use fuel_asm::{PanicReason, RegId};
use fuel_tx::Contract;
use fuel_types::bytes::{self, SizedBytes};
use fuel_types::{AssetId, ContractId, Word};
use crate::consts::*;
use crate::{arith::checked_add_usize, 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,
}
impl Call {
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 SizedBytes for Call {
fn serialized_size(&self) -> usize {
ContractId::LEN + 2 * WORD_SIZE
}
}
impl io::Read for Call {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let n = self.serialized_size();
if buf.len() < n {
return Err(bytes::eof());
}
let buf = bytes::store_array_unchecked(buf, &self.to);
let buf = bytes::store_number_unchecked(buf, self.a);
bytes::store_number_unchecked(buf, self.b);
Ok(n)
}
}
impl io::Write for Call {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let n = self.serialized_size();
if buf.len() < n {
return Err(bytes::eof());
}
let (to, buf) = unsafe { bytes::restore_array_unchecked(buf) };
let (a, buf) = unsafe { bytes::restore_word_unchecked(buf) };
let (b, _) = unsafe { bytes::restore_word_unchecked(buf) };
self.to = to.into();
self.a = a;
self.b = b;
Ok(n)
}
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],
a: Word,
b: Word,
code: Contract,
}
impl CallFrame {
pub const fn new(
to: ContractId,
asset_id: AssetId,
registers: [Word; VM_REGISTER_COUNT],
a: Word,
b: Word,
code: Contract,
) -> Self {
Self {
to,
asset_id,
registers,
a,
b,
code,
}
}
pub fn code(&self) -> &[u8] {
self.code.as_ref()
}
pub const fn code_offset() -> usize {
Self::code_size_offset() + WORD_SIZE
}
pub const fn code_size_offset() -> usize {
ContractId::LEN + AssetId::LEN + WORD_SIZE * (2 + VM_REGISTER_COUNT)
}
pub const fn a_offset() -> usize {
ContractId::LEN + AssetId::LEN + WORD_SIZE * (1 + VM_REGISTER_COUNT)
}
pub const fn b_offset() -> usize {
ContractId::LEN + AssetId::LEN + WORD_SIZE * (2 + VM_REGISTER_COUNT)
}
pub const fn registers(&self) -> &[Word] {
&self.registers
}
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 fn context_gas(&self) -> Word {
self.registers[RegId::CGAS]
}
pub const fn asset_id(&self) -> &AssetId {
&self.asset_id
}
pub fn set_context_gas(&mut self) -> &mut Word {
&mut self.registers[RegId::CGAS]
}
pub fn set_global_gas(&mut self) -> &mut Word {
&mut self.registers[RegId::GGAS]
}
}
impl SizedBytes for CallFrame {
fn serialized_size(&self) -> usize {
Self::code_offset() + bytes::padded_len(self.code.as_ref())
}
}
impl io::Read for CallFrame {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let n = self.serialized_size();
if buf.len() < n {
return Err(bytes::eof());
}
let buf = bytes::store_array_unchecked(buf, &self.to);
let buf = bytes::store_array_unchecked(buf, &self.asset_id);
let buf = self
.registers
.iter()
.fold(buf, |buf, reg| bytes::store_number_unchecked(buf, *reg));
let buf = bytes::store_number_unchecked(buf, self.code.as_ref().len() as Word);
let buf = bytes::store_number_unchecked(buf, self.a);
let buf = bytes::store_number_unchecked(buf, self.b);
bytes::store_raw_bytes(buf, self.code.as_ref())?;
Ok(n)
}
}
impl io::Write for CallFrame {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut n = Self::code_offset();
if buf.len() < n {
return Err(bytes::eof());
}
let (to, buf) = unsafe { bytes::restore_array_unchecked(buf) };
let (asset_id, buf) = unsafe { bytes::restore_array_unchecked(buf) };
let buf = self.registers.iter_mut().fold(buf, |buf, reg| {
let (r, buf) = unsafe { bytes::restore_word_unchecked(buf) };
*reg = r;
buf
});
let (code_len, buf) = unsafe { bytes::restore_usize_unchecked(buf) };
let (a, buf) = unsafe { bytes::restore_word_unchecked(buf) };
let (b, buf) = unsafe { bytes::restore_word_unchecked(buf) };
let (bytes, code, _) = bytes::restore_raw_bytes(buf, code_len)?;
n = checked_add_usize(n, bytes)?;
self.to = to.into();
self.asset_id = asset_id.into();
self.a = a;
self.b = b;
self.code = code.into();
Ok(n)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}