macro_rules! impl_get_packed_len_v0 {
($borsh:ident $(,#[$meta:meta])?) => {
$(#[$meta])?
pub fn get_packed_len<S: $borsh::BorshSchema>() -> usize {
let $borsh::schema::BorshSchemaContainer { declaration, definitions } =
&S::schema_container();
get_declaration_packed_len(declaration, definitions)
}
fn get_declaration_packed_len(
declaration: &str,
definitions: &std::collections::HashMap<$borsh::schema::Declaration, $borsh::schema::Definition>,
) -> usize {
match definitions.get(declaration) {
Some($borsh::schema::Definition::Array { length, elements }) => {
*length as usize * get_declaration_packed_len(elements, definitions)
}
Some($borsh::schema::Definition::Enum { variants }) => {
1 + variants
.iter()
.map(|(_, declaration)| get_declaration_packed_len(declaration, definitions))
.max()
.unwrap_or(0)
}
Some($borsh::schema::Definition::Struct { fields }) => match fields {
$borsh::schema::Fields::NamedFields(named_fields) => named_fields
.iter()
.map(|(_, declaration)| get_declaration_packed_len(declaration, definitions))
.sum(),
$borsh::schema::Fields::UnnamedFields(declarations) => declarations
.iter()
.map(|declaration| get_declaration_packed_len(declaration, definitions))
.sum(),
$borsh::schema::Fields::Empty => 0,
},
Some($borsh::schema::Definition::Sequence {
elements: _elements,
}) => panic!("Missing support for Definition::Sequence"),
Some($borsh::schema::Definition::Tuple { elements }) => elements
.iter()
.map(|element| get_declaration_packed_len(element, definitions))
.sum(),
None => match declaration {
"bool" | "u8" | "i8" => 1,
"u16" | "i16" => 2,
"u32" | "i32" => 4,
"u64" | "i64" => 8,
"u128" | "i128" => 16,
"nil" => 0,
_ => panic!("Missing primitive type: {declaration}"),
},
}
}
}
}
pub(crate) use impl_get_packed_len_v0;
macro_rules! impl_get_packed_len_v1 {
($borsh:ident $(,#[$meta:meta])?) => {
$(#[$meta])?
pub fn get_packed_len<S: $borsh::BorshSchema>() -> usize {
let container = $borsh::schema_container_of::<S>();
get_declaration_packed_len(container.declaration(), &container)
}
fn get_declaration_packed_len(
declaration: &str,
container: &$borsh::schema::BorshSchemaContainer,
) -> usize {
match container.get_definition(declaration) {
Some($borsh::schema::Definition::Sequence { length_width, length_range, elements }) if *length_width == 0 => {
*length_range.end() as usize * get_declaration_packed_len(elements, container)
}
Some($borsh::schema::Definition::Enum { tag_width, variants }) => {
(*tag_width as usize) + variants
.iter()
.map(|(_, _, declaration)| get_declaration_packed_len(declaration, container))
.max()
.unwrap_or(0)
}
Some($borsh::schema::Definition::Struct { fields }) => match fields {
$borsh::schema::Fields::NamedFields(named_fields) => named_fields
.iter()
.map(|(_, declaration)| get_declaration_packed_len(declaration, container))
.sum(),
$borsh::schema::Fields::UnnamedFields(declarations) => declarations
.iter()
.map(|declaration| get_declaration_packed_len(declaration, container))
.sum(),
$borsh::schema::Fields::Empty => 0,
},
Some($borsh::schema::Definition::Sequence {
..
}) => panic!("Missing support for Definition::Sequence"),
Some($borsh::schema::Definition::Tuple { elements }) => elements
.iter()
.map(|element| get_declaration_packed_len(element, container))
.sum(),
Some($borsh::schema::Definition::Primitive(size)) => *size as usize,
None => match declaration {
"bool" | "u8" | "i8" => 1,
"u16" | "i16" => 2,
"u32" | "i32" => 4,
"u64" | "i64" => 8,
"u128" | "i128" => 16,
"nil" => 0,
_ => panic!("Missing primitive type: {declaration}"),
},
}
}
}
}
pub(crate) use impl_get_packed_len_v1;
macro_rules! impl_try_from_slice_unchecked {
($borsh:ident, $borsh_io:ident $(,#[$meta:meta])?) => {
$(#[$meta])?
pub fn try_from_slice_unchecked<T: $borsh::BorshDeserialize>(data: &[u8]) -> Result<T, $borsh_io::Error> {
let mut data_mut = data;
let result = T::deserialize(&mut data_mut)?;
Ok(result)
}
}
}
pub(crate) use impl_try_from_slice_unchecked;
macro_rules! impl_get_instance_packed_len {
($borsh:ident, $borsh_io:ident $(,#[$meta:meta])?) => {
#[derive(Default)]
struct WriteCounter {
count: usize,
}
impl $borsh_io::Write for WriteCounter {
fn write(&mut self, data: &[u8]) -> Result<usize, $borsh_io::Error> {
let amount = data.len();
self.count += amount;
Ok(amount)
}
fn flush(&mut self) -> Result<(), $borsh_io::Error> {
Ok(())
}
}
$(#[$meta])?
pub fn get_instance_packed_len<T: $borsh::BorshSerialize>(instance: &T) -> Result<usize, $borsh_io::Error> {
let mut counter = WriteCounter::default();
instance.serialize(&mut counter)?;
Ok(counter.count)
}
}
}
pub(crate) use impl_get_instance_packed_len;
#[cfg(test)]
macro_rules! impl_tests {
($borsh:ident, $borsh_io:ident) => {
extern crate alloc;
use {
super::*,
std::{collections::HashMap, mem::size_of},
$borsh::{BorshDeserialize, BorshSerialize},
$borsh_io::ErrorKind,
};
type Child = [u8; 64];
type Parent = Vec<Child>;
#[test]
fn unchecked_deserialization() {
let parent = vec![[0u8; 64], [1u8; 64], [2u8; 64]];
let mut byte_vec = vec![0u8; 4 + get_packed_len::<Child>() * 3];
let mut bytes = byte_vec.as_mut_slice();
parent.serialize(&mut bytes).unwrap();
let deserialized = Parent::try_from_slice(&byte_vec).unwrap();
assert_eq!(deserialized, parent);
let deserialized = try_from_slice_unchecked::<Parent>(&byte_vec).unwrap();
assert_eq!(deserialized, parent);
let mut byte_vec = vec![0u8; 4 + get_packed_len::<Child>() * 10];
let mut bytes = byte_vec.as_mut_slice();
parent.serialize(&mut bytes).unwrap();
let err = Parent::try_from_slice(&byte_vec).unwrap_err();
assert_eq!(err.kind(), ErrorKind::InvalidData);
let deserialized = try_from_slice_unchecked::<Parent>(&byte_vec).unwrap();
assert_eq!(deserialized, parent);
}
#[test]
fn packed_len() {
assert_eq!(get_packed_len::<u64>(), size_of::<u64>());
assert_eq!(get_packed_len::<Child>(), size_of::<u8>() * 64);
}
#[test]
fn instance_packed_len_matches_packed_len() {
let child = [0u8; 64];
assert_eq!(
get_packed_len::<Child>(),
get_instance_packed_len(&child).unwrap(),
);
assert_eq!(
get_packed_len::<u8>(),
get_instance_packed_len(&0u8).unwrap(),
);
assert_eq!(
get_packed_len::<u16>(),
get_instance_packed_len(&0u16).unwrap(),
);
assert_eq!(
get_packed_len::<u32>(),
get_instance_packed_len(&0u32).unwrap(),
);
assert_eq!(
get_packed_len::<u64>(),
get_instance_packed_len(&0u64).unwrap(),
);
assert_eq!(
get_packed_len::<u128>(),
get_instance_packed_len(&0u128).unwrap(),
);
assert_eq!(
get_packed_len::<[u8; 10]>(),
get_instance_packed_len(&[0u8; 10]).unwrap(),
);
assert_eq!(
get_packed_len::<(i8, i16, i32, i64, i128)>(),
get_instance_packed_len(&(i8::MAX, i16::MAX, i32::MAX, i64::MAX, i128::MAX))
.unwrap(),
);
}
#[test]
fn instance_packed_len_with_vec() {
let parent = vec![
[0u8; 64], [1u8; 64], [2u8; 64], [3u8; 64], [4u8; 64], [5u8; 64],
];
assert_eq!(
get_instance_packed_len(&parent).unwrap(),
4 + parent.len() * get_packed_len::<Child>()
);
}
#[test]
fn instance_packed_len_with_varying_sizes_in_hashmap() {
let mut data = HashMap::new();
let key1 = "the first string, it's actually really really long".to_string();
let value1 = "".to_string();
let key2 = "second string, shorter".to_string();
let value2 = "a real value".to_string();
let key3 = "third".to_string();
let value3 = "an even longer value".to_string();
data.insert(key1.clone(), value1.clone());
data.insert(key2.clone(), value2.clone());
data.insert(key3.clone(), value3.clone());
assert_eq!(
get_instance_packed_len(&data).unwrap(),
4 + get_instance_packed_len(&key1).unwrap()
+ get_instance_packed_len(&value1).unwrap()
+ get_instance_packed_len(&key2).unwrap()
+ get_instance_packed_len(&value2).unwrap()
+ get_instance_packed_len(&key3).unwrap()
+ get_instance_packed_len(&value3).unwrap()
);
}
};
}
#[cfg(test)]
pub(crate) use impl_tests;