use crate::account_view::AccountView;
use crate::address::Address;
use crate::error::ProgramError;
use crate::raw_account::RuntimeAccount;
use crate::MAX_PERMITTED_DATA_INCREASE;
const BPF_ALIGN_OF_U128: usize = 8;
pub struct LazyContext {
cursor: *mut u8,
total_accounts: usize,
parsed_count: usize,
instruction_data: *const u8,
instruction_data_len: usize,
program_id: Address,
resolved: [AccountView; 254],
}
unsafe impl Send for LazyContext {}
unsafe impl Sync for LazyContext {}
impl LazyContext {
#[inline(always)]
pub fn instruction_data(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.instruction_data, self.instruction_data_len) }
}
#[inline(always)]
pub fn program_id(&self) -> &Address {
&self.program_id
}
#[inline(always)]
pub fn total_accounts(&self) -> usize {
self.total_accounts
}
#[inline(always)]
pub fn parsed_count(&self) -> usize {
self.parsed_count
}
#[inline(always)]
pub fn remaining(&self) -> usize {
self.total_accounts - self.parsed_count
}
#[inline]
pub fn next_account(&mut self) -> Result<AccountView, ProgramError> {
if self.parsed_count >= self.total_accounts {
return Err(ProgramError::NotEnoughAccountKeys);
}
let view = unsafe { self.parse_one_account() };
self.resolved[self.parsed_count] = view.clone();
self.parsed_count += 1;
Ok(view)
}
#[inline]
pub fn next_signer(&mut self) -> Result<AccountView, ProgramError> {
let acct = self.next_account()?;
acct.require_signer()?;
Ok(acct)
}
#[inline]
pub fn next_writable(&mut self) -> Result<AccountView, ProgramError> {
let acct = self.next_account()?;
acct.require_writable()?;
Ok(acct)
}
#[inline]
pub fn next_payer(&mut self) -> Result<AccountView, ProgramError> {
let acct = self.next_account()?;
acct.require_payer()?;
Ok(acct)
}
#[inline]
pub fn next_owned_by(&mut self, program: &Address) -> Result<AccountView, ProgramError> {
let acct = self.next_account()?;
acct.require_owned_by(program)?;
Ok(acct)
}
#[inline]
pub fn skip(&mut self, n: usize) -> Result<(), ProgramError> {
for _ in 0..n {
if self.parsed_count >= self.total_accounts {
return Err(ProgramError::NotEnoughAccountKeys);
}
unsafe { self.advance_cursor() };
self.parsed_count += 1;
}
Ok(())
}
#[inline]
pub fn drain_remaining(&mut self) -> Result<&[AccountView], ProgramError> {
let start = self.parsed_count;
while self.parsed_count < self.total_accounts {
let view = unsafe { self.parse_one_account() };
self.resolved[self.parsed_count] = view;
self.parsed_count += 1;
}
Ok(&self.resolved[start..self.parsed_count])
}
#[inline(always)]
pub fn get(&self, index: usize) -> Option<&AccountView> {
if index < self.parsed_count {
Some(&self.resolved[index])
} else {
None
}
}
#[inline(always)]
unsafe fn parse_one_account(&mut self) -> AccountView {
unsafe {
let dup_marker = *self.cursor;
if dup_marker == u8::MAX {
let raw = self.cursor as *mut RuntimeAccount;
let view = AccountView::new_unchecked(raw);
self.advance_non_dup_cursor(raw);
view
} else {
let original_idx = dup_marker as usize;
self.cursor = self.cursor.add(8); if original_idx >= self.parsed_count {
crate::raw_input::malformed_duplicate_marker(dup_marker, self.parsed_count);
}
self.resolved[original_idx].clone()
}
}
}
#[inline(always)]
unsafe fn advance_cursor(&mut self) {
unsafe {
let dup_marker = *self.cursor;
if dup_marker == u8::MAX {
let raw = self.cursor as *mut RuntimeAccount;
self.advance_non_dup_cursor(raw);
} else {
self.cursor = self.cursor.add(8);
}
}
}
#[inline(always)]
unsafe fn advance_non_dup_cursor(&mut self, raw: *mut RuntimeAccount) {
unsafe {
let data_len = (*raw).data_len as usize;
let mut offset = RuntimeAccount::SIZE + data_len + MAX_PERMITTED_DATA_INCREASE;
offset += self.cursor.add(offset).align_offset(BPF_ALIGN_OF_U128);
offset += 8;
self.cursor = self.cursor.add(offset);
}
}
}
#[inline(always)]
pub unsafe fn lazy_deserialize(input: *mut u8) -> LazyContext {
let frame = unsafe { crate::raw_input::scan_instruction_frame(input) };
let resolved: [AccountView; 254] = unsafe { core::mem::zeroed() };
LazyContext {
cursor: frame.accounts_start,
total_accounts: frame.account_count,
parsed_count: 0,
instruction_data: frame.instruction_data.as_ptr(),
instruction_data_len: frame.instruction_data.len(),
program_id: frame.program_id,
resolved,
}
}