From 0804a16e24d2c8e0ed5e466df195fc358bb91b48 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Tue, 20 Feb 2024 11:33:19 +0000 Subject: [PATCH] Implement `borsh` serialization routines --- src/borsh.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 ++++ 2 files changed, 102 insertions(+) create mode 100644 src/borsh.rs diff --git a/src/borsh.rs b/src/borsh.rs new file mode 100644 index 00000000..9338f435 --- /dev/null +++ b/src/borsh.rs @@ -0,0 +1,96 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "borsh")))] + +use alloc::vec::Vec; +use core::hash::BuildHasher; +use core::hash::Hash; +use core::iter::ExactSizeIterator; +use core::mem::size_of; + +use borsh::error::ERROR_ZST_FORBIDDEN; +use borsh::io::{Error, ErrorKind, Read, Result, Write}; +use borsh::{BorshDeserialize, BorshSerialize}; + +use crate::map::IndexMap; +use crate::set::IndexSet; + +impl BorshSerialize for IndexMap +where + K: BorshSerialize, + V: BorshSerialize, + H: BuildHasher, +{ + #[inline] + fn serialize(&self, writer: &mut W) -> Result<()> { + check_zst::()?; + + let iterator = self.iter(); + + u32::try_from(iterator.len()) + .map_err(|_| ErrorKind::InvalidData)? + .serialize(writer)?; + + for (key, value) in iterator { + key.serialize(writer)?; + value.serialize(writer)?; + } + + Ok(()) + } +} + +impl BorshDeserialize for IndexMap +where + K: BorshDeserialize + Eq + Hash, + V: BorshDeserialize, + H: BuildHasher + Default, +{ + #[inline] + fn deserialize_reader(reader: &mut R) -> Result { + check_zst::()?; + let vec = >::deserialize_reader(reader)?; + Ok(vec.into_iter().collect::>()) + } +} + +impl BorshSerialize for IndexSet +where + T: BorshSerialize, + H: BuildHasher, +{ + #[inline] + fn serialize(&self, writer: &mut W) -> Result<()> { + check_zst::()?; + + let iterator = self.iter(); + + u32::try_from(iterator.len()) + .map_err(|_| ErrorKind::InvalidData)? + .serialize(writer)?; + + for item in iterator { + item.serialize(writer)?; + } + + Ok(()) + } +} + +impl BorshDeserialize for IndexSet +where + T: BorshDeserialize + Eq + Hash, + H: BuildHasher + Default, +{ + #[inline] + fn deserialize_reader(reader: &mut R) -> Result { + check_zst::()?; + let vec = >::deserialize_reader(reader)?; + Ok(vec.into_iter().collect::>()) + } +} + +fn check_zst() -> Result<()> { + if size_of::() == 0 { + return Err(Error::new(ErrorKind::InvalidData, ERROR_ZST_FORBIDDEN)); + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index b88c1bce..d6d3ede9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,8 @@ //! to [`IndexMap`] and [`IndexSet`]. Alternative implementations for //! (de)serializing [`IndexMap`] as an ordered sequence are available in the //! [`map::serde_seq`] module. +//! * `borsh`: Adds implementations for [`BorshSerialize`] and [`BorshDeserialize`] +//! to [`IndexMap`] and [`IndexSet`]. //! * `arbitrary`: Adds implementations for the [`arbitrary::Arbitrary`] trait //! to [`IndexMap`] and [`IndexSet`]. //! * `quickcheck`: Adds implementations for the [`quickcheck::Arbitrary`] trait @@ -46,6 +48,8 @@ //! [`no_std`]: #no-standard-library-targets //! [`Serialize`]: `::serde::Serialize` //! [`Deserialize`]: `::serde::Deserialize` +//! [`BorshSerialize`]: `::borsh::BorshSerialize` +//! [`BorshDeserialize`]: `::borsh::BorshDeserialize` //! [`arbitrary::Arbitrary`]: `::arbitrary::Arbitrary` //! [`quickcheck::Arbitrary`]: `::quickcheck::Arbitrary` //! @@ -110,6 +114,8 @@ use alloc::vec::{self, Vec}; mod arbitrary; #[macro_use] mod macros; +#[cfg(feature = "borsh")] +mod borsh; mod mutable_keys; #[cfg(feature = "serde")] mod serde;