From 21e2fc595c23b109061e9f5e53b87e4d5fef5a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= <26653921+dj8yfo@users.noreply.github.com> Date: Thu, 7 Dec 2023 20:26:18 +0200 Subject: [PATCH 1/5] feat: impl `BorshSchema` for `Rc` and `Arc`; add doc for `rc` feature (#268) * chore: improve doc and module structure on `rc` feature * feat: impl `BorshSchema` for `Rc` and `Arc` --- .github/test.sh | 4 +-- borsh/Cargo.toml | 5 ++- borsh/src/de/mod.rs | 49 +++++++++++++++++++--------- borsh/src/lib.rs | 3 ++ borsh/src/schema.rs | 38 ++++++++++++++++++++++ borsh/src/ser/mod.rs | 40 ++++++++++++++++------- borsh/tests/test_rc.rs | 73 +++++++++++++++++++++++++++++++++++++++++- 7 files changed, 181 insertions(+), 31 deletions(-) diff --git a/.github/test.sh b/.github/test.sh index 3816b3e34..0d935f1ae 100755 --- a/.github/test.sh +++ b/.github/test.sh @@ -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 diff --git a/borsh/Cargo.toml b/borsh/Cargo.toml index 8e9114353..a241eeefa 100644 --- a/borsh/Cargo.toml +++ b/borsh/Cargo.toml @@ -45,7 +45,7 @@ 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] @@ -53,5 +53,8 @@ default = ["std"] derive = ["borsh-derive"] unstable__schema = ["derive", "borsh-derive/schema"] std = [] +# Opt into impls for Rc and Arc. 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 = [] diff --git a/borsh/src/de/mod.rs b/borsh/src/de/mod.rs index bb6630c99..ab3b1f30f 100644 --- a/borsh/src/de/mod.rs +++ b/borsh/src/de/mod.rs @@ -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; @@ -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 BorshDeserialize for Rc -where - Box: BorshDeserialize, -{ - fn deserialize_reader(reader: &mut R) -> Result { - Ok(Box::::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 BorshDeserialize for Rc + where + Box: BorshDeserialize, + { + fn deserialize_reader(reader: &mut R) -> Result { + Ok(Box::::deserialize_reader(reader)?.into()) + } } -} -#[cfg(feature = "rc")] -impl BorshDeserialize for Arc -where - Box: BorshDeserialize, -{ - fn deserialize_reader(reader: &mut R) -> Result { - Ok(Box::::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 BorshDeserialize for Arc + where + Box: BorshDeserialize, + { + fn deserialize_reader(reader: &mut R) -> Result { + Ok(Box::::deserialize_reader(reader)?.into()) + } } } diff --git a/borsh/src/lib.rs b/borsh/src/lib.rs index 3b7cdc99b..c163751fc 100644 --- a/borsh/src/lib.rs +++ b/borsh/src/lib.rs @@ -35,6 +35,9 @@ Gates implementation of [BorshSerialize] and [BorshDeserialize] for [`Rc`](std::rc::Rc)/[`Arc`](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. diff --git a/borsh/src/schema.rs b/borsh/src/schema.rs index 5c0825037..ffefc653f 100644 --- a/borsh/src/schema.rs +++ b/borsh/src/schema.rs @@ -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 BorshSchema for Rc + where + T: BorshSchema + ?Sized, + { + fn add_definitions_recursively(definitions: &mut BTreeMap) { + T::add_definitions_recursively(definitions); + } + + fn declaration() -> Declaration { + T::declaration() + } + } + + impl BorshSchema for Arc + where + T: BorshSchema + ?Sized, + { + fn add_definitions_recursively(definitions: &mut BTreeMap) { + T::add_definitions_recursively(definitions); + } + + fn declaration() -> Declaration { + T::declaration() + } + } +} macro_rules! impl_for_renamed_primitives { ($($ty: ty : $name: ident => $size: expr);+) => { diff --git a/borsh/src/ser/mod.rs b/borsh/src/ser/mod.rs index 86da9bd72..55e2e3c00 100644 --- a/borsh/src/ser/mod.rs +++ b/borsh/src/ser/mod.rs @@ -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."; @@ -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 BorshSerialize for Rc { - fn serialize(&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 BorshSerialize for Rc { + fn serialize(&self, writer: &mut W) -> Result<()> { + (**self).serialize(writer) + } } -} -#[cfg(feature = "rc")] -impl BorshSerialize for Arc { - fn serialize(&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 BorshSerialize for Arc { + fn serialize(&self, writer: &mut W) -> Result<()> { + (**self).serialize(writer) + } } } diff --git a/borsh/tests/test_rc.rs b/borsh/tests/test_rc.rs index ba1e3f9f1..010e9dc1c 100644 --- a/borsh/tests/test_rc.rs +++ b/borsh/tests/test_rc.rs @@ -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}; @@ -44,3 +43,75 @@ fn test_slice_arc() { let deserialized = from_slice::>(&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 { + map! { + + "i32" => Definition::Primitive(4) + } + } + + fn common_map_slice_i32() -> BTreeMap { + map! { + "Vec" => 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", as BorshSchema>::declaration()); + + let mut actual_defs = map!(); + as BorshSchema>::add_definitions_recursively(&mut actual_defs); + assert_eq!(common_map_i32(), actual_defs); + } + + #[test] + fn test_slice_rc() { + assert_eq!("Vec", as BorshSchema>::declaration()); + let mut actual_defs = map!(); + as BorshSchema>::add_definitions_recursively(&mut actual_defs); + assert_eq!(common_map_slice_i32(), actual_defs); + } + + #[test] + fn test_arc() { + assert_eq!("i32", as BorshSchema>::declaration()); + let mut actual_defs = map!(); + as BorshSchema>::add_definitions_recursively(&mut actual_defs); + assert_eq!(common_map_i32(), actual_defs); + } + + #[test] + fn test_slice_arc() { + assert_eq!("Vec", as BorshSchema>::declaration()); + let mut actual_defs = map!(); + as BorshSchema>::add_definitions_recursively(&mut actual_defs); + assert_eq!(common_map_slice_i32(), actual_defs); + } +} From 2e6dad673842afcb0c765dd7361a0ad12e02e5ef Mon Sep 17 00:00:00 2001 From: Vlad Frolov Date: Mon, 18 Dec 2023 15:11:48 +0100 Subject: [PATCH 2/5] chore: release (#269) Signed-off-by: Vlad Frolov <304265+frol@users.noreply.github.com> Co-authored-by: Vlad Frolov <304265+frol@users.noreply.github.com> --- CHANGELOG.md | 5 +++++ Cargo.toml | 2 +- borsh/Cargo.toml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb0c22f42..57591ede4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.3.0](https://github.com/near/borsh-rs/compare/borsh-v1.2.1...borsh-v1.3.0) - 2023-12-07 + +### Added +- impl `BorshSchema` for `Rc` and `Arc`; add doc for `rc` feature ([#268](https://github.com/near/borsh-rs/pull/268)) + ## [1.2.1](https://github.com/near/borsh-rs/compare/borsh-v1.2.0...borsh-v1.2.1) - 2023-12-06 ### Other diff --git a/Cargo.toml b/Cargo.toml index 803f51435..90c2c3f66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,5 @@ members = ["borsh", "borsh-derive", "fuzz/fuzz-run", "benchmarks"] [workspace.package] # shared version of all public crates in the workspace -version = "1.2.1" +version = "1.3.0" rust-version = "1.66.0" diff --git a/borsh/Cargo.toml b/borsh/Cargo.toml index a241eeefa..f8245863b 100644 --- a/borsh/Cargo.toml +++ b/borsh/Cargo.toml @@ -28,7 +28,7 @@ cfg_aliases = "0.1.0" [dependencies] ascii = { version = "1.1", optional = true } -borsh-derive = { path = "../borsh-derive", version = "~1.2.1", optional = true } +borsh-derive = { path = "../borsh-derive", version = "~1.3.0", optional = true } # hashbrown can be used in no-std context. # NOTE: There is no reason to restrict use of older versions, but we don't want to get From b3149578ebdec358682b60b2e5c5e2d8580593c4 Mon Sep 17 00:00:00 2001 From: Seth Westphal Date: Wed, 10 Jan 2024 07:48:03 -0600 Subject: [PATCH 3/5] chore: bump `proc-macro-crate` to `3`; bump MSRV to `1.67` (#274) * Update proc-macro-crate. * Update CHANGELOG. * Bump checkout version. * Bump MSRV to 1.67.0. * PR feedback. --- .github/workflows/rust.yml | 21 +++++++-------------- Cargo.toml | 2 +- README.md | 6 +++--- borsh-derive/Cargo.toml | 2 +- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c2d377f6b..670d42ac6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,11 +20,11 @@ jobs: tests: strategy: matrix: - rust_version: [1.66.0, stable] + rust_version: [1.67.0, stable] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install Rust toolchain @@ -36,13 +36,6 @@ jobs: run: rustup default ${{ matrix.rust_version }} - name: print rustc version run: rustc --version - # remove this step when MSRV >= 1.67.0 - - name: downgrade `toml_edit`, time`, `toml_datetime` crate to support older Rust toolchain - if: matrix.rust_version == '1.66.0' - run: | - cargo update -p toml_edit --precise 0.20.2 - cargo update -p toml_datetime --precise 0.6.3 - cargo update -p time --precise 0.3.23 - name: Run tests run: ./.github/test.sh @@ -50,7 +43,7 @@ jobs: test_exhaustive_checks: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install nightly for exhaustive check tests uses: dtolnay/rust-toolchain@nightly # a failure on this check means, that some of `syn` crate's enums have been extended @@ -64,7 +57,7 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Run clippy run: cargo clippy --features unstable__schema --benches -- -D clippy::all @@ -72,7 +65,7 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Run cargo fmt run: cargo fmt --check @@ -83,7 +76,7 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: run cargo doc run: RUSTDOCFLAGS="-D warnings" cargo doc --features derive,unstable__schema @@ -93,7 +86,7 @@ jobs: if: github.ref == 'refs/heads/master' steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index 90c2c3f66..725317b3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,4 @@ members = ["borsh", "borsh-derive", "fuzz/fuzz-run", "benchmarks"] [workspace.package] # shared version of all public crates in the workspace version = "1.3.0" -rust-version = "1.66.0" +rust-version = "1.67.0" diff --git a/README.md b/README.md index 96ff7fbfd..bb04fd5a1 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Borsh in Rust   [![Latest Version]][crates.io] [![borsh: rustc 1.66+]][Rust 1.66] [![License Apache-2.0 badge]][License Apache-2.0] [![License MIT badge]][License MIT] +# Borsh in Rust   [![Latest Version]][crates.io] [![borsh: rustc 1.67+]][Rust 1.67] [![License Apache-2.0 badge]][License Apache-2.0] [![License MIT badge]][License MIT] [Borsh]: https://borsh.io [Latest Version]: https://img.shields.io/crates/v/borsh.svg [crates.io]: https://crates.io/crates/borsh -[borsh: rustc 1.66+]: https://img.shields.io/badge/rustc-1.66+-lightgray.svg -[Rust 1.66]: https://blog.rust-lang.org/2022/12/15/Rust-1.66.0.html +[borsh: rustc 1.67+]: https://img.shields.io/badge/rustc-1.67+-lightgray.svg +[Rust 1.67]: https://blog.rust-lang.org/2023/01/26/Rust-1.67.0.html [License Apache-2.0 badge]: https://img.shields.io/badge/license-Apache2.0-blue.svg [License Apache-2.0]: https://opensource.org/licenses/Apache-2.0 [License MIT badge]: https://img.shields.io/badge/license-MIT-blue.svg diff --git a/borsh-derive/Cargo.toml b/borsh-derive/Cargo.toml index 7e8c5a3ef..fb87bccf9 100644 --- a/borsh-derive/Cargo.toml +++ b/borsh-derive/Cargo.toml @@ -19,7 +19,7 @@ proc-macro = true [dependencies] syn = { version = "2", features = ["full", "fold"] } -proc-macro-crate = "2" +proc-macro-crate = "3" proc-macro2 = "1" quote = "1" once_cell = "1.18.0" From 5adcd340f13a3e6b6cffdc524b2b658659548649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dj8yf0=CE=BCl?= <26653921+dj8yfo@users.noreply.github.com> Date: Wed, 10 Jan 2024 16:42:14 +0200 Subject: [PATCH 4/5] ci: fix clippy (#275) --- borsh-derive/src/internals/deserialize/structs/mod.rs | 2 +- borsh/src/schema/container_ext/max_size.rs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/borsh-derive/src/internals/deserialize/structs/mod.rs b/borsh-derive/src/internals/deserialize/structs/mod.rs index 61b11bf0b..837a38a11 100644 --- a/borsh-derive/src/internals/deserialize/structs/mod.rs +++ b/borsh-derive/src/internals/deserialize/structs/mod.rs @@ -22,7 +22,7 @@ pub fn process(input: &ItemStruct, cratename: Path) -> syn::Result } } Fields::Unnamed(fields) => { - for (_field_idx, field) in fields.unnamed.iter().enumerate() { + for field in fields.unnamed.iter() { deserialize::process_field(field, &cratename, &mut body, &mut generics_output)?; } quote! { diff --git a/borsh/src/schema/container_ext/max_size.rs b/borsh/src/schema/container_ext/max_size.rs index 782955a87..a6bebb793 100644 --- a/borsh/src/schema/container_ext/max_size.rs +++ b/borsh/src/schema/container_ext/max_size.rs @@ -103,9 +103,7 @@ fn max_serialized_size_impl<'a>( Ok(Definition::Primitive(size)) => match size { 0 => Ok(0), size => { - let count_sizes = usize::try_from(*size) - .ok() - .and_then(|size| size.checked_mul(count.get())); + let count_sizes = usize::from(*size).checked_mul(count.get()); count_sizes.ok_or(Error::Overflow) } }, From 19e9feee24749cbcb3226e09bf0df03059ee02a0 Mon Sep 17 00:00:00 2001 From: Vlad Frolov Date: Wed, 10 Jan 2024 16:23:27 +0100 Subject: [PATCH 5/5] chore: release (#276) Signed-off-by: Vlad Frolov <304265+frol@users.noreply.github.com> Co-authored-by: Vlad Frolov <304265+frol@users.noreply.github.com> --- CHANGELOG.md | 6 ++++++ Cargo.toml | 2 +- borsh/Cargo.toml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57591ede4..cebf3566b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.3.1](https://github.com/near/borsh-rs/compare/borsh-v1.3.0...borsh-v1.3.1) - 2024-01-10 + +### Other +- fix clippy ([#275](https://github.com/near/borsh-rs/pull/275)) +- bump `proc-macro-crate` to `3`; bump MSRV to `1.67` ([#274](https://github.com/near/borsh-rs/pull/274)) + ## [1.3.0](https://github.com/near/borsh-rs/compare/borsh-v1.2.1...borsh-v1.3.0) - 2023-12-07 ### Added diff --git a/Cargo.toml b/Cargo.toml index 725317b3d..528b36ab5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,5 @@ members = ["borsh", "borsh-derive", "fuzz/fuzz-run", "benchmarks"] [workspace.package] # shared version of all public crates in the workspace -version = "1.3.0" +version = "1.3.1" rust-version = "1.67.0" diff --git a/borsh/Cargo.toml b/borsh/Cargo.toml index f8245863b..7ccf3941f 100644 --- a/borsh/Cargo.toml +++ b/borsh/Cargo.toml @@ -28,7 +28,7 @@ cfg_aliases = "0.1.0" [dependencies] ascii = { version = "1.1", optional = true } -borsh-derive = { path = "../borsh-derive", version = "~1.3.0", optional = true } +borsh-derive = { path = "../borsh-derive", version = "~1.3.1", optional = true } # hashbrown can be used in no-std context. # NOTE: There is no reason to restrict use of older versions, but we don't want to get