miraland_program/
borsh.rs

1//! Utilities for the [borsh] serialization format.
2//!
3//! To avoid backwards-incompatibilities when the Solana SDK changes its dependency
4//! on borsh, it's recommended to instead use the version-specific file directly,
5//! ie. `borsh0_10`.
6//!
7//! This file remains for developers who use these borsh helpers, but it will
8//! be removed in a future release
9//!
10//! [borsh]: https://borsh.io/
11// MI: since Miraland has no backwards-incompatibility issue,
12// use borsh(means borsh1, representing borsh 1.2.1+) directly here.
13// use borsh0_10::{maybestd::io::Error, BorshDeserialize, BorshSchema, BorshSerialize};
14use borsh::{io::Error, BorshDeserialize, BorshSchema, BorshSerialize};
15
16/// Get the worst-case packed length for the given BorshSchema
17///
18/// Note: due to the serializer currently used by Borsh, this function cannot
19/// be used on-chain in the Solana SBF execution environment.
20// MI
21// #[deprecated(
22//     since = "1.17.0",
23//     note = "Please use `borsh0_10::get_packed_len` instead"
24// )]
25pub fn get_packed_len<S: BorshSchema>() -> usize {
26    // #[allow(deprecated)]
27    crate::borsh1::get_packed_len::<S>()
28}
29
30/// Deserializes without checking that the entire slice has been consumed
31///
32/// Normally, `try_from_slice` checks the length of the final slice to ensure
33/// that the deserialization uses up all of the bytes in the slice.
34///
35/// Note that there is a potential issue with this function. Any buffer greater than
36/// or equal to the expected size will properly deserialize. For example, if the
37/// user passes a buffer destined for a different type, the error won't get caught
38/// as easily.
39// MI
40// #[deprecated(
41//     since = "1.17.0",
42//     note = "Please use `borsh0_10::try_from_slice_unchecked` instead"
43// )]
44pub fn try_from_slice_unchecked<T: BorshDeserialize>(data: &[u8]) -> Result<T, Error> {
45    // #[allow(deprecated)]
46    crate::borsh1::try_from_slice_unchecked::<T>(data)
47}
48
49/// Get the packed length for the serialized form of this object instance.
50///
51/// Useful when working with instances of types that contain a variable-length
52/// sequence, such as a Vec or HashMap.  Since it is impossible to know the packed
53/// length only from the type's schema, this can be used when an instance already
54/// exists, to figure out how much space to allocate in an account.
55// MI
56// #[deprecated(
57//     since = "1.17.0",
58//     note = "Please use `borsh0_10::get_instance_packed_len` instead"
59// )]
60pub fn get_instance_packed_len<T: BorshSerialize>(instance: &T) -> Result<usize, Error> {
61    // #[allow(deprecated)]
62    crate::borsh1::get_instance_packed_len(instance)
63}
64
65macro_rules! impl_get_packed_len_v0 {
66    ($borsh:ident $(,#[$meta:meta])?) => {
67        /// Get the worst-case packed length for the given BorshSchema
68        ///
69        /// Note: due to the serializer currently used by Borsh, this function cannot
70        /// be used on-chain in the Solana SBF execution environment.
71        $(#[$meta])?
72        pub fn get_packed_len<S: $borsh::BorshSchema>() -> usize {
73            let $borsh::schema::BorshSchemaContainer { declaration, definitions } =
74                &S::schema_container();
75            get_declaration_packed_len(declaration, definitions)
76        }
77
78        /// Get packed length for the given BorshSchema Declaration
79        fn get_declaration_packed_len(
80            declaration: &str,
81            definitions: &std::collections::HashMap<$borsh::schema::Declaration, $borsh::schema::Definition>,
82        ) -> usize {
83            match definitions.get(declaration) {
84                Some($borsh::schema::Definition::Array { length, elements }) => {
85                    *length as usize * get_declaration_packed_len(elements, definitions)
86                }
87                Some($borsh::schema::Definition::Enum { variants }) => {
88                    1 + variants
89                        .iter()
90                        .map(|(_, declaration)| get_declaration_packed_len(declaration, definitions))
91                        .max()
92                        .unwrap_or(0)
93                }
94                Some($borsh::schema::Definition::Struct { fields }) => match fields {
95                    $borsh::schema::Fields::NamedFields(named_fields) => named_fields
96                        .iter()
97                        .map(|(_, declaration)| get_declaration_packed_len(declaration, definitions))
98                        .sum(),
99                    $borsh::schema::Fields::UnnamedFields(declarations) => declarations
100                        .iter()
101                        .map(|declaration| get_declaration_packed_len(declaration, definitions))
102                        .sum(),
103                    $borsh::schema::Fields::Empty => 0,
104                },
105                Some($borsh::schema::Definition::Sequence {
106                    elements: _elements,
107                }) => panic!("Missing support for Definition::Sequence"),
108                Some($borsh::schema::Definition::Tuple { elements }) => elements
109                    .iter()
110                    .map(|element| get_declaration_packed_len(element, definitions))
111                    .sum(),
112                None => match declaration {
113                    "bool" | "u8" | "i8" => 1,
114                    "u16" | "i16" => 2,
115                    "u32" | "i32" => 4,
116                    "u64" | "i64" => 8,
117                    "u128" | "i128" => 16,
118                    "nil" => 0,
119                    _ => panic!("Missing primitive type: {declaration}"),
120                },
121            }
122        }
123    }
124}
125pub(crate) use impl_get_packed_len_v0;
126
127macro_rules! impl_get_packed_len_v1 {
128    ($borsh:ident $(,#[$meta:meta])?) => {
129        /// Get the worst-case packed length for the given BorshSchema
130        ///
131        /// Note: due to the serializer currently used by Borsh, this function cannot
132        /// be used on-chain in the Solana SBF execution environment.
133        $(#[$meta])?
134        pub fn get_packed_len<S: $borsh::BorshSchema>() -> usize {
135            let container = $borsh::schema_container_of::<S>();
136            get_declaration_packed_len(container.declaration(), &container)
137        }
138
139        /// Get packed length for the given BorshSchema Declaration
140        fn get_declaration_packed_len(
141            declaration: &str,
142            container: &$borsh::schema::BorshSchemaContainer,
143        ) -> usize {
144            match container.get_definition(declaration) {
145                Some($borsh::schema::Definition::Sequence { length_width, length_range, elements }) if *length_width == 0 => {
146                    *length_range.end() as usize * get_declaration_packed_len(elements, container)
147                }
148                Some($borsh::schema::Definition::Enum { tag_width, variants }) => {
149                    (*tag_width as usize) + variants
150                        .iter()
151                        .map(|(_, _, declaration)| get_declaration_packed_len(declaration, container))
152                        .max()
153                        .unwrap_or(0)
154                }
155                Some($borsh::schema::Definition::Struct { fields }) => match fields {
156                    $borsh::schema::Fields::NamedFields(named_fields) => named_fields
157                        .iter()
158                        .map(|(_, declaration)| get_declaration_packed_len(declaration, container))
159                        .sum(),
160                    $borsh::schema::Fields::UnnamedFields(declarations) => declarations
161                        .iter()
162                        .map(|declaration| get_declaration_packed_len(declaration, container))
163                        .sum(),
164                    $borsh::schema::Fields::Empty => 0,
165                },
166                Some($borsh::schema::Definition::Sequence {
167                    ..
168                }) => panic!("Missing support for Definition::Sequence"),
169                Some($borsh::schema::Definition::Tuple { elements }) => elements
170                    .iter()
171                    .map(|element| get_declaration_packed_len(element, container))
172                    .sum(),
173                Some($borsh::schema::Definition::Primitive(size)) => *size as usize,
174                None => match declaration {
175                    "bool" | "u8" | "i8" => 1,
176                    "u16" | "i16" => 2,
177                    "u32" | "i32" => 4,
178                    "u64" | "i64" => 8,
179                    "u128" | "i128" => 16,
180                    "nil" => 0,
181                    _ => panic!("Missing primitive type: {declaration}"),
182                },
183            }
184        }
185    }
186}
187pub(crate) use impl_get_packed_len_v1;
188
189macro_rules! impl_try_from_slice_unchecked {
190    ($borsh:ident, $borsh_io:ident $(,#[$meta:meta])?) => {
191        /// Deserializes without checking that the entire slice has been consumed
192        ///
193        /// Normally, `try_from_slice` checks the length of the final slice to ensure
194        /// that the deserialization uses up all of the bytes in the slice.
195        ///
196        /// Note that there is a potential issue with this function. Any buffer greater than
197        /// or equal to the expected size will properly deserialize. For example, if the
198        /// user passes a buffer destined for a different type, the error won't get caught
199        /// as easily.
200        $(#[$meta])?
201        pub fn try_from_slice_unchecked<T: $borsh::BorshDeserialize>(data: &[u8]) -> Result<T, $borsh_io::Error> {
202            let mut data_mut = data;
203            let result = T::deserialize(&mut data_mut)?;
204            Ok(result)
205        }
206    }
207}
208pub(crate) use impl_try_from_slice_unchecked;
209
210macro_rules! impl_get_instance_packed_len {
211    ($borsh:ident, $borsh_io:ident $(,#[$meta:meta])?) => {
212        /// Helper struct which to count how much data would be written during serialization
213        #[derive(Default)]
214        struct WriteCounter {
215            count: usize,
216        }
217
218        impl $borsh_io::Write for WriteCounter {
219            fn write(&mut self, data: &[u8]) -> Result<usize, $borsh_io::Error> {
220                let amount = data.len();
221                self.count += amount;
222                Ok(amount)
223            }
224
225            fn flush(&mut self) -> Result<(), $borsh_io::Error> {
226                Ok(())
227            }
228        }
229
230        /// Get the packed length for the serialized form of this object instance.
231        ///
232        /// Useful when working with instances of types that contain a variable-length
233        /// sequence, such as a Vec or HashMap.  Since it is impossible to know the packed
234        /// length only from the type's schema, this can be used when an instance already
235        /// exists, to figure out how much space to allocate in an account.
236        $(#[$meta])?
237        pub fn get_instance_packed_len<T: $borsh::BorshSerialize>(instance: &T) -> Result<usize, $borsh_io::Error> {
238            let mut counter = WriteCounter::default();
239            instance.serialize(&mut counter)?;
240            Ok(counter.count)
241        }
242    }
243}
244pub(crate) use impl_get_instance_packed_len;
245
246#[cfg(test)]
247macro_rules! impl_tests {
248    ($borsh:ident, $borsh_io:ident) => {
249        extern crate alloc;
250        use {
251            super::*,
252            std::{collections::HashMap, mem::size_of},
253            $borsh::{BorshDeserialize, BorshSerialize},
254            $borsh_io::ErrorKind,
255        };
256
257        type Child = [u8; 64];
258        type Parent = Vec<Child>;
259
260        #[test]
261        fn unchecked_deserialization() {
262            let parent = vec![[0u8; 64], [1u8; 64], [2u8; 64]];
263
264            // exact size, both work
265            let mut byte_vec = vec![0u8; 4 + get_packed_len::<Child>() * 3];
266            let mut bytes = byte_vec.as_mut_slice();
267            parent.serialize(&mut bytes).unwrap();
268            let deserialized = Parent::try_from_slice(&byte_vec).unwrap();
269            assert_eq!(deserialized, parent);
270            let deserialized = try_from_slice_unchecked::<Parent>(&byte_vec).unwrap();
271            assert_eq!(deserialized, parent);
272
273            // too big, only unchecked works
274            let mut byte_vec = vec![0u8; 4 + get_packed_len::<Child>() * 10];
275            let mut bytes = byte_vec.as_mut_slice();
276            parent.serialize(&mut bytes).unwrap();
277            let err = Parent::try_from_slice(&byte_vec).unwrap_err();
278            assert_eq!(err.kind(), ErrorKind::InvalidData);
279            let deserialized = try_from_slice_unchecked::<Parent>(&byte_vec).unwrap();
280            assert_eq!(deserialized, parent);
281        }
282
283        #[test]
284        fn packed_len() {
285            assert_eq!(get_packed_len::<u64>(), size_of::<u64>());
286            assert_eq!(get_packed_len::<Child>(), size_of::<u8>() * 64);
287        }
288
289        #[test]
290        fn instance_packed_len_matches_packed_len() {
291            let child = [0u8; 64];
292            assert_eq!(
293                get_packed_len::<Child>(),
294                get_instance_packed_len(&child).unwrap(),
295            );
296            assert_eq!(
297                get_packed_len::<u8>(),
298                get_instance_packed_len(&0u8).unwrap(),
299            );
300            assert_eq!(
301                get_packed_len::<u16>(),
302                get_instance_packed_len(&0u16).unwrap(),
303            );
304            assert_eq!(
305                get_packed_len::<u32>(),
306                get_instance_packed_len(&0u32).unwrap(),
307            );
308            assert_eq!(
309                get_packed_len::<u64>(),
310                get_instance_packed_len(&0u64).unwrap(),
311            );
312            assert_eq!(
313                get_packed_len::<u128>(),
314                get_instance_packed_len(&0u128).unwrap(),
315            );
316            assert_eq!(
317                get_packed_len::<[u8; 10]>(),
318                get_instance_packed_len(&[0u8; 10]).unwrap(),
319            );
320            assert_eq!(
321                get_packed_len::<(i8, i16, i32, i64, i128)>(),
322                get_instance_packed_len(&(i8::MAX, i16::MAX, i32::MAX, i64::MAX, i128::MAX))
323                    .unwrap(),
324            );
325        }
326
327        #[test]
328        fn instance_packed_len_with_vec() {
329            let parent = vec![
330                [0u8; 64], [1u8; 64], [2u8; 64], [3u8; 64], [4u8; 64], [5u8; 64],
331            ];
332            assert_eq!(
333                get_instance_packed_len(&parent).unwrap(),
334                4 + parent.len() * get_packed_len::<Child>()
335            );
336        }
337
338        #[test]
339        fn instance_packed_len_with_varying_sizes_in_hashmap() {
340            let mut data = HashMap::new();
341            let key1 = "the first string, it's actually really really long".to_string();
342            let value1 = "".to_string();
343            let key2 = "second string, shorter".to_string();
344            let value2 = "a real value".to_string();
345            let key3 = "third".to_string();
346            let value3 = "an even longer value".to_string();
347            data.insert(key1.clone(), value1.clone());
348            data.insert(key2.clone(), value2.clone());
349            data.insert(key3.clone(), value3.clone());
350            assert_eq!(
351                get_instance_packed_len(&data).unwrap(),
352                4 + get_instance_packed_len(&key1).unwrap()
353                    + get_instance_packed_len(&value1).unwrap()
354                    + get_instance_packed_len(&key2).unwrap()
355                    + get_instance_packed_len(&value2).unwrap()
356                    + get_instance_packed_len(&key3).unwrap()
357                    + get_instance_packed_len(&value3).unwrap()
358            );
359        }
360    };
361}
362#[cfg(test)]
363pub(crate) use impl_tests;