Skip to content

Commit

Permalink
Add backwards-compatible borsh file
Browse files Browse the repository at this point in the history
  • Loading branch information
joncinque committed Jul 25, 2023
1 parent eb14fae commit dede6bc
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 0 deletions.
115 changes: 115 additions & 0 deletions sdk/program/src/borsh0_9.rs
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)
}
1 change: 1 addition & 0 deletions sdk/program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit dede6bc

Please sign in to comment.