diff --git a/sdk/program/src/borsh0_9.rs b/sdk/program/src/borsh0_9.rs new file mode 100644 index 00000000000000..81785e38e00f85 --- /dev/null +++ b/sdk/program/src/borsh0_9.rs @@ -0,0 +1,115 @@ +#![allow(clippy::integer_arithmetic)] +//! Utilities for the [borsh] serialization format, version 0.9. +//! +//! This file is provided for backwards compatibility with types that still use +//! borsh 0.9, even though this crate canonically uses borsh 0.10. +//! +//! [borsh]: https://borsh.io/ +use { + borsh0_9::{ + maybestd::io::{Error, Write}, + schema::{BorshSchema, Declaration, Definition, Fields}, + BorshDeserialize, BorshSerialize, + }, + std::collections::HashMap, +}; + +/// Get packed length for the given BorchSchema Declaration +fn get_declaration_packed_len( + declaration: &str, + definitions: &HashMap, +) -> usize { + match definitions.get(declaration) { + Some(Definition::Array { length, elements }) => { + *length as usize * get_declaration_packed_len(elements, definitions) + } + Some(Definition::Enum { variants }) => { + 1 + variants + .iter() + .map(|(_, declaration)| get_declaration_packed_len(declaration, definitions)) + .max() + .unwrap_or(0) + } + Some(Definition::Struct { fields }) => match fields { + Fields::NamedFields(named_fields) => named_fields + .iter() + .map(|(_, declaration)| get_declaration_packed_len(declaration, definitions)) + .sum(), + Fields::UnnamedFields(declarations) => declarations + .iter() + .map(|declaration| get_declaration_packed_len(declaration, definitions)) + .sum(), + Fields::Empty => 0, + }, + Some(Definition::Sequence { + elements: _elements, + }) => panic!("Missing support for Definition::Sequence"), + Some(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}"), + }, + } +} + +/// Get the worst-case packed length for the given BorshSchema +/// +/// Note: due to the serializer currently used by Borsh, this function cannot +/// be used on-chain in the Solana SBF execution environment. +pub fn get_packed_len() -> usize { + let schema_container = S::schema_container(); + get_declaration_packed_len(&schema_container.declaration, &schema_container.definitions) +} + +/// Deserializes without checking that the entire slice has been consumed +/// +/// Normally, `try_from_slice` checks the length of the final slice to ensure +/// that the deserialization uses up all of the bytes in the slice. +/// +/// Note that there is a potential issue with this function. Any buffer greater than +/// or equal to the expected size will properly deserialize. For example, if the +/// user passes a buffer destined for a different type, the error won't get caught +/// as easily. +pub fn try_from_slice_unchecked(data: &[u8]) -> Result { + let mut data_mut = data; + let result = T::deserialize(&mut data_mut)?; + Ok(result) +} + +/// Helper struct which to count how much data would be written during serialization +#[derive(Default)] +struct WriteCounter { + count: usize, +} + +impl Write for WriteCounter { + fn write(&mut self, data: &[u8]) -> Result { + let amount = data.len(); + self.count += amount; + Ok(amount) + } + + fn flush(&mut self) -> Result<(), Error> { + Ok(()) + } +} + +/// Get the packed length for the serialized form of this object instance. +/// +/// Useful when working with instances of types that contain a variable-length +/// sequence, such as a Vec or HashMap. Since it is impossible to know the packed +/// length only from the type's schema, this can be used when an instance already +/// exists, to figure out how much space to allocate in an account. +pub fn get_instance_packed_len(instance: &T) -> Result { + let mut counter = WriteCounter::default(); + instance.serialize(&mut counter)?; + Ok(counter.count) +} diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 2d0a679e4646c1..f35e1304286737 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -477,6 +477,7 @@ pub(crate) mod atomic_u64; pub mod big_mod_exp; pub mod blake3; pub mod borsh; +pub mod borsh0_9; pub mod bpf_loader; pub mod bpf_loader_deprecated; pub mod bpf_loader_upgradeable;