use hopper_runtime::{ProgramError, Address};
use super::header::HEADER_LEN;
macro_rules! impl_writer_body {
($( $name:ident ($ty:ty), $size:literal; )*) => {
$(
#[doc = concat!("Write a ", stringify!($ty), " (LE) to the body at the current position.")]
#[inline(always)]
pub fn $name(&mut self, val: $ty) -> Result<(), ProgramError> {
let abs = self.body_offset + self.body_pos;
if abs + $size > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
self.data[abs..abs + $size].copy_from_slice(&val.to_le_bytes());
self.body_pos += $size;
Ok(())
}
)*
};
}
pub struct AccountWriter<'a> {
data: &'a mut [u8],
body_offset: usize,
body_pos: usize,
}
impl<'a> AccountWriter<'a> {
#[inline(always)]
pub fn new(
data: &'a mut [u8],
discriminator: u8,
version: u8,
layout_id: &[u8; 8],
) -> Result<Self, 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(Self {
data,
body_offset: HEADER_LEN,
body_pos: 0,
})
}
#[inline(always)]
pub fn from_existing(data: &'a mut [u8]) -> Result<Self, ProgramError> {
if data.len() < HEADER_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(Self {
data,
body_offset: HEADER_LEN,
body_pos: 0,
})
}
#[inline(always)]
pub fn set_flags(&mut self, flags: u16) {
let bytes = flags.to_le_bytes();
self.data[2] = bytes[0];
self.data[3] = bytes[1];
}
#[inline(always)]
pub fn write_u8(&mut self, val: u8) -> Result<(), ProgramError> {
let abs = self.body_offset + self.body_pos;
if abs >= self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
self.data[abs] = val;
self.body_pos += 1;
Ok(())
}
impl_writer_body! {
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_address(&mut self, addr: &Address) -> Result<(), ProgramError> {
let abs = self.body_offset + self.body_pos;
if abs + 32 > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
self.data[abs..abs + 32].copy_from_slice(addr.as_ref());
self.body_pos += 32;
Ok(())
}
#[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 written(&self) -> usize {
self.body_pos
}
#[inline(always)]
pub fn body_mut(&mut self) -> &mut [u8] {
&mut self.data[self.body_offset..]
}
#[inline(always)]
pub fn raw(&self) -> &[u8] {
self.data
}
#[inline(always)]
pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ProgramError> {
let abs = self.body_offset + self.body_pos;
if abs + bytes.len() > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
self.data[abs..abs + bytes.len()].copy_from_slice(bytes);
self.body_pos += bytes.len();
Ok(())
}
#[inline(always)]
pub fn skip(&mut self, n: usize) -> Result<(), ProgramError> {
let abs = self.body_offset + self.body_pos;
if abs + n > self.data.len() {
return Err(ProgramError::AccountDataTooSmall);
}
self.body_pos += n;
Ok(())
}
#[inline(always)]
pub fn position(&self) -> usize {
self.body_pos
}
}