use hopper_runtime::{ProgramError, Address};
macro_rules! impl_cursor_read {
($( $name:ident -> $ty:ty, $size:literal; )*) => {
$(
#[inline(always)]
pub fn $name(&mut self) -> Result<$ty, ProgramError> {
let end = self.pos + $size;
if end > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
let val = <$ty>::from_le_bytes(self.data[self.pos..end].try_into().unwrap());
self.pos = end;
Ok(val)
}
)*
};
}
macro_rules! impl_cursor_write {
($( $name:ident ($ty:ty), $size:literal; )*) => {
$(
#[inline(always)]
pub fn $name(&mut self, val: $ty) -> Result<(), ProgramError> {
let end = self.pos + $size;
if end > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
self.data[self.pos..end].copy_from_slice(&val.to_le_bytes());
self.pos = end;
Ok(())
}
)*
};
}
pub struct SliceCursor<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> SliceCursor<'a> {
#[inline(always)]
pub fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
#[inline(always)]
pub fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.pos)
}
#[inline(always)]
pub fn position(&self) -> usize {
self.pos
}
#[inline(always)]
pub fn read_u8(&mut self) -> Result<u8, ProgramError> {
if self.pos >= self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
let val = self.data[self.pos];
self.pos += 1;
Ok(val)
}
impl_cursor_read! {
read_u16 -> u16, 2;
read_u32 -> u32, 4;
read_u64 -> u64, 8;
read_u128 -> u128, 16;
read_i16 -> i16, 2;
read_i32 -> i32, 4;
read_i64 -> i64, 8;
read_i128 -> i128, 16;
}
#[inline(always)]
pub fn read_bool(&mut self) -> Result<bool, ProgramError> {
Ok(self.read_u8()? != 0)
}
#[inline(always)]
pub fn read_i8(&mut self) -> Result<i8, ProgramError> {
Ok(self.read_u8()? as i8)
}
#[inline(always)]
pub fn read_address(&mut self) -> Result<Address, ProgramError> {
let end = self.pos + 32;
if end > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
let arr: [u8; 32] = self.data[self.pos..end].try_into().unwrap();
self.pos = end;
Ok(arr.into())
}
#[inline(always)]
pub fn skip(&mut self, n: usize) -> Result<(), ProgramError> {
let end = self.pos.checked_add(n).ok_or(ProgramError::AccountDataTooSmall)?;
if end > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
self.pos = end;
Ok(())
}
#[inline(always)]
pub fn data_from_position(&self) -> &'a [u8] {
if self.pos >= self.data.len() {
&[]
} else {
&self.data[self.pos..]
}
}
#[inline(always)]
pub fn from_instruction(data: &'a [u8], min_len: usize) -> Result<Self, ProgramError> {
if data.len() < min_len {
return Err(ProgramError::InvalidInstructionData);
}
Ok(Self { data, pos: 0 })
}
}
pub struct DataWriter<'a> {
data: &'a mut [u8],
pos: usize,
}
impl<'a> DataWriter<'a> {
#[inline(always)]
pub fn new(data: &'a mut [u8]) -> Self {
Self { data, pos: 0 }
}
#[inline(always)]
pub fn written(&self) -> usize {
self.pos
}
#[inline(always)]
pub fn write_u8(&mut self, val: u8) -> Result<(), ProgramError> {
if self.pos >= self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
self.data[self.pos] = val;
self.pos += 1;
Ok(())
}
impl_cursor_write! {
write_u16(u16), 2;
write_u32(u32), 4;
write_u64(u64), 8;
write_u128(u128), 16;
write_i16(i16), 2;
write_i32(i32), 4;
write_i64(i64), 8;
write_i128(i128), 16;
}
#[inline(always)]
pub fn write_bool(&mut self, val: bool) -> Result<(), ProgramError> {
self.write_u8(val as u8)
}
#[inline(always)]
pub fn write_i8(&mut self, val: i8) -> Result<(), ProgramError> {
self.write_u8(val as u8)
}
#[inline(always)]
pub fn write_address(&mut self, addr: &Address) -> Result<(), ProgramError> {
let end = self.pos + 32;
if end > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
self.data[self.pos..end].copy_from_slice(addr.as_array());
self.pos = end;
Ok(())
}
}
#[inline(always)]
pub fn zero_init(data: &mut [u8]) {
data.fill(0);
}
#[inline(always)]
pub fn write_discriminator(data: &mut [u8], discriminator: u8) -> Result<(), ProgramError> {
if data.is_empty() {
return Err(ProgramError::AccountDataTooSmall);
}
data[0] = discriminator;
Ok(())
}