Zero-copy account parsing for Solana programs.
solana_program::entrypoint::deserialize allocates AccountInfo structs and copies account data. peephole skips that with a zero-alloc and zero-copy view over the account and instruction data.
Real-world: 51 CU oracle update, 67 CUs for 3 oracle updates, 12 CU trading contract, and many others.
[dependencies]
peephole = { version = "1.0", features = ["solana-sdk"] }Feature flags: solana-sdk or pinocchio-sdk (exactly one required).
use peephole::account_iterator::{AccountIterator, NextHeader};
let mut iter = unsafe { AccountIterator::new_from_instruction(input) };
loop {
match iter.next_header() {
NextHeader::Header(cursor) => {
// Inspect header before parsing data
let key = &cursor.static_data.key;
let data_len = cursor.data_len();
// Parse the full account when ready
let (acc, next) = unsafe { cursor.parse_data() };
let lamports = acc.static_data.lamports;
let data: &[u8] = acc.data();
iter = next;
}
NextHeader::Dup(idx, next) => {
// Duplicate of account `idx`—runtime only serializes full data once
iter = next;
}
NextHeader::Data(instruction_data, program_id) => {
// instruction_data: &mut [u8], program_id: &Pubkey
break;
}
}
}Cast account data directly to your struct:
#[repr(C)]
#[derive(Pod, Zeroable, Copy, Clone)]
struct TokenAccount {
mint: [u8; 32],
owner: [u8; 32],
amount: u64,
}
let (account, iter) = unsafe { iter.static_slurp_typed_account::<TokenAccount>() };
let amount = account.data.amount;
account.data.amount = new_amount;Slurp N accounts of the same type—single pointer cast:
let (accounts, iter) = unsafe { iter.static_slurp_typed_accounts::<TokenAccount, 3>() };
for acc in accounts.iter() {
process(acc.data.amount);
}T: Pod + Zeroable(bytemuck)size_of::<T>() % 8 == 0— 8-byte alignmentdata_len == size_of::<T>()for each account- No duplicate markers in the range
The unsafe methods assume you know the account layout. Slurp wrong type = valid pointer to garbage.
Typical pattern—verify authority first, then trust layout:
let mut iter = unsafe { AccountIterator::new_from_instruction(input) };
let (authority, iter) = unsafe { iter.known_next_full_account() };
if !(is_program_authority(&authority.static_data.key) && authority.static_data.is_signer()) {
return Err(ProgramError::Unauthorized);
}
// Authority verified, trust the rest
let (token, iter) = unsafe { iter.static_slurp_typed_account::<TokenAccount>() };
let (vault, iter) = unsafe { iter.static_slurp_typed_account::<VaultAccount>() };Debug builds verify invariants (counts, no unexpected dups, sizes). Release builds skip checks.
NonDupAccountStatic provides convenience methods:
acc.static_data.is_signer() // -> bool
acc.static_data.is_writable() // -> bool
acc.static_data.is_executable() // -> bool┌─────────────────────────────────────────────┐
│ num_accounts: u64 │
├─────────────────────────────────────────────┤
│ Account 0 │
├─────────────────────────────────────────────┤
│ ... │
├─────────────────────────────────────────────┤
│ instruction_data_len: u64 │
├─────────────────────────────────────────────┤
│ instruction_data: [u8] │
├─────────────────────────────────────────────┤
│ program_id: Pubkey (32 bytes) │
└─────────────────────────────────────────────┘
Each account starts with a marker byte: 0xFF = real account, 0x00-0xFE = duplicate (index of original).
Real account layout:
┌──────────────────────────────────────────────┐
│ NonDupAccountStatic (128 bytes) │
│ is_dup, is_signer, is_writable, executable │
│ original_data_len, key, owner │
│ lamports, data_len │
├──────────────────────────────────────────────┤
│ data: [u8; data_len] │
├──────────────────────────────────────────────┤
│ _buffer: [u8; 10240] (realloc reserve) │
├──────────────────────────────────────────────┤
│ padding to 8-byte alignment │
├──────────────────────────────────────────────┤
│ rent_epoch: u64 │
└──────────────────────────────────────────────┘
pub enum NextHeader {
Header(AccountHeaderCursor<'static>), // Real account header
Dup(usize, AccountIterator), // Duplicate marker + next iterator
Data(&'static mut [u8], &'static Pubkey), // (instruction_data, program_id)
}
pub struct AccountHeaderCursor<'a> {
pub static_data: &'a NonDupAccountStatic,
// ... remaining_accounts_after (internal)
}
pub struct NonDupAccount<'a> {
pub static_data: &'a NonDupAccountStatic,
pub all_data: &'a mut [u8],
pub rent_epoch: &'a u64,
}
pub struct TypedNonDupAccount<T: Pod + Zeroable> {
pub static_data: NonDupAccountStatic,
pub data: T,
// ... 10KB buffer, rent_epoch
}O(1) pubkey lookup for known key sets. Indexes by first byte, so all keys must have unique first bytes.
use peephole::pubkey_byte_map::{pubkey_byte_map, PubkeyMap};
const AUTHORITIES: PubkeyMap = pubkey_byte_map(&[ADMIN_KEY, OPERATOR_KEY]);
if !AUTHORITIES.contains(&signer_key) {
return Err(Unauthorized);
}debug_assert! in debug builds, unreachable_unchecked in release. Use when you know a condition is true but want debug-time verification:
use peephole::assume;
unsafe {
assume!(account_count > 0, "expected at least one account");
}