Skip to main content

agsol_common/
max_serialized_len.rs

1use solana_program::pubkey::Pubkey;
2use std::marker::PhantomData;
3
4/// Trait that provides the maximum length of the serialized byte stream of a
5/// borsh-serializable data structure.
6///
7/// Useful when allocating space for a Solana account upon creation.
8///
9/// # Examples
10/// ```rust
11/// # #[macro_use]
12/// # extern crate agsol_common_derive;
13/// use agsol_common::MaxSerializedLen;
14/// use borsh::{BorshSerialize, BorshDeserialize};
15/// use solana_program::pubkey::Pubkey;
16///
17/// #[derive(BorshSerialize, BorshDeserialize, MaxSerializedLen)]
18/// struct FooStruct {
19///     foo: u64, // max len: 8
20///     bar: i32, // max len: 4
21/// }
22///
23/// #[derive(BorshSerialize, BorshDeserialize, MaxSerializedLen)]
24/// struct BarStruct {
25///     foo: [u8; 32], // max len: 32
26///     #[len(4 + 8 * 2)]
27///     bar: Vec<u16>, // max len: 20
28///     baz: Option<FooStruct>, // max len: 13
29/// }
30///
31/// #[derive(BorshSerialize, BorshDeserialize, MaxSerializedLen)]
32/// enum FooEnum {
33///     Foo { // max len: 40 + 1
34///         a: u64,
35///         b: Pubkey,
36///     },
37///     Bar, // max len: 1
38///     Baz(Option<Pubkey>), // max len: 1 + 1 + 32
39///     #[len(200)]
40///     Quux(String), // max len: 1 + 200
41/// }
42///
43/// # fn main() {
44/// assert_eq!(FooStruct::MAX_SERIALIZED_LEN, 12);
45/// assert_eq!(BarStruct::MAX_SERIALIZED_LEN, 65);
46/// assert_eq!(FooEnum::MAX_SERIALIZED_LEN, 201);
47/// # }
48/// ```
49///
50/// # Notes
51/// Note, that for enums with more than ~15 variants, the compiler hangs due to
52/// the way the maximum lengths of the variants are computed. Therefore, it is
53/// not recommended to derive `MaxSerializedLen` for enums like that, rather it
54/// should be implemented manually.
55pub trait MaxSerializedLen {
56    const MAX_SERIALIZED_LEN: usize;
57}
58
59macro_rules! impl_max_serialized_length {
60    ($this:ty, $len:expr) => {
61        impl MaxSerializedLen for $this {
62            const MAX_SERIALIZED_LEN: usize = $len;
63        }
64    };
65}
66
67impl_max_serialized_length!(bool, 1);
68impl_max_serialized_length!(u8, 1);
69impl_max_serialized_length!(u16, 2);
70impl_max_serialized_length!(u32, 4);
71impl_max_serialized_length!(u64, 8);
72impl_max_serialized_length!(u128, 16);
73impl_max_serialized_length!(i8, 1);
74impl_max_serialized_length!(i16, 2);
75impl_max_serialized_length!(i32, 4);
76impl_max_serialized_length!(i64, 8);
77impl_max_serialized_length!(i128, 16);
78impl_max_serialized_length!(Pubkey, 32);
79impl_max_serialized_length!([u8; 32], 32);
80
81impl<T> MaxSerializedLen for Option<T>
82where
83    T: MaxSerializedLen,
84{
85    const MAX_SERIALIZED_LEN: usize = 1 + T::MAX_SERIALIZED_LEN;
86}
87
88impl<T> MaxSerializedLen for PhantomData<T> {
89    const MAX_SERIALIZED_LEN: usize = 0;
90}
91
92#[cfg(test)]
93mod test {
94    use super::*;
95    use borsh::{BorshDeserialize, BorshSerialize};
96    use solana_program::clock::UnixTimestamp;
97
98    #[derive(BorshSerialize, BorshDeserialize, MaxSerializedLen, Debug)]
99    struct Something {
100        a: u64,
101        b: i32,
102    }
103
104    #[derive(BorshSerialize, BorshDeserialize, MaxSerializedLen, Debug)]
105    struct Dummy {
106        something: Something,
107        #[len(4 + 8 * 2)]
108        c: Vec<u16>,
109    }
110
111    #[repr(C)]
112    #[derive(BorshSerialize, BorshDeserialize, MaxSerializedLen, Debug)]
113    struct DummyOption {
114        a: u64,
115        b: Option<Dummy>,
116    }
117
118    #[test]
119    fn test_derive() {
120        assert_eq!(Dummy::MAX_SERIALIZED_LEN, 32);
121    }
122
123    #[test]
124    fn serialized_lenghts() {
125        let u: UnixTimestamp = 234232;
126        assert_eq!(
127            u.try_to_vec().unwrap().len(),
128            UnixTimestamp::MAX_SERIALIZED_LEN
129        );
130    }
131
132    #[test]
133    fn option_max_serialized_len() {
134        let none: Option<u64> = None;
135        assert!(none.try_to_vec().unwrap().len() <= Option::<u64>::MAX_SERIALIZED_LEN);
136        let none: Option<u64> = Some(15_u64);
137        assert_eq!(
138            none.try_to_vec().unwrap().len(),
139            Option::<u64>::MAX_SERIALIZED_LEN
140        );
141
142        let mut dummy_option = DummyOption {
143            a: u64::MAX,
144            b: None,
145        };
146        assert!(dummy_option.try_to_vec().unwrap().len() <= DummyOption::MAX_SERIALIZED_LEN);
147        dummy_option.b = Some(Dummy {
148            something: Something { a: 0, b: 1456 },
149            c: vec![542; 8],
150        });
151        assert_eq!(
152            dummy_option.try_to_vec().unwrap().len(),
153            DummyOption::MAX_SERIALIZED_LEN
154        );
155    }
156
157    #[derive(BorshSerialize, BorshDeserialize, MaxSerializedLen, Debug)]
158    enum DummyEnum {
159        Hello {
160            a: u64,
161            b: Pubkey,
162        },
163        Bello,
164        Yello(Option<Pubkey>),
165        #[len(200)]
166        Zello(String),
167    }
168
169    #[derive(BorshSerialize, BorshDeserialize, MaxSerializedLen, Debug)]
170    enum OtherEnum {
171        Dummy(DummyEnum),
172        Foo,
173        Bar {
174            #[len(220)]
175            foo: String,
176            bar: u8,
177        },
178        Baz(DummyOption),
179    }
180
181    #[test]
182    fn enum_max_serialized_len() {
183        let en = DummyEnum::Hello {
184            a: 89,
185            b: Pubkey::new_unique(),
186        };
187        assert_eq!(DummyEnum::MAX_SERIALIZED_LEN, 201);
188        assert_eq!(en.try_to_vec().unwrap().len(), 41);
189
190        let en = OtherEnum::Dummy(en);
191        assert_eq!(OtherEnum::MAX_SERIALIZED_LEN, 222);
192        assert_eq!(en.try_to_vec().unwrap().len(), 42);
193
194        let en = OtherEnum::Baz(DummyOption {
195            a: 1234,
196            b: Some(Dummy {
197                something: Something { a: 100, b: 200 },
198                c: vec![2256; 8],
199            }),
200        });
201        assert_eq!(
202            en.try_to_vec().unwrap().len(),
203            1 + DummyOption::MAX_SERIALIZED_LEN
204        );
205    }
206
207    #[derive(MaxSerializedLen, BorshSerialize, BorshDeserialize, Debug)]
208    enum DummyUnitEnum {
209        Hello,
210        Bello,
211        Yello,
212    }
213
214    #[derive(MaxSerializedLen, BorshSerialize, BorshDeserialize, Debug)]
215    struct DummyStructWithArray {
216        foo: [u32; 3],
217        bar: [Option<Pubkey>; 2],
218        baz: DummyUnitEnum,
219    }
220
221    #[test]
222    fn unit_enum_and_arrays() {
223        let mut dummy = DummyStructWithArray {
224            foo: [324, 222, 432224],
225            bar: [Some(Pubkey::new_unique()), Some(Pubkey::new_unique())],
226            baz: DummyUnitEnum::Bello,
227        };
228
229        assert_eq!(
230            dummy.try_to_vec().unwrap().len(),
231            DummyStructWithArray::MAX_SERIALIZED_LEN
232        );
233
234        dummy.bar[0] = None;
235        assert_eq!(
236            dummy.try_to_vec().unwrap().len(),
237            DummyStructWithArray::MAX_SERIALIZED_LEN - 32
238        );
239    }
240
241    #[derive(MaxSerializedLen, BorshDeserialize, BorshSerialize, Debug)]
242    struct GhastlyStruct<T> {
243        foo: u8,
244        bar: Option<Pubkey>,
245        baz: PhantomData<T>,
246    }
247
248    #[test]
249    fn phantom_data() {
250        assert_eq!(
251            GhastlyStruct::<DummyStructWithArray>::MAX_SERIALIZED_LEN,
252            34
253        );
254        assert_eq!(GhastlyStruct::<DummyUnitEnum>::MAX_SERIALIZED_LEN, 34);
255    }
256}