Skip to content

Commit

Permalink
feat: impl BorshSchema for Rc and Arc; add doc for rc feature (
Browse files Browse the repository at this point in the history
…#268)

* chore: improve doc and module structure on `rc` feature

* feat: impl `BorshSchema` for `Rc` and `Arc`
  • Loading branch information
dj8yfo authored Dec 7, 2023
1 parent 2b1f6c9 commit 21e2fc5
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 31 deletions.
4 changes: 2 additions & 2 deletions .github/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ cargo test
cargo test --features unstable__schema,ascii --test test_ascii_strings
cargo test --features derive
cargo test --features unstable__schema
cargo test --test test_rc --features rc
cargo test --test test_rc --features unstable__schema,rc
cargo test --test test_hash_map --test test_btree_map --features de_strict_order

cargo test --no-default-features
cargo test --no-default-features --features unstable__schema,ascii --test test_ascii_strings
cargo test --no-default-features --features derive
cargo test --no-default-features --features unstable__schema
cargo test --no-default-features --test test_rc --features rc
cargo test --no-default-features --test test_rc --features unstable__schema,rc
cargo test --no-default-features --features hashbrown
popd
pushd borsh-derive
Expand Down
5 changes: 4 additions & 1 deletion borsh/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,16 @@ borsh = { path = ".", default_features = false, features = ["bytes", "bson"] }
insta = "1.29.0"

[package.metadata.docs.rs]
features = ["derive", "unstable__schema"]
features = ["derive", "unstable__schema", "rc"]
targets = ["x86_64-unknown-linux-gnu"]

[features]
default = ["std"]
derive = ["borsh-derive"]
unstable__schema = ["derive", "borsh-derive/schema"]
std = []
# Opt into impls for Rc<T> and Arc<T>. Serializing and deserializing these types
# does not preserve identity and may result in multiple copies of the same data.
# Be sure that this is what you want before enabling this feature.
rc = []
de_strict_order = []
49 changes: 33 additions & 16 deletions borsh/src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ use crate::__private::maybestd::{
};
use crate::io::{Error, ErrorKind, Read, Result};

#[cfg(feature = "rc")]
use crate::__private::maybestd::{rc::Rc, sync::Arc};
use crate::error::check_zst;

mod hint;
Expand Down Expand Up @@ -883,23 +881,42 @@ impl_range!(RangeFrom, start.., start);
impl_range!(RangeTo, ..end, end);
impl_range!(RangeToInclusive, ..=end, end);

/// Module is available if borsh is built with `features = ["rc"]`.
#[cfg(feature = "rc")]
impl<T: ?Sized> BorshDeserialize for Rc<T>
where
Box<T>: BorshDeserialize,
{
fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
Ok(Box::<T>::deserialize_reader(reader)?.into())
pub mod rc {
//!
//! Module defines [BorshDeserialize] implementation for
//! [alloc::rc::Rc](std::rc::Rc) and [alloc::sync::Arc](std::sync::Arc).
use crate::__private::maybestd::{boxed::Box, rc::Rc, sync::Arc};
use crate::io::{Read, Result};
use crate::BorshDeserialize;

/// This impl requires the [`"rc"`] Cargo feature of borsh.
///
/// Deserializing a data structure containing `Rc` will not attempt to
/// deduplicate `Rc` references to the same data. Every deserialized `Rc`
/// will end up with a strong count of 1.
impl<T: ?Sized> BorshDeserialize for Rc<T>
where
Box<T>: BorshDeserialize,
{
fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
Ok(Box::<T>::deserialize_reader(reader)?.into())
}
}
}

#[cfg(feature = "rc")]
impl<T: ?Sized> BorshDeserialize for Arc<T>
where
Box<T>: BorshDeserialize,
{
fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
Ok(Box::<T>::deserialize_reader(reader)?.into())
/// This impl requires the [`"rc"`] Cargo feature of borsh.
///
/// Deserializing a data structure containing `Arc` will not attempt to
/// deduplicate `Arc` references to the same data. Every deserialized `Arc`
/// will end up with a strong count of 1.
impl<T: ?Sized> BorshDeserialize for Arc<T>
where
Box<T>: BorshDeserialize,
{
fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
Ok(Box::<T>::deserialize_reader(reader)?.into())
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions borsh/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
Gates implementation of [BorshSerialize] and [BorshDeserialize]
for [`Rc<T>`](std::rc::Rc)/[`Arc<T>`](std::sync::Arc) respectively.
In `no_std` setting `Rc`/`Arc` are pulled from `alloc` crate.
Serializing and deserializing these types
does not preserve identity and may result in multiple copies of the same data.
Be sure that this is what you want before enabling this feature.
* **hashbrown** -
Pulls in [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet) when no `std` is available.
This feature is set to be mutually exclusive with **std** feature.
Expand Down
38 changes: 38 additions & 0 deletions borsh/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,44 @@ where
T::declaration()
}
}
/// Module is available if borsh is built with `features = ["rc"]`.
#[cfg(feature = "rc")]
pub mod rc {
//!
//! Module defines [BorshSchema] implementation for
//! [alloc::rc::Rc](std::rc::Rc) and [alloc::sync::Arc](std::sync::Arc).
use crate::BorshSchema;

use super::{Declaration, Definition};
use crate::__private::maybestd::collections::BTreeMap;
use crate::__private::maybestd::{rc::Rc, sync::Arc};

impl<T> BorshSchema for Rc<T>
where
T: BorshSchema + ?Sized,
{
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
T::add_definitions_recursively(definitions);
}

fn declaration() -> Declaration {
T::declaration()
}
}

impl<T> BorshSchema for Arc<T>
where
T: BorshSchema + ?Sized,
{
fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
T::add_definitions_recursively(definitions);
}

fn declaration() -> Declaration {
T::declaration()
}
}
}

macro_rules! impl_for_renamed_primitives {
($($ty: ty : $name: ident => $size: expr);+) => {
Expand Down
40 changes: 29 additions & 11 deletions borsh/src/ser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ use crate::__private::maybestd::{
use crate::error::check_zst;
use crate::io::{Error, ErrorKind, Result, Write};

#[cfg(feature = "rc")]
use crate::__private::maybestd::{rc::Rc, sync::Arc};

pub(crate) mod helpers;

const FLOAT_NAN_ERR: &str = "For portability reasons we do not allow to serialize NaNs.";
Expand Down Expand Up @@ -600,17 +597,38 @@ impl_range!(RangeFrom, this, &this.start);
impl_range!(RangeTo, this, &this.end);
impl_range!(RangeToInclusive, this, &this.end);

/// Module is available if borsh is built with `features = ["rc"]`.
#[cfg(feature = "rc")]
impl<T: BorshSerialize + ?Sized> BorshSerialize for Rc<T> {
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
(**self).serialize(writer)
pub mod rc {
//!
//! Module defines [BorshSerialize] implementation for
//! [alloc::rc::Rc](std::rc::Rc) and [alloc::sync::Arc](std::sync::Arc).
use crate::__private::maybestd::{rc::Rc, sync::Arc};
use crate::io::{Result, Write};
use crate::BorshSerialize;

/// This impl requires the [`"rc"`] Cargo feature of borsh.
///
/// Serializing a data structure containing `Rc` will serialize a copy of
/// the contents of the `Rc` each time the `Rc` is referenced within the
/// data structure. Serialization will not attempt to deduplicate these
/// repeated data.
impl<T: BorshSerialize + ?Sized> BorshSerialize for Rc<T> {
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
(**self).serialize(writer)
}
}
}

#[cfg(feature = "rc")]
impl<T: BorshSerialize + ?Sized> BorshSerialize for Arc<T> {
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
(**self).serialize(writer)
/// This impl requires the [`"rc"`] Cargo feature of borsh.
///
/// Serializing a data structure containing `Arc` will serialize a copy of
/// the contents of the `Arc` each time the `Arc` is referenced within the
/// data structure. Serialization will not attempt to deduplicate these
/// repeated data.
impl<T: BorshSerialize + ?Sized> BorshSerialize for Arc<T> {
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
(**self).serialize(writer)
}
}
}

Expand Down
73 changes: 72 additions & 1 deletion borsh/tests/test_rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#[cfg(feature = "std")]
pub use std::{rc, sync};

#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
pub use alloc::{rc, sync};
Expand Down Expand Up @@ -44,3 +43,75 @@ fn test_slice_arc() {
let deserialized = from_slice::<sync::Arc<[i32]>>(&serialized).unwrap();
assert_eq!(original, &*deserialized);
}

#[cfg(feature = "unstable__schema")]
mod schema {
use super::{rc, sync};
use alloc::{
collections::BTreeMap,
string::{String, ToString},
};
use borsh::schema::{BorshSchema, Definition};
macro_rules! map(
() => { BTreeMap::new() };
{ $($key:expr => $value:expr),+ } => {
{
let mut m = BTreeMap::new();
$(
m.insert($key.to_string(), $value);
)+
m
}
};
);
fn common_map_i32() -> BTreeMap<String, Definition> {
map! {

"i32" => Definition::Primitive(4)
}
}

fn common_map_slice_i32() -> BTreeMap<String, Definition> {
map! {
"Vec<i32>" => Definition::Sequence {
length_width: Definition::DEFAULT_LENGTH_WIDTH,
length_range: Definition::DEFAULT_LENGTH_RANGE,
elements: "i32".to_string()
},
"i32" => Definition::Primitive(4)
}
}

#[test]
fn test_rc() {
assert_eq!("i32", <rc::Rc<i32> as BorshSchema>::declaration());

let mut actual_defs = map!();
<rc::Rc<i32> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(common_map_i32(), actual_defs);
}

#[test]
fn test_slice_rc() {
assert_eq!("Vec<i32>", <rc::Rc<[i32]> as BorshSchema>::declaration());
let mut actual_defs = map!();
<rc::Rc<[i32]> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(common_map_slice_i32(), actual_defs);
}

#[test]
fn test_arc() {
assert_eq!("i32", <sync::Arc<i32> as BorshSchema>::declaration());
let mut actual_defs = map!();
<sync::Arc<i32> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(common_map_i32(), actual_defs);
}

#[test]
fn test_slice_arc() {
assert_eq!("Vec<i32>", <sync::Arc<[i32]> as BorshSchema>::declaration());
let mut actual_defs = map!();
<sync::Arc<[i32]> as BorshSchema>::add_definitions_recursively(&mut actual_defs);
assert_eq!(common_map_slice_i32(), actual_defs);
}
}

0 comments on commit 21e2fc5

Please sign in to comment.