diff --git a/src/bounded_encoded_length.rs b/src/bounded_encoded_length.rs new file mode 100644 index 00000000..87930a46 --- /dev/null +++ b/src/bounded_encoded_length.rs @@ -0,0 +1,102 @@ +// Copyright 2021 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::{mem, marker::PhantomData}; +use crate::Encode; + +/// Items implementing `BoundedEncodedLen` have a statically known maximum encoded size. +/// +/// Some containers, such as `BoundedVec`, have enforced size limits and this trait +/// can be implemented accurately. Other containers, such as `StorageMap`, do not have enforced size +/// limits. For those containers, it is necessary to make a documented assumption about the maximum +/// usage, and compute the max encoded length based on that assumption. +pub trait BoundedEncodedLen: Encode { + /// Upper bound, in bytes, of the maximum encoded size of this item. + fn max_encoded_len() -> usize; +} + +macro_rules! impl_primitives { + ( $($t:ty),+ ) => { + $( + impl BoundedEncodedLen for $t { + fn max_encoded_len() -> usize { + // a compact encoding of a primitive can take 1 byte more than its actual size + mem::size_of::<$t>().saturating_add(1) + } + } + )+ + }; +} + +impl_primitives!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + +impl BoundedEncodedLen for () { + fn max_encoded_len() -> usize { + 0 + } +} + +macro_rules! impl_tuples { + // end of recursive descent + () => {}; + // recursive producer + ( $head:ident $(, $rest:ident )* ) => { + impl<$head $(, $rest)*> BoundedEncodedLen for ( $head, $($rest),* ) + where + $head: BoundedEncodedLen, + $( + $rest: BoundedEncodedLen, + )* + { + fn max_encoded_len() -> usize { + $head::max_encoded_len() + $( + .saturating_add($rest::max_encoded_len()) + )* + } + } + + impl_tuples!($($rest),*); + }; +} + +impl_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R); + +impl BoundedEncodedLen for [T; N] { + fn max_encoded_len() -> usize { + T::max_encoded_len().saturating_mul(N) + } +} + +impl BoundedEncodedLen for Option { + fn max_encoded_len() -> usize { + T::max_encoded_len().saturating_add(1) + } +} + +impl BoundedEncodedLen for Result +where + T: BoundedEncodedLen, + E: BoundedEncodedLen, +{ + fn max_encoded_len() -> usize { + T::max_encoded_len().max(E::max_encoded_len()).saturating_add(1) + } +} + +impl BoundedEncodedLen for PhantomData { + fn max_encoded_len() -> usize { + 0 + } +} diff --git a/src/lib.rs b/src/lib.rs index ca0d8c7a..49d65b3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2018 Parity Technologies +// Copyright 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,20 +14,20 @@ //! # Parity SCALE Codec //! -//! Rust implementation of the SCALE (Simple Concatenated Aggregate Little-Endian) data format -//! for types used in the Parity Substrate framework. +//! Rust implementation of the SCALE (Simple Concatenated Aggregate Little-Endian) data format for +//! types used in the Parity Substrate framework. //! //! SCALE is a light-weight format which allows encoding (and decoding) which makes it highly //! suitable for resource-constrained execution environments like blockchain runtimes and low-power, //! low-memory devices. //! -//! It is important to note that the encoding context (knowledge of how the types and data structures look) -//! needs to be known separately at both encoding and decoding ends. -//! The encoded data does not include this contextual information. +//! It is important to note that the encoding context (knowledge of how the types and data +//! structures look) needs to be known separately at both encoding and decoding ends. The encoded +//! data does not include this contextual information. //! -//! To get a better understanding of how the encoding is done for different types, -//! take a look at the -//! [SCALE Code page at the Substrate Knowledge Base](https://substrate.dev/docs/en/knowledgebase/advanced/codec). +//! To get a better understanding of how the encoding is done for different types, take a look at +//! the [SCALE Code page at the Substrate Knowledge +//! Base](https://substrate.dev/docs/en/knowledgebase/advanced/codec). //! //! ## Implementation //! @@ -35,46 +35,53 @@ //! //! ### Encode //! -//! The `Encode` trait is used for encoding of data into the SCALE format. The `Encode` trait contains the following functions: - -//! * `size_hint(&self) -> usize`: Gets the capacity (in bytes) required for the encoded data. -//! This is to avoid double-allocation of memory needed for the encoding. -//! It can be an estimate and does not need to be an exact number. -//! If the size is not known, even no good maximum, then we can skip this function from the trait implementation. -//! This is required to be a cheap operation, so should not involve iterations etc. -//! * `encode_to(&self, dest: &mut T)`: Encodes the value and appends it to a destination buffer. +//! The `Encode` trait is used for encoding of data into the SCALE format. The `Encode` trait +//! contains the following functions: +//! +//! * `size_hint(&self) -> usize`: Gets the capacity (in bytes) required for the encoded data. This +//! is to avoid double-allocation of memory needed for the encoding. It can be an estimate and +//! does not need to be an exact number. If the size is not known, even no good maximum, then we +//! can skip this function from the trait implementation. This is required to be a cheap +//! operation, so should not involve iterations etc. +//! * `encode_to(&self, dest: &mut T)`: Encodes the value and appends it to a destination +//! buffer. //! * `encode(&self) -> Vec`: Encodes the type data and returns a slice. -//! * `using_encoded R>(&self, f: F) -> R`: Encodes the type data and executes a closure on the encoded value. -//! Returns the result from the executed closure. +//! * `using_encoded R>(&self, f: F) -> R`: Encodes the type data and +//! executes a closure on the encoded value. Returns the result from the executed closure. //! -//! **Note:** Implementations should override `using_encoded` for value types and `encode_to` for allocating types. -//! `size_hint` should be implemented for all types, wherever possible. Wrapper types should override all methods. +//! **Note:** Implementations should override `using_encoded` for value types and `encode_to` for +//! allocating types. `size_hint` should be implemented for all types, wherever possible. Wrapper +//! types should override all methods. //! //! ### Decode //! -//! The `Decode` trait is used for deserialization/decoding of encoded data into the respective types. +//! The `Decode` trait is used for deserialization/decoding of encoded data into the respective +//! types. //! -//! * `fn decode(value: &mut I) -> Result`: Tries to decode the value from SCALE format to the type it is called on. -//! Returns an `Err` if the decoding fails. +//! * `fn decode(value: &mut I) -> Result`: Tries to decode the value from +//! SCALE format to the type it is called on. Returns an `Err` if the decoding fails. //! //! ### CompactAs //! -//! The `CompactAs` trait is used for wrapping custom types/structs as compact types, which makes them even more space/memory efficient. -//! The compact encoding is described [here](https://substrate.dev/docs/en/knowledgebase/advanced/codec#compactgeneral-integers). +//! The `CompactAs` trait is used for wrapping custom types/structs as compact types, which makes +//! them even more space/memory efficient. The compact encoding is described +//! [here](https://substrate.dev/docs/en/knowledgebase/advanced/codec#compactgeneral-integers). //! -//! * `encode_as(&self) -> &Self::As`: Encodes the type (self) as a compact type. -//! The type `As` is defined in the same trait and its implementation should be compact encode-able. -//! * `decode_from(_: Self::As) -> Result`: Decodes the type (self) from a compact encode-able type. +//! * `encode_as(&self) -> &Self::As`: Encodes the type (self) as a compact type. The type `As` is +//! defined in the same trait and its implementation should be compact encode-able. +//! * `decode_from(_: Self::As) -> Result`: Decodes the type (self) from a compact +//! encode-able type. //! //! ### HasCompact //! -//! The `HasCompact` trait, if implemented, tells that the corresponding type is a compact encode-able type. +//! The `HasCompact` trait, if implemented, tells that the corresponding type is a compact +//! encode-able type. //! //! ### EncodeLike //! //! The `EncodeLike` trait needs to be implemented for each type manually. When using derive, it is -//! done automatically for you. Basically the trait gives you the opportunity to accept multiple types -//! to a function that all encode to the same representation. +//! done automatically for you. Basically the trait gives you the opportunity to accept multiple +//! types to a function that all encode to the same representation. //! //! ## Usage Examples //! @@ -212,24 +219,22 @@ //! ## Derive attributes //! //! The derive implementation supports the following attributes: -//! - `codec(dumb_trait_bound)`: This attribute needs to be placed above the type that one of the trait -//! should be implemented for. It will make the algorithm that determines the to-add trait bounds -//! fall back to just use the type parameters of the type. This can be useful for situation where -//! the algorithm includes private types in the public interface. By using this attribute, you should -//! not get this error/warning again. +//! - `codec(dumb_trait_bound)`: This attribute needs to be placed above the type that one of the +//! trait should be implemented for. It will make the algorithm that determines the to-add trait +//! bounds fall back to just use the type parameters of the type. This can be useful for situation +//! where the algorithm includes private types in the public interface. By using this attribute, +//! you should not get this error/warning again. //! - `codec(skip)`: Needs to be placed above a field or variant and makes it to be skipped while //! encoding/decoding. //! - `codec(compact)`: Needs to be placed above a field and makes the field use compact encoding. //! (The type needs to support compact encoding.) -//! - `codec(encoded_as = "OtherType")`: Needs to be placed above a field and makes the field being encoded -//! by using `OtherType`. +//! - `codec(encoded_as = "OtherType")`: Needs to be placed above a field and makes the field being +//! encoded by using `OtherType`. //! - `codec(index = 0)`: Needs to be placed above an enum variant to make the variant use the given //! index when encoded. By default the index is determined by counting from `0` beginning wth the //! first variant. -//! #![warn(missing_docs)] - #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] @@ -261,25 +266,27 @@ pub mod alloc { pub use std::rc; } -mod codec; -mod compact; -mod joiner; -mod keyedvec; #[cfg(feature = "bit-vec")] mod bit_vec; -#[cfg(feature = "generic-array")] -mod generic_array; +mod bounded_encoded_length; +mod codec; +mod compact; mod decode_all; mod depth_limit; mod encode_append; mod encode_like; mod error; +#[cfg(feature = "generic-array")] +mod generic_array; +mod joiner; +mod keyedvec; pub use self::error::Error; pub use self::codec::{ Input, Output, Decode, Encode, Codec, EncodeAsRef, WrapperTypeEncode, WrapperTypeDecode, OptionBool, DecodeLength, FullCodec, FullEncode, }; +pub use bounded_encoded_length::BoundedEncodedLen; #[cfg(feature = "std")] pub use self::codec::IoReader; pub use self::compact::{Compact, HasCompact, CompactAs, CompactLen};