use hopper_runtime::ProgramError;
pub const HEADER_LEN: usize = 16;
pub const HEADER_FORMAT: u8 = 1;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct AccountHeader {
pub discriminator: u8,
pub version: u8,
pub flags: u16,
pub layout_id: [u8; 8],
pub reserved: [u8; 4],
}
impl AccountHeader {
#[inline(always)]
pub fn from_bytes(data: &[u8]) -> Result<&Self, ProgramError> {
if data.len() < HEADER_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(unsafe { &*(data.as_ptr() as *const Self) })
}
#[inline(always)]
pub fn from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, ProgramError> {
if data.len() < HEADER_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(unsafe { &mut *(data.as_mut_ptr() as *mut Self) })
}
}
#[inline(always)]
pub fn write_header(
data: &mut [u8],
discriminator: u8,
version: u8,
layout_id: &[u8; 8],
) -> Result<(), ProgramError> {
if data.len() < HEADER_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
data[0] = discriminator;
data[1] = version;
data[2] = 0;
data[3] = 0;
data[4] = layout_id[0];
data[5] = layout_id[1];
data[6] = layout_id[2];
data[7] = layout_id[3];
data[8] = layout_id[4];
data[9] = layout_id[5];
data[10] = layout_id[6];
data[11] = layout_id[7];
data[12] = 0;
data[13] = 0;
data[14] = 0;
data[15] = 0;
Ok(())
}
#[inline(always)]
pub fn check_header(
data: &[u8],
expected_discriminator: u8,
min_version: u8,
layout_id: &[u8; 8],
) -> Result<(), ProgramError> {
if data.len() < HEADER_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
if data[0] != expected_discriminator {
return Err(ProgramError::InvalidAccountData);
}
if data[1] < min_version {
return Err(ProgramError::InvalidAccountData);
}
if data[4..12] != *layout_id {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline(always)]
pub fn read_version(data: &[u8]) -> Result<u8, ProgramError> {
if data.len() < 2 {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(data[1])
}
#[inline(always)]
pub fn read_header_flags(data: &[u8]) -> Result<u16, ProgramError> {
if data.len() < 4 {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(u16::from_le_bytes([data[2], data[3]]))
}
#[inline(always)]
pub fn read_layout_id(data: &[u8]) -> Result<[u8; 8], ProgramError> {
if data.len() < 12 {
return Err(ProgramError::AccountDataTooSmall);
}
let mut id = [0u8; 8];
id.copy_from_slice(&data[4..12]);
Ok(id)
}
#[inline(always)]
pub fn check_layout_id(data: &[u8], expected: &[u8; 8]) -> Result<(), ProgramError> {
if data.len() < 12 {
return Err(ProgramError::AccountDataTooSmall);
}
if data[4..12] != *expected {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
#[inline(always)]
pub fn header_payload(data: &[u8]) -> Result<&[u8], ProgramError> {
if data.len() < HEADER_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(&data[HEADER_LEN..])
}
#[inline(always)]
pub fn header_payload_mut(data: &mut [u8]) -> Result<&mut [u8], ProgramError> {
if data.len() < HEADER_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(&mut data[HEADER_LEN..])
}
#[inline(always)]
pub fn body(data: &[u8]) -> Result<&[u8], ProgramError> {
header_payload(data)
}
#[inline(always)]
pub fn body_mut(data: &mut [u8]) -> Result<&mut [u8], ProgramError> {
header_payload_mut(data)
}
unsafe impl super::pod::Pod for AccountHeader {}
impl super::pod::FixedLayout for AccountHeader {
const SIZE: usize = HEADER_LEN;
}