├── .gitignore ├── tests ├── max_encoded_len_ui │ ├── not_encode.rs │ ├── union.stderr │ ├── union.rs │ ├── incomplete_attr.rs │ ├── crate_str.rs │ ├── missing_crate_specifier.rs │ ├── unsupported_variant.rs │ ├── unsupported_field.rs │ ├── not_mel.rs │ ├── pass │ │ └── codec_crate.rs │ ├── unsupported_encoded_as_field.rs │ ├── not_encode.stderr │ ├── unsupported_field.stderr │ ├── unsupported_variant.stderr │ ├── unsupported_encoded_as_field.stderr │ ├── not_mel.stderr │ ├── crate_str.stderr │ ├── incomplete_attr.stderr │ └── missing_crate_specifier.stderr ├── scale_codec_ui │ ├── codec_invalid_index.rs │ ├── codec_duplicate_index.rs │ ├── codec_discriminiant_variant_counted_in_default_index.rs │ ├── pass │ │ ├── encode-no-implicit-prelude.rs │ │ └── decode-no-implicit-prelude.rs │ ├── codec_invalid_index.stderr │ ├── codec_duplicate_index.stderr │ └── codec_discriminiant_variant_counted_in_default_index.stderr ├── decode_with_mem_tracking_ui │ ├── trait_bound_not_satisfied.rs │ └── trait_bound_not_satisfied.stderr ├── variant_number.rs ├── scale_codec_ui.rs ├── bitflags.rs ├── decode_with_mem_tracking_ui.rs ├── max_encoded_len_ui.rs ├── clippy.rs ├── bounds.rs ├── type_inference.rs ├── size_hint.rs ├── skip.rs ├── chain-error.rs ├── single_field_struct_encoding.rs ├── mem_tracking.rs └── max_encoded_len.rs ├── .github ├── dependabot.yml └── workflows │ ├── release-bot.yml │ ├── publish.yml │ └── ci.yml ├── .editorconfig ├── fuzzer ├── README.md ├── Cargo.toml └── src │ └── main.rs ├── rustfmt.toml ├── RELEASE.md ├── derive ├── Cargo.toml └── src │ ├── max_encoded_len.rs │ ├── trait_bounds.rs │ └── decode.rs ├── CODEOWNERS ├── scripts └── ci │ └── pre_cache.sh ├── src ├── joiner.rs ├── keyedvec.rs ├── decode_finished.rs ├── generic_array.rs ├── mem_tracking.rs ├── decode_all.rs ├── error.rs ├── btree_utils.rs ├── depth_limit.rs ├── const_encoded_len.rs ├── counted_input.rs ├── lib.rs ├── max_encoded_len.rs ├── encode_like.rs ├── bit_vec.rs └── encode_append.rs ├── Cargo.toml ├── benches └── benches.rs ├── README.md ├── CHANGELOG.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .DS* 3 | .*.swp 4 | .idea 5 | .vscode 6 | .DS_Store 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/not_encode.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{MaxEncodedLen}; 2 | 3 | #[derive(MaxEncodedLen)] 4 | struct NotEncode; 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/union.stderr: -------------------------------------------------------------------------------- 1 | error: Union types are not supported. 2 | --> tests/max_encoded_len_ui/union.rs:4:1 3 | | 4 | 4 | union Union { 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/union.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{Encode, MaxEncodedLen}; 2 | 3 | #[derive(Encode, MaxEncodedLen)] 4 | union Union { 5 | a: u8, 6 | b: u16, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/incomplete_attr.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{Encode, MaxEncodedLen}; 2 | 3 | #[derive(Encode, MaxEncodedLen)] 4 | #[codec(crate)] 5 | struct Example; 6 | 7 | fn main() { 8 | let _ = Example::max_encoded_len(); 9 | } 10 | -------------------------------------------------------------------------------- /tests/scale_codec_ui/codec_invalid_index.rs: -------------------------------------------------------------------------------- 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 2 | #[codec(crate = ::parity_scale_codec)] 3 | enum T { 4 | A = 3, 5 | #[codec(index = 524)] 6 | B, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/crate_str.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{Encode, MaxEncodedLen}; 2 | 3 | #[derive(Encode, MaxEncodedLen)] 4 | #[codec(crate = "parity_scale_codec")] 5 | struct Example; 6 | 7 | fn main() { 8 | let _ = Example::max_encoded_len(); 9 | } 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: '/' 6 | schedule: 7 | interval: daily 8 | - package-ecosystem: cargo 9 | directory: '/' 10 | schedule: 11 | interval: 'daily' 12 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/missing_crate_specifier.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{Encode, MaxEncodedLen}; 2 | 3 | #[derive(Encode, MaxEncodedLen)] 4 | #[codec(parity_scale_codec)] 5 | struct Example; 6 | 7 | fn main() { 8 | let _ = Example::max_encoded_len(); 9 | } 10 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/unsupported_variant.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{Encode, MaxEncodedLen}; 2 | 3 | #[derive(Encode)] 4 | struct NotMel; 5 | 6 | #[derive(Encode, MaxEncodedLen)] 7 | enum UnsupportedVariant { 8 | NotMel(NotMel), 9 | } 10 | 11 | fn main() {} 12 | -------------------------------------------------------------------------------- /tests/decode_with_mem_tracking_ui/trait_bound_not_satisfied.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{Decode, DecodeWithMemTracking}; 2 | 3 | #[derive(Decode)] 4 | struct Base {} 5 | 6 | #[derive(Decode, DecodeWithMemTracking)] 7 | struct Wrapper { 8 | base: Base, 9 | } 10 | 11 | fn main() {} 12 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/unsupported_field.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{Encode, MaxEncodedLen}; 2 | 3 | #[derive(Encode)] 4 | struct NotMel; 5 | 6 | #[derive(Encode, MaxEncodedLen)] 7 | struct UnsupportedField { 8 | mel: u32, 9 | not_mel: NotMel, 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/not_mel.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{Encode, MaxEncodedLen}; 2 | 3 | #[derive(Encode)] 4 | struct NotMel; 5 | 6 | #[derive(Encode, MaxEncodedLen)] 7 | struct Generic { 8 | t: T, 9 | } 10 | 11 | fn main() { 12 | let _ = Generic::::max_encoded_len(); 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | indent_style=tab 4 | indent_size=tab 5 | tab_width=4 6 | end_of_line=lf 7 | charset=utf-8 8 | trim_trailing_whitespace=true 9 | max_line_length=120 10 | insert_final_newline=true 11 | 12 | [*.{yml,sh}] 13 | indent_style=space 14 | indent_size=2 15 | tab_width=8 16 | end_of_line=lf 17 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/pass/codec_crate.rs: -------------------------------------------------------------------------------- 1 | //! This test case demonstrates correct use of the `#[codec(crate = path)]` attribute. 2 | 3 | use parity_scale_codec::{self as codec, Encode, MaxEncodedLen}; 4 | 5 | #[derive(Encode, MaxEncodedLen)] 6 | #[codec(crate = codec)] 7 | struct Example; 8 | 9 | fn main() { 10 | let _ = Example::max_encoded_len(); 11 | } 12 | -------------------------------------------------------------------------------- /tests/variant_number.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::Encode; 2 | use parity_scale_codec_derive::Encode as DeriveEncode; 3 | 4 | #[test] 5 | fn skipped_variant_not_counted_in_default_index() { 6 | #[derive(DeriveEncode)] 7 | enum T { 8 | #[codec(skip)] 9 | A, 10 | B, 11 | } 12 | 13 | assert_eq!(T::A.encode(), vec![]); 14 | assert_eq!(T::B.encode(), vec![0]); 15 | } 16 | -------------------------------------------------------------------------------- /fuzzer/README.md: -------------------------------------------------------------------------------- 1 | # Parity SCALE Codec fuzzer 2 | 3 | ## Requirements: 4 | 5 | Make sure you have the requirements installed: 6 | 7 | https://github.com/rust-fuzz/honggfuzz-rs#dependencies 8 | 9 | Install [honggfuzz-rs](https://github.com/rust-fuzz/honggfuzz-rs): 10 | ``` 11 | cargo install honggfuzz 12 | ``` 13 | 14 | Run the fuzzer: 15 | ``` 16 | cargo hfuzz run codec-fuzzer 17 | ``` 18 | -------------------------------------------------------------------------------- /tests/scale_codec_ui/codec_duplicate_index.rs: -------------------------------------------------------------------------------- 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 2 | #[codec(crate = ::parity_scale_codec)] 3 | enum T { 4 | A = 3, 5 | #[codec(index = 3)] 6 | B, 7 | } 8 | 9 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 10 | #[codec(crate = ::parity_scale_codec)] 11 | enum T1 { 12 | A, 13 | #[codec(index = 0)] 14 | B, 15 | } 16 | 17 | fn main() {} 18 | -------------------------------------------------------------------------------- /tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs: -------------------------------------------------------------------------------- 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 2 | #[codec(crate = ::parity_scale_codec)] 3 | enum T { 4 | A = 1, 5 | B, 6 | } 7 | 8 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 9 | #[codec(crate = ::parity_scale_codec)] 10 | enum T2 { 11 | #[codec(index = 1)] 12 | A, 13 | B, 14 | } 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /fuzzer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "codec-fuzzer" 3 | version = "0.1.0" 4 | authors = ["Parity Technologies ", "Vincent Ulitzsch "] 5 | edition = "2021" 6 | rust-version = "1.56.1" 7 | publish = false 8 | 9 | [dependencies] 10 | parity-scale-codec = { path = "..", features = ["derive", "bit-vec", "fuzz"] } 11 | honggfuzz = "0.5.58" 12 | arbitrary = { version = "1.4.2", features = ["derive"] } 13 | bitvec = { version = "1", features = ["alloc"] } 14 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/unsupported_encoded_as_field.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{Encode, EncodeAsRef, MaxEncodedLen}; 2 | 3 | #[derive(Encode)] 4 | struct NotMel; 5 | 6 | impl<'a> EncodeAsRef<'a, u32> for NotMel { 7 | // Obviously broken but will do for this test 8 | type RefType = &'a u32; 9 | } 10 | 11 | #[derive(Encode, MaxEncodedLen)] 12 | struct UnsupportedEncodedAsField { 13 | mel: u32, 14 | #[codec(encoded_as = "NotMel")] 15 | not_mel: u32, 16 | } 17 | 18 | fn main() {} 19 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Basic 2 | hard_tabs = true 3 | max_width = 100 4 | use_small_heuristics = "Max" 5 | # Imports 6 | imports_granularity = "Crate" 7 | reorder_imports = true 8 | # Consistency 9 | newline_style = "Unix" 10 | # Format comments 11 | comment_width = 100 12 | wrap_comments = true 13 | # Misc 14 | chain_width = 80 15 | spaces_around_ranges = false 16 | binop_separator = "Back" 17 | reorder_impl_items = false 18 | match_arm_leading_pipes = "Preserve" 19 | match_arm_blocks = false 20 | match_block_trailing_comma = true 21 | trailing_comma = "Vertical" 22 | trailing_semicolon = false 23 | use_field_init_shorthand = true 24 | -------------------------------------------------------------------------------- /tests/scale_codec_ui/pass/encode-no-implicit-prelude.rs: -------------------------------------------------------------------------------- 1 | #![no_implicit_prelude] 2 | 3 | #[derive(::parity_scale_codec::Encode)] 4 | #[codec(crate = ::parity_scale_codec)] 5 | pub struct Struct { 6 | field_1: i8, 7 | field_2: i16, 8 | field_3: i32, 9 | field_4: i64, 10 | } 11 | 12 | #[derive(::parity_scale_codec::Encode)] 13 | #[codec(crate = ::parity_scale_codec)] 14 | pub enum Enum { 15 | Variant1, 16 | Variant2(i8, i16, i32, i64), 17 | Variant3 { 18 | field_1: i8, 19 | field_2: i16, 20 | field_3: i32, 21 | field_4: i64, 22 | } 23 | } 24 | 25 | fn main() {} 26 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | ## Release process 2 | 3 | 1. Update the version `[workspace.package]` in `Cargo.toml`. 4 | 2. Update the version of `parity-scale-codec-derive` dependency for `parity-scale-codec` in `Cargo.toml` 5 | (`parity-scale-codec-derive = { .., version = "=x.y.z", ...}`). 6 | 3. Test the new release on `polkadot-sdk`, ensure `parity-scale-codec-derive` is also updated when testing. 7 | 4. Update the `CHANGELOG.md`. 8 | 5. Merge all these changes to master. 9 | 6. Add and push a git tag with the version number: `git tag "vX.Y.Z"; git push --tags`. 10 | 7. Publish on `crates.io` the package `parity-scale-codec-derive` and then `parity-scale-codec`. 11 | -------------------------------------------------------------------------------- /tests/scale_codec_ui/pass/decode-no-implicit-prelude.rs: -------------------------------------------------------------------------------- 1 | #![no_implicit_prelude] 2 | 3 | #[derive(::parity_scale_codec::Decode)] 4 | #[codec(crate = ::parity_scale_codec)] 5 | pub struct Struct { 6 | field_1: i8, 7 | field_2: i16, 8 | field_3: i32, 9 | field_4: i64, 10 | } 11 | 12 | #[derive(::parity_scale_codec::Decode)] 13 | #[repr(transparent)] 14 | struct Transparent { 15 | a: u8 16 | } 17 | 18 | #[derive(::parity_scale_codec::Decode)] 19 | #[codec(crate = ::parity_scale_codec)] 20 | pub enum Enum { 21 | Variant1, 22 | Variant2(i8, i16, i32, i64), 23 | Variant3 { 24 | field_1: i8, 25 | field_2: i16, 26 | field_3: i32, 27 | field_4: i64, 28 | } 29 | } 30 | 31 | fn main() {} 32 | -------------------------------------------------------------------------------- /.github/workflows/release-bot.yml: -------------------------------------------------------------------------------- 1 | name: Pushes release updates to a pre-defined Matrix room 2 | on: 3 | release: 4 | types: 5 | - edited 6 | - prereleased 7 | - published 8 | jobs: 9 | ping_matrix: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: send message 13 | uses: s3krit/matrix-message-action@70ad3fb812ee0e45ff8999d6af11cafad11a6ecf # v0.0.3 14 | with: 15 | room_id: ${{ secrets.MATRIX_ROOM_ID }} 16 | access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }} 17 | message: "**${{github.event.repository.full_name}}:** A release has been ${{github.event.action}}
Release version [${{github.event.release.tag_name}}](${{github.event.release.html_url}})

***Description:***
${{github.event.release.body}}
" 18 | server: "matrix.parity.io" 19 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish release to crates.io 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | publish-crate: 10 | runs-on: ubuntu-latest 11 | environment: release 12 | container: paritytech/ci-unified:bullseye-1.79.0 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 16 | 17 | - name: Cache Rust dependencies 18 | uses: swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 19 | with: 20 | key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} 21 | 22 | - name: Publish Crate 23 | run: | 24 | cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} -p parity-scale-codec-derive 25 | cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} -p parity-scale-codec 26 | -------------------------------------------------------------------------------- /derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parity-scale-codec-derive" 3 | description = "Serialization and deserialization derive macro for Parity SCALE Codec" 4 | version.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | edition.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | syn = { version = "2", features = ["full", "visit"] } 16 | quote = "1.0.41" 17 | proc-macro2 = "1.0.101" 18 | proc-macro-crate = "3.3.0" 19 | 20 | [dev-dependencies] 21 | parity-scale-codec = { path = "..", features = ["derive", "max-encoded-len"] } 22 | 23 | [features] 24 | # Enables the new `MaxEncodedLen` trait. 25 | # NOTE: This is still considered experimental and is exempt from the usual 26 | # SemVer guarantees. We do not guarantee no code breakage when using this. 27 | max-encoded-len = [] 28 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/not_encode.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `NotEncode: Encode` is not satisfied 2 | --> tests/max_encoded_len_ui/not_encode.rs:4:8 3 | | 4 | 4 | struct NotEncode; 5 | | ^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NotEncode`, which is required by `NotEncode: Encode` 6 | | 7 | = help: the following other types implement trait `WrapperTypeEncode`: 8 | &T 9 | &mut T 10 | Arc 11 | Box 12 | Cow<'_, T> 13 | Rc 14 | String 15 | Vec 16 | parity_scale_codec::Ref<'_, T, U> 17 | = note: required for `NotEncode` to implement `Encode` 18 | note: required by a bound in `MaxEncodedLen` 19 | --> src/max_encoded_len.rs 20 | | 21 | | pub trait MaxEncodedLen: Encode { 22 | | ^^^^^^ required by this bound in `MaxEncodedLen` 23 | -------------------------------------------------------------------------------- /tests/scale_codec_ui.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020-2021 Parity Technologies (UK) Ltd. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #[test] 17 | #[cfg(feature = "derive")] 18 | fn scale_codec_ui_tests() { 19 | let t = trybuild::TestCases::new(); 20 | t.compile_fail("tests/scale_codec_ui/*.rs"); 21 | t.pass("tests/scale_codec_ui/pass/*.rs"); 22 | } 23 | -------------------------------------------------------------------------------- /tests/bitflags.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017, 2018 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use parity_scale_codec_derive::{Decode, Encode}; 16 | 17 | // test for regression 18 | #[enumflags2::bitflags] 19 | #[repr(u64)] 20 | #[derive(Copy, Clone, Encode, Decode)] 21 | pub enum EnumWithU64ReprAndBitflags { 22 | Variant1, 23 | Variant2, 24 | Variant3, 25 | Variant4, 26 | } 27 | -------------------------------------------------------------------------------- /tests/decode_with_mem_tracking_ui.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020-2021 Parity Technologies (UK) Ltd. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #[test] 17 | #[cfg(feature = "derive")] 18 | fn derive_no_bound_ui() { 19 | let t = trybuild::TestCases::new(); 20 | t.compile_fail("tests/decode_with_mem_tracking_ui/*.rs"); 21 | t.pass("tests/decode_with_mem_tracking_ui/pass/*.rs"); 22 | } 23 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020-2021 Parity Technologies (UK) Ltd. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #[test] 17 | #[cfg(all(feature = "derive", feature = "max-encoded-len"))] 18 | fn derive_no_bound_ui() { 19 | let t = trybuild::TestCases::new(); 20 | t.compile_fail("tests/max_encoded_len_ui/*.rs"); 21 | t.pass("tests/max_encoded_len_ui/pass/*.rs"); 22 | } 23 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lists some code owners. 2 | # 3 | # A codeowner just oversees some part of the codebase. If an owned file is changed then the 4 | # corresponding codeowner receives a review request. An approval of the codeowner might be 5 | # required for merging a PR (depends on repository settings). 6 | # 7 | # For details about syntax, see: 8 | # https://help.github.com/en/articles/about-code-owners 9 | # But here are some important notes: 10 | # 11 | # - Glob syntax is git-like, e.g. `/core` means the core directory in the root, unlike `core` 12 | # which can be everywhere. 13 | # - Multiple owners are supported. 14 | # - Either handle (e.g, @github_user or @github_org/team) or email can be used. Keep in mind, 15 | # that handles might work better because they are more recognizable on GitHub, 16 | # eyou can use them for mentioning unlike an email. 17 | # - The latest matching rule, if multiple, takes precedence. 18 | 19 | # CI 20 | /.github/ @paritytech/ci 21 | /scripts/ @paritytech/ci 22 | /.gitlab-ci.yml @paritytech/ci 23 | -------------------------------------------------------------------------------- /scripts/ci/pre_cache.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -u 4 | 5 | # if there is no directory for this $CI_COMMIT_REF_NAME/$CI_JOB_NAME 6 | # create such directory and 7 | # copy recursively all the files from the newest dir which has $CI_JOB_NAME, if it exists 8 | 9 | # cache lives in /ci-cache/${CI_PROJECT_NAME}/${2}/${CI_COMMIT_REF_NAME}/${CI_JOB_NAME} 10 | 11 | function prepopulate { 12 | if [[ ! -d $1 ]]; then 13 | mkdir -p "/ci-cache/$CI_PROJECT_NAME/$2/$CI_COMMIT_REF_NAME"; 14 | FRESH_CACHE=$(find "/ci-cache/$CI_PROJECT_NAME/$2" -mindepth 2 -maxdepth 2 \ 15 | -type d -name "$CI_JOB_NAME" -exec stat --printf="%Y\t%n\n" {} \; |sort -n -r |head -1 |cut -f2); 16 | if [[ -d $FRESH_CACHE ]]; then 17 | echo "____Using" "$FRESH_CACHE" "to prepopulate the cache____"; 18 | time cp -r "$FRESH_CACHE" "$1"; 19 | else 20 | echo "_____No such $2 dir, proceeding from scratch_____"; 21 | fi 22 | else 23 | echo "____No need to prepopulate $2 cache____"; 24 | fi 25 | } 26 | 27 | prepopulate "$CARGO_TARGET_DIR" targets 28 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/unsupported_field.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `NotMel: MaxEncodedLen` is not satisfied 2 | --> tests/max_encoded_len_ui/unsupported_field.rs:9:11 3 | | 4 | 9 | not_mel: NotMel, 5 | | ^^^^^^ the trait `MaxEncodedLen` is not implemented for `NotMel` 6 | | 7 | = help: the following other types implement trait `MaxEncodedLen`: 8 | () 9 | (TupleElement0, TupleElement1) 10 | (TupleElement0, TupleElement1, TupleElement2) 11 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3) 12 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) 13 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) 14 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) 15 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) 16 | and $N others 17 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/unsupported_variant.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `NotMel: MaxEncodedLen` is not satisfied 2 | --> tests/max_encoded_len_ui/unsupported_variant.rs:8:9 3 | | 4 | 8 | NotMel(NotMel), 5 | | ^^^^^^ the trait `MaxEncodedLen` is not implemented for `NotMel` 6 | | 7 | = help: the following other types implement trait `MaxEncodedLen`: 8 | () 9 | (TupleElement0, TupleElement1) 10 | (TupleElement0, TupleElement1, TupleElement2) 11 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3) 12 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) 13 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) 14 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) 15 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) 16 | and $N others 17 | -------------------------------------------------------------------------------- /tests/clippy.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) Parity Technologies (UK) Ltd. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! This file is checked by clippy to make sure that the code generated by the derive macro 17 | //! doesn't spew out warnings/errors in users' code. 18 | 19 | use parity_scale_codec_derive::{Decode, DecodeWithMemTracking, Encode}; 20 | 21 | #[repr(u8)] 22 | #[derive(Decode, DecodeWithMemTracking, Encode)] 23 | pub enum CLike { 24 | Foo = 0, 25 | Bar = 1, 26 | } 27 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/unsupported_encoded_as_field.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `NotMel: MaxEncodedLen` is not satisfied 2 | --> tests/max_encoded_len_ui/unsupported_encoded_as_field.rs:14:23 3 | | 4 | 14 | #[codec(encoded_as = "NotMel")] 5 | | ^^^^^^^^ the trait `MaxEncodedLen` is not implemented for `NotMel` 6 | | 7 | = help: the following other types implement trait `MaxEncodedLen`: 8 | () 9 | (TupleElement0, TupleElement1) 10 | (TupleElement0, TupleElement1, TupleElement2) 11 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3) 12 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) 13 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) 14 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) 15 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) 16 | and $N others 17 | -------------------------------------------------------------------------------- /tests/bounds.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017, 2018 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use parity_scale_codec::{Decode, Encode, HasCompact}; 16 | use parity_scale_codec_derive::{Decode as DeriveDecode, Encode as DeriveEncode}; 17 | 18 | #[test] 19 | fn ensure_derive_macro_derives_bounds_correctly() { 20 | #[derive(DeriveEncode, DeriveDecode)] 21 | pub struct Header { 22 | #[codec(compact)] 23 | pub number: Number, 24 | } 25 | 26 | trait _IsEncodeDecode: Encode + Decode {} 27 | 28 | impl _IsEncodeDecode for Header {} 29 | } 30 | -------------------------------------------------------------------------------- /src/joiner.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017, 2018 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Trait 16 | 17 | use core::iter::Extend; 18 | 19 | use crate::codec::Codec; 20 | 21 | /// Trait to allow itself to be serialised into a value which can be extended 22 | /// by bytes. 23 | pub trait Joiner { 24 | /// Append encoding of value to `Self`. 25 | fn and(self, value: &V) -> Self; 26 | } 27 | 28 | impl Joiner for T 29 | where 30 | T: for<'a> Extend<&'a u8>, 31 | { 32 | fn and(mut self, value: &V) -> Self { 33 | value.using_encoded(|s| self.extend(s)); 34 | self 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/not_mel.stderr: -------------------------------------------------------------------------------- 1 | error[E0599]: the function or associated item `max_encoded_len` exists for struct `Generic`, but its trait bounds were not satisfied 2 | --> tests/max_encoded_len_ui/not_mel.rs:12:29 3 | | 4 | 4 | struct NotMel; 5 | | ------------- doesn't satisfy `NotMel: MaxEncodedLen` 6 | ... 7 | 7 | struct Generic { 8 | | ----------------- function or associated item `max_encoded_len` not found for this struct because it doesn't satisfy `Generic: MaxEncodedLen` 9 | ... 10 | 12 | let _ = Generic::::max_encoded_len(); 11 | | ^^^^^^^^^^^^^^^ function or associated item cannot be called on `Generic` due to unsatisfied trait bounds 12 | | 13 | = note: trait bound `NotMel: MaxEncodedLen` was not satisfied 14 | note: the trait `MaxEncodedLen` must be implemented 15 | --> src/max_encoded_len.rs 16 | | 17 | | pub trait MaxEncodedLen: Encode { 18 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 19 | = help: items from traits can only be used if the trait is implemented and in scope 20 | = note: the following trait defines an item `max_encoded_len`, perhaps you need to implement it: 21 | candidate #1: `MaxEncodedLen` 22 | -------------------------------------------------------------------------------- /src/keyedvec.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017, 2018 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Serializer and prepender. 16 | 17 | use core::iter::Extend; 18 | 19 | use crate::{alloc::vec::Vec, codec::Codec}; 20 | 21 | /// Trait to allow itself to be serialised and prepended by a given slice. 22 | pub trait KeyedVec { 23 | /// Return an encoding of `Self` prepended by given slice. 24 | fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec; 25 | } 26 | 27 | impl KeyedVec for T { 28 | fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec { 29 | self.using_encoded(|slice| { 30 | let mut r = prepend_key.to_vec(); 31 | r.extend(slice); 32 | r 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/scale_codec_ui/codec_invalid_index.stderr: -------------------------------------------------------------------------------- 1 | error[E0080]: evaluation of constant value failed 2 | --> tests/scale_codec_ui/codec_invalid_index.rs:1:10 3 | | 4 | 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variant `B` with invalid index: `524`. Max supported index is 255.', $DIR/tests/scale_codec_ui/codec_invalid_index.rs:1:10 6 | | 7 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | 9 | error[E0080]: evaluation of constant value failed 10 | --> tests/scale_codec_ui/codec_invalid_index.rs:1:40 11 | | 12 | 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 13 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variant `B` with invalid index: `524`. Max supported index is 255.', $DIR/tests/scale_codec_ui/codec_invalid_index.rs:1:40 14 | | 15 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | -------------------------------------------------------------------------------- /tests/type_inference.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Test for type inference issue in decode. 16 | 17 | use parity_scale_codec::Decode; 18 | use parity_scale_codec_derive::{ 19 | Decode as DeriveDecode, DecodeWithMemTracking as DeriveDecodeWithMemTracking, 20 | }; 21 | 22 | pub trait Trait { 23 | type Value; 24 | type AccountId: Decode; 25 | } 26 | 27 | #[derive(DeriveDecode, DeriveDecodeWithMemTracking)] 28 | pub enum A { 29 | _C((T::AccountId, T::AccountId), Vec<(T::Value, T::Value)>), 30 | } 31 | 32 | #[derive(DeriveDecode, DeriveDecodeWithMemTracking)] 33 | pub struct B((T::AccountId, T::AccountId), #[allow(dead_code)] Vec<(T::Value, T::Value)>); 34 | -------------------------------------------------------------------------------- /tests/decode_with_mem_tracking_ui/trait_bound_not_satisfied.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `Base: DecodeWithMemTracking` is not satisfied 2 | --> tests/decode_with_mem_tracking_ui/trait_bound_not_satisfied.rs:8:8 3 | | 4 | 8 | base: Base, 5 | | ^^^^ the trait `DecodeWithMemTracking` is not implemented for `Base` 6 | | 7 | = help: the following other types implement trait `DecodeWithMemTracking`: 8 | () 9 | (TupleElement0, TupleElement1) 10 | (TupleElement0, TupleElement1, TupleElement2) 11 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3) 12 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) 13 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) 14 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) 15 | (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) 16 | and $N others 17 | note: required by a bound in `check_field` 18 | --> tests/decode_with_mem_tracking_ui/trait_bound_not_satisfied.rs:6:18 19 | | 20 | 6 | #[derive(Decode, DecodeWithMemTracking)] 21 | | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check_field` 22 | = note: this error originates in the derive macro `DecodeWithMemTracking` (in Nightly builds, run with -Z macro-backtrace for more info) 23 | -------------------------------------------------------------------------------- /src/decode_finished.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) Parity Technologies (UK) Ltd. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Contains the [`DecodeFinished`] type, sequestered into its own module 17 | //! to prevent its direct construction in the whole crate. 18 | 19 | use core::marker::PhantomData; 20 | 21 | /// A zero-sized type signifying that the decoding finished. 22 | /// 23 | /// To be used in [`Decode::decode_into`] to allow the implementation to explicitly 24 | /// assert that the `MaybeUninit` passed into that function was properly initialized. 25 | pub struct DecodeFinished(PhantomData<*const ()>); 26 | 27 | impl DecodeFinished { 28 | /// Assert that the decoding has finished. 29 | /// 30 | /// # Safety 31 | /// 32 | /// Should be used in [`Decode::decode_into`] to signify that 33 | /// the `MaybeUninit` passed into that function was properly initialized. 34 | #[inline] 35 | pub unsafe fn assert_decoding_finished() -> DecodeFinished { 36 | DecodeFinished(PhantomData) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/size_hint.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::Encode; 2 | use parity_scale_codec_derive::Encode as DeriveEncode; 3 | 4 | #[test] 5 | fn size_hint_for_struct() { 6 | #[derive(DeriveEncode)] 7 | struct Struct { 8 | pub a: A, 9 | pub b: B, 10 | #[codec(skip)] 11 | pub c: C, 12 | } 13 | 14 | let v = Struct::, u32> { a: String::from("foo"), b: vec![1, 2, 3], c: 0 }; 15 | assert_eq!(v.size_hint(), 23); 16 | } 17 | 18 | #[test] 19 | fn size_hint_for_tuple_struct() { 20 | #[derive(DeriveEncode)] 21 | struct Tuple(String, Vec, #[codec(skip)] u32); 22 | 23 | let v = Tuple(String::from("foo"), vec![1, 2, 3], 0); 24 | assert_eq!(v.size_hint(), 23); 25 | } 26 | 27 | #[test] 28 | fn size_hint_for_unit_struct() { 29 | #[derive(DeriveEncode)] 30 | struct Unit; 31 | 32 | let v = Unit; 33 | assert_eq!(v.size_hint(), 0); 34 | } 35 | 36 | #[test] 37 | fn size_hint_for_simple_enum() { 38 | #[derive(DeriveEncode)] 39 | enum EnumType { 40 | #[codec(index = 15)] 41 | A, 42 | B(u32, u64), 43 | C { 44 | a: u32, 45 | b: u64, 46 | }, 47 | } 48 | 49 | let v = EnumType::A; 50 | assert_eq!(v.size_hint(), 1); 51 | 52 | let v = EnumType::B(1, 2); 53 | assert_eq!(v.size_hint(), 13); 54 | 55 | let v = EnumType::C { a: 0, b: 0 }; 56 | assert_eq!(v.size_hint(), 13); 57 | } 58 | 59 | #[test] 60 | fn size_hint_for_enum_with_discriminant() { 61 | #[derive(DeriveEncode)] 62 | enum EnumWithDiscriminant { 63 | A = 1, 64 | B = 15, 65 | C = 255, 66 | } 67 | 68 | let discriminant = core::mem::size_of::(); 69 | 70 | let v = EnumWithDiscriminant::A; 71 | assert_eq!(v.size_hint(), discriminant); 72 | 73 | let v = EnumWithDiscriminant::B; 74 | assert_eq!(v.size_hint(), discriminant); 75 | 76 | let v = EnumWithDiscriminant::C; 77 | assert_eq!(v.size_hint(), discriminant); 78 | } 79 | -------------------------------------------------------------------------------- /tests/skip.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{Decode, Encode}; 2 | use parity_scale_codec_derive::{ 3 | Decode as DeriveDecode, DecodeWithMemTracking as DeriveDecodeWithMemTracking, 4 | Encode as DeriveEncode, 5 | }; 6 | 7 | #[test] 8 | fn enum_struct_test() { 9 | #[derive(PartialEq, Debug, Default)] 10 | struct UncodecType; 11 | 12 | #[derive(PartialEq, Debug)] 13 | struct UncodecUndefaultType; 14 | 15 | use parity_scale_codec_derive::{ 16 | Decode as DeriveDecode, DecodeWithMemTracking as DeriveDecodeWithMemTracking, 17 | Encode as DeriveEncode, 18 | }; 19 | #[derive(PartialEq, Debug, DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 20 | enum Enum { 21 | #[codec(skip)] 22 | A(S), 23 | B { 24 | #[codec(skip)] 25 | _b1: T, 26 | b2: u32, 27 | }, 28 | C(#[codec(skip)] T, u32), 29 | } 30 | 31 | #[derive(PartialEq, Debug, DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 32 | struct StructNamed { 33 | #[codec(skip)] 34 | a: T, 35 | b: u32, 36 | } 37 | 38 | #[derive(PartialEq, Debug, DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 39 | struct StructUnnamed(#[codec(skip)] T, u32); 40 | 41 | let ea: Enum = Enum::A(UncodecUndefaultType); 42 | let eb: Enum = Enum::B { _b1: UncodecType, b2: 1 }; 43 | let ec: Enum = Enum::C(UncodecType, 1); 44 | let sn = StructNamed { a: UncodecType, b: 1 }; 45 | let su = StructUnnamed(UncodecType, 1); 46 | 47 | assert_eq!(ea.encode(), Vec::new()); 48 | 49 | let mut eb_encoded: &[u8] = &eb.encode(); 50 | let mut ec_encoded: &[u8] = &ec.encode(); 51 | let mut sn_encoded: &[u8] = &sn.encode(); 52 | let mut su_encoded: &[u8] = &su.encode(); 53 | 54 | assert_eq!(Enum::decode(&mut eb_encoded).unwrap(), eb); 55 | assert_eq!(Enum::decode(&mut ec_encoded).unwrap(), ec); 56 | assert_eq!(StructNamed::decode(&mut sn_encoded).unwrap(), sn); 57 | assert_eq!(StructUnnamed::decode(&mut su_encoded).unwrap(), su); 58 | } 59 | 60 | #[test] 61 | fn skip_enum_struct_inner_variant() { 62 | // Make sure the skipping does not generates a warning. 63 | #![deny(warnings)] 64 | 65 | #[derive(DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 66 | enum Enum { 67 | Data { 68 | some_named: u32, 69 | #[codec(skip)] 70 | ignore: Option, 71 | }, 72 | } 73 | 74 | let encoded = Enum::Data { some_named: 1, ignore: Some(1) }.encode(); 75 | assert_eq!(vec![0, 1, 0, 0, 0], encoded); 76 | } 77 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/crate_str.stderr: -------------------------------------------------------------------------------- 1 | error: Invalid attribute: only `#[codec(dumb_trait_bound)]`, `#[codec(crate = path::to::crate)]`, `#[codec(encode_bound(T: Encode))]`, `#[codec(decode_bound(T: Decode))]`, `#[codec(decode_with_mem_tracking_bound(T: DecodeWithMemTracking))]` or `#[codec(mel_bound(T: MaxEncodedLen))]` are accepted as top attribute 2 | --> tests/max_encoded_len_ui/crate_str.rs:4:9 3 | | 4 | 4 | #[codec(crate = "parity_scale_codec")] 5 | | ^^^^^ 6 | 7 | error[E0277]: the trait bound `Example: Encode` is not satisfied 8 | --> tests/max_encoded_len_ui/crate_str.rs:5:8 9 | | 10 | 5 | struct Example; 11 | | ^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Example`, which is required by `Example: Encode` 12 | | 13 | = help: the following other types implement trait `WrapperTypeEncode`: 14 | &T 15 | &mut T 16 | Arc 17 | Box 18 | Cow<'_, T> 19 | Rc 20 | String 21 | Vec 22 | parity_scale_codec::Ref<'_, T, U> 23 | = note: required for `Example` to implement `Encode` 24 | note: required by a bound in `MaxEncodedLen` 25 | --> src/max_encoded_len.rs 26 | | 27 | | pub trait MaxEncodedLen: Encode { 28 | | ^^^^^^ required by this bound in `MaxEncodedLen` 29 | 30 | error[E0277]: the trait bound `Example: Encode` is not satisfied 31 | --> tests/max_encoded_len_ui/crate_str.rs:8:10 32 | | 33 | 8 | let _ = Example::max_encoded_len(); 34 | | ^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Example`, which is required by `Example: Encode` 35 | | 36 | = help: the following other types implement trait `WrapperTypeEncode`: 37 | &T 38 | &mut T 39 | Arc 40 | Box 41 | Cow<'_, T> 42 | Rc 43 | String 44 | Vec 45 | parity_scale_codec::Ref<'_, T, U> 46 | = note: required for `Example` to implement `Encode` 47 | note: required by a bound in `max_encoded_len` 48 | --> src/max_encoded_len.rs 49 | | 50 | | pub trait MaxEncodedLen: Encode { 51 | | ^^^^^^ required by this bound in `MaxEncodedLen::max_encoded_len` 52 | | /// Upper bound, in bytes, of the maximum encoded size of this item. 53 | | fn max_encoded_len() -> usize; 54 | | --------------- required by a bound in this associated function 55 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/incomplete_attr.stderr: -------------------------------------------------------------------------------- 1 | error: Invalid attribute: only `#[codec(dumb_trait_bound)]`, `#[codec(crate = path::to::crate)]`, `#[codec(encode_bound(T: Encode))]`, `#[codec(decode_bound(T: Decode))]`, `#[codec(decode_with_mem_tracking_bound(T: DecodeWithMemTracking))]` or `#[codec(mel_bound(T: MaxEncodedLen))]` are accepted as top attribute 2 | --> tests/max_encoded_len_ui/incomplete_attr.rs:4:9 3 | | 4 | 4 | #[codec(crate)] 5 | | ^^^^^ 6 | 7 | error[E0277]: the trait bound `Example: Encode` is not satisfied 8 | --> tests/max_encoded_len_ui/incomplete_attr.rs:5:8 9 | | 10 | 5 | struct Example; 11 | | ^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Example`, which is required by `Example: Encode` 12 | | 13 | = help: the following other types implement trait `WrapperTypeEncode`: 14 | &T 15 | &mut T 16 | Arc 17 | Box 18 | Cow<'_, T> 19 | Rc 20 | String 21 | Vec 22 | parity_scale_codec::Ref<'_, T, U> 23 | = note: required for `Example` to implement `Encode` 24 | note: required by a bound in `MaxEncodedLen` 25 | --> src/max_encoded_len.rs 26 | | 27 | | pub trait MaxEncodedLen: Encode { 28 | | ^^^^^^ required by this bound in `MaxEncodedLen` 29 | 30 | error[E0277]: the trait bound `Example: Encode` is not satisfied 31 | --> tests/max_encoded_len_ui/incomplete_attr.rs:8:10 32 | | 33 | 8 | let _ = Example::max_encoded_len(); 34 | | ^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Example`, which is required by `Example: Encode` 35 | | 36 | = help: the following other types implement trait `WrapperTypeEncode`: 37 | &T 38 | &mut T 39 | Arc 40 | Box 41 | Cow<'_, T> 42 | Rc 43 | String 44 | Vec 45 | parity_scale_codec::Ref<'_, T, U> 46 | = note: required for `Example` to implement `Encode` 47 | note: required by a bound in `max_encoded_len` 48 | --> src/max_encoded_len.rs 49 | | 50 | | pub trait MaxEncodedLen: Encode { 51 | | ^^^^^^ required by this bound in `MaxEncodedLen::max_encoded_len` 52 | | /// Upper bound, in bytes, of the maximum encoded size of this item. 53 | | fn max_encoded_len() -> usize; 54 | | --------------- required by a bound in this associated function 55 | -------------------------------------------------------------------------------- /src/generic_array.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{alloc::vec::Vec, encode_like::EncodeLike, Decode, Encode, Error, Input, Output}; 16 | 17 | impl> Encode for generic_array::GenericArray { 18 | fn encode_to(&self, dest: &mut W) { 19 | for item in self.iter() { 20 | item.encode_to(dest); 21 | } 22 | } 23 | } 24 | 25 | impl> EncodeLike for generic_array::GenericArray {} 26 | 27 | impl> Decode for generic_array::GenericArray { 28 | fn decode(input: &mut I) -> Result { 29 | let mut r = Vec::with_capacity(L::to_usize()); 30 | for _ in 0..L::to_usize() { 31 | r.push(T::decode(input)?); 32 | } 33 | let i = generic_array::GenericArray::from_exact_iter(r); 34 | 35 | match i { 36 | Some(a) => Ok(a), 37 | None => Err("array length does not match definition".into()), 38 | } 39 | } 40 | } 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use super::*; 45 | use generic_array::{arr, typenum, GenericArray}; 46 | 47 | #[test] 48 | fn generic_array() { 49 | let test = arr![u8; 3, 4, 5]; 50 | let encoded = test.encode(); 51 | assert_eq!(test, GenericArray::::decode(&mut &encoded[..]).unwrap()); 52 | 53 | let test = arr![u16; 3, 4, 5, 6, 7, 8, 0]; 54 | let encoded = test.encode(); 55 | assert_eq!(test, GenericArray::::decode(&mut &encoded[..]).unwrap()); 56 | 57 | let test = arr![u32; 3, 4, 5, 0, 1]; 58 | let encoded = test.encode(); 59 | assert_eq!(test, GenericArray::::decode(&mut &encoded[..]).unwrap()); 60 | 61 | let test = arr![u64; 3]; 62 | let encoded = test.encode(); 63 | assert_eq!(test, GenericArray::::decode(&mut &encoded[..]).unwrap()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/max_encoded_len_ui/missing_crate_specifier.stderr: -------------------------------------------------------------------------------- 1 | error: Invalid attribute: only `#[codec(dumb_trait_bound)]`, `#[codec(crate = path::to::crate)]`, `#[codec(encode_bound(T: Encode))]`, `#[codec(decode_bound(T: Decode))]`, `#[codec(decode_with_mem_tracking_bound(T: DecodeWithMemTracking))]` or `#[codec(mel_bound(T: MaxEncodedLen))]` are accepted as top attribute 2 | --> tests/max_encoded_len_ui/missing_crate_specifier.rs:4:9 3 | | 4 | 4 | #[codec(parity_scale_codec)] 5 | | ^^^^^^^^^^^^^^^^^^ 6 | 7 | error[E0277]: the trait bound `Example: Encode` is not satisfied 8 | --> tests/max_encoded_len_ui/missing_crate_specifier.rs:5:8 9 | | 10 | 5 | struct Example; 11 | | ^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Example`, which is required by `Example: Encode` 12 | | 13 | = help: the following other types implement trait `WrapperTypeEncode`: 14 | &T 15 | &mut T 16 | Arc 17 | Box 18 | Cow<'_, T> 19 | Rc 20 | String 21 | Vec 22 | parity_scale_codec::Ref<'_, T, U> 23 | = note: required for `Example` to implement `Encode` 24 | note: required by a bound in `MaxEncodedLen` 25 | --> src/max_encoded_len.rs 26 | | 27 | | pub trait MaxEncodedLen: Encode { 28 | | ^^^^^^ required by this bound in `MaxEncodedLen` 29 | 30 | error[E0277]: the trait bound `Example: Encode` is not satisfied 31 | --> tests/max_encoded_len_ui/missing_crate_specifier.rs:8:10 32 | | 33 | 8 | let _ = Example::max_encoded_len(); 34 | | ^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Example`, which is required by `Example: Encode` 35 | | 36 | = help: the following other types implement trait `WrapperTypeEncode`: 37 | &T 38 | &mut T 39 | Arc 40 | Box 41 | Cow<'_, T> 42 | Rc 43 | String 44 | Vec 45 | parity_scale_codec::Ref<'_, T, U> 46 | = note: required for `Example` to implement `Encode` 47 | note: required by a bound in `max_encoded_len` 48 | --> src/max_encoded_len.rs 49 | | 50 | | pub trait MaxEncodedLen: Encode { 51 | | ^^^^^^ required by this bound in `MaxEncodedLen::max_encoded_len` 52 | | /// Upper bound, in bytes, of the maximum encoded size of this item. 53 | | fn max_encoded_len() -> usize; 54 | | --------------- required by a bound in this associated function 55 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parity-scale-codec" 3 | description = "SCALE - Simple Concatenating Aggregated Little Endians" 4 | version.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | repository.workspace = true 8 | categories.workspace = true 9 | edition.workspace = true 10 | build = "build.rs" 11 | rust-version.workspace = true 12 | 13 | [dependencies] 14 | arrayvec = { version = "0.7", default-features = false } 15 | serde = { version = "1.0.228", default-features = false, optional = true } 16 | parity-scale-codec-derive = { path = "derive", version = "=3.7.5", default-features = false, optional = true } 17 | bitvec = { version = "1", default-features = false, features = ["alloc"], optional = true } 18 | bytes = { version = "1", default-features = false, optional = true } 19 | byte-slice-cast = { version = "1.2.3", default-features = false } 20 | generic-array = { version = "0.14.7", optional = true } 21 | arbitrary = { version = "1.4.2", features = ["derive"], optional = true } 22 | impl-trait-for-tuples = "0.2.3" 23 | const_format = { version = "0.2.35" } 24 | 25 | [dev-dependencies] 26 | criterion = "0.7.0" 27 | serde_derive = { version = "1.0" } 28 | parity-scale-codec-derive = { path = "derive", default-features = false } 29 | quickcheck = "1.0" 30 | proptest = "1.8.0" 31 | trybuild = "1.0.112" 32 | paste = "1" 33 | rustversion = "1.0.6" 34 | enumflags2 = "0.7.12" 35 | 36 | [build-dependencies] 37 | rustversion = "1.0.6" 38 | 39 | [[bench]] 40 | name = "benches" 41 | harness = false 42 | 43 | [lib] 44 | bench = false 45 | 46 | [features] 47 | default = ["std"] 48 | derive = ["parity-scale-codec-derive"] 49 | std = ["serde/std", "bitvec?/std", "byte-slice-cast/std", "chain-error"] 50 | bit-vec = ["bitvec"] 51 | fuzz = ["std", "arbitrary"] 52 | 53 | # Enables the new `MaxEncodedLen` trait. 54 | # NOTE: This is still considered experimental and is exempt from the usual 55 | # SemVer guarantees. We do not guarantee no code breakage when using this. 56 | max-encoded-len = ["parity-scale-codec-derive?/max-encoded-len"] 57 | 58 | # Make error fully descriptive with chaining error message. 59 | # Should not be used in a constrained environment. 60 | chain-error = [] 61 | 62 | # This does not do anthing anymore. Remove with the next major release. 63 | full = [] 64 | 65 | [workspace] 66 | members = ["derive", "fuzzer"] 67 | 68 | [workspace.package] 69 | version = "3.7.5" 70 | authors = ["Parity Technologies "] 71 | license = "Apache-2.0" 72 | repository = "https://github.com/paritytech/parity-scale-codec" 73 | categories = ["encoding"] 74 | edition = "2021" 75 | rust-version = "1.79.0" 76 | -------------------------------------------------------------------------------- /tests/chain-error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use parity_scale_codec::Decode; 16 | use parity_scale_codec_derive::Decode as DeriveDecode; 17 | 18 | #[derive(DeriveDecode, Debug)] 19 | struct Wrapper(T); 20 | 21 | #[derive(DeriveDecode, Debug)] 22 | struct StructNamed { 23 | _foo: u16, 24 | } 25 | 26 | #[derive(DeriveDecode, Debug)] 27 | struct StructUnnamed(#[allow(dead_code)] u16); 28 | 29 | #[derive(DeriveDecode, Debug)] 30 | enum E { 31 | VariantNamed { _foo: u16 }, 32 | VariantUnnamed(#[allow(dead_code)] u16), 33 | } 34 | 35 | #[test] 36 | fn full_error_struct_named() { 37 | let encoded = [0]; 38 | let err = r#"Could not decode `Wrapper.0`: 39 | Could not decode `StructNamed::_foo`: 40 | Not enough data to fill buffer 41 | "#; 42 | 43 | assert_eq!( 44 | Wrapper::::decode(&mut &encoded[..]).unwrap_err().to_string(), 45 | String::from(err), 46 | ); 47 | } 48 | 49 | #[test] 50 | fn full_error_struct_unnamed() { 51 | let encoded = [0]; 52 | let err = r#"Could not decode `Wrapper.0`: 53 | Could not decode `StructUnnamed.0`: 54 | Not enough data to fill buffer 55 | "#; 56 | 57 | assert_eq!( 58 | Wrapper::::decode(&mut &encoded[..]).unwrap_err().to_string(), 59 | String::from(err), 60 | ); 61 | } 62 | 63 | #[test] 64 | fn full_error_enum_unknown_variant() { 65 | let encoded = [2]; 66 | let err = r#"Could not decode `E`, variant doesn't exist"#; 67 | 68 | assert_eq!(E::decode(&mut &encoded[..]).unwrap_err().to_string(), String::from(err),); 69 | } 70 | 71 | #[test] 72 | fn full_error_enum_named_field() { 73 | let encoded = [0, 0]; 74 | let err = r#"Could not decode `E::VariantNamed::_foo`: 75 | Not enough data to fill buffer 76 | "#; 77 | 78 | assert_eq!(E::decode(&mut &encoded[..]).unwrap_err().to_string(), String::from(err),); 79 | } 80 | 81 | #[test] 82 | fn full_error_enum_unnamed_field() { 83 | let encoded = [1, 0]; 84 | let err = r#"Could not decode `E::VariantUnnamed.0`: 85 | Not enough data to fill buffer 86 | "#; 87 | 88 | assert_eq!(E::decode(&mut &encoded[..]).unwrap_err().to_string(), String::from(err),); 89 | } 90 | -------------------------------------------------------------------------------- /tests/scale_codec_ui/codec_duplicate_index.stderr: -------------------------------------------------------------------------------- 1 | error[E0080]: evaluation of constant value failed 2 | --> tests/scale_codec_ui/codec_duplicate_index.rs:1:10 3 | | 4 | 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `3`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:1:10 6 | | 7 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | 9 | error[E0080]: evaluation of constant value failed 10 | --> tests/scale_codec_ui/codec_duplicate_index.rs:1:40 11 | | 12 | 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 13 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `3`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:1:40 14 | | 15 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | 17 | error[E0080]: evaluation of constant value failed 18 | --> tests/scale_codec_ui/codec_duplicate_index.rs:9:10 19 | | 20 | 9 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 21 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `0`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:9:10 22 | | 23 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) 24 | 25 | error[E0080]: evaluation of constant value failed 26 | --> tests/scale_codec_ui/codec_duplicate_index.rs:9:40 27 | | 28 | 9 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 29 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `0`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_duplicate_index.rs:9:40 30 | | 31 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) 32 | -------------------------------------------------------------------------------- /tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.stderr: -------------------------------------------------------------------------------- 1 | error[E0080]: evaluation of constant value failed 2 | --> tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:1:10 3 | | 4 | 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `1`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:1:10 6 | | 7 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | 9 | error[E0080]: evaluation of constant value failed 10 | --> tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:1:40 11 | | 12 | 1 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 13 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `1`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:1:40 14 | | 15 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | 17 | error[E0080]: evaluation of constant value failed 18 | --> tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:8:10 19 | | 20 | 8 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 21 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `1`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:8:10 22 | | 23 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) 24 | 25 | error[E0080]: evaluation of constant value failed 26 | --> tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:8:40 27 | | 28 | 8 | #[derive(::parity_scale_codec::Decode, ::parity_scale_codec::Encode)] 29 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Found variants that have duplicate indexes. Both `A` and `B` have the index `1`. Use different indexes for each variant.', $DIR/tests/scale_codec_ui/codec_discriminiant_variant_counted_in_default_index.rs:8:40 30 | | 31 | = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) 32 | -------------------------------------------------------------------------------- /src/mem_tracking.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) Parity Technologies (UK) Ltd. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use crate::{Decode, Error, Input}; 17 | use impl_trait_for_tuples::impl_for_tuples; 18 | 19 | /// Marker trait used for identifying types that call the [`Input::on_before_alloc_mem`] hook 20 | /// while decoding. 21 | pub trait DecodeWithMemTracking: Decode {} 22 | 23 | const DECODE_OOM_MSG: &str = "Heap memory limit exceeded while decoding"; 24 | 25 | #[impl_for_tuples(18)] 26 | impl DecodeWithMemTracking for Tuple {} 27 | 28 | /// `Input` implementation that can be used for limiting the heap memory usage while decoding. 29 | pub struct MemTrackingInput<'a, I> { 30 | input: &'a mut I, 31 | used_mem: usize, 32 | mem_limit: usize, 33 | } 34 | 35 | impl<'a, I: Input> MemTrackingInput<'a, I> { 36 | /// Create a new instance of `MemTrackingInput`. 37 | pub fn new(input: &'a mut I, mem_limit: usize) -> Self { 38 | Self { input, used_mem: 0, mem_limit } 39 | } 40 | 41 | /// Get the `used_mem` field. 42 | pub fn used_mem(&self) -> usize { 43 | self.used_mem 44 | } 45 | } 46 | 47 | impl Input for MemTrackingInput<'_, I> { 48 | fn remaining_len(&mut self) -> Result, Error> { 49 | self.input.remaining_len() 50 | } 51 | 52 | fn read(&mut self, into: &mut [u8]) -> Result<(), Error> { 53 | self.input.read(into) 54 | } 55 | 56 | fn read_byte(&mut self) -> Result { 57 | self.input.read_byte() 58 | } 59 | 60 | fn descend_ref(&mut self) -> Result<(), Error> { 61 | self.input.descend_ref() 62 | } 63 | 64 | fn ascend_ref(&mut self) { 65 | self.input.ascend_ref() 66 | } 67 | 68 | fn on_before_alloc_mem(&mut self, size: usize) -> Result<(), Error> { 69 | self.input.on_before_alloc_mem(size)?; 70 | 71 | self.used_mem = self.used_mem.saturating_add(size); 72 | if self.used_mem >= self.mem_limit { 73 | return Err(DECODE_OOM_MSG.into()); 74 | } 75 | 76 | Ok(()) 77 | } 78 | } 79 | 80 | /// Extension trait to [`Decode`] for decoding with a maximum memory limit. 81 | pub trait DecodeWithMemLimit: DecodeWithMemTracking { 82 | /// Decode `Self` with the given maximum memory limit and advance `input` by the number of 83 | /// bytes consumed. 84 | /// 85 | /// If `mem_limit` is hit, an error is returned. 86 | fn decode_with_mem_limit(input: &mut I, mem_limit: usize) -> Result; 87 | } 88 | 89 | impl DecodeWithMemLimit for T 90 | where 91 | T: DecodeWithMemTracking, 92 | { 93 | fn decode_with_mem_limit(input: &mut I, mem_limit: usize) -> Result { 94 | let mut input = MemTrackingInput::new(input, mem_limit); 95 | T::decode(&mut input) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/decode_all.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017, 2018 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{Decode, Error}; 16 | 17 | /// The error message returned when `decode_all` fails. 18 | pub(crate) const DECODE_ALL_ERR_MSG: &str = "Input buffer has still data left after decoding!"; 19 | 20 | /// Extension trait to [`Decode`] that ensures that the given input data is consumed completely 21 | /// while decoding. 22 | pub trait DecodeAll: Sized { 23 | /// Decode `Self` and consume all of the given input data. 24 | /// 25 | /// If not all data is consumed, an error is returned. 26 | fn decode_all(input: &mut &[u8]) -> Result; 27 | } 28 | 29 | impl DecodeAll for T { 30 | fn decode_all(input: &mut &[u8]) -> Result { 31 | let res = T::decode(input)?; 32 | 33 | if input.is_empty() { 34 | Ok(res) 35 | } else { 36 | Err(DECODE_ALL_ERR_MSG.into()) 37 | } 38 | } 39 | } 40 | 41 | #[cfg(test)] 42 | mod tests { 43 | use super::*; 44 | use crate::{Compact, Encode, EncodeLike, Input}; 45 | 46 | macro_rules! test_decode_all { 47 | ( 48 | $( $type:ty => $value:expr; )* 49 | ) => { 50 | $( 51 | { 52 | let mut encoded = <$type as Encode>::encode(&$value); 53 | <$type>::decode_all(&mut encoded.as_slice()).expect( 54 | &format!("`{} => {}` decodes all!", stringify!($type), stringify!($value)), 55 | ); 56 | 57 | encoded.extend(&[1, 2, 3, 4, 5, 6]); 58 | assert_eq!( 59 | <$type>::decode_all(&mut encoded.as_slice()).unwrap_err().to_string(), 60 | "Input buffer has still data left after decoding!", 61 | ); 62 | } 63 | )* 64 | }; 65 | } 66 | 67 | #[derive(Debug)] 68 | struct TestStruct { 69 | data: Vec, 70 | other: u8, 71 | compact: Compact, 72 | } 73 | 74 | impl EncodeLike for TestStruct {} 75 | 76 | impl Encode for TestStruct { 77 | fn encode(&self) -> Vec { 78 | let mut res = Vec::new(); 79 | self.data.encode_to(&mut res); 80 | self.other.encode_to(&mut res); 81 | self.compact.encode_to(&mut res); 82 | res 83 | } 84 | } 85 | 86 | impl Decode for TestStruct { 87 | fn decode(input: &mut I) -> Result { 88 | Ok(Self { 89 | data: Vec::::decode(input)?, 90 | other: u8::decode(input)?, 91 | compact: Compact::::decode(input)?, 92 | }) 93 | } 94 | } 95 | 96 | #[test] 97 | fn decode_all_works() { 98 | test_decode_all! { 99 | u8 => 120; 100 | u16 => 30; 101 | u32 => 1; 102 | u64 => 2343545; 103 | u128 => 34358394245459854; 104 | Vec => vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 105 | Vec => vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 106 | Compact => Compact(32445); 107 | Compact => Compact(34353454453545); 108 | TestStruct => TestStruct { data: vec![1, 2, 4, 5, 6], other: 45, compact: Compact(123234545) }; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Error type, descriptive or undescriptive depending on features. 16 | 17 | use crate::alloc::borrow::Cow; 18 | #[cfg(feature = "chain-error")] 19 | use crate::alloc::boxed::Box; 20 | 21 | /// Error type. 22 | /// 23 | /// Descriptive on `std` environment, with chaining error on `chain-error` environment, 24 | /// underscriptive otherwise. 25 | #[derive(PartialEq, Eq, Clone, Debug)] 26 | pub struct Error { 27 | #[cfg(feature = "chain-error")] 28 | cause: Option>, 29 | #[cfg(feature = "chain-error")] 30 | desc: Cow<'static, str>, 31 | } 32 | 33 | impl Error { 34 | /// Chain error message with description. 35 | /// 36 | /// When compiled with `chain-error` feature, the description is chained, otherwise the 37 | /// description is ditched. 38 | pub fn chain(self, desc: impl Into>) -> Self { 39 | #[cfg(feature = "chain-error")] 40 | { 41 | Self { desc: desc.into(), cause: Some(Box::new(self)) } 42 | } 43 | 44 | #[cfg(not(feature = "chain-error"))] 45 | { 46 | let _ = desc; 47 | self 48 | } 49 | } 50 | 51 | /// Display error with indentation. 52 | #[cfg(feature = "chain-error")] 53 | fn display_with_indent(&self, indent: u32, f: &mut core::fmt::Formatter) -> core::fmt::Result { 54 | for _ in 0..indent { 55 | f.write_str("\t")?; 56 | } 57 | f.write_str(&self.desc)?; 58 | if let Some(cause) = &self.cause { 59 | f.write_str(":")?; 60 | f.write_str("\n")?; 61 | cause.display_with_indent(indent + 1, f) 62 | } else { 63 | // Only return to new line if the error has been displayed with some indent, 64 | // i.e. if the error has some causes. 65 | if indent != 0 { 66 | f.write_str("\n")?; 67 | } 68 | Ok(()) 69 | } 70 | } 71 | } 72 | 73 | impl core::fmt::Display for Error { 74 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 75 | #[cfg(feature = "chain-error")] 76 | { 77 | self.display_with_indent(0, f) 78 | } 79 | 80 | #[cfg(not(feature = "chain-error"))] 81 | { 82 | f.write_str("Codec error") 83 | } 84 | } 85 | } 86 | 87 | impl From<&'static str> for Error { 88 | fn from(desc: &'static str) -> Error { 89 | #[cfg(feature = "chain-error")] 90 | { 91 | Error { desc: desc.into(), cause: None } 92 | } 93 | 94 | #[cfg(not(feature = "chain-error"))] 95 | { 96 | let _ = desc; 97 | Error {} 98 | } 99 | } 100 | } 101 | 102 | #[cfg(feature = "std")] 103 | impl std::error::Error for Error { 104 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 105 | #[cfg(feature = "chain-error")] 106 | { 107 | self.cause.as_ref().map(|e| e as &(dyn std::error::Error + 'static)) 108 | } 109 | 110 | #[cfg(not(feature = "chain-error"))] 111 | { 112 | None 113 | } 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod tests { 119 | use crate::Error; 120 | 121 | #[test] 122 | fn test_full_error() { 123 | let msg: &str = "final type:\n\twrap cause:\n\t\troot cause\n"; 124 | 125 | let error = Error::from("root cause").chain("wrap cause").chain("final type"); 126 | 127 | assert_eq!(&error.to_string(), msg); 128 | } 129 | 130 | #[test] 131 | fn impl_std_error() { 132 | use std::error::Error as _; 133 | 134 | let error = Error::from("root cause").chain("wrap cause").chain("final type"); 135 | let s = error.source().unwrap(); 136 | 137 | assert_eq!(&s.to_string(), "wrap cause:\n\troot cause\n"); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/btree_utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2018 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use core::mem::size_of; 16 | 17 | // Constants from rust's source: 18 | // https://doc.rust-lang.org/src/alloc/collections/btree/node.rs.html#43-45 19 | const B: usize = 6; 20 | const CAPACITY: usize = 2 * B - 1; 21 | const MIN_LEN_AFTER_SPLIT: usize = B - 1; 22 | 23 | /// Estimate the mem size of a btree. 24 | pub fn mem_size_of_btree(len: u32) -> usize { 25 | if len == 0 { 26 | return 0; 27 | } 28 | 29 | // We try to estimate the size of the `InternalNode` struct from: 30 | // https://doc.rust-lang.org/src/alloc/collections/btree/node.rs.html#97 31 | // A btree `LeafNode` has 2*B - 1 (K,V) pairs and (usize, u16, u16) overhead. 32 | let leaf_node_size = size_of::<(usize, u16, u16, [T; CAPACITY])>(); 33 | // An `InternalNode` additionally has 2*B `usize` overhead. 34 | let internal_node_size = leaf_node_size + size_of::<[usize; 2 * B]>(); 35 | // A node can contain between B - 1 and 2*B - 1 elements. We assume 2/3 occupancy. 36 | let num_nodes = (len as usize).saturating_div((CAPACITY + MIN_LEN_AFTER_SPLIT) * 2 / 3); 37 | 38 | // If the tree has only one node, it's a leaf node. 39 | if num_nodes == 0 { 40 | return leaf_node_size; 41 | } 42 | num_nodes.saturating_mul(internal_node_size) 43 | } 44 | 45 | #[cfg(test)] 46 | #[cfg(not(miri))] 47 | #[rustversion::nightly] 48 | mod test { 49 | use super::*; 50 | use crate::alloc::{ 51 | collections::{BTreeMap, BTreeSet}, 52 | sync::{Arc, Mutex}, 53 | }; 54 | use core::{ 55 | alloc::{AllocError, Allocator, Layout}, 56 | ptr::NonNull, 57 | }; 58 | 59 | #[derive(Clone)] 60 | struct MockAllocator { 61 | total: Arc>, 62 | } 63 | 64 | unsafe impl Allocator for MockAllocator { 65 | fn allocate(&self, layout: Layout) -> Result, AllocError> { 66 | let ptr = std::alloc::System.allocate(layout); 67 | if ptr.is_ok() { 68 | *self.total.lock().unwrap() += layout.size(); 69 | } 70 | ptr 71 | } 72 | 73 | unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { 74 | *self.total.lock().unwrap() -= layout.size(); 75 | std::alloc::System.deallocate(ptr, layout) 76 | } 77 | } 78 | 79 | fn check_btree_size(expected_size: usize, actual_size: Arc>) { 80 | // Check that the margin of error is at most 25%. 81 | assert!(*actual_size.lock().unwrap() as f64 * 0.75 <= expected_size as f64); 82 | assert!(*actual_size.lock().unwrap() as f64 * 1.25 >= expected_size as f64); 83 | } 84 | 85 | #[test] 86 | fn mem_size_of_btree_works() { 87 | let map_allocator = MockAllocator { total: Arc::new(Mutex::new(0)) }; 88 | let map_actual_size = map_allocator.total.clone(); 89 | let mut map = BTreeMap::::new_in(map_allocator); 90 | 91 | let set_allocator = MockAllocator { total: Arc::new(Mutex::new(0)) }; 92 | let set_actual_size = set_allocator.total.clone(); 93 | let mut set = BTreeSet::::new_in(set_allocator); 94 | 95 | for i in 0..1000000 { 96 | map.insert(i, 0); 97 | set.insert(i as u128); 98 | 99 | // For small number of elements, the differences between the expected size and 100 | // the actual size can be higher. 101 | if i > 100 { 102 | let map_expected_size = mem_size_of_btree::<(u32, u32)>(map.len() as u32); 103 | check_btree_size(map_expected_size, map_actual_size.clone()); 104 | 105 | let set_expected_size = mem_size_of_btree::(set.len() as u32); 106 | check_btree_size(set_expected_size, set_actual_size.clone()); 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/depth_limit.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017, 2018 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{Decode, Error, Input}; 16 | 17 | /// The error message returned when depth limit is reached. 18 | const DECODE_MAX_DEPTH_MSG: &str = "Maximum recursion depth reached when decoding"; 19 | 20 | /// Extension trait to [`Decode`] for decoding with a maximum recursion depth. 21 | pub trait DecodeLimit: Sized { 22 | /// Decode `Self` with the given maximum recursion depth and advance `input` by the number of 23 | /// bytes consumed. 24 | /// 25 | /// If `limit` is hit, an error is returned. 26 | fn decode_with_depth_limit(limit: u32, input: &mut I) -> Result; 27 | 28 | /// Decode `Self` and consume all of the given input data. 29 | /// 30 | /// If not all data is consumed or `limit` is hit, an error is returned. 31 | fn decode_all_with_depth_limit(limit: u32, input: &mut &[u8]) -> Result; 32 | } 33 | 34 | struct DepthTrackingInput<'a, I> { 35 | input: &'a mut I, 36 | depth: u32, 37 | max_depth: u32, 38 | } 39 | 40 | impl Input for DepthTrackingInput<'_, I> { 41 | fn remaining_len(&mut self) -> Result, Error> { 42 | self.input.remaining_len() 43 | } 44 | 45 | fn read(&mut self, into: &mut [u8]) -> Result<(), Error> { 46 | self.input.read(into) 47 | } 48 | 49 | fn read_byte(&mut self) -> Result { 50 | self.input.read_byte() 51 | } 52 | 53 | fn descend_ref(&mut self) -> Result<(), Error> { 54 | self.input.descend_ref()?; 55 | self.depth += 1; 56 | if self.depth > self.max_depth { 57 | Err(DECODE_MAX_DEPTH_MSG.into()) 58 | } else { 59 | Ok(()) 60 | } 61 | } 62 | 63 | fn ascend_ref(&mut self) { 64 | self.input.ascend_ref(); 65 | self.depth -= 1; 66 | } 67 | 68 | fn on_before_alloc_mem(&mut self, size: usize) -> Result<(), Error> { 69 | self.input.on_before_alloc_mem(size) 70 | } 71 | } 72 | 73 | impl DecodeLimit for T { 74 | fn decode_all_with_depth_limit(limit: u32, input: &mut &[u8]) -> Result { 75 | let t = ::decode_with_depth_limit(limit, input)?; 76 | 77 | if input.is_empty() { 78 | Ok(t) 79 | } else { 80 | Err(crate::decode_all::DECODE_ALL_ERR_MSG.into()) 81 | } 82 | } 83 | 84 | fn decode_with_depth_limit(limit: u32, input: &mut I) -> Result { 85 | let mut input = DepthTrackingInput { input, depth: 0, max_depth: limit }; 86 | T::decode(&mut input) 87 | } 88 | } 89 | 90 | #[cfg(test)] 91 | mod tests { 92 | use super::*; 93 | use crate::Encode; 94 | 95 | #[test] 96 | fn decode_limit_works() { 97 | type NestedVec = Vec>>>; 98 | let nested: NestedVec = vec![vec![vec![vec![1]]]]; 99 | let encoded = nested.encode(); 100 | 101 | let decoded = NestedVec::decode_with_depth_limit(3, &mut encoded.as_slice()).unwrap(); 102 | assert_eq!(decoded, nested); 103 | assert!(NestedVec::decode_with_depth_limit(2, &mut encoded.as_slice()).is_err()); 104 | } 105 | 106 | #[test] 107 | fn decode_limit_advances_input() { 108 | type NestedVec = Vec>>>; 109 | let nested: NestedVec = vec![vec![vec![vec![1]]]]; 110 | let encoded = nested.encode(); 111 | let encoded_slice = &mut encoded.as_slice(); 112 | 113 | let decoded = Vec::::decode_with_depth_limit(1, encoded_slice).unwrap(); 114 | assert_eq!(decoded, vec![4]); 115 | assert!(NestedVec::decode_with_depth_limit(3, encoded_slice).is_err()); 116 | } 117 | 118 | #[test] 119 | fn decode_all_with_limit_advances_input() { 120 | type NestedVec = Vec>>>; 121 | let nested: NestedVec = vec![vec![vec![vec![1]]]]; 122 | let mut encoded = NestedVec::encode(&nested); 123 | 124 | let decoded = NestedVec::decode_all_with_depth_limit(3, &mut encoded.as_slice()).unwrap(); 125 | assert_eq!(decoded, nested); 126 | 127 | encoded.extend(&[1, 2, 3, 4, 5, 6]); 128 | assert_eq!( 129 | NestedVec::decode_all_with_depth_limit(3, &mut encoded.as_slice()) 130 | .unwrap_err() 131 | .to_string(), 132 | "Input buffer has still data left after decoding!", 133 | ); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/const_encoded_len.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 Parity Technologies (UK) Ltd. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Contains the [`ConstEncodedLen`] trait. 17 | 18 | use crate::{alloc::boxed::Box, MaxEncodedLen}; 19 | use core::{ 20 | marker::PhantomData, 21 | num::*, 22 | ops::{Range, RangeInclusive}, 23 | time::Duration, 24 | }; 25 | use impl_trait_for_tuples::impl_for_tuples; 26 | 27 | /// Types that have a constant encoded length. This implies [`MaxEncodedLen`]. 28 | /// 29 | /// No derive macros is provided; instead use an empty implementation like for a marker trait. 30 | pub trait ConstEncodedLen: MaxEncodedLen {} 31 | 32 | #[impl_for_tuples(18)] 33 | impl ConstEncodedLen for Tuple {} 34 | 35 | impl ConstEncodedLen for [T; N] {} 36 | 37 | /// Mark `T` or `T` as `CEL`. 38 | macro_rules! mark_cel { 39 | ( $($n:ident <$t:ident>),+ ) => { 40 | $( 41 | impl<$t: ConstEncodedLen> ConstEncodedLen for $n<$t> { } 42 | )+ 43 | }; 44 | ( $($t:ty),+ ) => { 45 | $( 46 | impl ConstEncodedLen for $t { } 47 | )+ 48 | }; 49 | } 50 | 51 | mark_cel!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, bool); 52 | mark_cel!( 53 | NonZeroU8, 54 | NonZeroU16, 55 | NonZeroU32, 56 | NonZeroU64, 57 | NonZeroU128, 58 | NonZeroI8, 59 | NonZeroI16, 60 | NonZeroI32, 61 | NonZeroI64, 62 | NonZeroI128 63 | ); 64 | 65 | mark_cel!(Duration); 66 | mark_cel!(PhantomData); 67 | mark_cel!(Box); 68 | mark_cel!(Range, RangeInclusive); 69 | 70 | // `Option`, `Result` and `Compact` are sum types, therefore not `CEL`. 71 | 72 | #[cfg(test)] 73 | mod tests { 74 | use super::*; 75 | use crate::Encode; 76 | use proptest::prelude::*; 77 | 78 | /// Test that some random instances of `T` have encoded len `T::max_encoded_len()`. 79 | macro_rules! test_cel_compliance { 80 | ( $( $t:ty ),+ ) => { 81 | $( 82 | paste::paste! { 83 | proptest::proptest! { 84 | #[test] 85 | fn [< cel_compliance_ $t:snake >](x: $t) { 86 | prop_assert_eq!(x.encode().len(), $t::max_encoded_len()); 87 | } 88 | } 89 | } 90 | )* 91 | }; 92 | } 93 | 94 | type Void = (); 95 | test_cel_compliance!(Void); 96 | 97 | test_cel_compliance!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, bool); 98 | 99 | type TupleArithmetic = (u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); 100 | test_cel_compliance!(TupleArithmetic); 101 | 102 | test_cel_compliance!( 103 | NonZeroU8, 104 | NonZeroU16, 105 | NonZeroU32, 106 | NonZeroU64, 107 | NonZeroU128, 108 | NonZeroI8, 109 | NonZeroI16, 110 | NonZeroI32, 111 | NonZeroI64, 112 | NonZeroI128 113 | ); 114 | 115 | type TupleNonZero = ( 116 | NonZeroU8, 117 | NonZeroU16, 118 | NonZeroU32, 119 | NonZeroU64, 120 | NonZeroU128, 121 | NonZeroI8, 122 | NonZeroI16, 123 | NonZeroI32, 124 | NonZeroI64, 125 | NonZeroI128, 126 | ); 127 | test_cel_compliance!(TupleNonZero); 128 | 129 | type ArrayArithmetic = [(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); 10]; 130 | test_cel_compliance!(ArrayArithmetic); 131 | 132 | test_cel_compliance!(Duration); 133 | 134 | type BoxedArithmetic = Box<(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128)>; 135 | test_cel_compliance!(BoxedArithmetic); 136 | 137 | type PhantomArithmetic = PhantomData<(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128)>; 138 | test_cel_compliance!(PhantomArithmetic); 139 | 140 | type Ranges = (Range, Range, Range, Range, Range); 141 | test_cel_compliance!(Ranges); 142 | 143 | type Ranges2D = ( 144 | Range<(u8, u8)>, 145 | Range<(u16, u16)>, 146 | Range<(u32, u32)>, 147 | Range<(u64, u64)>, 148 | Range<(u128, u128)>, 149 | ); 150 | test_cel_compliance!(Ranges2D); 151 | 152 | type RangesInc = ( 153 | RangeInclusive, 154 | RangeInclusive, 155 | RangeInclusive, 156 | RangeInclusive, 157 | RangeInclusive, 158 | ); 159 | test_cel_compliance!(RangesInc); 160 | 161 | type RangesInc2D = ( 162 | RangeInclusive<(u8, u8)>, 163 | RangeInclusive<(u16, u16)>, 164 | RangeInclusive<(u32, u32)>, 165 | RangeInclusive<(u64, u64)>, 166 | RangeInclusive<(u128, u128)>, 167 | ); 168 | test_cel_compliance!(RangesInc2D); 169 | } 170 | -------------------------------------------------------------------------------- /src/counted_input.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2024 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// A wrapper for `Input` which tracks the number fo bytes that are successfully read. 16 | /// 17 | /// If inner `Input` fails to read, the counter is not incremented. 18 | /// 19 | /// It can count until `u64::MAX - 1` accurately then saturate. 20 | pub struct CountedInput<'a, I: crate::Input> { 21 | input: &'a mut I, 22 | counter: u64, 23 | } 24 | 25 | impl<'a, I: crate::Input> CountedInput<'a, I> { 26 | /// Create a new `CountedInput` with the given input. 27 | pub fn new(input: &'a mut I) -> Self { 28 | Self { input, counter: 0 } 29 | } 30 | 31 | /// Get the number of bytes successfully read. 32 | pub fn count(&self) -> u64 { 33 | self.counter 34 | } 35 | } 36 | 37 | impl crate::Input for CountedInput<'_, I> { 38 | fn remaining_len(&mut self) -> Result, crate::Error> { 39 | self.input.remaining_len() 40 | } 41 | 42 | fn read(&mut self, into: &mut [u8]) -> Result<(), crate::Error> { 43 | self.input.read(into).inspect(|_r| { 44 | self.counter = self.counter.saturating_add(into.len().try_into().unwrap_or(u64::MAX)); 45 | }) 46 | } 47 | 48 | fn read_byte(&mut self) -> Result { 49 | self.input.read_byte().inspect(|_r| { 50 | self.counter = self.counter.saturating_add(1); 51 | }) 52 | } 53 | 54 | fn ascend_ref(&mut self) { 55 | self.input.ascend_ref() 56 | } 57 | 58 | fn descend_ref(&mut self) -> Result<(), crate::Error> { 59 | self.input.descend_ref() 60 | } 61 | 62 | fn on_before_alloc_mem(&mut self, size: usize) -> Result<(), crate::Error> { 63 | self.input.on_before_alloc_mem(size) 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod test { 69 | use super::*; 70 | use crate::Input; 71 | 72 | #[test] 73 | fn test_counted_input_input_impl() { 74 | let mut input = &[1u8, 2, 3, 4, 5][..]; 75 | let mut counted_input = CountedInput::new(&mut input); 76 | 77 | assert_eq!(counted_input.remaining_len().unwrap(), Some(5)); 78 | assert_eq!(counted_input.count(), 0); 79 | 80 | counted_input.read_byte().unwrap(); 81 | 82 | assert_eq!(counted_input.remaining_len().unwrap(), Some(4)); 83 | assert_eq!(counted_input.count(), 1); 84 | 85 | counted_input.read(&mut [0u8; 2][..]).unwrap(); 86 | 87 | assert_eq!(counted_input.remaining_len().unwrap(), Some(2)); 88 | assert_eq!(counted_input.count(), 3); 89 | 90 | counted_input.ascend_ref(); 91 | counted_input.descend_ref().unwrap(); 92 | 93 | counted_input.read(&mut [0u8; 2][..]).unwrap(); 94 | 95 | assert_eq!(counted_input.remaining_len().unwrap(), Some(0)); 96 | assert_eq!(counted_input.count(), 5); 97 | 98 | assert_eq!(counted_input.read_byte(), Err("Not enough data to fill buffer".into())); 99 | 100 | assert_eq!(counted_input.remaining_len().unwrap(), Some(0)); 101 | assert_eq!(counted_input.count(), 5); 102 | 103 | assert_eq!( 104 | counted_input.read(&mut [0u8; 2][..]), 105 | Err("Not enough data to fill buffer".into()) 106 | ); 107 | 108 | assert_eq!(counted_input.remaining_len().unwrap(), Some(0)); 109 | assert_eq!(counted_input.count(), 5); 110 | } 111 | 112 | #[test] 113 | fn test_counted_input_max_count_read_byte() { 114 | let max_exact_count = u64::MAX - 1; 115 | 116 | let mut input = &[0u8; 1000][..]; 117 | let mut counted_input = CountedInput::new(&mut input); 118 | 119 | counted_input.counter = max_exact_count; 120 | 121 | assert_eq!(counted_input.count(), max_exact_count); 122 | 123 | counted_input.read_byte().unwrap(); 124 | 125 | assert_eq!(counted_input.count(), u64::MAX); 126 | 127 | counted_input.read_byte().unwrap(); 128 | 129 | assert_eq!(counted_input.count(), u64::MAX); 130 | } 131 | 132 | #[test] 133 | fn test_counted_input_max_count_read() { 134 | let max_exact_count = u64::MAX - 1; 135 | 136 | let mut input = &[0u8; 1000][..]; 137 | let mut counted_input = CountedInput::new(&mut input); 138 | 139 | counted_input.counter = max_exact_count; 140 | 141 | assert_eq!(counted_input.count(), max_exact_count); 142 | 143 | counted_input.read(&mut [0u8; 2][..]).unwrap(); 144 | 145 | assert_eq!(counted_input.count(), u64::MAX); 146 | 147 | counted_input.read(&mut [0u8; 2][..]).unwrap(); 148 | 149 | assert_eq!(counted_input.count(), u64::MAX); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![cfg_attr(all(test, nightly), feature(allocator_api))] 16 | #![cfg_attr(all(test, nightly), feature(btreemap_alloc))] 17 | #![doc = include_str!("../README.md")] 18 | #![warn(missing_docs)] 19 | #![cfg_attr(not(feature = "std"), no_std)] 20 | 21 | #[cfg(not(feature = "std"))] 22 | #[macro_use] 23 | #[doc(hidden)] 24 | pub extern crate alloc; 25 | 26 | #[cfg(feature = "derive")] 27 | #[allow(unused_imports)] 28 | #[macro_use] 29 | extern crate parity_scale_codec_derive; 30 | 31 | #[cfg(all(feature = "std", test))] 32 | #[macro_use] 33 | extern crate serde_derive; 34 | 35 | #[cfg(feature = "derive")] 36 | pub use parity_scale_codec_derive::*; 37 | 38 | #[cfg(feature = "std")] 39 | #[doc(hidden)] 40 | pub mod alloc { 41 | pub use std::{alloc, borrow, boxed, collections, rc, string, sync, vec}; 42 | } 43 | 44 | /// Private module to reexport items used by derive macros. 45 | // We don't feature gate this module with `derive` to avoid compilation error when 46 | // `parity-scale-codec-derive` is used on its own and this crate doesn't have the feature enabled. 47 | #[doc(hidden)] 48 | pub mod __private { 49 | pub use const_format::concatcp; 50 | } 51 | 52 | #[cfg(feature = "bit-vec")] 53 | mod bit_vec; 54 | mod btree_utils; 55 | mod codec; 56 | mod compact; 57 | #[cfg(feature = "max-encoded-len")] 58 | mod const_encoded_len; 59 | mod counted_input; 60 | mod decode_all; 61 | mod decode_finished; 62 | mod depth_limit; 63 | mod encode_append; 64 | mod encode_like; 65 | mod error; 66 | #[cfg(feature = "generic-array")] 67 | mod generic_array; 68 | mod joiner; 69 | mod keyedvec; 70 | #[cfg(feature = "max-encoded-len")] 71 | mod max_encoded_len; 72 | mod mem_tracking; 73 | 74 | #[cfg(feature = "std")] 75 | pub use self::codec::IoReader; 76 | pub use self::{ 77 | codec::{ 78 | decode_vec_with_len, Codec, Decode, DecodeLength, Encode, EncodeAsRef, FullCodec, 79 | FullEncode, Input, OptionBool, Output, WrapperTypeDecode, WrapperTypeEncode, 80 | }, 81 | compact::{Compact, CompactAs, CompactLen, CompactRef, HasCompact}, 82 | counted_input::CountedInput, 83 | decode_all::DecodeAll, 84 | decode_finished::DecodeFinished, 85 | depth_limit::DecodeLimit, 86 | encode_append::EncodeAppend, 87 | encode_like::{EncodeLike, Ref}, 88 | error::Error, 89 | joiner::Joiner, 90 | keyedvec::KeyedVec, 91 | mem_tracking::{DecodeWithMemLimit, DecodeWithMemTracking, MemTrackingInput}, 92 | }; 93 | #[cfg(feature = "max-encoded-len")] 94 | pub use const_encoded_len::ConstEncodedLen; 95 | #[cfg(feature = "max-encoded-len")] 96 | pub use max_encoded_len::MaxEncodedLen; 97 | 98 | /// Derive macro for [`MaxEncodedLen`][max_encoded_len::MaxEncodedLen]. 99 | /// 100 | /// # Examples 101 | /// 102 | /// ``` 103 | /// # use parity_scale_codec::{Encode, MaxEncodedLen}; 104 | /// #[derive(Encode, MaxEncodedLen)] 105 | /// struct Example; 106 | /// ``` 107 | /// 108 | /// ``` 109 | /// # use parity_scale_codec::{Encode, MaxEncodedLen}; 110 | /// #[derive(Encode, MaxEncodedLen)] 111 | /// struct TupleStruct(u8, u32); 112 | /// 113 | /// assert_eq!(TupleStruct::max_encoded_len(), u8::max_encoded_len() + u32::max_encoded_len()); 114 | /// ``` 115 | /// 116 | /// ``` 117 | /// # use parity_scale_codec::{Encode, MaxEncodedLen}; 118 | /// #[derive(Encode, MaxEncodedLen)] 119 | /// enum GenericEnum { 120 | /// A, 121 | /// B(T), 122 | /// } 123 | /// 124 | /// assert_eq!(GenericEnum::::max_encoded_len(), u8::max_encoded_len() + u8::max_encoded_len()); 125 | /// assert_eq!(GenericEnum::::max_encoded_len(), u8::max_encoded_len() + u128::max_encoded_len()); 126 | /// ``` 127 | /// 128 | /// # Within other macros 129 | /// 130 | /// Sometimes the `MaxEncodedLen` trait and macro are used within another macro, and it can't 131 | /// be guaranteed that the `parity_scale_codec` module is available at the call site. In that 132 | /// case, the macro should reexport the `parity_scale_codec` module and specify the path to the 133 | /// reexport: 134 | /// 135 | /// ```ignore 136 | /// pub use parity_scale_codec as codec; 137 | /// 138 | /// #[derive(Encode, MaxEncodedLen)] 139 | /// #[codec(crate = $crate::codec)] 140 | /// struct Example; 141 | /// ``` 142 | #[cfg(all(feature = "derive", feature = "max-encoded-len"))] 143 | pub use parity_scale_codec_derive::MaxEncodedLen; 144 | 145 | #[cfg(feature = "bytes")] 146 | pub use self::codec::decode_from_bytes; 147 | -------------------------------------------------------------------------------- /derive/src/max_encoded_len.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Parity Technologies (UK) Ltd. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #![cfg(feature = "max-encoded-len")] 17 | 18 | use crate::{ 19 | trait_bounds, 20 | utils::{ 21 | codec_crate_path, custom_mel_trait_bound, get_compact_type, get_encoded_as_type, 22 | has_dumb_trait_bound, should_skip, 23 | }, 24 | }; 25 | use quote::{quote, ToTokens}; 26 | use syn::{parse_quote, spanned::Spanned, Data, DeriveInput, Error, Field, Fields}; 27 | 28 | /// impl for `#[derive(MaxEncodedLen)]` 29 | pub fn derive_max_encoded_len(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 30 | let mut input: DeriveInput = match syn::parse(input) { 31 | Ok(input) => input, 32 | Err(e) => return e.to_compile_error().into(), 33 | }; 34 | 35 | let crate_path = match codec_crate_path(&input.attrs) { 36 | Ok(crate_path) => crate_path, 37 | Err(error) => return error.into_compile_error().into(), 38 | }; 39 | 40 | let name = &input.ident; 41 | if let Err(e) = trait_bounds::add( 42 | &input.ident, 43 | &mut input.generics, 44 | &input.data, 45 | custom_mel_trait_bound(&input.attrs), 46 | parse_quote!(#crate_path::MaxEncodedLen), 47 | None, 48 | has_dumb_trait_bound(&input.attrs), 49 | &crate_path, 50 | false, 51 | ) { 52 | return e.to_compile_error().into(); 53 | } 54 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 55 | 56 | let data_expr = data_length_expr(&input.data, &crate_path); 57 | 58 | quote::quote!( 59 | const _: () = { 60 | #[automatically_derived] 61 | impl #impl_generics #crate_path::MaxEncodedLen for #name #ty_generics #where_clause { 62 | fn max_encoded_len() -> ::core::primitive::usize { 63 | #data_expr 64 | } 65 | } 66 | }; 67 | ) 68 | .into() 69 | } 70 | 71 | /// generate an expression to sum up the max encoded length from several fields 72 | fn fields_length_expr(fields: &Fields, crate_path: &syn::Path) -> proc_macro2::TokenStream { 73 | let fields_iter: Box> = match fields { 74 | Fields::Named(ref fields) => 75 | Box::new(fields.named.iter().filter(|field| !should_skip(&field.attrs))), 76 | Fields::Unnamed(ref fields) => 77 | Box::new(fields.unnamed.iter().filter(|field| !should_skip(&field.attrs))), 78 | Fields::Unit => Box::new(std::iter::empty()), 79 | }; 80 | // expands to an expression like 81 | // 82 | // 0 83 | // .saturating_add(::max_encoded_len()) 84 | // .saturating_add(::max_encoded_len()) 85 | let types = fields_iter.map(|field| { 86 | match (get_encoded_as_type(field), get_compact_type(field, crate_path)) { 87 | (Some(encoded_as), None) => encoded_as, 88 | (None, Some(compact)) => compact, 89 | (None, None) => field.ty.to_token_stream(), 90 | (Some(_), Some(_)) => 91 | return Error::new( 92 | field.span(), 93 | "`encoded_as` and `compact` can not be used at the same time!", 94 | ) 95 | .to_compile_error(), 96 | } 97 | }); 98 | quote! { 99 | 0_usize #( .saturating_add(<#types as #crate_path::MaxEncodedLen>::max_encoded_len()) )* 100 | } 101 | } 102 | 103 | // generate an expression to sum up the max encoded length of each field 104 | fn data_length_expr(data: &Data, crate_path: &syn::Path) -> proc_macro2::TokenStream { 105 | match *data { 106 | Data::Struct(ref data) => fields_length_expr(&data.fields, crate_path), 107 | Data::Enum(ref data) => { 108 | // We need an expression expanded for each variant like 109 | // 110 | // 0 111 | // .max() 112 | // .max() 113 | // .saturating_add(1) 114 | // 115 | // The 1 derives from the discriminant; see 116 | // https://github.com/paritytech/parity-scale-codec/ 117 | // blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/derive/src/encode.rs#L211-L216 118 | // 119 | // Each variant expression's sum is computed the way an equivalent struct's would be. 120 | 121 | let expansion = 122 | data.variants.iter().filter(|variant| !should_skip(&variant.attrs)).map( 123 | |variant| { 124 | let variant_expression = fields_length_expr(&variant.fields, crate_path); 125 | quote! { 126 | .max(#variant_expression) 127 | } 128 | }, 129 | ); 130 | 131 | quote! { 132 | 0_usize #( #expansion )* .saturating_add(1) 133 | } 134 | }, 135 | Data::Union(ref data) => { 136 | // https://github.com/paritytech/parity-scale-codec/ 137 | // blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/derive/src/encode.rs#L290-L293 138 | syn::Error::new(data.union_token.span(), "Union types are not supported.") 139 | .to_compile_error() 140 | }, 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /tests/single_field_struct_encoding.rs: -------------------------------------------------------------------------------- 1 | use parity_scale_codec::{Compact, Decode, DecodeWithMemTracking, Encode, HasCompact}; 2 | use parity_scale_codec_derive::{ 3 | CompactAs as DeriveCompactAs, Decode as DeriveDecode, 4 | DecodeWithMemTracking as DeriveDecodeWithMemTracking, Encode as DeriveEncode, 5 | }; 6 | use serde_derive::{Deserialize, Serialize}; 7 | 8 | #[derive(Debug, PartialEq, DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 9 | struct S { 10 | x: u32, 11 | } 12 | 13 | #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] 14 | #[derive( 15 | Debug, 16 | PartialEq, 17 | Eq, 18 | Clone, 19 | Copy, 20 | DeriveEncode, 21 | DeriveDecode, 22 | DeriveDecodeWithMemTracking, 23 | DeriveCompactAs, 24 | )] 25 | struct SSkip { 26 | #[codec(skip)] 27 | s1: u32, 28 | x: u32, 29 | #[codec(skip)] 30 | s2: u32, 31 | } 32 | 33 | #[derive(Debug, PartialEq, DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 34 | struct Sc { 35 | #[codec(compact)] 36 | x: u32, 37 | } 38 | 39 | #[derive(Debug, PartialEq, DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 40 | struct Sh 41 | where 42 | ::Type: DecodeWithMemTracking, 43 | { 44 | #[codec(encoded_as = "::Type")] 45 | x: T, 46 | } 47 | 48 | #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] 49 | #[derive( 50 | Debug, 51 | PartialEq, 52 | Eq, 53 | Clone, 54 | Copy, 55 | DeriveEncode, 56 | DeriveDecode, 57 | DeriveDecodeWithMemTracking, 58 | DeriveCompactAs, 59 | )] 60 | struct U(u32); 61 | 62 | #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] 63 | #[derive( 64 | Debug, 65 | PartialEq, 66 | Eq, 67 | Clone, 68 | Copy, 69 | DeriveEncode, 70 | DeriveDecode, 71 | DeriveDecodeWithMemTracking, 72 | DeriveCompactAs, 73 | )] 74 | struct U2 { 75 | a: u64, 76 | } 77 | 78 | #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] 79 | #[derive( 80 | Debug, 81 | PartialEq, 82 | Eq, 83 | Clone, 84 | Copy, 85 | DeriveEncode, 86 | DeriveDecode, 87 | DeriveDecodeWithMemTracking, 88 | DeriveCompactAs, 89 | )] 90 | struct USkip(#[codec(skip)] u32, u32, #[codec(skip)] u32); 91 | 92 | #[derive(Debug, PartialEq, DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 93 | struct Uc(#[codec(compact)] u32); 94 | 95 | #[derive(Debug, PartialEq, Clone, DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 96 | struct Ucas(#[codec(compact)] U); 97 | 98 | #[derive(Debug, PartialEq, Clone, DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 99 | struct USkipcas(#[codec(compact)] USkip); 100 | 101 | #[derive(Debug, PartialEq, Clone, DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 102 | struct SSkipcas(#[codec(compact)] SSkip); 103 | 104 | #[derive(Debug, PartialEq, DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking)] 105 | struct Uh(#[codec(encoded_as = "::Type")] T) 106 | where 107 | ::Type: DecodeWithMemTracking; 108 | 109 | #[test] 110 | fn test_encoding() { 111 | let x = 3u32; 112 | let s = S { x }; 113 | let s_skip = SSkip { x, s1: Default::default(), s2: Default::default() }; 114 | let sc = Sc { x }; 115 | let sh = Sh { x }; 116 | let u = U(x); 117 | let u_skip = USkip(Default::default(), x, Default::default()); 118 | let uc = Uc(x); 119 | let ucom = Compact(u); 120 | let ucas = Ucas(u); 121 | let u_skip_cas = USkipcas(u_skip); 122 | let s_skip_cas = SSkipcas(s_skip); 123 | let uh = Uh(x); 124 | 125 | let mut s_encoded: &[u8] = &[3, 0, 0, 0]; 126 | let mut s_skip_encoded: &[u8] = &[3, 0, 0, 0]; 127 | let mut sc_encoded: &[u8] = &[12]; 128 | let mut sh_encoded: &[u8] = &[12]; 129 | let mut u_encoded: &[u8] = &[3, 0, 0, 0]; 130 | let mut u_skip_encoded: &[u8] = &[3, 0, 0, 0]; 131 | let mut uc_encoded: &[u8] = &[12]; 132 | let mut ucom_encoded: &[u8] = &[12]; 133 | let mut ucas_encoded: &[u8] = &[12]; 134 | let mut u_skip_cas_encoded: &[u8] = &[12]; 135 | let mut s_skip_cas_encoded: &[u8] = &[12]; 136 | let mut uh_encoded: &[u8] = &[12]; 137 | 138 | assert_eq!(s.encode(), s_encoded); 139 | assert_eq!(s_skip.encode(), s_skip_encoded); 140 | assert_eq!(sc.encode(), sc_encoded); 141 | assert_eq!(sh.encode(), sh_encoded); 142 | assert_eq!(u.encode(), u_encoded); 143 | assert_eq!(u_skip.encode(), u_skip_encoded); 144 | assert_eq!(uc.encode(), uc_encoded); 145 | assert_eq!(ucom.encode(), ucom_encoded); 146 | assert_eq!(ucas.encode(), ucas_encoded); 147 | assert_eq!(u_skip_cas.encode(), u_skip_cas_encoded); 148 | assert_eq!(s_skip_cas.encode(), s_skip_cas_encoded); 149 | assert_eq!(uh.encode(), uh_encoded); 150 | 151 | assert_eq!(s, S::decode(&mut s_encoded).unwrap()); 152 | assert_eq!(s_skip, SSkip::decode(&mut s_skip_encoded).unwrap()); 153 | assert_eq!(sc, Sc::decode(&mut sc_encoded).unwrap()); 154 | assert_eq!(sh, Sh::decode(&mut sh_encoded).unwrap()); 155 | assert_eq!(u, U::decode(&mut u_encoded).unwrap()); 156 | assert_eq!(u_skip, USkip::decode(&mut u_skip_encoded).unwrap()); 157 | assert_eq!(uc, Uc::decode(&mut uc_encoded).unwrap()); 158 | assert_eq!(ucom, >::decode(&mut ucom_encoded).unwrap()); 159 | assert_eq!(ucas, Ucas::decode(&mut ucas_encoded).unwrap()); 160 | assert_eq!(u_skip_cas, USkipcas::decode(&mut u_skip_cas_encoded).unwrap()); 161 | assert_eq!(s_skip_cas, SSkipcas::decode(&mut s_skip_cas_encoded).unwrap()); 162 | assert_eq!(uh, Uh::decode(&mut uh_encoded).unwrap()); 163 | } 164 | -------------------------------------------------------------------------------- /tests/mem_tracking.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) Parity Technologies (UK) Ltd. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use core::fmt::Debug; 17 | use parity_scale_codec::{ 18 | alloc::{ 19 | collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, 20 | rc::Rc, 21 | }, 22 | DecodeWithMemLimit, DecodeWithMemTracking, Encode, Error, MemTrackingInput, 23 | }; 24 | use parity_scale_codec_derive::{ 25 | Decode as DeriveDecode, DecodeWithMemTracking as DeriveDecodeWithMemTracking, 26 | Encode as DeriveEncode, 27 | }; 28 | 29 | const ARRAY: [u8; 1000] = [11; 1000]; 30 | 31 | #[derive(DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking, PartialEq, Debug)] 32 | #[allow(clippy::large_enum_variant)] 33 | enum TestEnum { 34 | Empty, 35 | Array([u8; 1000]), 36 | } 37 | 38 | #[derive(DeriveEncode, DeriveDecode, DeriveDecodeWithMemTracking, PartialEq, Debug)] 39 | struct ComplexStruct { 40 | test_enum: TestEnum, 41 | boxed_test_enum: Box, 42 | box_field: Box, 43 | vec: Vec, 44 | } 45 | 46 | fn decode_object(obj: T, mem_limit: usize, expected_used_mem: usize) -> Result 47 | where 48 | T: Encode + DecodeWithMemTracking + DecodeWithMemLimit + PartialEq + Debug, 49 | { 50 | let encoded_bytes = obj.encode(); 51 | 52 | let decoded_obj = T::decode_with_mem_limit(&mut &encoded_bytes[..], mem_limit)?; 53 | assert_eq!(&decoded_obj, &obj); 54 | 55 | let raw_input = &mut &encoded_bytes[..]; 56 | let mut input = MemTrackingInput::new(raw_input, mem_limit); 57 | let decoded_obj = T::decode(&mut input)?; 58 | assert_eq!(&decoded_obj, &obj); 59 | assert_eq!(input.used_mem(), expected_used_mem); 60 | if expected_used_mem > 0 { 61 | let raw_input = &mut &encoded_bytes[..]; 62 | let mut input = MemTrackingInput::new(raw_input, expected_used_mem); 63 | assert!(T::decode(&mut input).is_err()); 64 | } 65 | 66 | Ok(decoded_obj) 67 | } 68 | 69 | #[test] 70 | fn decode_simple_objects_works() { 71 | // Test simple objects 72 | assert!(decode_object(ARRAY, usize::MAX, 0).is_ok()); 73 | assert!(decode_object(Some(ARRAY), usize::MAX, 0).is_ok()); 74 | assert!(decode_object((ARRAY, ARRAY), usize::MAX, 0).is_ok()); 75 | assert!(decode_object(1u8, usize::MAX, 0).is_ok()); 76 | assert!(decode_object(1u32, usize::MAX, 0).is_ok()); 77 | assert!(decode_object(1f64, usize::MAX, 0).is_ok()); 78 | 79 | // Test heap objects 80 | assert!(decode_object(Box::new(ARRAY), usize::MAX, 1000).is_ok()); 81 | #[cfg(target_has_atomic = "ptr")] 82 | { 83 | use parity_scale_codec::alloc::sync::Arc; 84 | assert!(decode_object(Arc::new(ARRAY), usize::MAX, 1000).is_ok()); 85 | } 86 | assert!(decode_object(Rc::new(ARRAY), usize::MAX, 1000).is_ok()); 87 | // Simple collections 88 | assert!(decode_object(vec![ARRAY; 3], usize::MAX, 3000).is_ok()); 89 | assert!(decode_object(VecDeque::from(vec![ARRAY; 5]), usize::MAX, 5000).is_ok()); 90 | assert!(decode_object(String::from("test"), usize::MAX, 4).is_ok()); 91 | #[cfg(feature = "bytes")] 92 | assert!(decode_object(bytes::Bytes::from(&ARRAY[..]), usize::MAX, 1000).is_ok()); 93 | // Complex Collections 94 | assert!(decode_object(BTreeMap::::from([(1, 2), (2, 3)]), usize::MAX, 40).is_ok()); 95 | assert!(decode_object( 96 | BTreeMap::from([ 97 | ("key1".to_string(), "value1".to_string()), 98 | ("key2".to_string(), "value2".to_string()), 99 | ]), 100 | usize::MAX, 101 | 564, 102 | ) 103 | .is_ok()); 104 | assert!(decode_object(BTreeSet::::from([1, 2, 3, 4, 5]), usize::MAX, 24).is_ok()); 105 | assert!(decode_object(LinkedList::::from([1, 2, 3, 4, 5]), usize::MAX, 120).is_ok()); 106 | } 107 | 108 | #[test] 109 | fn decode_complex_objects_works() { 110 | assert!(decode_object(vec![vec![vec![vec![vec![1u8]]]]], usize::MAX, 97).is_ok()); 111 | assert!(decode_object(Box::new(Rc::new(vec![String::from("test")])), usize::MAX, 60).is_ok()); 112 | } 113 | 114 | #[test] 115 | fn decode_complex_derived_struct_works() { 116 | assert!(decode_object( 117 | ComplexStruct { 118 | test_enum: TestEnum::Array([0; 1000]), 119 | boxed_test_enum: Box::new(TestEnum::Empty), 120 | box_field: Box::new(1), 121 | vec: vec![1; 10], 122 | }, 123 | usize::MAX, 124 | 1015 125 | ) 126 | .is_ok()); 127 | } 128 | 129 | #[test] 130 | fn mem_limit_exceeded_is_triggered() { 131 | // Test simple heap object 132 | assert_eq!( 133 | decode_object(Box::new(ARRAY), 999, 999).unwrap_err().to_string(), 134 | "Heap memory limit exceeded while decoding" 135 | ); 136 | 137 | // Test complex derived struct 138 | assert_eq!( 139 | decode_object( 140 | ComplexStruct { 141 | test_enum: TestEnum::Array([0; 1000]), 142 | boxed_test_enum: Box::new(TestEnum::Empty), 143 | box_field: Box::new(1), 144 | vec: vec![1; 10], 145 | }, 146 | 1014, 147 | 1014 148 | ) 149 | .unwrap_err() 150 | .to_string(), 151 | "Could not decode `ComplexStruct::vec`:\n\tHeap memory limit exceeded while decoding\n" 152 | ); 153 | } 154 | -------------------------------------------------------------------------------- /src/max_encoded_len.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2021 Parity Technologies (UK) Ltd. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! `trait MaxEncodedLen` bounds the maximum encoded length of items. 17 | 18 | use crate::{alloc::boxed::Box, Compact, Encode}; 19 | use core::{ 20 | marker::PhantomData, 21 | mem, 22 | num::*, 23 | ops::{Range, RangeInclusive}, 24 | time::Duration, 25 | }; 26 | use impl_trait_for_tuples::impl_for_tuples; 27 | 28 | #[cfg(target_has_atomic = "ptr")] 29 | use crate::alloc::sync::Arc; 30 | 31 | /// Items implementing `MaxEncodedLen` have a statically known maximum encoded size. 32 | /// 33 | /// Some containers, such as `BoundedVec`, have enforced size limits and this trait 34 | /// can be implemented accurately. Other containers, such as `StorageMap`, do not have enforced size 35 | /// limits. For those containers, it is necessary to make a documented assumption about the maximum 36 | /// usage, and compute the max encoded length based on that assumption. 37 | pub trait MaxEncodedLen: Encode { 38 | /// Upper bound, in bytes, of the maximum encoded size of this item. 39 | fn max_encoded_len() -> usize; 40 | } 41 | 42 | macro_rules! impl_primitives { 43 | ( $($t:ty),+ ) => { 44 | $( 45 | impl MaxEncodedLen for $t { 46 | fn max_encoded_len() -> usize { 47 | mem::size_of::<$t>() 48 | } 49 | } 50 | )+ 51 | }; 52 | } 53 | 54 | impl_primitives!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, bool); 55 | 56 | impl_primitives!( 57 | NonZeroU8, 58 | NonZeroU16, 59 | NonZeroU32, 60 | NonZeroU64, 61 | NonZeroU128, 62 | NonZeroI8, 63 | NonZeroI16, 64 | NonZeroI32, 65 | NonZeroI64, 66 | NonZeroI128 67 | ); 68 | 69 | macro_rules! impl_compact { 70 | ($( $t:ty => $e:expr; )*) => { 71 | $( 72 | impl MaxEncodedLen for Compact<$t> { 73 | fn max_encoded_len() -> usize { 74 | $e 75 | } 76 | } 77 | )* 78 | }; 79 | } 80 | 81 | impl_compact!( 82 | () => 0; 83 | // github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L261 84 | u8 => 2; 85 | // github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L291 86 | u16 => 4; 87 | // github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L326 88 | u32 => 5; 89 | // github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L369 90 | u64 => 9; 91 | // github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L413 92 | u128 => 17; 93 | ); 94 | 95 | // impl_for_tuples for values 19 and higher fails because that's where the WrapperTypeEncode impl 96 | // stops. 97 | #[impl_for_tuples(18)] 98 | impl MaxEncodedLen for Tuple { 99 | fn max_encoded_len() -> usize { 100 | let mut len: usize = 0; 101 | for_tuples!( #( len = len.saturating_add(Tuple::max_encoded_len()); )* ); 102 | len 103 | } 104 | } 105 | 106 | impl MaxEncodedLen for [T; N] { 107 | fn max_encoded_len() -> usize { 108 | T::max_encoded_len().saturating_mul(N) 109 | } 110 | } 111 | 112 | impl MaxEncodedLen for Box { 113 | fn max_encoded_len() -> usize { 114 | T::max_encoded_len() 115 | } 116 | } 117 | 118 | #[cfg(target_has_atomic = "ptr")] 119 | impl MaxEncodedLen for Arc { 120 | fn max_encoded_len() -> usize { 121 | T::max_encoded_len() 122 | } 123 | } 124 | 125 | impl MaxEncodedLen for Option { 126 | fn max_encoded_len() -> usize { 127 | T::max_encoded_len().saturating_add(1) 128 | } 129 | } 130 | 131 | impl MaxEncodedLen for Result 132 | where 133 | T: MaxEncodedLen, 134 | E: MaxEncodedLen, 135 | { 136 | fn max_encoded_len() -> usize { 137 | T::max_encoded_len().max(E::max_encoded_len()).saturating_add(1) 138 | } 139 | } 140 | 141 | impl MaxEncodedLen for PhantomData { 142 | fn max_encoded_len() -> usize { 143 | 0 144 | } 145 | } 146 | 147 | impl MaxEncodedLen for Duration { 148 | fn max_encoded_len() -> usize { 149 | u64::max_encoded_len() + u32::max_encoded_len() 150 | } 151 | } 152 | 153 | impl MaxEncodedLen for Range { 154 | fn max_encoded_len() -> usize { 155 | T::max_encoded_len().saturating_mul(2) 156 | } 157 | } 158 | 159 | impl MaxEncodedLen for RangeInclusive { 160 | fn max_encoded_len() -> usize { 161 | T::max_encoded_len().saturating_mul(2) 162 | } 163 | } 164 | 165 | #[cfg(test)] 166 | mod tests { 167 | use super::*; 168 | 169 | macro_rules! test_compact_length { 170 | ($(fn $name:ident($t:ty);)*) => { 171 | $( 172 | #[test] 173 | fn $name() { 174 | assert_eq!(Compact(<$t>::MAX).encode().len(), Compact::<$t>::max_encoded_len()); 175 | } 176 | )* 177 | }; 178 | } 179 | 180 | test_compact_length!( 181 | fn compact_u8(u8); 182 | fn compact_u16(u16); 183 | fn compact_u32(u32); 184 | fn compact_u64(u64); 185 | fn compact_u128(u128); 186 | ); 187 | } 188 | -------------------------------------------------------------------------------- /src/encode_like.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::codec::Encode; 16 | 17 | /// A marker trait that tells the compiler that a type encode to the same representation as another 18 | /// type. 19 | /// 20 | /// E.g. `Vec` has the same encoded representation as `&[u8]`. 21 | /// 22 | /// # Example 23 | /// 24 | /// ``` 25 | /// # use parity_scale_codec::{EncodeLike, Encode}; 26 | /// fn encode_like>(data: &R) { 27 | /// data.encode(); // Valid `T` encoded value. 28 | /// } 29 | /// 30 | /// fn main() { 31 | /// // Just pass the a reference to the normal tuple. 32 | /// encode_like::<(u32, u32), _>(&(1u32, 2u32)); 33 | /// // Pass a tuple of references 34 | /// encode_like::<(u32, u32), _>(&(&1u32, &2u32)); 35 | /// // Pass a tuple of a reference and a value. 36 | /// encode_like::<(u32, u32), _>(&(&1u32, 2u32)); 37 | /// } 38 | /// ``` 39 | /// 40 | /// # Warning 41 | /// 42 | /// The relation is not symetric, `T` implements `EncodeLike` does not mean `U` has same 43 | /// representation as `T`. 44 | /// For instance we could imaging a non zero integer to be encoded to the same representation as 45 | /// the said integer but not the other way around. 46 | /// 47 | /// # Limitation 48 | /// 49 | /// Not all possible implementations of EncodeLike are implemented (for instance `Box>` 50 | /// does not implement `EncodeLike`). To bypass this issue either open a PR to add the new 51 | /// combination or use [`Ref`](./struct.Ref.html) reference wrapper or define your own wrapper 52 | /// and implement `EncodeLike` on it as such: 53 | /// ``` 54 | /// # use parity_scale_codec::{EncodeLike, Encode, WrapperTypeEncode}; 55 | /// fn encode_like>(data: &R) { 56 | /// data.encode(); // Valid `T` encoded value. 57 | /// } 58 | /// 59 | /// struct MyWrapper<'a>(&'a (Box>, u32)); 60 | /// impl<'a> core::ops::Deref for MyWrapper<'a> { // Or use derive_deref crate 61 | /// type Target = (Box>, u32); 62 | /// fn deref(&self) -> &Self::Target { &self.0 } 63 | /// } 64 | /// 65 | /// impl<'a> parity_scale_codec::WrapperTypeEncode for MyWrapper<'a> {} 66 | /// impl<'a> parity_scale_codec::EncodeLike<(u32, u32)> for MyWrapper<'a> {} 67 | /// 68 | /// fn main() { 69 | /// let v = (Box::new(Box::new(0)), 0); 70 | /// encode_like::<(u32, u32), _>(&MyWrapper(&v)); 71 | /// } 72 | /// ``` 73 | pub trait EncodeLike: Sized + Encode {} 74 | 75 | /// Reference wrapper that implement encode like any type that is encoded like its inner type. 76 | /// 77 | /// # Example 78 | /// 79 | /// ```rust 80 | /// # use parity_scale_codec::{EncodeLike, Ref}; 81 | /// fn foo>(t: T) -> T { 82 | /// store_t(Ref::from(&t)); // Store t without moving it, but only using a reference. 83 | /// t 84 | /// } 85 | /// 86 | /// fn store_t>(t: T) { 87 | /// } 88 | /// ``` 89 | pub struct Ref<'a, T: EncodeLike, U: Encode>(&'a T, core::marker::PhantomData); 90 | impl, U: Encode> core::ops::Deref for Ref<'_, T, U> { 91 | type Target = T; 92 | fn deref(&self) -> &Self::Target { 93 | self.0 94 | } 95 | } 96 | 97 | impl<'a, T: EncodeLike, U: Encode> From<&'a T> for Ref<'a, T, U> { 98 | fn from(x: &'a T) -> Self { 99 | Ref(x, Default::default()) 100 | } 101 | } 102 | impl, U: Encode> crate::WrapperTypeEncode for Ref<'_, T, U> {} 103 | impl, U: Encode> EncodeLike for Ref<'_, T, U> {} 104 | impl, U: Encode> EncodeLike for &Ref<'_, T, U> {} 105 | 106 | #[cfg(test)] 107 | mod tests { 108 | use super::*; 109 | use std::collections::BTreeMap; 110 | 111 | struct ComplexStuff(T); 112 | 113 | impl ComplexStuff { 114 | fn complex_method(value: &R) -> Vec 115 | where 116 | T: EncodeLike, 117 | { 118 | value.encode() 119 | } 120 | } 121 | 122 | #[test] 123 | fn vec_and_slice_are_working() { 124 | let slice: &[u8] = &[1, 2, 3, 4]; 125 | let data: Vec = slice.to_vec(); 126 | 127 | let data_encoded = data.encode(); 128 | let slice_encoded = ComplexStuff::>::complex_method(&slice); 129 | 130 | assert_eq!(slice_encoded, data_encoded); 131 | } 132 | 133 | #[test] 134 | fn btreemap_and_slice_are_working() { 135 | let slice: &[(u32, u32)] = &[(1, 2), (23, 24), (28, 30), (45, 80)]; 136 | let data: BTreeMap = slice.iter().copied().collect(); 137 | 138 | let data_encoded = data.encode(); 139 | let slice_encoded = ComplexStuff::>::complex_method(&slice); 140 | 141 | assert_eq!(slice_encoded, data_encoded); 142 | } 143 | 144 | #[test] 145 | fn interface_testing() { 146 | let value = 10u32; 147 | let data = (value, value, value); 148 | let encoded = ComplexStuff::<(u32, u32, u32)>::complex_method(&data); 149 | assert_eq!(data.encode(), encoded); 150 | let data = (&value, &value, &value); 151 | let encoded = ComplexStuff::<(u32, u32, u32)>::complex_method(&data); 152 | assert_eq!(data.encode(), encoded); 153 | let data = (&value, value, &value); 154 | let encoded = ComplexStuff::<(u32, u32, u32)>::complex_method(&data); 155 | assert_eq!(data.encode(), encoded); 156 | 157 | let vec_data: Vec = vec![1, 2, 3]; 158 | ComplexStuff::>::complex_method(&vec_data); 159 | ComplexStuff::<&'static str>::complex_method(&String::from("test")); 160 | ComplexStuff::<&'static str>::complex_method(&"test"); 161 | 162 | let slice: &[u8] = &vec_data; 163 | assert_eq!( 164 | ComplexStuff::<(u32, Vec)>::complex_method(&(1u32, slice.to_vec())), 165 | ComplexStuff::<(u32, Vec)>::complex_method(&(1u32, slice)) 166 | ); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /tests/max_encoded_len.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020-2021 Parity Technologies (UK) Ltd. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Tests for MaxEncodedLen derive macro 17 | #![cfg(all(feature = "derive", feature = "max-encoded-len"))] 18 | 19 | use parity_scale_codec::{Compact, Decode, Encode, EncodeAsRef, MaxEncodedLen}; 20 | 21 | #[derive(Encode, MaxEncodedLen)] 22 | struct Primitives { 23 | bool: bool, 24 | eight: u8, 25 | } 26 | 27 | #[test] 28 | fn primitives_max_length() { 29 | assert_eq!(Primitives::max_encoded_len(), 2); 30 | } 31 | 32 | #[derive(Encode, MaxEncodedLen)] 33 | struct SkippedField { 34 | bool: bool, 35 | #[codec(skip)] 36 | _skipped: u64, 37 | } 38 | 39 | #[test] 40 | fn skipped_field_max_length() { 41 | assert_eq!(SkippedField::max_encoded_len(), 1); 42 | } 43 | 44 | #[derive(Encode, MaxEncodedLen)] 45 | struct Composites { 46 | fixed_size_array: [u8; 128], 47 | tuple: (u128, u128), 48 | } 49 | 50 | #[test] 51 | fn composites_max_length() { 52 | assert_eq!(Composites::max_encoded_len(), 128 + 16 + 16); 53 | } 54 | 55 | #[derive(Encode, MaxEncodedLen)] 56 | struct Generic { 57 | one: T, 58 | two: T, 59 | } 60 | 61 | #[test] 62 | fn generic_max_length() { 63 | assert_eq!(Generic::::max_encoded_len(), u8::max_encoded_len() * 2); 64 | assert_eq!(Generic::::max_encoded_len(), u32::max_encoded_len() * 2); 65 | } 66 | 67 | #[derive(Encode, MaxEncodedLen)] 68 | struct TwoGenerics { 69 | t: T, 70 | u: U, 71 | } 72 | 73 | #[test] 74 | fn two_generics_max_length() { 75 | assert_eq!( 76 | TwoGenerics::::max_encoded_len(), 77 | u8::max_encoded_len() + u16::max_encoded_len() 78 | ); 79 | assert_eq!( 80 | TwoGenerics::, [u16; 8]>::max_encoded_len(), 81 | Compact::::max_encoded_len() + <[u16; 8]>::max_encoded_len() 82 | ); 83 | } 84 | 85 | #[derive(Encode, MaxEncodedLen)] 86 | struct UnitStruct; 87 | 88 | #[test] 89 | fn unit_struct_max_length() { 90 | assert_eq!(UnitStruct::max_encoded_len(), 0); 91 | } 92 | 93 | #[derive(Encode, MaxEncodedLen)] 94 | struct TupleStruct(u8, u32); 95 | 96 | #[test] 97 | fn tuple_struct_max_length() { 98 | assert_eq!(TupleStruct::max_encoded_len(), u8::max_encoded_len() + u32::max_encoded_len()); 99 | } 100 | 101 | #[derive(Encode, MaxEncodedLen)] 102 | struct TupleGeneric(T, T); 103 | 104 | #[test] 105 | fn tuple_generic_max_length() { 106 | assert_eq!(TupleGeneric::::max_encoded_len(), u8::max_encoded_len() * 2); 107 | assert_eq!(TupleGeneric::::max_encoded_len(), u32::max_encoded_len() * 2); 108 | } 109 | 110 | #[derive(Encode)] 111 | struct ConstU32; 112 | 113 | trait Get: Encode { 114 | fn get() -> T; 115 | } 116 | 117 | impl Get for ConstU32 { 118 | fn get() -> u32 { 119 | N 120 | } 121 | } 122 | 123 | #[derive(Encode)] 124 | struct SomeVec { 125 | element: T, 126 | size: N, 127 | } 128 | 129 | impl> MaxEncodedLen for SomeVec { 130 | fn max_encoded_len() -> usize { 131 | T::max_encoded_len() * N::get() as usize 132 | } 133 | } 134 | 135 | #[derive(Encode, MaxEncodedLen)] 136 | #[codec(mel_bound(N: Get))] 137 | struct SizeGeneric { 138 | vec: SomeVec, 139 | } 140 | 141 | #[test] 142 | fn some_vec_max_length() { 143 | assert_eq!(SomeVec::>::max_encoded_len(), u64::max_encoded_len() * 3); 144 | assert_eq!(SizeGeneric::>::max_encoded_len(), u64::max_encoded_len() * 5); 145 | } 146 | 147 | #[derive(Encode, MaxEncodedLen)] 148 | #[allow(unused)] 149 | enum UnitEnum { 150 | A, 151 | B, 152 | } 153 | 154 | #[test] 155 | fn unit_enum_max_length() { 156 | assert_eq!(UnitEnum::max_encoded_len(), 1); 157 | } 158 | 159 | #[derive(Encode, MaxEncodedLen)] 160 | #[allow(unused)] 161 | enum TupleEnum { 162 | A(u32), 163 | B, 164 | } 165 | 166 | #[test] 167 | fn tuple_enum_max_length() { 168 | assert_eq!(TupleEnum::max_encoded_len(), 1 + u32::max_encoded_len()); 169 | } 170 | 171 | #[derive(Encode, MaxEncodedLen)] 172 | #[allow(unused)] 173 | enum StructEnum { 174 | A { sixty_four: u64, one_twenty_eight: u128 }, 175 | B, 176 | } 177 | 178 | #[test] 179 | fn struct_enum_max_length() { 180 | assert_eq!(StructEnum::max_encoded_len(), 1 + u64::max_encoded_len() + u128::max_encoded_len()); 181 | } 182 | 183 | // ensure that enums take the max of variant length, not the sum 184 | #[derive(Encode, MaxEncodedLen)] 185 | #[allow(unused)] 186 | enum EnumMaxNotSum { 187 | A(u32), 188 | B(u32), 189 | } 190 | 191 | #[test] 192 | fn enum_max_not_sum_max_length() { 193 | assert_eq!(EnumMaxNotSum::max_encoded_len(), 1 + u32::max_encoded_len()); 194 | } 195 | 196 | #[derive(Encode, MaxEncodedLen)] 197 | struct CompactField { 198 | #[codec(compact)] 199 | a: u32, 200 | b: u32, 201 | } 202 | 203 | #[test] 204 | fn compact_field_max_length() { 205 | assert_eq!( 206 | CompactField::max_encoded_len(), 207 | Compact::::max_encoded_len() + u32::max_encoded_len() 208 | ); 209 | } 210 | 211 | #[derive(Encode, MaxEncodedLen)] 212 | #[allow(unused)] 213 | enum CompactVariant { 214 | A(u8), 215 | B(#[codec(compact)] u32), 216 | } 217 | 218 | #[test] 219 | fn compact_variant_max_length() { 220 | assert_eq!(CompactVariant::max_encoded_len(), 1 + Compact::::max_encoded_len()); 221 | } 222 | 223 | #[derive(Encode, MaxEncodedLen)] 224 | struct U64(u64); 225 | 226 | impl<'a> EncodeAsRef<'a, u32> for U64 { 227 | // Obviously broken but will do for this test 228 | type RefType = &'a u32; 229 | } 230 | 231 | #[derive(Encode, MaxEncodedLen)] 232 | struct EncodedAsField { 233 | a: u32, 234 | #[codec(encoded_as = "U64")] 235 | b: u32, 236 | } 237 | 238 | #[test] 239 | fn encoded_as_field_max_length() { 240 | assert_eq!(EncodedAsField::max_encoded_len(), u32::max_encoded_len() + U64::max_encoded_len()); 241 | } 242 | 243 | #[derive(Encode, MaxEncodedLen)] 244 | #[allow(unused)] 245 | enum EncodedAsVariant { 246 | A(#[codec(encoded_as = "U64")] u32), 247 | B(u32), 248 | } 249 | 250 | #[test] 251 | fn encoded_as_variant_max_length() { 252 | assert_eq!(EncodedAsVariant::max_encoded_len(), 1 + U64::max_encoded_len()); 253 | } 254 | 255 | #[test] 256 | fn skip_type_params() { 257 | #[derive(Encode, Decode, MaxEncodedLen)] 258 | #[codec(mel_bound(skip_type_params(N)))] 259 | struct SomeData { 260 | element: T, 261 | size: std::marker::PhantomData, 262 | } 263 | 264 | trait SomeTrait {} 265 | 266 | struct SomeStruct; 267 | 268 | impl SomeTrait for SomeStruct {} 269 | 270 | assert_eq!(SomeData::::max_encoded_len(), 4); 271 | } 272 | 273 | #[test] 274 | fn skip_enum_struct_test() { 275 | #[derive(Default)] 276 | struct NoCodecType; 277 | 278 | struct NoCodecNoDefaultType; 279 | 280 | #[derive(Encode, Decode, MaxEncodedLen)] 281 | enum Enum { 282 | #[codec(skip)] 283 | A(S), 284 | B { 285 | #[codec(skip)] 286 | _b1: T, 287 | b2: u32, 288 | }, 289 | C(#[codec(skip)] T, u32), 290 | } 291 | 292 | #[derive(Encode, Decode, MaxEncodedLen)] 293 | struct StructNamed { 294 | #[codec(skip)] 295 | a: T, 296 | b: u32, 297 | } 298 | 299 | #[derive(Encode, Decode, MaxEncodedLen)] 300 | struct StructUnnamed(#[codec(skip)] T, u32); 301 | 302 | assert_eq!(Enum::::max_encoded_len(), 5); 303 | assert_eq!(StructNamed::::max_encoded_len(), 4); 304 | assert_eq!(StructUnnamed::::max_encoded_len(), 4); 305 | 306 | // Use the fields to avoid unused warnings. 307 | let _ = Enum::::A(NoCodecNoDefaultType); 308 | let _ = StructNamed:: { a: NoCodecType, b: 0 }.a; 309 | } 310 | -------------------------------------------------------------------------------- /src/bit_vec.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! `BitVec` specific serialization. 16 | 17 | use crate::{ 18 | codec::decode_vec_with_len, Compact, Decode, DecodeWithMemTracking, Encode, EncodeLike, Error, 19 | Input, Output, 20 | }; 21 | use bitvec::{ 22 | boxed::BitBox, order::BitOrder, slice::BitSlice, store::BitStore, vec::BitVec, view::BitView, 23 | }; 24 | 25 | impl Encode for BitSlice { 26 | fn encode_to(&self, dest: &mut W) { 27 | let bits = self.len(); 28 | assert!( 29 | bits <= ARCH32BIT_BITSLICE_MAX_BITS, 30 | "Attempted to encode a BitSlice with too many bits.", 31 | ); 32 | Compact(bits as u32).encode_to(dest); 33 | 34 | // Iterate over chunks 35 | for chunk in self.chunks(core::mem::size_of::() * 8) { 36 | let mut element = T::ZERO; 37 | element.view_bits_mut::()[..chunk.len()].copy_from_bitslice(chunk); 38 | element.encode_to(dest); 39 | } 40 | } 41 | } 42 | 43 | impl Encode for BitVec { 44 | fn encode_to(&self, dest: &mut W) { 45 | self.as_bitslice().encode_to(dest) 46 | } 47 | } 48 | 49 | impl EncodeLike for BitVec {} 50 | 51 | /// Equivalent of `BitStore::MAX_BITS` on 32bit machine. 52 | const ARCH32BIT_BITSLICE_MAX_BITS: usize = 0x1fff_ffff; 53 | 54 | impl Decode for BitVec { 55 | fn decode(input: &mut I) -> Result { 56 | >::decode(input).and_then(move |Compact(bits)| { 57 | // Otherwise it is impossible to store it on 32bit machine. 58 | if bits as usize > ARCH32BIT_BITSLICE_MAX_BITS { 59 | return Err("Attempt to decode a BitVec with too many bits".into()); 60 | } 61 | let vec = decode_vec_with_len(input, bitvec::mem::elts::(bits as usize))?; 62 | 63 | let mut result = Self::try_from_vec(vec).map_err(|_| { 64 | Error::from( 65 | "UNEXPECTED ERROR: `bits` is less or equal to 66 | `ARCH32BIT_BITSLICE_MAX_BITS`; So BitVec must be able to handle the number of 67 | segment needed for `bits` to be represented; qed", 68 | ) 69 | })?; 70 | 71 | assert!(bits as usize <= result.len()); 72 | result.truncate(bits as usize); 73 | Ok(result) 74 | }) 75 | } 76 | } 77 | 78 | impl DecodeWithMemTracking for BitVec {} 79 | 80 | impl Encode for BitBox { 81 | fn encode_to(&self, dest: &mut W) { 82 | self.as_bitslice().encode_to(dest) 83 | } 84 | } 85 | 86 | impl EncodeLike for BitBox {} 87 | 88 | impl Decode for BitBox { 89 | fn decode(input: &mut I) -> Result { 90 | Ok(BitVec::::decode(input)?.into()) 91 | } 92 | } 93 | 94 | impl DecodeWithMemTracking for BitBox {} 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | use super::*; 99 | use crate::{codec::INITIAL_PREALLOCATION, CompactLen}; 100 | use bitvec::{ 101 | bitvec, 102 | order::{Lsb0, Msb0}, 103 | }; 104 | 105 | macro_rules! test_data { 106 | ($inner_type:ident) => ( 107 | [ 108 | BitVec::<$inner_type, Msb0>::new(), 109 | bitvec![$inner_type, Msb0; 0], 110 | bitvec![$inner_type, Msb0; 1], 111 | bitvec![$inner_type, Msb0; 0, 0], 112 | bitvec![$inner_type, Msb0; 1, 0], 113 | bitvec![$inner_type, Msb0; 0, 1], 114 | bitvec![$inner_type, Msb0; 1, 1], 115 | bitvec![$inner_type, Msb0; 1, 0, 1], 116 | bitvec![$inner_type, Msb0; 0, 1, 0, 1, 0, 1, 1], 117 | bitvec![$inner_type, Msb0; 0, 1, 0, 1, 0, 1, 1, 0], 118 | bitvec![$inner_type, Msb0; 1, 1, 0, 1, 0, 1, 1, 0, 1], 119 | bitvec![$inner_type, Msb0; 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0], 120 | bitvec![$inner_type, Msb0; 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0], 121 | bitvec![$inner_type, Msb0; 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0], 122 | bitvec![$inner_type, Msb0; 0; 15], 123 | bitvec![$inner_type, Msb0; 1; 16], 124 | bitvec![$inner_type, Msb0; 0; 17], 125 | bitvec![$inner_type, Msb0; 1; 31], 126 | bitvec![$inner_type, Msb0; 0; 32], 127 | bitvec![$inner_type, Msb0; 1; 33], 128 | bitvec![$inner_type, Msb0; 0; 63], 129 | bitvec![$inner_type, Msb0; 1; 64], 130 | bitvec![$inner_type, Msb0; 0; 65], 131 | bitvec![$inner_type, Msb0; 1; INITIAL_PREALLOCATION * 8 + 1], 132 | bitvec![$inner_type, Msb0; 0; INITIAL_PREALLOCATION * 9], 133 | bitvec![$inner_type, Msb0; 1; INITIAL_PREALLOCATION * 32 + 1], 134 | bitvec![$inner_type, Msb0; 0; INITIAL_PREALLOCATION * 33], 135 | ] 136 | ) 137 | } 138 | 139 | #[test] 140 | fn bitvec_u8() { 141 | for v in &test_data!(u8) { 142 | let encoded = v.encode(); 143 | assert_eq!(*v, BitVec::::decode(&mut &encoded[..]).unwrap()); 144 | 145 | let elements = bitvec::mem::elts::(v.len()); 146 | let compact_len = Compact::compact_len(&(v.len() as u32)); 147 | assert_eq!(compact_len + elements, encoded.len(), "{}", v); 148 | } 149 | } 150 | 151 | #[test] 152 | fn bitvec_u16() { 153 | for v in &test_data!(u16) { 154 | let encoded = v.encode(); 155 | assert_eq!(*v, BitVec::::decode(&mut &encoded[..]).unwrap()); 156 | 157 | let elements = bitvec::mem::elts::(v.len()); 158 | let compact_len = Compact::compact_len(&(v.len() as u32)); 159 | assert_eq!(compact_len + elements * 2, encoded.len(), "{}", v); 160 | } 161 | } 162 | 163 | #[test] 164 | fn bitvec_u32() { 165 | for v in &test_data!(u32) { 166 | let encoded = v.encode(); 167 | assert_eq!(*v, BitVec::::decode(&mut &encoded[..]).unwrap()); 168 | 169 | let elements = bitvec::mem::elts::(v.len()); 170 | let compact_len = Compact::compact_len(&(v.len() as u32)); 171 | assert_eq!(compact_len + elements * 4, encoded.len(), "{}", v); 172 | } 173 | } 174 | 175 | #[test] 176 | fn bitvec_u64() { 177 | for v in &test_data!(u64) { 178 | let encoded = v.encode(); 179 | assert_eq!(*v, BitVec::::decode(&mut &encoded[..]).unwrap()); 180 | 181 | let elements = bitvec::mem::elts::(v.len()); 182 | let compact_len = Compact::compact_len(&(v.len() as u32)); 183 | assert_eq!(compact_len + elements * 8, encoded.len(), "{}", v); 184 | } 185 | } 186 | 187 | #[test] 188 | fn bitslice() { 189 | let data: &[u8] = &[0x69]; 190 | let slice = BitSlice::::from_slice(data); 191 | let encoded = slice.encode(); 192 | let decoded = BitVec::::decode(&mut &encoded[..]).unwrap(); 193 | assert_eq!(slice, decoded.as_bitslice()); 194 | } 195 | 196 | #[test] 197 | fn bitbox() { 198 | let data: &[u8] = &[5, 10]; 199 | let slice = BitSlice::::from_slice(data); 200 | let bb = BitBox::::from_bitslice(slice); 201 | let encoded = bb.encode(); 202 | let decoded = BitBox::::decode(&mut &encoded[..]).unwrap(); 203 | assert_eq!(bb, decoded); 204 | } 205 | 206 | #[test] 207 | fn bitvec_u8_encodes_as_expected() { 208 | let cases = vec![ 209 | (bitvec![u8, Lsb0; 0, 0, 1, 1].encode(), (Compact(4u32), 0b00001100u8).encode()), 210 | (bitvec![u8, Lsb0; 0, 1, 1, 1].encode(), (Compact(4u32), 0b00001110u8).encode()), 211 | (bitvec![u8, Lsb0; 1, 1, 1, 1].encode(), (Compact(4u32), 0b00001111u8).encode()), 212 | (bitvec![u8, Lsb0; 1, 1, 1, 1, 1].encode(), (Compact(5u32), 0b00011111u8).encode()), 213 | (bitvec![u8, Lsb0; 1, 1, 1, 1, 1, 0].encode(), (Compact(6u32), 0b00011111u8).encode()), 214 | ( 215 | bitvec![u8, Lsb0; 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1].encode(), 216 | (Compact(12u32), 0b00011111u8, 0b00001011u8).encode(), 217 | ), 218 | ]; 219 | 220 | for (idx, (actual, expected)) in cases.into_iter().enumerate() { 221 | assert_eq!(actual, expected, "case at index {} failed; encodings differ", idx); 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Rust CI/CD 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | env: 10 | IMAGE: paritytech/ci-unified:bullseye-1.81.0 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 13 | cancel-in-progress: true 14 | jobs: 15 | set-image: 16 | # GitHub Actions does not allow using 'env' in a container context. 17 | # This workaround sets the container image for each job using 'set-image' job output. 18 | runs-on: ubuntu-latest 19 | outputs: 20 | IMAGE: ${{ steps.set_image.outputs.IMAGE }} 21 | steps: 22 | - id: set_image 23 | run: echo "IMAGE=${{ env.IMAGE }}" >> $GITHUB_OUTPUT 24 | 25 | # Checks 26 | fmt: 27 | runs-on: ubuntu-latest 28 | needs: [ set-image ] 29 | container: ${{ needs.set-image.outputs.IMAGE }} 30 | steps: 31 | - name: Checkout code 32 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 33 | 34 | - name: Show Cargo version 35 | run: cargo +nightly -vV 36 | 37 | - name: Cargo fmt 38 | run: cargo +nightly fmt --all -- --check 39 | 40 | clippy: 41 | runs-on: ubuntu-latest 42 | needs: [ set-image ] 43 | container: ${{ needs.set-image.outputs.IMAGE }} 44 | steps: 45 | - name: Checkout code/. 46 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 47 | 48 | - name: Show Rust version 49 | run: | 50 | cargo -vV 51 | rustc -vV 52 | rustup show 53 | 54 | - name: Cache Rust dependencies 55 | uses: swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 56 | with: 57 | key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} 58 | 59 | - name: Run Clippy 60 | run: | 61 | cargo +stable clippy --locked -- -Dwarnings 62 | cargo +stable clippy --locked -p parity-scale-codec-derive -- -Dwarnings 63 | cargo +stable clippy --locked --test clippy -- -Dwarnings 64 | 65 | checks: 66 | runs-on: ubuntu-latest 67 | needs: [ set-image ] 68 | container: ${{ needs.set-image.outputs.IMAGE }} 69 | steps: 70 | - name: Checkout code 71 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 72 | 73 | - name: Show Rust version 74 | run: | 75 | cargo -vV 76 | rustc -vV 77 | rustup show 78 | 79 | - name: Cache Rust dependencies 80 | uses: swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 81 | with: 82 | key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} 83 | 84 | - name: Check Rust Stable (no_derive_no_std) 85 | run: time cargo +stable check --verbose --no-default-features --features bit-vec,bytes,generic-array 86 | 87 | - name: Check Rust Stable (no_std-chain-error) 88 | run: | 89 | export RUSTFLAGS='-Cdebug-assertions=y -Dwarnings' 90 | time cargo +stable check --verbose --no-default-features --features chain-error 91 | 92 | - name: check-rust-stable-no_derive 93 | run: | 94 | export RUSTFLAGS='-Cdebug-assertions=y -Dwarnings' 95 | time cargo +stable check --verbose --features bit-vec,bytes,generic-array 96 | 97 | - name: check-rust-stable-only_mel 98 | run: | 99 | export RUSTFLAGS='-Cdebug-assertions=y -Dwarnings' 100 | time cargo +stable check --verbose --features max-encoded-len 101 | 102 | # Tests 103 | tests: 104 | runs-on: ubuntu-latest 105 | needs: [ set-image ] 106 | container: ${{ needs.set-image.outputs.IMAGE }} 107 | steps: 108 | - name: Checkout code 109 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 110 | 111 | - name: Show Rust version 112 | run: | 113 | cargo -vV 114 | rustc -vV 115 | rustup show 116 | 117 | - name: Cache Rust dependencies 118 | uses: swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 119 | with: 120 | key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} 121 | 122 | - name: Test Rust Stable 123 | run: | 124 | export RUSTFLAGS='-Cdebug-assertions=y -Dwarnings' 125 | time cargo +stable test --verbose --all --features bit-vec,bytes,generic-array,derive,max-encoded-len 126 | 127 | - name: Test Rust Stable (no_derive) 128 | run: | 129 | export RUSTFLAGS='-Cdebug-assertions=y -Dwarnings' 130 | time cargo +stable test --verbose --features bit-vec,bytes,generic-array 131 | 132 | - name: Test Rust Stable (only_mel) 133 | run: | 134 | export RUSTFLAGS='-Cdebug-assertions=y -Dwarnings' 135 | time cargo +stable test --verbose --features max-encoded-len 136 | 137 | - name: Test Rust Stable (only_mel-no_default_std) 138 | run: | 139 | export RUSTFLAGS='-Cdebug-assertions=y -Dwarnings' 140 | time cargo +stable test --verbose --features max-encoded-len,std --no-default-features 141 | 142 | - name: Run Nightly Tests 143 | run: | 144 | export RUSTFLAGS='-Cdebug-assertions=y -Dwarnings' 145 | time cargo +nightly test --verbose --lib btree_utils 146 | 147 | # Benches 148 | bench-rust-nightly: 149 | runs-on: ubuntu-latest 150 | needs: [ set-image ] 151 | strategy: 152 | matrix: 153 | feature: [ bit-vec,bytes,generic-array,derive,max-encoded-len ] 154 | container: ${{ needs.set-image.outputs.IMAGE }} 155 | steps: 156 | - name: Checkout code 157 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 158 | 159 | - name: Cache Rust dependencies 160 | uses: swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 161 | with: 162 | key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} 163 | 164 | - name: Bench Rust Nightly 165 | run: | 166 | export RUSTFLAGS='-Cdebug-assertions=y -Dwarnings' 167 | time cargo +nightly bench --features ${{ matrix.feature }} 168 | 169 | miri: 170 | runs-on: ubuntu-latest 171 | needs: [ set-image ] 172 | strategy: 173 | matrix: 174 | feature: [ bit-vec,bytes,generic-array,arbitrary ] 175 | container: ${{ needs.set-image.outputs.IMAGE }} 176 | steps: 177 | - name: Checkout code 178 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 179 | 180 | - name: Cache Rust dependencies 181 | uses: swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 182 | with: 183 | key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} 184 | 185 | - name: Run Miri 186 | run: | 187 | export RUST_BACKTRACE=1 188 | export RUSTFLAGS='-Cdebug-assertions=y -Dwarnings' 189 | export MIRIFLAGS='-Zmiri-disable-isolation' 190 | time cargo +nightly miri test --features ${{ matrix.feature }} --release 191 | 192 | # Build 193 | 194 | build-linux-ubuntu-amd64: 195 | runs-on: ubuntu-latest 196 | needs: [ set-image, clippy, checks, tests ] 197 | container: ${{ needs.set-image.outputs.IMAGE }} 198 | steps: 199 | - name: Checkout code 200 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 201 | 202 | - name: Cache Rust dependencies 203 | uses: swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 204 | with: 205 | key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} 206 | 207 | - name: Build for Linux (Ubuntu, AMD64) 208 | run: cargo build --verbose --release --features bit-vec,bytes,generic-array,derive 209 | 210 | publish-dry-run: 211 | runs-on: ubuntu-latest 212 | needs: [ set-image, build-linux-ubuntu-amd64 ] 213 | container: ${{ needs.set-image.outputs.IMAGE }} 214 | steps: 215 | - name: Checkout code 216 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 217 | 218 | - name: Cache Rust dependencies 219 | uses: swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 220 | with: 221 | key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.lock') }} 222 | 223 | - name: Dry Run Publish 224 | if: github.event_name == 'pull_request' 225 | run: cargo publish -p parity-scale-codec-derive --dry-run 226 | -------------------------------------------------------------------------------- /src/encode_append.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use core::iter::ExactSizeIterator; 16 | 17 | use crate::{ 18 | alloc::vec::Vec, 19 | compact::{Compact, CompactLen}, 20 | encode_like::EncodeLike, 21 | Decode, Encode, Error, 22 | }; 23 | 24 | /// Trait that allows to append items to an encoded representation without 25 | /// decoding all previous added items. 26 | pub trait EncodeAppend { 27 | /// The item that will be appended. 28 | type Item: Encode; 29 | 30 | /// Append all items in `iter` to the given `self_encoded` representation 31 | /// or if `self_encoded` value is empty, `iter` is encoded to the `Self` representation. 32 | /// 33 | /// # Example 34 | /// 35 | /// ``` 36 | /// # use parity_scale_codec::EncodeAppend; 37 | /// 38 | /// // Some encoded data 39 | /// let data = Vec::new(); 40 | /// 41 | /// let item = 8u32; 42 | /// let encoded = as EncodeAppend>::append_or_new(data, std::iter::once(&item)).expect("Adds new element"); 43 | /// 44 | /// // Add multiple element 45 | /// as EncodeAppend>::append_or_new(encoded, &[700u32, 800u32, 10u32]).expect("Adds new elements"); 46 | /// ``` 47 | fn append_or_new(self_encoded: Vec, iter: I) -> Result, Error> 48 | where 49 | I: IntoIterator, 50 | EncodeLikeItem: EncodeLike, 51 | I::IntoIter: ExactSizeIterator; 52 | } 53 | 54 | impl EncodeAppend for Vec { 55 | type Item = T; 56 | 57 | fn append_or_new(self_encoded: Vec, iter: I) -> Result, Error> 58 | where 59 | I: IntoIterator, 60 | EncodeLikeItem: EncodeLike, 61 | I::IntoIter: ExactSizeIterator, 62 | { 63 | append_or_new_impl(self_encoded, iter) 64 | } 65 | } 66 | 67 | impl EncodeAppend for crate::alloc::collections::VecDeque { 68 | type Item = T; 69 | 70 | fn append_or_new(self_encoded: Vec, iter: I) -> Result, Error> 71 | where 72 | I: IntoIterator, 73 | EncodeLikeItem: EncodeLike, 74 | I::IntoIter: ExactSizeIterator, 75 | { 76 | append_or_new_impl(self_encoded, iter) 77 | } 78 | } 79 | 80 | /// Extends a SCALE-encoded vector with elements from the given `iter`. 81 | /// 82 | /// `vec` must either be empty, or contain a valid SCALE-encoded `Vec` payload. 83 | fn append_or_new_impl(mut vec: Vec, iter: I) -> Result, Error> 84 | where 85 | Item: Encode, 86 | I: IntoIterator, 87 | I::IntoIter: ExactSizeIterator, 88 | { 89 | let iter = iter.into_iter(); 90 | let items_to_append = iter.len(); 91 | 92 | if vec.is_empty() { 93 | crate::codec::compact_encode_len_to(&mut vec, items_to_append)?; 94 | } else { 95 | let old_item_count = u32::from(Compact::::decode(&mut &vec[..])?); 96 | let new_item_count = old_item_count 97 | .checked_add(items_to_append as u32) 98 | .ok_or("cannot append new items into a SCALE-encoded vector: length overflow due to too many items")?; 99 | 100 | let old_item_count_encoded_bytesize = Compact::::compact_len(&old_item_count); 101 | let new_item_count_encoded_bytesize = Compact::::compact_len(&new_item_count); 102 | 103 | if old_item_count_encoded_bytesize == new_item_count_encoded_bytesize { 104 | // The size of the length as encoded by SCALE didn't change, so we can just 105 | // keep the old buffer as-is. We just need to update the length prefix. 106 | Compact(new_item_count).using_encoded(|length_encoded| { 107 | vec[..old_item_count_encoded_bytesize].copy_from_slice(length_encoded) 108 | }); 109 | } else { 110 | // We can't update the length as the new length prefix will take up more 111 | // space when encoded, so we need to move our data to make space for it. 112 | 113 | // If this overflows then it means that `vec` is bigger that half of the 114 | // total address space, which means that it will be impossible to allocate 115 | // enough memory for another vector of at least the same size. 116 | // 117 | // So let's just immediately bail with an error if this happens. 118 | let new_capacity = vec.len().checked_mul(2) 119 | .ok_or("cannot append new items into a SCALE-encoded vector: new vector won't fit in memory")?; 120 | let mut new_vec = Vec::with_capacity(new_capacity); 121 | 122 | crate::codec::compact_encode_len_to(&mut new_vec, new_item_count as usize)?; 123 | new_vec.extend_from_slice(&vec[old_item_count_encoded_bytesize..]); 124 | vec = new_vec; 125 | } 126 | } 127 | 128 | // And now we just need to append the new items. 129 | iter.for_each(|e| e.encode_to(&mut vec)); 130 | Ok(vec) 131 | } 132 | 133 | #[cfg(test)] 134 | mod tests { 135 | use super::*; 136 | use crate::{Encode, EncodeLike, Input}; 137 | use std::collections::VecDeque; 138 | 139 | const TEST_VALUE: u32 = { 140 | #[cfg(not(miri))] 141 | { 142 | 1_000_000 143 | } 144 | #[cfg(miri)] 145 | { 146 | 1_000 147 | } 148 | }; 149 | 150 | #[test] 151 | fn vec_encode_append_works() { 152 | let encoded = (0..TEST_VALUE).fold(Vec::new(), |encoded, v| { 153 | as EncodeAppend>::append_or_new(encoded, std::iter::once(&v)).unwrap() 154 | }); 155 | 156 | let decoded = Vec::::decode(&mut &encoded[..]).unwrap(); 157 | assert_eq!(decoded, (0..TEST_VALUE).collect::>()); 158 | } 159 | 160 | #[test] 161 | fn vec_encode_append_multiple_items_works() { 162 | let encoded = (0..TEST_VALUE).fold(Vec::new(), |encoded, v| { 163 | as EncodeAppend>::append_or_new(encoded, [v, v, v, v]).unwrap() 164 | }); 165 | 166 | let decoded = Vec::::decode(&mut &encoded[..]).unwrap(); 167 | let expected = (0..TEST_VALUE).fold(Vec::new(), |mut vec, i| { 168 | vec.append(&mut vec![i, i, i, i]); 169 | vec 170 | }); 171 | assert_eq!(decoded, expected); 172 | } 173 | 174 | #[test] 175 | fn vecdeque_encode_append_works() { 176 | let encoded = (0..TEST_VALUE).fold(Vec::new(), |encoded, v| { 177 | as EncodeAppend>::append_or_new(encoded, std::iter::once(&v)).unwrap() 178 | }); 179 | 180 | let decoded = VecDeque::::decode(&mut &encoded[..]).unwrap(); 181 | assert_eq!(decoded, (0..TEST_VALUE).collect::>()); 182 | } 183 | 184 | #[test] 185 | fn vecdeque_encode_append_multiple_items_works() { 186 | let encoded = (0..TEST_VALUE).fold(Vec::new(), |encoded, v| { 187 | as EncodeAppend>::append_or_new(encoded, [v, v, v, v]).unwrap() 188 | }); 189 | 190 | let decoded = VecDeque::::decode(&mut &encoded[..]).unwrap(); 191 | let expected = (0..TEST_VALUE).fold(Vec::new(), |mut vec, i| { 192 | vec.append(&mut vec![i, i, i, i]); 193 | vec 194 | }); 195 | assert_eq!(decoded, expected); 196 | } 197 | 198 | #[test] 199 | fn append_non_copyable() { 200 | #[derive(Eq, PartialEq, Debug)] 201 | struct NoCopy { 202 | data: u32, 203 | } 204 | 205 | impl EncodeLike for NoCopy {} 206 | 207 | impl Encode for NoCopy { 208 | fn encode(&self) -> Vec { 209 | self.data.encode() 210 | } 211 | } 212 | 213 | impl Decode for NoCopy { 214 | fn decode(input: &mut I) -> Result { 215 | u32::decode(input).map(|data| Self { data }) 216 | } 217 | } 218 | 219 | let append = NoCopy { data: 100 }; 220 | let data = Vec::new(); 221 | let encoded = 222 | as EncodeAppend>::append_or_new(data, std::iter::once(&append)).unwrap(); 223 | 224 | let decoded = >::decode(&mut &encoded[..]).unwrap(); 225 | assert_eq!(vec![append], decoded); 226 | } 227 | 228 | #[test] 229 | fn vec_encode_like_append_works() { 230 | let encoded = (0..TEST_VALUE).fold(Vec::new(), |encoded, v| { 231 | as EncodeAppend>::append_or_new(encoded, std::iter::once(Box::new(v))) 232 | .unwrap() 233 | }); 234 | 235 | let decoded = Vec::::decode(&mut &encoded[..]).unwrap(); 236 | assert_eq!(decoded, (0..TEST_VALUE).collect::>()); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /benches/benches.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::{ 16 | any::type_name, 17 | convert::{TryFrom, TryInto}, 18 | hint::black_box, 19 | time::Duration, 20 | }; 21 | 22 | #[cfg(feature = "bit-vec")] 23 | use bitvec::{order::Lsb0, vec::BitVec}; 24 | use criterion::{criterion_group, criterion_main, Bencher, Criterion}; 25 | use parity_scale_codec::*; 26 | use parity_scale_codec_derive::{Decode, Encode}; 27 | 28 | fn array_vec_write_u128(b: &mut Bencher) { 29 | b.iter(|| { 30 | for b in 0..black_box(1_000_000) { 31 | let a = 0xffff_ffff_ffff_ffff_ffff_u128; 32 | Compact(a ^ b).using_encoded(|x| black_box(x).len()); 33 | } 34 | }); 35 | } 36 | 37 | fn test_vec, &[u8])>(b: &mut Bencher, f: F) { 38 | let f = black_box(f); 39 | let x = black_box([0xff; 10240]); 40 | 41 | b.iter(|| { 42 | for _b in 0..black_box(10_000) { 43 | let mut vec = Vec::::new(); 44 | f(&mut vec, &x); 45 | } 46 | }); 47 | } 48 | 49 | fn vec_write_as_output(b: &mut Bencher) { 50 | test_vec(b, |vec, a| { 51 | Output::write(vec, a); 52 | }); 53 | } 54 | 55 | fn vec_extend(b: &mut Bencher) { 56 | test_vec(b, |vec, a| { 57 | vec.extend(a); 58 | }); 59 | } 60 | 61 | fn vec_extend_from_slice(b: &mut Bencher) { 62 | test_vec(b, |vec, a| { 63 | vec.extend_from_slice(a); 64 | }); 65 | } 66 | 67 | struct NoLimitInput<'a>(&'a [u8]); 68 | 69 | impl Input for NoLimitInput<'_> { 70 | fn remaining_len(&mut self) -> Result, Error> { 71 | Ok(None) 72 | } 73 | 74 | fn read(&mut self, into: &mut [u8]) -> Result<(), Error> { 75 | self.0.read(into) 76 | } 77 | } 78 | 79 | #[derive(Encode, Decode)] 80 | enum Event { 81 | ComplexEvent(Vec, u32, i32, u128, i8), 82 | } 83 | 84 | fn vec_append_with_decode_and_encode(b: &mut Bencher) { 85 | let data = b"PCX"; 86 | 87 | b.iter(|| { 88 | let mut encoded_events_vec = Vec::new(); 89 | for _ in 0..1000 { 90 | let mut events = Vec::::decode(&mut &encoded_events_vec[..]).unwrap_or_default(); 91 | 92 | events.push(Event::ComplexEvent(data.to_vec(), 4, 5, 6, 9)); 93 | 94 | encoded_events_vec = events.encode(); 95 | } 96 | }) 97 | } 98 | 99 | fn vec_append_with_encode_append(b: &mut Bencher) { 100 | let data = b"PCX"; 101 | 102 | b.iter(|| { 103 | let mut encoded_events_vec; 104 | 105 | let events = vec![Event::ComplexEvent(data.to_vec(), 4, 5, 6, 9)]; 106 | encoded_events_vec = events.encode(); 107 | 108 | for _ in 1..1000 { 109 | encoded_events_vec = as EncodeAppend>::append_or_new( 110 | encoded_events_vec, 111 | &[Event::ComplexEvent(data.to_vec(), 4, 5, 6, 9)], 112 | ) 113 | .unwrap(); 114 | } 115 | }); 116 | } 117 | 118 | fn encode_decode_vec + Codec>(c: &mut Criterion) 119 | where 120 | T::Error: std::fmt::Debug, 121 | { 122 | let mut g = c.benchmark_group("vec_encode"); 123 | for vec_size in [1, 2, 5, 32, 1024, 2048, 16384] { 124 | g.bench_with_input( 125 | format!("{}/{}", type_name::(), vec_size), 126 | &vec_size, 127 | |b, &vec_size| { 128 | let vec: Vec = 129 | (0..=127u8).cycle().take(vec_size).map(|v| v.try_into().unwrap()).collect(); 130 | 131 | let vec = black_box(vec); 132 | b.iter(|| vec.encode()) 133 | }, 134 | ); 135 | } 136 | 137 | drop(g); 138 | let mut g = c.benchmark_group("vec_decode"); 139 | for vec_size in [1, 2, 5, 32, 1024, 2048, 16384] { 140 | g.bench_with_input( 141 | format!("{}/{}", type_name::(), vec_size), 142 | &vec_size, 143 | |b, &vec_size| { 144 | let vec: Vec = 145 | (0..=127u8).cycle().take(vec_size).map(|v| v.try_into().unwrap()).collect(); 146 | 147 | let vec = vec.encode(); 148 | 149 | let vec = black_box(vec); 150 | b.iter(|| { 151 | let _: Vec = Decode::decode(&mut &vec[..]).unwrap(); 152 | }) 153 | }, 154 | ); 155 | } 156 | 157 | drop(g); 158 | let mut g = c.benchmark_group("vec_decode_no_limit"); 159 | for vec_size in [16384, 131072] { 160 | g.bench_with_input( 161 | format!("vec_decode_no_limit_{}/{}", type_name::(), vec_size), 162 | &vec_size, 163 | |b, &vec_size| { 164 | let vec: Vec = 165 | (0..=127u8).cycle().take(vec_size).map(|v| v.try_into().unwrap()).collect(); 166 | 167 | let vec = vec.encode(); 168 | 169 | let vec = black_box(vec); 170 | b.iter(|| { 171 | let _: Vec = Decode::decode(&mut NoLimitInput(&vec[..])).unwrap(); 172 | }) 173 | }, 174 | ); 175 | } 176 | } 177 | 178 | fn encode_decode_complex_type(c: &mut Criterion) { 179 | #[derive(Encode, Decode, Clone)] 180 | struct ComplexType { 181 | _val: u32, 182 | _other_val: u128, 183 | _vec: Vec, 184 | } 185 | 186 | let complex_types = vec![ 187 | ComplexType { _val: 3, _other_val: 345634635, _vec: vec![1, 2, 3, 5, 6, 7] }, 188 | ComplexType { _val: 1000, _other_val: 980345634635, _vec: vec![1, 2, 3, 5, 6, 7] }, 189 | ComplexType { _val: 43564, _other_val: 342342345634635, _vec: vec![1, 2, 3, 5, 6, 7] }, 190 | ]; 191 | 192 | let mut g = c.benchmark_group("vec_encode_complex_type"); 193 | for vec_size in [1, 2, 5, 32, 1024, 2048, 16384] { 194 | let complex_types = complex_types.clone(); 195 | g.bench_with_input( 196 | format!("vec_encode_complex_type/{}", vec_size), 197 | &vec_size, 198 | move |b, &vec_size| { 199 | let vec: Vec = 200 | complex_types.clone().into_iter().cycle().take(vec_size).collect(); 201 | 202 | let vec = black_box(vec); 203 | b.iter(|| vec.encode()) 204 | }, 205 | ); 206 | } 207 | 208 | drop(g); 209 | let mut g = c.benchmark_group("vec_decode_complex_type"); 210 | for vec_size in [1, 2, 5, 32, 1024, 2048, 16384] { 211 | let complex_types = complex_types.clone(); 212 | g.bench_with_input( 213 | format!("vec_decode_complex_type/{}", vec_size), 214 | &vec_size, 215 | move |b, &vec_size| { 216 | let vec: Vec = 217 | complex_types.clone().into_iter().cycle().take(vec_size).collect(); 218 | 219 | let vec = vec.encode(); 220 | 221 | let vec = black_box(vec); 222 | b.iter(|| { 223 | let _: Vec = Decode::decode(&mut &vec[..]).unwrap(); 224 | }) 225 | }, 226 | ); 227 | } 228 | } 229 | 230 | fn bench_fn(c: &mut Criterion) { 231 | c.bench_function("vec_write_as_output", vec_write_as_output); 232 | c.bench_function("vec_extend", vec_extend); 233 | c.bench_function("vec_extend_from_slice", vec_extend_from_slice); 234 | c.bench_function("vec_append_with_decode_and_encode", vec_append_with_decode_and_encode); 235 | c.bench_function("vec_append_with_encode_append", vec_append_with_encode_append); 236 | c.bench_function("array_vec_write_u128", array_vec_write_u128); 237 | } 238 | 239 | fn encode_decode_bitvec_u8(c: &mut Criterion) { 240 | let _ = c; 241 | 242 | #[cfg(feature = "bit-vec")] 243 | { 244 | let mut g = c.benchmark_group("bitvec_u8_encode"); 245 | for size in [1, 2, 5, 32, 1024] { 246 | g.bench_with_input(size.to_string(), &size, |b, &size| { 247 | let vec: BitVec = 248 | [true, false].iter().cloned().cycle().take(size).collect(); 249 | 250 | let vec = black_box(vec); 251 | b.iter(|| vec.encode()) 252 | }); 253 | } 254 | } 255 | 256 | #[cfg(feature = "bit-vec")] 257 | { 258 | let mut g = c.benchmark_group("bitvec_u8_decode"); 259 | for size in [1, 2, 5, 32, 1024] { 260 | g.bench_with_input(size.to_string(), &size, |b, &size| { 261 | let vec: BitVec = 262 | [true, false].iter().cloned().cycle().take(size).collect(); 263 | 264 | let vec = vec.encode(); 265 | 266 | let vec = black_box(vec); 267 | b.iter(|| { 268 | let _: BitVec = Decode::decode(&mut &vec[..]).unwrap(); 269 | }) 270 | }); 271 | } 272 | } 273 | } 274 | 275 | criterion_group! { 276 | name = benches; 277 | config = Criterion::default().warm_up_time(Duration::from_millis(500)).without_plots(); 278 | targets = encode_decode_vec::, encode_decode_vec::, encode_decode_vec::, encode_decode_vec::, 279 | encode_decode_vec::, encode_decode_vec::, encode_decode_vec::, encode_decode_vec::, 280 | bench_fn, encode_decode_bitvec_u8, encode_decode_complex_type 281 | } 282 | criterion_main!(benches); 283 | -------------------------------------------------------------------------------- /fuzzer/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}, 3 | time::Duration, 4 | }; 5 | 6 | use bitvec::{ 7 | order::{BitOrder, Msb0}, 8 | store::BitStore, 9 | vec::BitVec, 10 | }; 11 | use honggfuzz::{ 12 | arbitrary::{Arbitrary, Result as ArbResult, Unstructured}, 13 | fuzz, 14 | }; 15 | use parity_scale_codec::{Compact, Decode, Encode}; 16 | 17 | #[derive(Encode, Decode, Clone, PartialEq, Debug, Arbitrary)] 18 | pub struct MockStruct { 19 | vec_u: Vec, 20 | } 21 | 22 | /// Used for implementing the Arbitrary trait for a BitVec. 23 | #[derive(Encode, Decode, Clone, PartialEq, Debug)] 24 | pub struct BitVecWrapper(BitVec); 25 | 26 | impl<'a, O: 'static + BitOrder, T: 'static + BitStore + Arbitrary<'a>> Arbitrary<'a> 27 | for BitVecWrapper 28 | { 29 | fn arbitrary(u: &mut Unstructured<'a>) -> ArbResult { 30 | let v = Vec::::arbitrary(u)?; 31 | Ok(BitVecWrapper(BitVec::::from_vec(v))) 32 | } 33 | } 34 | 35 | /// Used for implementing the PartialEq trait for a BinaryHeap. 36 | #[derive(Encode, Decode, Debug, Clone, Arbitrary)] 37 | struct BinaryHeapWrapper(BinaryHeap); 38 | 39 | impl PartialEq for BinaryHeapWrapper { 40 | fn eq(&self, other: &BinaryHeapWrapper) -> bool { 41 | self.0.clone().into_sorted_vec() == other.0.clone().into_sorted_vec() 42 | } 43 | } 44 | 45 | #[derive(Encode, Decode, Clone, PartialEq, Debug, Arbitrary)] 46 | pub enum MockEnum { 47 | Empty, 48 | Unit(u32), 49 | UnitVec(Vec), 50 | Complex { 51 | data: Vec, 52 | bitvec: BitVecWrapper, 53 | string: String, 54 | }, 55 | Mock(MockStruct), 56 | #[allow(clippy::type_complexity)] 57 | NestedVec(Vec>>>>>>>>), 58 | } 59 | 60 | /// `fuzz_flow` parameter can either be `round_trip` or `only_decode`. 61 | /// `round_trip` will decode -> encode and compare the obtained encoded bytes with the original 62 | /// data. `only_decode` will only decode, without trying to encode the decoded object. 63 | /// `round_trip_sort` will decode -> encode and compare the obtained encoded SORTED bytes with the 64 | /// original SORTED data. 65 | macro_rules! fuzz_decoder { 66 | ( 67 | $fuzz_flow:ident; 68 | $data:ident; 69 | $first:ty, 70 | $( $rest:ty, )* 71 | ) => { 72 | fuzz_decoder! { 73 | @INTERNAL 74 | $fuzz_flow; 75 | $data; 76 | 1u8; 77 | { $first; 0u8 } 78 | $( $rest, )* 79 | } 80 | }; 81 | (@INTERNAL 82 | $fuzz_flow:ident; 83 | $data:ident; 84 | $counter:expr; 85 | { $( $parsed:ty; $index:expr ),* } 86 | $current:ty, 87 | $( $rest:ty, )* 88 | ) => { 89 | fuzz_decoder! { 90 | @INTERNAL 91 | $fuzz_flow; 92 | $data; 93 | $counter + 1u8; 94 | { $current; $counter $(, $parsed; $index )* } 95 | $( $rest, )* 96 | } 97 | }; 98 | // round_trip flow arm. 99 | (@INTERNAL 100 | round_trip; 101 | $data:ident; 102 | $counter:expr; 103 | { $( $parsed:ty; $index:expr ),* } 104 | ) => { 105 | let num = $counter; 106 | $( 107 | if $data[0] % num == $index { 108 | let mut d = &$data[1..]; 109 | let raw1 = d.clone(); 110 | let maybe_obj = <$parsed>::decode(&mut d); 111 | 112 | match maybe_obj { 113 | Ok(obj) => { 114 | let mut d2: &[u8] = &obj.encode(); 115 | let raw2 = d2.clone(); 116 | let exp_obj = <$parsed>::decode(&mut d2); 117 | match exp_obj { 118 | Ok(obj2) => { 119 | if obj == obj2 { 120 | let raw1_trunc_to_obj_size = &raw1[..raw1.len()-d.len()]; 121 | if raw1_trunc_to_obj_size != raw2 { 122 | println!("raw1 = {:?}", raw1); 123 | println!("d (leftover/undecoded data) = {:?}", d); 124 | println!("- Decoded data:"); 125 | println!("raw1_trunc = {:?}", raw1_trunc_to_obj_size); 126 | println!("raw2 = {:?}", raw2); 127 | println!("- Encoded objects:"); 128 | println!("obj1 = '{:?}'", obj); 129 | println!("obj2 = '{:?}'", obj2); 130 | println!("Type: {}", std::any::type_name::<$parsed>()); 131 | panic!("raw1 != raw2"); 132 | } 133 | return 134 | } else { 135 | panic!("obj != obj2; obj={:?}, obj2={:?}", obj, obj2); 136 | } 137 | } 138 | Err(e) => panic!("Shouldn’t happen: can't .decode() after .decode().encode(): {}", e), 139 | } 140 | } 141 | Err(_) => return 142 | } 143 | } 144 | )* 145 | }; 146 | // only_decode flow arm. 147 | (@INTERNAL 148 | only_decode; 149 | $data:ident; 150 | $counter:expr; 151 | { $( $parsed:ty; $index:expr ),* } 152 | ) => { 153 | let num = $counter; 154 | $( 155 | if $data[0] % num == $index { 156 | // Check that decode doesn't panic 157 | let _ = <$parsed>::decode(&mut &$data[1..]); 158 | return 159 | } 160 | )* 161 | }; 162 | // round_trip_sorted flow arm. 163 | (@INTERNAL 164 | round_trip_sorted; 165 | $data:ident; 166 | $counter:expr; 167 | { $( $parsed:ty; $index:expr ),* } 168 | ) => { 169 | let num = $counter; 170 | $( 171 | if $data[0] % num == $index { 172 | let mut d = &$data[1..]; 173 | let raw1 = &d.clone(); 174 | 175 | let maybe_obj = <$parsed>::decode(&mut d); 176 | match maybe_obj { 177 | Ok(obj) => { 178 | let d2 = obj.encode(); 179 | let mut raw2 = d2.clone(); 180 | // We are sorting here because we're in the "sorted" flow. Useful for container types 181 | // which can have multiple valid encoded versions. 182 | raw2.sort(); 183 | let exp_obj = <$parsed>::decode(&mut &d2[..]); 184 | match exp_obj { 185 | Ok(obj2) => { 186 | if obj == obj2 { 187 | let mut raw1_trunc_to_obj_size = Vec::from(&raw1[..raw1.len() - d.len()]); 188 | // Sorting here is necessary: see above comment. 189 | raw1_trunc_to_obj_size.sort(); 190 | if raw1_trunc_to_obj_size != raw2 { 191 | println!("raw1 = {:?}", raw1); 192 | println!("d (leftover/undecoded data) = {:?}", d); 193 | println!("- Decoded data:"); 194 | println!("raw1_trunc = {:?}", raw1_trunc_to_obj_size); 195 | println!("raw2 = {:?}", raw2); 196 | println!("- Encoded objects:"); 197 | println!("obj1 = '{:?}'", obj); 198 | println!("obj2 = '{:?}'", obj2); 199 | println!("Type: {}", std::any::type_name::<$parsed>()); 200 | panic!("raw1 != raw2"); 201 | } 202 | return 203 | } 204 | panic!("obj != obj2; obj={:?}, obj2={:?}", obj, obj2); 205 | }, 206 | Err(e) => panic!("Shouldn’t happen: can't .decode() after .decode().encode(): {}", e), 207 | } 208 | } 209 | Err(_) => return, 210 | } 211 | } 212 | )* 213 | }; 214 | } 215 | 216 | fn fuzz_decode(data: &[u8]) { 217 | // Types for which we wish to apply the "round_trip" method. 218 | fuzz_decoder! { 219 | round_trip; 220 | data; 221 | u8, 222 | u16, 223 | u32, 224 | u64, 225 | u128, 226 | Compact, 227 | Compact, 228 | Compact, 229 | Compact, 230 | Compact, 231 | String, 232 | Vec, 233 | Vec>, 234 | Option>, 235 | Vec, 236 | LinkedList, 237 | VecDeque, 238 | MockStruct, 239 | MockEnum, 240 | BitVec, 241 | BitVec, 242 | Duration, 243 | }; 244 | // Types for which we wish to apply the "sorted" method. 245 | fuzz_decoder! { 246 | round_trip_sorted; 247 | data; 248 | BinaryHeapWrapper, 249 | }; 250 | // Types for which we only wish to decode. 251 | fuzz_decoder! { 252 | only_decode; 253 | data; 254 | BTreeMap>, 255 | BTreeMap, 256 | BTreeSet, 257 | }; 258 | } 259 | 260 | macro_rules! fuzz_encoder { 261 | () => {}; 262 | ($( $type:ty, )*) => { 263 | $(fuzz!(|data: $type| { fuzz_encode(data) });)* 264 | }; 265 | } 266 | 267 | fn fuzz_encode(data: T) { 268 | let original = data.clone(); 269 | let mut obj: &[u8] = &data.encode(); 270 | let decoded = ::decode(&mut obj); 271 | match decoded { 272 | Ok(object) => 273 | if object != original { 274 | println!("original object: {:?}", original); 275 | println!("decoded object: {:?}", object); 276 | panic!("Original object differs from decoded object") 277 | }, 278 | Err(e) => { 279 | println!("original object: {:?}", original); 280 | println!("decoding error: {:?}", e); 281 | panic!("Failed to decode the encoded object"); 282 | }, 283 | } 284 | } 285 | 286 | macro_rules! fuzz_encoding { 287 | () => { 288 | fuzz_encoder! { 289 | u8, 290 | u16, 291 | u32, 292 | u64, 293 | u128, 294 | Compact, 295 | Compact, 296 | Compact, 297 | Compact, 298 | Compact, 299 | String, 300 | Vec, 301 | Vec>, 302 | Option>, 303 | Vec, 304 | LinkedList, 305 | BTreeMap>, 306 | BTreeMap, 307 | BTreeSet, 308 | VecDeque, 309 | BinaryHeapWrapper, 310 | MockStruct, 311 | MockEnum, 312 | BitVecWrapper, 313 | BitVecWrapper, 314 | Duration, 315 | } 316 | }; 317 | } 318 | 319 | fn main() { 320 | loop { 321 | fuzz!(|data: &[u8]| { 322 | fuzz_decode(data); 323 | }); 324 | fuzz_encoding!(); 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /derive/src/trait_bounds.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::iter; 16 | 17 | use proc_macro2::Ident; 18 | use syn::{ 19 | spanned::Spanned, 20 | visit::{self, Visit}, 21 | Generics, Result, Type, TypePath, 22 | }; 23 | 24 | use crate::utils::{self, CustomTraitBound}; 25 | 26 | /// Visits the ast and checks if one of the given idents is found. 27 | struct ContainIdents<'a> { 28 | result: bool, 29 | idents: &'a [Ident], 30 | } 31 | 32 | impl<'a, 'ast> Visit<'ast> for ContainIdents<'a> { 33 | fn visit_ident(&mut self, i: &'ast Ident) { 34 | if self.idents.iter().any(|id| id == i) { 35 | self.result = true; 36 | } 37 | } 38 | } 39 | 40 | /// Checks if the given type contains one of the given idents. 41 | fn type_contain_idents(ty: &Type, idents: &[Ident]) -> bool { 42 | let mut visitor = ContainIdents { result: false, idents }; 43 | visitor.visit_type(ty); 44 | visitor.result 45 | } 46 | 47 | /// Visits the ast and checks if the a type path starts with the given ident. 48 | struct TypePathStartsWithIdent<'a> { 49 | result: bool, 50 | ident: &'a Ident, 51 | } 52 | 53 | impl<'a, 'ast> Visit<'ast> for TypePathStartsWithIdent<'a> { 54 | fn visit_type_path(&mut self, i: &'ast TypePath) { 55 | if let Some(segment) = i.path.segments.first() { 56 | if &segment.ident == self.ident { 57 | self.result = true; 58 | return; 59 | } 60 | } 61 | 62 | visit::visit_type_path(self, i); 63 | } 64 | } 65 | 66 | /// Checks if the given type path or any containing type path starts with the given ident. 67 | fn type_path_or_sub_starts_with_ident(ty: &TypePath, ident: &Ident) -> bool { 68 | let mut visitor = TypePathStartsWithIdent { result: false, ident }; 69 | visitor.visit_type_path(ty); 70 | visitor.result 71 | } 72 | 73 | /// Checks if the given type or any containing type path starts with the given ident. 74 | fn type_or_sub_type_path_starts_with_ident(ty: &Type, ident: &Ident) -> bool { 75 | let mut visitor = TypePathStartsWithIdent { result: false, ident }; 76 | visitor.visit_type(ty); 77 | visitor.result 78 | } 79 | 80 | /// Visits the ast and collects all type paths that do not start or contain the given ident. 81 | /// 82 | /// Returns `T`, `N`, `A` for `Vec<(Recursive, A)>` with `Recursive` as ident. 83 | struct FindTypePathsNotStartOrContainIdent<'a> { 84 | result: Vec, 85 | ident: &'a Ident, 86 | } 87 | 88 | impl<'a, 'ast> Visit<'ast> for FindTypePathsNotStartOrContainIdent<'a> { 89 | fn visit_type_path(&mut self, i: &'ast TypePath) { 90 | if type_path_or_sub_starts_with_ident(i, self.ident) { 91 | visit::visit_type_path(self, i); 92 | } else { 93 | self.result.push(i.clone()); 94 | } 95 | } 96 | } 97 | 98 | /// Collects all type paths that do not start or contain the given ident in the given type. 99 | /// 100 | /// Returns `T`, `N`, `A` for `Vec<(Recursive, A)>` with `Recursive` as ident. 101 | fn find_type_paths_not_start_or_contain_ident(ty: &Type, ident: &Ident) -> Vec { 102 | let mut visitor = FindTypePathsNotStartOrContainIdent { result: Vec::new(), ident }; 103 | visitor.visit_type(ty); 104 | visitor.result 105 | } 106 | 107 | #[allow(clippy::too_many_arguments)] 108 | /// Add required trait bounds to all generic types. 109 | /// 110 | /// Arguments: 111 | /// * `bound_compact_type`: If true, the trait bound is added to the compact type 112 | /// 113 | /// ```ignore 114 | /// where <#type as HasCompact>::Type: #codec_bound 115 | /// ``` 116 | /// Otherwise only `HasCompact` bound is added. 117 | pub fn add( 118 | input_ident: &Ident, 119 | generics: &mut Generics, 120 | data: &syn::Data, 121 | custom_trait_bound: Option>, 122 | codec_bound: syn::Path, 123 | codec_skip_bound: Option, 124 | dumb_trait_bounds: bool, 125 | crate_path: &syn::Path, 126 | bound_compact_type: bool, 127 | ) -> Result<()> { 128 | let skip_type_params = match custom_trait_bound { 129 | Some(CustomTraitBound::SpecifiedBounds { bounds, .. }) => { 130 | generics.make_where_clause().predicates.extend(bounds); 131 | return Ok(()); 132 | }, 133 | Some(CustomTraitBound::SkipTypeParams { type_names, .. }) => 134 | type_names.into_iter().collect::>(), 135 | None => Vec::new(), 136 | }; 137 | 138 | let ty_params = generics 139 | .type_params() 140 | .filter(|tp| skip_type_params.iter().all(|skip| skip != &tp.ident)) 141 | .map(|tp| tp.ident.clone()) 142 | .collect::>(); 143 | if ty_params.is_empty() { 144 | return Ok(()); 145 | } 146 | 147 | let codec_types = 148 | get_types_to_add_trait_bound(input_ident, data, &ty_params, dumb_trait_bounds)?; 149 | 150 | let compact_types = collect_types(data, utils::is_compact)? 151 | .into_iter() 152 | // Only add a bound if the type uses a generic 153 | .filter(|ty| type_contain_idents(ty, &ty_params)) 154 | .collect::>(); 155 | 156 | let skip_types = if codec_skip_bound.is_some() { 157 | let needs_default_bound = |f: &syn::Field| utils::should_skip(&f.attrs); 158 | collect_types(data, needs_default_bound)? 159 | .into_iter() 160 | // Only add a bound if the type uses a generic 161 | .filter(|ty| type_contain_idents(ty, &ty_params)) 162 | .collect::>() 163 | } else { 164 | Vec::new() 165 | }; 166 | 167 | if !codec_types.is_empty() || !compact_types.is_empty() || !skip_types.is_empty() { 168 | let where_clause = generics.make_where_clause(); 169 | 170 | codec_types 171 | .into_iter() 172 | .for_each(|ty| where_clause.predicates.push(parse_quote!(#ty : #codec_bound))); 173 | 174 | compact_types.into_iter().for_each(|ty| { 175 | where_clause.predicates.push(parse_quote!(#ty : #crate_path::HasCompact)); 176 | if bound_compact_type { 177 | where_clause 178 | .predicates 179 | .push(parse_quote!(<#ty as #crate_path::HasCompact>::Type : #codec_bound)); 180 | } 181 | }); 182 | 183 | skip_types.into_iter().for_each(|ty| { 184 | let codec_skip_bound = codec_skip_bound.as_ref(); 185 | where_clause.predicates.push(parse_quote!(#ty : #codec_skip_bound)) 186 | }); 187 | } 188 | 189 | Ok(()) 190 | } 191 | 192 | /// Returns all types that must be added to the where clause with the respective trait bound. 193 | fn get_types_to_add_trait_bound( 194 | input_ident: &Ident, 195 | data: &syn::Data, 196 | ty_params: &[Ident], 197 | dumb_trait_bound: bool, 198 | ) -> Result> { 199 | if dumb_trait_bound { 200 | Ok(ty_params.iter().map(|t| parse_quote!( #t )).collect()) 201 | } else { 202 | let needs_codec_bound = |f: &syn::Field| { 203 | !utils::is_compact(f) && 204 | utils::get_encoded_as_type(f).is_none() && 205 | !utils::should_skip(&f.attrs) 206 | }; 207 | let res = collect_types(data, needs_codec_bound)? 208 | .into_iter() 209 | // Only add a bound if the type uses a generic 210 | .filter(|ty| type_contain_idents(ty, ty_params)) 211 | // If a struct contains itself as field type, we can not add this type into the where 212 | // clause. This is required to work a round the following compiler bug: https://github.com/rust-lang/rust/issues/47032 213 | .flat_map(|ty| { 214 | find_type_paths_not_start_or_contain_ident(&ty, input_ident) 215 | .into_iter() 216 | .map(Type::Path) 217 | // Remove again types that do not contain any of our generic parameters 218 | .filter(|ty| type_contain_idents(ty, ty_params)) 219 | // Add back the original type, as we don't want to loose it. 220 | .chain(iter::once(ty)) 221 | }) 222 | // Remove all remaining types that start/contain the input ident to not have them in the 223 | // where clause. 224 | .filter(|ty| !type_or_sub_type_path_starts_with_ident(ty, input_ident)) 225 | .collect(); 226 | 227 | Ok(res) 228 | } 229 | } 230 | 231 | fn collect_types(data: &syn::Data, type_filter: fn(&syn::Field) -> bool) -> Result> { 232 | use syn::*; 233 | 234 | let types = match *data { 235 | Data::Struct(ref data) => match &data.fields { 236 | | Fields::Named(FieldsNamed { named: fields, .. }) | 237 | Fields::Unnamed(FieldsUnnamed { unnamed: fields, .. }) => 238 | fields.iter().filter(|f| type_filter(f)).map(|f| f.ty.clone()).collect(), 239 | 240 | Fields::Unit => Vec::new(), 241 | }, 242 | 243 | Data::Enum(ref data) => data 244 | .variants 245 | .iter() 246 | .filter(|variant| !utils::should_skip(&variant.attrs)) 247 | .flat_map(|variant| match &variant.fields { 248 | | Fields::Named(FieldsNamed { named: fields, .. }) | 249 | Fields::Unnamed(FieldsUnnamed { unnamed: fields, .. }) => 250 | fields.iter().filter(|f| type_filter(f)).map(|f| f.ty.clone()).collect(), 251 | 252 | Fields::Unit => Vec::new(), 253 | }) 254 | .collect(), 255 | 256 | Data::Union(ref data) => 257 | return Err(Error::new(data.union_token.span(), "Union types are not supported.")), 258 | }; 259 | 260 | Ok(types) 261 | } 262 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Parity SCALE Codec 2 | 3 | Rust implementation of the SCALE (Simple Concatenated Aggregate Little-Endian) data format 4 | for types used in the Parity Substrate framework. 5 | 6 | SCALE is a light-weight format which allows encoding (and decoding) which makes it highly 7 | suitable for resource-constrained execution environments like blockchain runtimes and low-power, 8 | low-memory devices. 9 | 10 | It is important to note that the encoding context (knowledge of how the types and data 11 | structures look) needs to be known separately at both encoding and decoding ends. 12 | The encoded data does not include this contextual information. 13 | 14 | To get a better understanding of how the encoding is done for different types, 15 | take a look at the ["Type encoding (SCALE)" page in Substrate docs](https://docs.substrate.io/reference/scale-codec/). 16 | 17 | ## Implementation 18 | 19 | The codec is implemented using the following traits: 20 | 21 | ### Encode 22 | 23 | The `Encode` trait is used for encoding of data into the SCALE format. The `Encode` trait 24 | contains the following functions: 25 | 26 | * `size_hint(&self) -> usize`: Gets the capacity (in bytes) required for the encoded data. 27 | This is to avoid double-allocation of memory needed for the encoding. It can be an estimate 28 | and does not need to be an exact number. If the size is not known, even no good maximum, then 29 | we can skip this function from the trait implementation. This is required to be a cheap operation, 30 | so should not involve iterations etc. 31 | * `encode_to(&self, dest: &mut T)`: Encodes the value and appends it to a destination 32 | buffer. 33 | * `encode(&self) -> Vec`: Encodes the type data and returns a slice. 34 | * `using_encoded R>(&self, f: F) -> R`: Encodes the type data and 35 | executes a closure on the encoded value. Returns the result from the executed closure. 36 | 37 | **Note:** Implementations should override `using_encoded` for value types and `encode_to` for 38 | allocating types. `size_hint` should be implemented for all types, wherever possible. Wrapper 39 | types should override all methods. 40 | 41 | ### Decode 42 | 43 | The `Decode` trait is used for deserialization/decoding of encoded data into the respective 44 | types. 45 | 46 | * `fn decode(value: &mut I) -> Result`: Tries to decode the value from 47 | SCALE format to the type it is called on. Returns an `Err` if the decoding fails. 48 | 49 | ### CompactAs 50 | 51 | The `CompactAs` trait is used for wrapping custom types/structs as compact types, which makes 52 | them even more space/memory efficient. The compact encoding is described [here](https://docs.substrate.io/reference/scale-codec/#fn-1). 53 | 54 | * `encode_as(&self) -> &Self::As`: Encodes the type (self) as a compact type. 55 | The type `As` is defined in the same trait and its implementation should be compact encode-able. 56 | * `decode_from(_: Self::As) -> Result`: Decodes the type (self) from a compact 57 | encode-able type. 58 | 59 | ### HasCompact 60 | 61 | The `HasCompact` trait, if implemented, tells that the corresponding type is a compact 62 | encode-able type. 63 | 64 | ### EncodeLike 65 | 66 | The `EncodeLike` trait needs to be implemented for each type manually. When using derive, it is 67 | done automatically for you. Basically the trait gives you the opportunity to accept multiple 68 | types to a function that all encode to the same representation. 69 | 70 | ## Usage Examples 71 | 72 | Following are some examples to demonstrate usage of the codec. 73 | 74 | ### Simple types 75 | 76 | ```rust 77 | # // Import macros if derive feature is not used. 78 | # #[cfg(not(feature="derive"))] 79 | # use parity_scale_codec_derive::{Encode, Decode}; 80 | 81 | use parity_scale_codec::{Encode, Decode}; 82 | 83 | #[derive(Debug, PartialEq, Encode, Decode)] 84 | enum EnumType { 85 | #[codec(index = 15)] 86 | A, 87 | B(u32, u64), 88 | C { 89 | a: u32, 90 | b: u64, 91 | }, 92 | } 93 | 94 | let a = EnumType::A; 95 | let b = EnumType::B(1, 2); 96 | let c = EnumType::C { a: 1, b: 2 }; 97 | 98 | a.using_encoded(|ref slice| { 99 | assert_eq!(slice, &b"\x0f"); 100 | }); 101 | 102 | b.using_encoded(|ref slice| { 103 | assert_eq!(slice, &b"\x01\x01\0\0\0\x02\0\0\0\0\0\0\0"); 104 | }); 105 | 106 | c.using_encoded(|ref slice| { 107 | assert_eq!(slice, &b"\x02\x01\0\0\0\x02\0\0\0\0\0\0\0"); 108 | }); 109 | 110 | let mut da: &[u8] = b"\x0f"; 111 | assert_eq!(EnumType::decode(&mut da).ok(), Some(a)); 112 | 113 | let mut db: &[u8] = b"\x01\x01\0\0\0\x02\0\0\0\0\0\0\0"; 114 | assert_eq!(EnumType::decode(&mut db).ok(), Some(b)); 115 | 116 | let mut dc: &[u8] = b"\x02\x01\0\0\0\x02\0\0\0\0\0\0\0"; 117 | assert_eq!(EnumType::decode(&mut dc).ok(), Some(c)); 118 | 119 | let mut dz: &[u8] = &[0]; 120 | assert_eq!(EnumType::decode(&mut dz).ok(), None); 121 | 122 | # fn main() { } 123 | ``` 124 | 125 | ### Compact type with HasCompact 126 | 127 | ```rust 128 | # // Import macros if derive feature is not used. 129 | # #[cfg(not(feature="derive"))] 130 | # use parity_scale_codec_derive::{Encode, Decode}; 131 | 132 | use parity_scale_codec::{Encode, Decode, Compact, HasCompact}; 133 | 134 | #[derive(Debug, PartialEq, Encode, Decode)] 135 | struct Test1CompactHasCompact { 136 | #[codec(compact)] 137 | bar: T, 138 | } 139 | 140 | #[derive(Debug, PartialEq, Encode, Decode)] 141 | struct Test1HasCompact { 142 | #[codec(encoded_as = "::Type")] 143 | bar: T, 144 | } 145 | 146 | let test_val: (u64, usize) = (0u64, 1usize); 147 | 148 | let encoded = Test1HasCompact { bar: test_val.0 }.encode(); 149 | assert_eq!(encoded.len(), test_val.1); 150 | assert_eq!(>::decode(&mut &encoded[..]).unwrap().bar, test_val.0); 151 | 152 | # fn main() { } 153 | ``` 154 | 155 | ### Type with CompactAs 156 | 157 | ```rust 158 | # // Import macros if derive feature is not used. 159 | # #[cfg(not(feature="derive"))] 160 | # use parity_scale_codec_derive::{Encode, Decode}; 161 | 162 | use serde_derive::{Serialize, Deserialize}; 163 | use parity_scale_codec::{Encode, Decode, Compact, HasCompact, CompactAs, Error}; 164 | 165 | #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] 166 | #[derive(PartialEq, Eq, Clone)] 167 | struct StructHasCompact(u32); 168 | 169 | impl CompactAs for StructHasCompact { 170 | type As = u32; 171 | 172 | fn encode_as(&self) -> &Self::As { 173 | &12 174 | } 175 | 176 | fn decode_from(_: Self::As) -> Result { 177 | Ok(StructHasCompact(12)) 178 | } 179 | } 180 | 181 | impl From> for StructHasCompact { 182 | fn from(_: Compact) -> Self { 183 | StructHasCompact(12) 184 | } 185 | } 186 | 187 | #[derive(Debug, PartialEq, Encode, Decode)] 188 | enum TestGenericHasCompact { 189 | A { 190 | #[codec(compact)] a: T 191 | }, 192 | } 193 | 194 | let a = TestGenericHasCompact::A:: { 195 | a: StructHasCompact(12325678), 196 | }; 197 | 198 | let encoded = a.encode(); 199 | assert_eq!(encoded.len(), 2); 200 | 201 | # fn main() { } 202 | ``` 203 | 204 | ## Derive attributes 205 | 206 | The derive implementation supports the following attributes: 207 | - `codec(dumb_trait_bound)`: This attribute needs to be placed above the type that one of the 208 | trait should be implemented for. It will make the algorithm that determines the to-add trait 209 | bounds fall back to just use the type parameters of the type. This can be useful for situation 210 | where the algorithm includes private types in the public interface. By using this attribute, 211 | you should not get this error/warning again. 212 | - `codec(skip)`: Needs to be placed above a field or variant and makes it to be skipped while 213 | encoding/decoding. 214 | - `codec(compact)`: Needs to be placed above a field and makes the field use compact encoding. 215 | (The type needs to support compact encoding.) 216 | - `codec(encoded_as = "OtherType")`: Needs to be placed above a field and makes the field being 217 | encoded by using `OtherType`. 218 | - `codec(index = 0)`: Needs to be placed above an enum variant to make the variant use the given 219 | index when encoded. By default the index is determined by counting from `0` beginning wth the 220 | first variant. 221 | - `codec(encode_bound)`, `codec(decode_bound)` and `codec(mel_bound)`: All 3 attributes take 222 | in a `where` clause for the `Encode`, `Decode` and `MaxEncodedLen` trait implementation for 223 | the annotated type respectively. 224 | - `codec(encode_bound(skip_type_params))`, `codec(decode_bound(skip_type_params))` and 225 | `codec(mel_bound(skip_type_params))`: All 3 sub-attributes take in types as arguments to skip 226 | trait derivation of the corresponding trait, e.g. T in 227 | `codec(encode_bound(skip_type_params(T)))` will not contain a `Encode` trait bound while 228 | `Encode` is being derived for the annotated type. 229 | 230 | ## Known issues 231 | 232 | Even though this crate supports deserialization of arbitrarily sized array (e.g. `[T; 1024 * 1024 * 1024]`) 233 | using such types is not recommended and will most likely result in a stack overflow. If you have a big 234 | array inside of your structure which you want to decode you should wrap it in a `Box`, e.g. `Box<[T; 1024 * 1024 * 1024]>`. 235 | 236 | ------------------------- 237 | 238 | License: Apache-2.0 239 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this crate are documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this crate adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## Unreleased 9 | 10 | ### [3.7.5] - 2025-05-20 11 | 12 | ### Fixed 13 | 14 | - Implement `on_before_alloc_mem()` for `CountedInput` [#716](https://github.com/paritytech/parity-scale-codec/pull/716) 15 | - Fix derive(Decode) for enums with lifetime parameters and struct-like variants 16 | [#726](https://github.com/paritytech/parity-scale-codec/pull/726) 17 | - Fix performance regression ([#731](https://github.com/paritytech/parity-scale-codec/pull/731)) 18 | - Fix `DecodeWithMemTracking` bounds for `Cow` [#735](https://github.com/paritytech/parity-scale-codec/pull/735) 19 | 20 | 21 | ### [3.7.4] - 2025-02-05 22 | 23 | ### Added 24 | 25 | - Disallow duplicate indexes using constant evaluation ([#653](https://github.com/paritytech/parity-scale-codec/pull/653)) 26 | 27 | ### [3.7.3] - 2025-01-30 28 | 29 | ### Added 30 | 31 | - Fix added bounds in `Encode` and other derive macros. ([#689](https://github.com/paritytech/parity-scale-codec/pull/689)) 32 | 33 | ## [3.7.0] - 2024-11-18 34 | 35 | ### Added 36 | 37 | - Allow decoding with a memory limit. ([616](https://github.com/paritytech/parity-scale-codec/pull/616)) 38 | - Introduce `CountedInput`, an wrapper on `Input` that counts the bytes read. ([630](https://github.com/paritytech/parity-scale-codec/pull/630)) 39 | 40 | ### Changed 41 | 42 | - This release bumps some dependencies, primarily bumping `syn` to 2. ([#640](https://github.com/paritytech/parity-scale-codec/pull/640)). 43 | 44 | ### Fixed 45 | 46 | - Fix MaxEncodedLen derive macro for enum with skipped variant ([#622](https://github.com/paritytech/parity-scale-codec/pull/622)) 47 | - Use MAX_PREALLOCATION consistently [#605](https://github.com/paritytech/parity-scale-codec/pull/605) 48 | 49 | ## [3.6.4] - 2023-07-14 50 | 51 | ### Added 52 | 53 | - Now `#[derive(Encode)]` implements the `size_hint()` method for structures and enumerations. 54 | This improves the performance of the `encode()` method by pre-allocating memory. 55 | 56 | ## [3.6.3] - 2023-07-03 57 | 58 | ### Fixed 59 | 60 | - Provide full path to elements from `::core` in `Decode` derivation (caused compilation error when 61 | `no-implicit-prelude` was used). 62 | 63 | ## [3.6.2] - 2023-06-30 64 | 65 | ### Fixed 66 | 67 | - Trying to deserialize a boxed newtype containing a big array won't overflow the stack anymore. 68 | 69 | ## [3.6.1] - 2023-06-19 70 | 71 | ### Fixed 72 | 73 | - Deriving `Decode` will not trigger clippy warnings anymore 74 | 75 | ## [3.6.0] - 2023-06-15 76 | 77 | ### Added 78 | 79 | - Added `Decode::decode_into` to allow deserializing into unitialized memory. 80 | - Added a `DecodeFinished` type to be used with `Decode::decode_into`. 81 | 82 | ### Fixed 83 | 84 | - Trying to deserialize a big boxed array (e.g. `Box<[u8; 1024 * 1024 * 1024]>`) won't overflow the stack anymore. 85 | - Trying to deserialize big nested enums with many variants won't overflow the stack anymore. 86 | - Elements of partially read arrays will now be properly dropped if the whole array wasn't decoded. 87 | 88 | ### Changed 89 | 90 | - The derive macros will now be reexported only when the `derive` feature is enabled, 91 | as opposed to how it was previously where enabling `parity-scale-codec-derive` would suffice. 92 | - The `max-encoded-len` feature won't automatically enable the derive macros nor pull in the 93 | `parity-scale-codec-derive` dependency. 94 | 95 | ## [3.5.0] 96 | 97 | ### Added 98 | 99 | - `ConstEncodedLen` marker trait for types that implement `MaxEncodedLen`. [#428](https://github.com/paritytech/parity-scale-codec/pull/428) 100 | 101 | ## [3.4.0] 102 | 103 | This release renders the `full` feature defunct. The implementations guarded behind 104 | this feature are now always provided. 105 | 106 | ### Changes 107 | 108 | - All implementations guarded behind `full` are not unconditionally implemented. 109 | 110 | ## [3.3.0] 111 | 112 | This release exports `decode_vec_with_len` to support custom decoding of `Vec`s. 113 | 114 | ### Added 115 | 116 | - Export `decode_vec_with_len`. 117 | 118 | ## [3.2.1] - 2022-09-14 119 | 120 | This release fixes compilation on no-std envs. 121 | 122 | ### Changed 123 | 124 | - Use core RangeInclusive instead of std [#378](https://github.com/paritytech/parity-scale-codec/pull/378) 125 | 126 | ## [3.2.0] - 2022-09-13 127 | 128 | This release (specifically [#375](https://github.com/paritytech/parity-scale-codec/pull/375)) bumps the MSRV to 1.60.0 as it depends on the Cargo.toml weak dependency feature. 129 | 130 | ### Changed 131 | 132 | - Don't include bitvec with std feature unless asked for explicitly. [#375](https://github.com/paritytech/parity-scale-codec/pull/375) 133 | - Implement `MaxEncodedLen` on more core lib types. [#350](https://github.com/paritytech/parity-scale-codec/pull/350) 134 | 135 | ## [3.1.5] - 2022-06-11 136 | 137 | A quick release to fix an issue introduced in 3.1.4 that broke compiling on no-std. 138 | 139 | ### Changed 140 | 141 | - Fix compiling on no-std. (see https://github.com/paritytech/parity-scale-codec/commit/c25f14a46546c75e4208363ced9d89aa81c85e7f) 142 | 143 | ## [3.1.3] - 2022-06-10 144 | 145 | ### Changed 146 | 147 | - Impl `MaxEncodedLen` for `Box`. [#349](https://github.com/paritytech/parity-scale-codec/pull/349) 148 | - Add `decode_from_bytes`. [#342](https://github.com/paritytech/parity-scale-codec/pull/342) 149 | 150 | ## [3.1.2] - 2022-03-22 151 | 152 | Be aware that version 3.0.0. up to 3.1.1 contained some bugs in the `BitVec` encoder that could lead to an invalid encoding. Thus, we yanked these crate version and it is advised to upgrade to 3.1.2. Any release before 3.0.0 wasn't affected by this bug. 153 | 154 | ### Changed 155 | 156 | - Optimised the `Decode::decode` for `[T; N]` by @xgreenx. [#299](https://github.com/paritytech/parity-scale-codec/pull/299) 157 | - Add some doc for the derive macro by @thiolliere. [#301](https://github.com/paritytech/parity-scale-codec/pull/301) 158 | - Add bytes::Bytes implementation by @vorot93. [#309](https://github.com/paritytech/parity-scale-codec/pull/309) 159 | - Upgrade to BitVec 1.0 by @bkchr. [#311](https://github.com/paritytech/parity-scale-codec/pull/311) 160 | - BREAKING CHANGE: DecodeLimit and DecodeAll extensions now advance input by @wigy-opensource-developer. [#314](https://github.com/paritytech/parity-scale-codec/pull/314) 161 | - Make `CompactRef` public by @andrenth. [#321](https://github.com/paritytech/parity-scale-codec/pull/321) 162 | - Add ability to re-export parity-scale-codec crate by @gshep. [#325](https://github.com/paritytech/parity-scale-codec/pull/325) 163 | - BitVec: Improve the encoding and consolidate the implementations by @bkchr. [#327](https://github.com/paritytech/parity-scale-codec/pull/327) 164 | - Fix crate access by putting a leading `::` by @bkchr. [#328](https://github.com/paritytech/parity-scale-codec/pull/328) 165 | 166 | ## [3.0.0] - 2022-02-02 167 | 168 | ### Fix 169 | 170 | - Optimised the Decode::decode for [T; N] [#299](https://github.com/paritytech/parity-scale-codec/pull/299) 171 | 172 | ### Changed 173 | 174 | - Migrated to 2021 edition, enforcing MSRV of `1.56.1`. [#298](https://github.com/paritytech/parity-scale-codec/pull/298) 175 | - Upgrade to BitVec 1.0 [#311](https://github.com/paritytech/parity-scale-codec/pull/311) 176 | - DecodeLimit and DecodeAll extensions now advance input [#314](https://github.com/paritytech/parity-scale-codec/pull/314) 177 | 178 | ### Added 179 | 180 | - Add bytes::Bytes implementation [#309](https://github.com/paritytech/parity-scale-codec/pull/309) 181 | 182 | ## [2.3.1] - 2021-09-28 183 | 184 | ### Fix 185 | 186 | - Improve macro hygiene of `Encode` and `Decode` proc. macro expansions. ([#291](https://github.com/paritytech/parity-scale-codec/pull/291), [#293](https://github.com/paritytech/parity-scale-codec/pull/293)) 187 | 188 | ## [2.3.0] - 2021-09-11 189 | 190 | ### Added 191 | 192 | - `decode_and_advance_with_depth_limit` to the `DecodeLimit` trait. This allows advancing the cursor while decoding the input. PR #286 193 | 194 | ## [2.2.0] - 2021-07-02 195 | 196 | ### Added 197 | 198 | - Add support for custom where bounds `codec(mel_bound(T: MaxEncodedLen))` when deriving the traits. PR #279 199 | - `MaxEncodedLen` trait for items that have a statically known maximum encoded size. ([#268](https://github.com/paritytech/parity-scale-codec/pull/268)) 200 | - `#[codec(crate = )]` top-level attribute to be used with the new `MaxEncodedLen` 201 | trait, which allows to specify a different path to the crate that contains the `MaxEncodedLen` trait. 202 | Useful when using generating a type through a macro and this type should implement `MaxEncodedLen` and the final crate doesn't have `parity-scale-codec` as dependency. 203 | 204 | ## [2.1.3] - 2021-06-14 205 | 206 | ### Changed 207 | 208 | - Lint attributes now pass through to the derived impls of `Encode`, `Decode` and `CompactAs`. PR #272 209 | 210 | ## [2.1.0] - 2021-04-06 211 | 212 | ### Fix 213 | 214 | - Add support for custom where bounds `codec(encode_bound(T: Encode))` and `codec(decode_bound(T: Decode))` when 215 | deriving the traits. Pr #262 216 | - Switch to const generics for array implementations. Pr #261 217 | 218 | ## [2.0.1] - 2021-02-26 219 | 220 | ### Fix 221 | 222 | - Fix type inference issue in `Decode` derive macro. Pr #254 223 | 224 | ## [2.0.0] - 2021-01-26 225 | 226 | ### Added 227 | 228 | - `Decode::skip` allows to skip some encoded types. Pr #243 229 | - `Decode::encoded_fixed_size` allows to get the fixed encoded size of a type. PR #243 230 | - `Error` now contains a chain of causes. This full error description can also be activated on 231 | no std using the feature `chain-error`. PR #242 232 | - `Encode::encoded_size` allows to get the encoded size of a type more efficiently. PR #245 233 | 234 | ### Changed 235 | 236 | - `CompactAs::decode_from` now returns result. This allow for decoding to fail from their compact 237 | form. 238 | - derive macro use literal index e.g. `#[codec(index = 15)]` instead of `#[codec(index = "15")]` 239 | - Version of crates `bitvec` and `generic-array` is updated. 240 | - `Encode::encode_to` now bounds the generic `W: Output + ?Sized` instead of `W: Output`. 241 | - `Output` can now be used as a trait object. 242 | 243 | ### Removed 244 | 245 | - `EncodeAppend::append` is removed in favor of `EncodeAppend::append_or_new`. 246 | - `Output::push` is removed in favor of `Encode::encode_to`. 247 | - Some bounds on `HasCompact::Type` are removed. 248 | - `Error::what` is removed in favor of `Error::to_string` (implemented through trait `Display`). 249 | - `Error::description` is removed in favor of `Error::to_string` (implemented through trait `Display`). 250 | -------------------------------------------------------------------------------- /derive/src/decode.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017, 2018 Parity Technologies 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::utils; 16 | use proc_macro2::{Ident, Span, TokenStream}; 17 | use quote::ToTokens; 18 | use std::iter; 19 | use syn::{spanned::Spanned, Data, Error, Field, Fields}; 20 | 21 | /// Generate function block for function `Decode::decode`. 22 | /// 23 | /// * data: data info of the type, 24 | /// * type_name: name of the type, 25 | /// * type_generics: the generics of the type in turbofish format, without bounds, e.g. `::` 26 | /// * input: the variable name for the argument of function `decode`. 27 | pub fn quote( 28 | data: &Data, 29 | type_name: &Ident, 30 | type_generics: &TokenStream, 31 | input: &TokenStream, 32 | crate_path: &syn::Path, 33 | ) -> TokenStream { 34 | match *data { 35 | Data::Struct(ref data) => create_instance( 36 | quote! { #type_name #type_generics }, 37 | &type_name.to_string(), 38 | input, 39 | &data.fields, 40 | crate_path, 41 | ), 42 | Data::Enum(ref data) => { 43 | let variants = match utils::try_get_variants(data) { 44 | Ok(variants) => variants, 45 | Err(e) => return e.to_compile_error(), 46 | }; 47 | 48 | let recurse = variants.iter().enumerate().map(|(i, v)| { 49 | let name = &v.ident; 50 | let index = utils::variant_index(v, i); 51 | 52 | let create = create_instance( 53 | quote! { #type_name :: #name #type_generics }, 54 | &format!("{}::{}", type_name, name), 55 | input, 56 | &v.fields, 57 | crate_path, 58 | ); 59 | 60 | quote_spanned! { v.span() => 61 | #[allow(clippy::unnecessary_cast)] 62 | #[allow(clippy::cast_possible_truncation)] 63 | __codec_x_edqy if __codec_x_edqy == (#index) as ::core::primitive::u8 => { 64 | // NOTE: This lambda is necessary to work around an upstream bug 65 | // where each extra branch results in excessive stack usage: 66 | // https://github.com/rust-lang/rust/issues/34283 67 | #[allow(clippy::redundant_closure_call)] 68 | return (move || { 69 | #create 70 | })(); 71 | }, 72 | } 73 | }); 74 | let recurse_indices = variants 75 | .iter() 76 | .enumerate() 77 | .map(|(i, v)| (v.ident.clone(), utils::variant_index(v, i))); 78 | 79 | let const_eval_check = 80 | utils::const_eval_check_variant_indexes(recurse_indices, crate_path); 81 | 82 | let read_byte_err_msg = 83 | format!("Could not decode `{type_name}`, failed to read variant byte"); 84 | let invalid_variant_err_msg = 85 | format!("Could not decode `{type_name}`, variant doesn't exist"); 86 | quote! { 87 | #const_eval_check 88 | match #input.read_byte() 89 | .map_err(|e| e.chain(#read_byte_err_msg))? 90 | { 91 | #( #recurse )* 92 | _ => { 93 | #[allow(clippy::redundant_closure_call)] 94 | return (move || { 95 | ::core::result::Result::Err( 96 | <_ as ::core::convert::Into<_>>::into(#invalid_variant_err_msg) 97 | ) 98 | })(); 99 | }, 100 | } 101 | } 102 | }, 103 | Data::Union(_) => 104 | Error::new(Span::call_site(), "Union types are not supported.").to_compile_error(), 105 | } 106 | } 107 | 108 | pub fn quote_decode_into( 109 | data: &Data, 110 | crate_path: &syn::Path, 111 | input: &TokenStream, 112 | attrs: &[syn::Attribute], 113 | ) -> Option { 114 | // Make sure the type is `#[repr(transparent)]`, as this guarantees that 115 | // there can be only one field that is not zero-sized. 116 | if !crate::utils::is_transparent(attrs) { 117 | return None; 118 | } 119 | 120 | let fields = match data { 121 | Data::Struct(syn::DataStruct { 122 | fields: 123 | Fields::Named(syn::FieldsNamed { named: fields, .. }) | 124 | Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }), 125 | .. 126 | }) => fields, 127 | _ => return None, 128 | }; 129 | 130 | if fields.is_empty() { 131 | return None; 132 | } 133 | 134 | // Bail if there are any extra attributes which could influence how the type is decoded. 135 | if fields.iter().any(|field| { 136 | utils::get_encoded_as_type(field).is_some() || 137 | utils::is_compact(field) || 138 | utils::should_skip(&field.attrs) 139 | }) { 140 | return None; 141 | } 142 | 143 | // Go through each field and call `decode_into` on it. 144 | // 145 | // Normally if there's more than one field in the struct this would be incorrect, 146 | // however since the struct's marked as `#[repr(transparent)]` we're guaranteed that 147 | // there's at most one non zero-sized field, so only one of these `decode_into` calls 148 | // should actually do something, and the rest should just be dummy calls that do nothing. 149 | let mut decode_fields = Vec::new(); 150 | let mut sizes = Vec::new(); 151 | let mut non_zst_field_count = Vec::new(); 152 | for field in fields { 153 | let field_type = &field.ty; 154 | decode_fields.push(quote! {{ 155 | let dst_: &mut ::core::mem::MaybeUninit = dst_; // To make sure the type is what we expect. 156 | 157 | // Here we cast `&mut MaybeUninit` into a `&mut MaybeUninit<#field_type>`. 158 | // 159 | // SAFETY: The struct is marked as `#[repr(transparent)]` so the address of every field will 160 | // be the same as the address of the struct itself. 161 | let dst_: &mut ::core::mem::MaybeUninit<#field_type> = unsafe { 162 | &mut *dst_.as_mut_ptr().cast::<::core::mem::MaybeUninit<#field_type>>() 163 | }; 164 | <#field_type as #crate_path::Decode>::decode_into(#input, dst_)?; 165 | }}); 166 | 167 | if !sizes.is_empty() { 168 | sizes.push(quote! { + }); 169 | } 170 | sizes.push(quote! { ::core::mem::size_of::<#field_type>() }); 171 | 172 | if !non_zst_field_count.is_empty() { 173 | non_zst_field_count.push(quote! { + }); 174 | } 175 | non_zst_field_count 176 | .push(quote! { if ::core::mem::size_of::<#field_type>() > 0 { 1 } else { 0 } }); 177 | } 178 | 179 | Some(quote! { 180 | // Just a sanity check. These should always be true and will be optimized-out. 181 | ::core::assert_eq!(#(#sizes)*, ::core::mem::size_of::()); 182 | ::core::assert!(#(#non_zst_field_count)* <= 1); 183 | 184 | #(#decode_fields)* 185 | 186 | // SAFETY: We've successfully called `decode_into` for all of the fields. 187 | unsafe { ::core::result::Result::Ok(#crate_path::DecodeFinished::assert_decoding_finished()) } 188 | }) 189 | } 190 | 191 | fn create_decode_expr( 192 | field: &Field, 193 | name: &str, 194 | input: &TokenStream, 195 | crate_path: &syn::Path, 196 | ) -> TokenStream { 197 | let encoded_as = utils::get_encoded_as_type(field); 198 | let compact = utils::get_compact_type(field, crate_path); 199 | let skip = utils::should_skip(&field.attrs); 200 | 201 | let res = quote!(__codec_res_edqy); 202 | 203 | if encoded_as.is_some() as u8 + compact.is_some() as u8 + skip as u8 > 1 { 204 | return Error::new( 205 | field.span(), 206 | "`encoded_as`, `compact` and `skip` can only be used one at a time!", 207 | ) 208 | .to_compile_error(); 209 | } 210 | 211 | let err_msg = format!("Could not decode `{}`", name); 212 | 213 | if let Some(compact) = compact { 214 | quote_spanned! { field.span() => 215 | { 216 | let #res = <#compact as #crate_path::Decode>::decode(#input); 217 | match #res { 218 | ::core::result::Result::Err(e) => return ::core::result::Result::Err(e.chain(#err_msg)), 219 | ::core::result::Result::Ok(#res) => #res.into(), 220 | } 221 | } 222 | } 223 | } else if let Some(encoded_as) = encoded_as { 224 | quote_spanned! { field.span() => 225 | { 226 | let #res = <#encoded_as as #crate_path::Decode>::decode(#input); 227 | match #res { 228 | ::core::result::Result::Err(e) => return ::core::result::Result::Err(e.chain(#err_msg)), 229 | ::core::result::Result::Ok(#res) => #res.into(), 230 | } 231 | } 232 | } 233 | } else if skip { 234 | quote_spanned! { field.span() => ::core::default::Default::default() } 235 | } else { 236 | let field_type = &field.ty; 237 | quote_spanned! { field.span() => 238 | { 239 | let #res = <#field_type as #crate_path::Decode>::decode(#input); 240 | match #res { 241 | ::core::result::Result::Err(e) => return ::core::result::Result::Err(e.chain(#err_msg)), 242 | ::core::result::Result::Ok(#res) => #res, 243 | } 244 | } 245 | } 246 | } 247 | } 248 | 249 | fn create_instance( 250 | name: TokenStream, 251 | name_str: &str, 252 | input: &TokenStream, 253 | fields: &Fields, 254 | crate_path: &syn::Path, 255 | ) -> TokenStream { 256 | match *fields { 257 | Fields::Named(ref fields) => { 258 | let recurse = fields.named.iter().map(|f| { 259 | let name_ident = &f.ident; 260 | let field_name = match name_ident { 261 | Some(a) => format!("{}::{}", name_str, a), 262 | None => name_str.to_string(), // Should never happen, fields are named. 263 | }; 264 | let decode = create_decode_expr(f, &field_name, input, crate_path); 265 | 266 | quote_spanned! { f.span() => 267 | #name_ident: #decode 268 | } 269 | }); 270 | 271 | quote_spanned! { fields.span() => 272 | ::core::result::Result::Ok(#name { 273 | #( #recurse, )* 274 | }) 275 | } 276 | }, 277 | Fields::Unnamed(ref fields) => { 278 | let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { 279 | let field_name = format!("{}.{}", name_str, i); 280 | 281 | create_decode_expr(f, &field_name, input, crate_path) 282 | }); 283 | 284 | quote_spanned! { fields.span() => 285 | ::core::result::Result::Ok(#name ( 286 | #( #recurse, )* 287 | )) 288 | } 289 | }, 290 | Fields::Unit => { 291 | quote_spanned! { fields.span() => 292 | ::core::result::Result::Ok(#name) 293 | } 294 | }, 295 | } 296 | } 297 | 298 | pub fn quote_decode_with_mem_tracking_checks(data: &Data, crate_path: &syn::Path) -> TokenStream { 299 | let fields: Box> = match data { 300 | Data::Struct(data) => Box::new(data.fields.iter()), 301 | Data::Enum(ref data) => { 302 | let variants = match utils::try_get_variants(data) { 303 | Ok(variants) => variants, 304 | Err(e) => return e.to_compile_error(), 305 | }; 306 | 307 | let mut fields: Box> = Box::new(iter::empty()); 308 | for variant in variants { 309 | fields = Box::new(fields.chain(variant.fields.iter())); 310 | } 311 | fields 312 | }, 313 | Data::Union(_) => { 314 | return Error::new(Span::call_site(), "Union types are not supported.") 315 | .to_compile_error(); 316 | }, 317 | }; 318 | 319 | let processed_fields = fields.filter_map(|field| { 320 | if utils::should_skip(&field.attrs) { 321 | return None; 322 | } 323 | 324 | let field_type = if let Some(compact) = utils::get_compact_type(field, crate_path) { 325 | compact 326 | } else if let Some(encoded_as) = utils::get_encoded_as_type(field) { 327 | encoded_as 328 | } else { 329 | field.ty.to_token_stream() 330 | }; 331 | Some(quote_spanned! {field.span() => #field_type}) 332 | }); 333 | 334 | quote! { 335 | fn check_field() {} 336 | 337 | #( 338 | check_field::<#processed_fields>(); 339 | )* 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------