Skip to content
Closed
73 changes: 73 additions & 0 deletions src/bounded_encoded_length.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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;
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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't see why this should be implemented in scale. This is Substrate specific and not SCALE specific.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The short answer is that Encode::size_hint is toothless: it's a 'best-effort' thing with a useless default implementation. Still, we do have the need sometimes for a more realistic size hint, and that need is generalizable to all of SCALE, not just Substrate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also "toothless" as what is the maximum size of a vec?

As already said in element, I don't see this in SCALE.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not implemented for Vec 😁. I'll move this work to the substrate repo instead though.

/// 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 {
mem::size_of::<$t>()
}
}
)+
};
}

impl_primitives!((), u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);

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()
$(
+ $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<T: BoundedEncodedLen, const N: usize> BoundedEncodedLen for [T; N] {
fn max_encoded_len() -> usize {
T::max_encoded_len() * N
}
}
101 changes: 54 additions & 47 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -14,67 +14,74 @@

//! # 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
//!
//! The codec is implemented using the following traits:
//!
//! ### 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<T: Output>(&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<T: Output>(&self, dest: &mut T)`: Encodes the value and appends it to a destination
//! buffer.
//! * `encode(&self) -> Vec<u8>`: Encodes the type data and returns a slice.
//! * `using_encoded<R, F: FnOnce(&[u8]) -> 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, F: FnOnce(&[u8]) -> 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<I: Input>(value: &mut I) -> Result<Self, Error>`: Tries to decode the value from SCALE format to the type it is called on.
//! Returns an `Err` if the decoding fails.
//! * `fn decode<I: Input>(value: &mut I) -> Result<Self, Error>`: 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<Self, Error>`: 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<Self, Error>`: 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
//!
Expand Down Expand Up @@ -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"))]
Expand Down Expand Up @@ -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};
Expand Down