-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
116 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Declaration, Definition>, | ||
) -> 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<S: BorshSchema>() -> 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<T: BorshDeserialize>(data: &[u8]) -> Result<T, Error> { | ||
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<usize, Error> { | ||
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<T: BorshSerialize>(instance: &T) -> Result<usize, Error> { | ||
let mut counter = WriteCounter::default(); | ||
instance.serialize(&mut counter)?; | ||
Ok(counter.count) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters