use crate::{error::PinaPodError, pod::*};
pub trait ZcValidate: Copy {
fn validate_ref(value: &Self) -> Result<(), PinaPodError>;
}
impl ZcValidate for u8 {
#[inline(always)]
fn validate_ref(_: &Self) -> Result<(), PinaPodError> {
Ok(())
}
}
impl ZcValidate for i8 {
#[inline(always)]
fn validate_ref(_: &Self) -> Result<(), PinaPodError> {
Ok(())
}
}
macro_rules! impl_zc_validate_trivial {
($($ty:ty),*) => {
$(
impl ZcValidate for $ty {
#[inline(always)]
fn validate_ref(_: &Self) -> Result<(), PinaPodError> { Ok(()) }
}
)*
};
}
impl_zc_validate_trivial!(PodU16, PodU32, PodU64, PodU128, PodI16, PodI32, PodI64, PodI128);
impl<const N: usize> ZcValidate for [u8; N] {
#[inline(always)]
fn validate_ref(_: &Self) -> Result<(), PinaPodError> {
Ok(())
}
}
impl ZcValidate for PodBool {
#[inline(always)]
fn validate_ref(value: &Self) -> Result<(), PinaPodError> {
let byte = unsafe { *(value as *const PodBool as *const u8) };
if byte > 1 {
Err(PinaPodError::InvalidBool)
} else {
Ok(())
}
}
}
impl<const N: usize, const PFX: usize> ZcValidate for PodString<N, PFX> {
#[inline(always)]
fn validate_ref(value: &Self) -> Result<(), PinaPodError> {
let raw_len = value.try_decode_len()?;
if raw_len > N {
return Err(PinaPodError::InvalidLength);
}
let bytes =
unsafe { core::slice::from_raw_parts(value.data.as_ptr() as *const u8, raw_len) };
if core::str::from_utf8(bytes).is_err() {
return Err(PinaPodError::InvalidUtf8);
}
Ok(())
}
}
impl<T: ZcElem, const N: usize, const PFX: usize> ZcValidate for PodVecRepr<T, N, PFX> {
#[inline(always)]
fn validate_ref(value: &Self) -> Result<(), PinaPodError> {
if value.try_decode_len()? > N {
return Err(PinaPodError::InvalidLength);
}
for item in value.as_slice() {
T::validate_ref(item)?;
}
Ok(())
}
}
impl<T: ZcElem, const PFX: usize> ZcValidate for PodOption<T, PFX> {
#[inline(always)]
fn validate_ref(value: &Self) -> Result<(), PinaPodError> {
match value.raw_tag() {
0 => Ok(()),
1 => {
let inner = unsafe { value.assume_init_ref() };
T::validate_ref(inner)
}
_ => Err(PinaPodError::InvalidTag),
}
}
}
pub unsafe trait ZcElem: Copy + ZcValidate {}
unsafe impl ZcElem for u8 {}
unsafe impl ZcElem for i8 {}
unsafe impl ZcElem for PodU16 {}
unsafe impl ZcElem for PodU32 {}
unsafe impl ZcElem for PodU64 {}
unsafe impl ZcElem for PodU128 {}
unsafe impl ZcElem for PodI16 {}
unsafe impl ZcElem for PodI32 {}
unsafe impl ZcElem for PodI64 {}
unsafe impl ZcElem for PodI128 {}
unsafe impl ZcElem for PodBool {}
unsafe impl<const N: usize> ZcElem for [u8; N] {}
unsafe impl<T: ZcElem, const PFX: usize> ZcElem for PodOption<T, PFX> {}
unsafe impl<const N: usize, const PFX: usize> ZcElem for PodString<N, PFX> {}
unsafe impl<T: ZcElem, const N: usize, const PFX: usize> ZcElem for PodVecRepr<T, N, PFX> {}
#[cfg(feature = "solana-address")]
mod solana_address_impls {
use super::*;
const _: () = assert!(core::mem::align_of::<solana_address::Address>() == 1);
impl ZcValidate for solana_address::Address {
#[inline(always)]
fn validate_ref(_: &Self) -> Result<(), PinaPodError> {
Ok(())
}
}
unsafe impl ZcElem for solana_address::Address {}
unsafe impl ZcField for solana_address::Address {
type Pod = solana_address::Address;
}
}
pub trait PinaPod: Sized {}
pub unsafe trait PinaPodFixed: PinaPod {
type Zc: ZcElem;
fn read_exact(data: &[u8]) -> Result<&Self::Zc, PinaPodError> {
Self::validate_exact(data)?;
Ok(unsafe { &*data.as_ptr().cast::<Self::Zc>() })
}
fn read_exact_mut(data: &mut [u8]) -> Result<&mut Self::Zc, PinaPodError> {
Self::validate_exact(data)?;
Ok(unsafe { &mut *data.as_mut_ptr().cast::<Self::Zc>() })
}
fn read_prefix(data: &[u8]) -> Result<&Self::Zc, PinaPodError> {
Self::validate_prefix(data)?;
Ok(unsafe { &*data.as_ptr().cast::<Self::Zc>() })
}
fn read_prefix_mut(data: &mut [u8]) -> Result<&mut Self::Zc, PinaPodError> {
Self::validate_prefix(data)?;
Ok(unsafe { &mut *data.as_mut_ptr().cast::<Self::Zc>() })
}
fn validate_exact(data: &[u8]) -> Result<(), PinaPodError> {
let size = core::mem::size_of::<Self::Zc>();
if data.len() < size {
return Err(PinaPodError::BufferTooSmall);
}
if data.len() != size {
return Err(PinaPodError::InvalidLength);
}
Self::validate_prefix(data)
}
fn validate_prefix(data: &[u8]) -> Result<(), PinaPodError> {
let size = core::mem::size_of::<Self::Zc>();
if data.len() < size {
return Err(PinaPodError::BufferTooSmall);
}
let value = unsafe { &*data.as_ptr().cast::<Self::Zc>() };
<Self::Zc as ZcValidate>::validate_ref(value)
}
fn initialize(
data: &mut [u8],
initialize: impl FnOnce(&mut Self::Zc) -> Result<(), PinaPodError>,
) -> Result<&mut Self::Zc, PinaPodError> {
let size = core::mem::size_of::<Self::Zc>();
if data.len() < size {
return Err(PinaPodError::BufferTooSmall);
}
if data.len() != size {
return Err(PinaPodError::InvalidLength);
}
data.fill(0);
let pointer = data.as_mut_ptr().cast::<Self::Zc>();
let result = {
let value = unsafe { &mut *pointer };
initialize(value).and_then(|()| <Self::Zc as ZcValidate>::validate_ref(value))
};
if let Err(error) = result {
data.fill(0);
return Err(error);
}
Ok(unsafe { &mut *pointer })
}
}
pub unsafe trait PinaPodCompact: PinaPod {
type Header: ZcElem;
const MIN_SIZE: usize;
const MAX_SIZE: usize;
const TAIL_ALIGNMENT: usize;
const HEADER_SIZE: usize;
fn validate_storage_len(size: usize) -> Result<(), PinaPodError> {
if Self::TAIL_ALIGNMENT == 0
|| size < Self::MIN_SIZE
|| size > Self::MAX_SIZE
|| !(size - Self::MIN_SIZE).is_multiple_of(Self::TAIL_ALIGNMENT)
{
return Err(PinaPodError::InvalidLength);
}
Ok(())
}
fn validate(data: &[u8]) -> Result<(), PinaPodError>;
}
pub trait PinaPodPatch<T: PinaPodCompact> {
fn updated_len(&self, data: &[u8]) -> Result<usize, PinaPodError>;
fn update(&self, data: &mut [u8]) -> Result<usize, PinaPodError>;
fn initialize(&self, data: &mut [u8]) -> Result<usize, PinaPodError>;
}
impl<T, P> PinaPodPatch<T> for &P
where
T: PinaPodCompact,
P: PinaPodPatch<T> + ?Sized,
{
fn updated_len(&self, data: &[u8]) -> Result<usize, PinaPodError> {
<P as PinaPodPatch<T>>::updated_len(*self, data)
}
fn update(&self, data: &mut [u8]) -> Result<usize, PinaPodError> {
<P as PinaPodPatch<T>>::update(*self, data)
}
fn initialize(&self, data: &mut [u8]) -> Result<usize, PinaPodError> {
<P as PinaPodPatch<T>>::initialize(*self, data)
}
}
pub unsafe trait ZcField: Sized {
type Pod: ZcElem;
}
#[doc(hidden)]
pub trait IntoPodOption<T: ZcField> {
fn into_pod_option(self) -> PodOption<T::Pod>;
}
impl<T> IntoPodOption<T> for Option<T>
where
T: ZcField,
T::Pod: From<T>,
{
#[inline(always)]
fn into_pod_option(self) -> PodOption<T::Pod> {
match self {
Some(value) => PodOption::some(value.into()),
None => PodOption::none(),
}
}
}
impl<T> IntoPodOption<T> for PodOption<T::Pod>
where
T: ZcField,
{
#[inline(always)]
fn into_pod_option(self) -> PodOption<T::Pod> {
self
}
}
macro_rules! impl_zc_field {
($native:ty, $pod:ty) => {
unsafe impl ZcField for $native {
type Pod = $pod;
}
};
}
impl_zc_field!(u8, u8);
impl_zc_field!(u16, PodU16);
impl_zc_field!(u32, PodU32);
impl_zc_field!(u64, PodU64);
impl_zc_field!(u128, PodU128);
impl_zc_field!(i8, i8);
impl_zc_field!(i16, PodI16);
impl_zc_field!(i32, PodI32);
impl_zc_field!(i64, PodI64);
impl_zc_field!(i128, PodI128);
impl_zc_field!(bool, PodBool);
#[cfg(feature = "fixed")]
mod fixed_impls {
use super::*;
macro_rules! impl_fixed_zc_field {
($fixed:ident, $pod:ty) => {
unsafe impl<Frac> ZcField for fixed::$fixed<Frac> {
type Pod = $pod;
}
};
}
impl_fixed_zc_field!(FixedI8, i8);
impl_fixed_zc_field!(FixedI16, PodI16);
impl_fixed_zc_field!(FixedI32, PodI32);
impl_fixed_zc_field!(FixedI64, PodI64);
impl_fixed_zc_field!(FixedI128, PodI128);
impl_fixed_zc_field!(FixedU8, u8);
impl_fixed_zc_field!(FixedU16, PodU16);
impl_fixed_zc_field!(FixedU32, PodU32);
impl_fixed_zc_field!(FixedU64, PodU64);
impl_fixed_zc_field!(FixedU128, PodU128);
}
unsafe impl<const N: usize> ZcField for [u8; N] {
type Pod = [u8; N];
}
macro_rules! impl_zc_field_identity {
($($ty:ty),*) => {
$(
unsafe impl ZcField for $ty {
type Pod = Self;
}
)*
};
}
impl_zc_field_identity!(PodU16, PodU32, PodU64, PodU128, PodI16, PodI32, PodI64, PodI128, PodBool);
unsafe impl<const N: usize, const PFX: usize> ZcField for PodString<N, PFX> {
type Pod = Self;
}
unsafe impl<T: ZcElem, const N: usize, const PFX: usize> ZcField for PodVecRepr<T, N, PFX> {
type Pod = Self;
}
unsafe impl<T: ZcElem, const PFX: usize> ZcField for PodOption<T, PFX> {
type Pod = Self;
}
unsafe impl<T> ZcField for Option<T>
where
T: ZcField,
{
type Pod = PodOption<T::Pod, 1>;
}