use {
crate::{
account::{AccountView, RuntimeAccount},
entrypoint::{NON_DUP_MARKER, STATIC_ACCOUNT_DATA},
error::ProgramError,
Address, BPF_ALIGN_OF_U128,
},
core::mem::size_of,
};
#[macro_export]
macro_rules! lazy_program_entrypoint {
( $process_instruction:expr ) => {
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
match $process_instruction(unsafe {
$crate::entrypoint::lazy::InstructionContext::new_unchecked(input)
}) {
Ok(_) => $crate::SUCCESS,
Err(error) => error.into(),
}
}
};
}
#[derive(Debug)]
pub struct InstructionContext {
buffer: *mut u8,
remaining: u64,
}
impl InstructionContext {
#[inline(always)]
pub unsafe fn new_unchecked(input: *mut u8) -> Self {
Self {
buffer: unsafe { input.add(size_of::<u64>()) },
remaining: unsafe { *(input as *const u64) },
}
}
#[inline(always)]
pub fn next_account(&mut self) -> Result<MaybeAccount, ProgramError> {
self.remaining = self
.remaining
.checked_sub(1)
.ok_or(ProgramError::NotEnoughAccountKeys)?;
Ok(unsafe { self.read_account() })
}
#[inline(always)]
pub unsafe fn next_account_unchecked(&mut self) -> MaybeAccount {
self.read_account()
}
#[inline(always)]
pub fn remaining(&self) -> u64 {
self.remaining
}
#[inline(always)]
pub fn instruction_data(&self) -> Result<&[u8], ProgramError> {
if self.remaining > 0 {
return Err(ProgramError::InvalidInstructionData);
}
Ok(unsafe { self.instruction_data_unchecked() })
}
#[inline(always)]
pub unsafe fn instruction_data_unchecked(&self) -> &[u8] {
let data_len = *(self.buffer as *const usize);
let data = self.buffer.add(core::mem::size_of::<u64>());
core::slice::from_raw_parts(data, data_len)
}
#[inline(always)]
pub fn program_id(&self) -> Result<&Address, ProgramError> {
if self.remaining > 0 {
return Err(ProgramError::InvalidInstructionData);
}
Ok(unsafe { self.program_id_unchecked() })
}
#[inline(always)]
pub unsafe fn program_id_unchecked(&self) -> &Address {
let data_len = *(self.buffer as *const usize);
&*(self.buffer.add(core::mem::size_of::<u64>() + data_len) as *const Address)
}
#[allow(clippy::cast_ptr_alignment, clippy::missing_safety_doc)]
#[inline(always)]
unsafe fn read_account(&mut self) -> MaybeAccount {
let account: *mut RuntimeAccount = self.buffer as *mut RuntimeAccount;
self.buffer = self.buffer.add(core::mem::size_of::<u64>());
if (*account).borrow_state == NON_DUP_MARKER {
#[cfg(feature = "account-resize")]
{
(*account).padding = u32::to_le_bytes((*account).data_len as u32);
}
self.buffer = self.buffer.add(STATIC_ACCOUNT_DATA);
self.buffer = self.buffer.add((*account).data_len as usize);
self.buffer = self.buffer.add(self.buffer.align_offset(BPF_ALIGN_OF_U128));
MaybeAccount::Account(AccountView::new_unchecked(account))
} else {
MaybeAccount::Duplicated((*account).borrow_state)
}
}
}
#[cfg_attr(feature = "copy", derive(Copy))]
#[derive(Debug, Clone)]
pub enum MaybeAccount {
Account(AccountView),
Duplicated(u8),
}
impl MaybeAccount {
#[inline(always)]
pub fn assume_account(self) -> AccountView {
let MaybeAccount::Account(account) = self else {
panic!("Duplicated account")
};
account
}
}