use hopper_runtime::ProgramError;
use super::cursor::SliceCursor;
use super::header::{AccountHeader, HEADER_LEN};
pub struct AccountReader<'a> {
data: &'a [u8],
body_offset: usize,
}
impl<'a> AccountReader<'a> {
#[inline(always)]
pub fn new(data: &'a [u8]) -> Result<Self, ProgramError> {
if data.len() < HEADER_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(Self {
data,
body_offset: HEADER_LEN,
})
}
#[inline(always)]
pub fn new_checked(
data: &'a [u8],
expected_disc: u8,
min_version: u8,
layout_id: &[u8; 8],
) -> Result<Self, ProgramError> {
if data.len() < HEADER_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
if data[0] != expected_disc {
return Err(ProgramError::InvalidAccountData);
}
if data[1] < min_version {
return Err(ProgramError::InvalidAccountData);
}
if data[4..12] != *layout_id {
return Err(ProgramError::InvalidAccountData);
}
Ok(Self {
data,
body_offset: HEADER_LEN,
})
}
#[inline(always)]
pub fn header(&self) -> &AccountHeader {
unsafe { &*(self.data.as_ptr() as *const AccountHeader) }
}
#[inline(always)]
pub fn discriminator(&self) -> u8 {
self.data[0]
}
#[inline(always)]
pub fn version(&self) -> u8 {
self.data[1]
}
#[inline(always)]
pub fn flags(&self) -> u16 {
u16::from_le_bytes([self.data[2], self.data[3]])
}
#[inline(always)]
pub fn layout_id(&self) -> [u8; 8] {
let mut id = [0u8; 8];
id.copy_from_slice(&self.data[4..12]);
id
}
#[inline(always)]
pub fn body(&self) -> SliceCursor<'a> {
SliceCursor::new(&self.data[self.body_offset..])
}
#[inline(always)]
pub fn body_bytes(&self) -> &'a [u8] {
&self.data[self.body_offset..]
}
#[inline(always)]
pub fn raw(&self) -> &'a [u8] {
self.data
}
#[inline(always)]
pub fn pubkey_at(&self, offset: usize) -> Result<&'a [u8; 32], ProgramError> {
let abs = self.body_offset + offset;
if abs + 32 > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(unsafe { &*(self.data.as_ptr().add(abs) as *const [u8; 32]) })
}
#[inline(always)]
pub fn u64_at(&self, offset: usize) -> Result<u64, ProgramError> {
let abs = self.body_offset + offset;
if abs + 8 > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(u64::from_le_bytes([
self.data[abs],
self.data[abs + 1],
self.data[abs + 2],
self.data[abs + 3],
self.data[abs + 4],
self.data[abs + 5],
self.data[abs + 6],
self.data[abs + 7],
]))
}
#[inline(always)]
pub fn u32_at(&self, offset: usize) -> Result<u32, ProgramError> {
let abs = self.body_offset + offset;
if abs + 4 > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(u32::from_le_bytes([
self.data[abs],
self.data[abs + 1],
self.data[abs + 2],
self.data[abs + 3],
]))
}
#[inline(always)]
pub fn u16_at(&self, offset: usize) -> Result<u16, ProgramError> {
let abs = self.body_offset + offset;
if abs + 2 > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(u16::from_le_bytes([self.data[abs], self.data[abs + 1]]))
}
#[inline(always)]
pub fn u8_at(&self, offset: usize) -> Result<u8, ProgramError> {
let abs = self.body_offset + offset;
if abs >= self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(self.data[abs])
}
#[inline(always)]
pub fn i64_at(&self, offset: usize) -> Result<i64, ProgramError> {
let abs = self.body_offset + offset;
if abs + 8 > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(i64::from_le_bytes([
self.data[abs],
self.data[abs + 1],
self.data[abs + 2],
self.data[abs + 3],
self.data[abs + 4],
self.data[abs + 5],
self.data[abs + 6],
self.data[abs + 7],
]))
}
#[inline(always)]
pub fn bool_at(&self, offset: usize) -> Result<bool, ProgramError> {
Ok(self.u8_at(offset)? != 0)
}
#[inline(always)]
pub fn bytes_at<const N: usize>(&self, offset: usize) -> Result<[u8; N], ProgramError> {
let abs = self.body_offset + offset;
if abs + N > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
let mut out = [0u8; N];
out.copy_from_slice(&self.data[abs..abs + N]);
Ok(out)
}
#[inline(always)]
pub fn body_len(&self) -> usize {
self.data.len().saturating_sub(self.body_offset)
}
}