123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- use crate::{AnchorDeserialize, Pubkey};
- /// A helper trait to make lazy deserialization work.
- ///
- /// Currently this is only implemented for [`borsh`], as it's not necessary for zero copy via
- /// [`bytemuck`]. However, the functionality can be extended when we support custom serialization
- /// in the future.
- ///
- /// # Note
- ///
- /// You should avoid implementing this trait manually.
- ///
- /// It's currently implemented automatically if you derive [`AnchorDeserialize`]:
- ///
- /// ```ignore
- /// #[derive(AnchorDeserialize)]
- /// pub struct MyStruct {
- /// field: u8,
- /// }
- /// ```
- pub trait Lazy: AnchorDeserialize {
- /// Get the serialized size of the type from the given buffer.
- ///
- /// For performance reasons, this method does not verify the validity of the data, and should
- /// never fail.
- ///
- /// # Panics
- ///
- /// If the given buffer cannot be used to deserialize the data e.g. it's shorter than the
- /// expected data. However, this doesn't mean it will panic **whenever** there is an incorrect
- /// data e.g. passing **any** data for `bool::size_of` works, even when the buffer is empty.
- fn size_of(buf: &[u8]) -> usize;
- }
- macro_rules! impl_sized {
- ($ty: ty) => {
- impl Lazy for $ty {
- #[inline(always)]
- fn size_of(_buf: &[u8]) -> usize {
- ::core::mem::size_of::<$ty>()
- }
- }
- };
- }
- impl_sized!(bool);
- impl_sized!(u8);
- impl_sized!(u16);
- impl_sized!(u32);
- impl_sized!(u64);
- impl_sized!(u128);
- impl_sized!(i8);
- impl_sized!(i16);
- impl_sized!(i32);
- impl_sized!(i64);
- impl_sized!(i128);
- impl_sized!(f32);
- impl_sized!(f64);
- impl_sized!(Pubkey);
- impl<T: Lazy, const N: usize> Lazy for [T; N] {
- #[inline(always)]
- fn size_of(buf: &[u8]) -> usize {
- N * T::size_of(buf)
- }
- }
- impl Lazy for String {
- #[inline(always)]
- fn size_of(buf: &[u8]) -> usize {
- LEN + get_len(buf)
- }
- }
- impl<T: Lazy> Lazy for Option<T> {
- #[inline(always)]
- fn size_of(buf: &[u8]) -> usize {
- 1 + match buf.first() {
- Some(0) => 0,
- Some(1) => T::size_of(&buf[1..]),
- _ => unreachable!(),
- }
- }
- }
- impl<T: Lazy> Lazy for Vec<T> {
- #[inline(always)]
- fn size_of(buf: &[u8]) -> usize {
- (0..get_len(buf)).fold(LEN, |acc, _| acc + T::size_of(&buf[acc..]))
- }
- }
- /// `borsh` length identifier of unsized types.
- const LEN: usize = 4;
- #[inline(always)]
- fn get_len(buf: &[u8]) -> usize {
- u32::from_le_bytes((buf[..LEN].try_into()).unwrap())
- .try_into()
- .unwrap()
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- use crate::AnchorSerialize;
- macro_rules! len {
- ($val: expr) => {
- $val.try_to_vec().unwrap().len()
- };
- }
- #[test]
- fn sized() {
- // Sized inputs don't care about the passed data
- const EMPTY: &[u8] = &[];
- assert_eq!(bool::size_of(EMPTY), len!(true));
- assert_eq!(u8::size_of(EMPTY), len!(0u8));
- assert_eq!(u16::size_of(EMPTY), len!(0u16));
- assert_eq!(u32::size_of(EMPTY), len!(0u32));
- assert_eq!(u64::size_of(EMPTY), len!(0u64));
- assert_eq!(u128::size_of(EMPTY), len!(0u128));
- assert_eq!(i8::size_of(EMPTY), len!(0i8));
- assert_eq!(i16::size_of(EMPTY), len!(0i16));
- assert_eq!(i32::size_of(EMPTY), len!(0i32));
- assert_eq!(i64::size_of(EMPTY), len!(0i64));
- assert_eq!(i128::size_of(EMPTY), len!(0i128));
- assert_eq!(f32::size_of(EMPTY), len!(0f32));
- assert_eq!(f64::size_of(EMPTY), len!(0f64));
- assert_eq!(Pubkey::size_of(EMPTY), len!(Pubkey::default()));
- assert_eq!(<[i32; 4]>::size_of(EMPTY), len!([0i32; 4]));
- }
- #[test]
- fn r#unsized() {
- assert_eq!(String::size_of(&[1, 0, 0, 0, 65]), len!(String::from("a")));
- assert_eq!(<Option<u8>>::size_of(&[0]), len!(Option::<u8>::None));
- assert_eq!(<Option<u8>>::size_of(&[1, 1]), len!(Some(1u8)));
- assert_eq!(<Vec<u8>>::size_of(&[1, 0, 0, 0, 1]), len!(vec![1u8]));
- assert_eq!(
- <Vec<String>>::size_of(&[1, 0, 0, 0, 1, 0, 0, 0, 65]),
- len!(vec![String::from("a")])
- );
- assert_eq!(
- <Vec<String>>::size_of(&[2, 0, 0, 0, 1, 0, 0, 0, 65, 2, 0, 0, 0, 65, 66]),
- len!(vec![String::from("a"), String::from("ab")])
- );
- }
- #[test]
- fn defined() {
- // Struct
- #[derive(AnchorSerialize, AnchorDeserialize)]
- struct MyStruct {
- a: u8,
- b: Vec<u8>,
- c: Option<String>,
- }
- assert_eq!(
- MyStruct::size_of(&[1, 2, 0, 0, 0, 1, 2, 1, 1, 0, 0, 0, 65]),
- len!(MyStruct {
- a: 1,
- b: vec![1u8, 2],
- c: Some(String::from("a"))
- })
- );
- // Enum
- #[derive(AnchorSerialize, AnchorDeserialize)]
- enum MyEnum {
- Unit,
- Named { a: u8 },
- Unnamed(i16, i16),
- }
- assert_eq!(MyEnum::size_of(&[0]), len!(MyEnum::Unit));
- assert_eq!(MyEnum::size_of(&[1, 23]), len!(MyEnum::Named { a: 1 }));
- assert_eq!(
- MyEnum::size_of(&[2, 1, 2, 1, 2]),
- len!(MyEnum::Unnamed(1, 2))
- );
- }
- #[test]
- fn generic() {
- #[derive(AnchorSerialize, AnchorDeserialize)]
- struct GenericStruct<T: Lazy> {
- t: T,
- }
- assert_eq!(
- GenericStruct::<i64>::size_of(&[1, 2, 3, 4, 5, 6, 7, 8]),
- len!(GenericStruct { t: 1i64 })
- );
- assert_eq!(
- GenericStruct::<Vec<u8>>::size_of(&[8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8]),
- len!(GenericStruct { t: vec![0u8; 8] })
- );
- }
- }
|