use super::header::HEADER_LEN;
use super::pod::{FixedLayout, Pod};
use hopper_runtime::error::ProgramError;
use hopper_runtime::{Ref, RefMut};
enum VerifiedBytes<'a> {
Borrowed(Ref<'a, [u8]>),
Raw(&'a [u8]),
}
impl<'a> VerifiedBytes<'a> {
#[inline(always)]
fn as_slice(&self) -> &[u8] {
match self {
Self::Borrowed(bytes) => bytes,
Self::Raw(bytes) => bytes,
}
}
}
pub struct VerifiedAccount<'a, T: Pod + FixedLayout> {
data: VerifiedBytes<'a>,
_phantom: core::marker::PhantomData<T>,
}
impl<'a, T: Pod + FixedLayout> VerifiedAccount<'a, T> {
#[inline(always)]
pub fn new(data: &'a [u8]) -> Result<Self, ProgramError> {
if data.len() < T::SIZE {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(Self {
data: VerifiedBytes::Raw(data),
_phantom: core::marker::PhantomData,
})
}
#[inline(always)]
pub fn from_ref(data: Ref<'a, [u8]>) -> Result<Self, ProgramError> {
if data.len() < T::SIZE {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(Self {
data: VerifiedBytes::Borrowed(data),
_phantom: core::marker::PhantomData,
})
}
#[inline(always)]
pub fn get(&self) -> &T {
unsafe { &*(self.data().as_ptr() as *const T) }
}
#[inline(always)]
pub fn with<U, F>(&self, f: F) -> U
where
F: FnOnce(&T) -> U,
{
f(self.get())
}
#[inline(always)]
pub fn data(&self) -> &[u8] {
self.data.as_slice()
}
#[inline(always)]
pub fn body(&self) -> &[u8] {
let data = self.data();
if data.len() > HEADER_LEN {
&data[HEADER_LEN..]
} else {
&[]
}
}
#[inline(always)]
pub fn map<U, F>(&self, f: F) -> U
where
F: FnOnce(&T) -> U,
{
f(self.get())
}
#[inline]
pub fn slice(&self, offset: usize, len: usize) -> Result<&[u8], ProgramError> {
let end = offset
.checked_add(len)
.ok_or(ProgramError::ArithmeticOverflow)?;
if end > self.data().len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(&self.data()[offset..end])
}
#[inline]
pub fn overlay_at<U: Pod + FixedLayout>(&self, offset: usize) -> Result<&U, ProgramError> {
let end = offset
.checked_add(U::SIZE)
.ok_or(ProgramError::ArithmeticOverflow)?;
if end > self.data().len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(unsafe { &*(self.data().as_ptr().add(offset) as *const U) })
}
}
enum VerifiedBytesMut<'a> {
Borrowed(RefMut<'a, [u8]>),
Raw(&'a mut [u8]),
}
impl VerifiedBytesMut<'_> {
#[inline(always)]
fn as_slice(&self) -> &[u8] {
match self {
Self::Borrowed(bytes) => bytes,
Self::Raw(bytes) => bytes,
}
}
#[inline(always)]
fn as_mut_slice(&mut self) -> &mut [u8] {
match self {
Self::Borrowed(bytes) => bytes,
Self::Raw(bytes) => bytes,
}
}
}
pub struct VerifiedAccountMut<'a, T: Pod + FixedLayout> {
data: VerifiedBytesMut<'a>,
_phantom: core::marker::PhantomData<T>,
}
impl<'a, T: Pod + FixedLayout> VerifiedAccountMut<'a, T> {
#[inline(always)]
pub fn new(data: &'a mut [u8]) -> Result<Self, ProgramError> {
if data.len() < T::SIZE {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(Self {
data: VerifiedBytesMut::Raw(data),
_phantom: core::marker::PhantomData,
})
}
#[inline(always)]
pub fn from_ref_mut(data: RefMut<'a, [u8]>) -> Result<Self, ProgramError> {
if data.len() < T::SIZE {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(Self {
data: VerifiedBytesMut::Borrowed(data),
_phantom: core::marker::PhantomData,
})
}
#[inline(always)]
pub fn get(&self) -> &T {
unsafe { &*(self.data().as_ptr() as *const T) }
}
#[inline(always)]
pub fn get_mut(&mut self) -> &mut T {
unsafe { &mut *(self.data_mut().as_mut_ptr() as *mut T) }
}
#[inline(always)]
pub fn with<U, F>(&self, f: F) -> U
where
F: FnOnce(&T) -> U,
{
f(self.get())
}
#[inline(always)]
pub fn with_mut<U, F>(&mut self, f: F) -> U
where
F: FnOnce(&mut T) -> U,
{
f(self.get_mut())
}
#[inline(always)]
pub fn data(&self) -> &[u8] {
self.data.as_slice()
}
#[inline(always)]
pub fn data_mut(&mut self) -> &mut [u8] {
self.data.as_mut_slice()
}
#[inline(always)]
pub fn map<U, F>(&self, f: F) -> U
where
F: FnOnce(&T) -> U,
{
f(self.get())
}
#[inline(always)]
pub fn map_mut<U, F>(&mut self, f: F) -> U
where
F: FnOnce(&mut T) -> U,
{
f(self.get_mut())
}
#[inline]
pub fn overlay_at<U: Pod + FixedLayout>(&self, offset: usize) -> Result<&U, ProgramError> {
let end = offset
.checked_add(U::SIZE)
.ok_or(ProgramError::ArithmeticOverflow)?;
if end > self.data().len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(unsafe { &*(self.data().as_ptr().add(offset) as *const U) })
}
#[inline]
pub fn overlay_at_mut<U: Pod + FixedLayout>(
&mut self,
offset: usize,
) -> Result<&mut U, ProgramError> {
let end = offset
.checked_add(U::SIZE)
.ok_or(ProgramError::ArithmeticOverflow)?;
if end > self.data().len() {
return Err(ProgramError::AccountDataTooSmall);
}
Ok(unsafe { &mut *(self.data_mut().as_mut_ptr().add(offset) as *mut U) })
}
}