Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
implement BoundedEncodedLen (#8720)
Browse files Browse the repository at this point in the history
* implement BoundedEncodedLen

* update header

* update imports

* use impl_for_tuples instead of a custom macro

* remove redundant where clause

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* impl for Compact<T>

* impl BoundedEncodedLen for BoundedVec (#8727)

* impl BoundedEncodedLen for bool

* explicitly implement BoundedEncodedLen for each Compact form

Turns out that u16 doesn't play nicely with the pattern; those values
take two extra bytes, where all other cases take one. :(

* rename BoundedEncodedLen -> MaxEncodedLen

* add tests of compact encoded length

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
  • Loading branch information
coriolinus and bkchr committed May 4, 2021
1 parent e708448 commit 6bfdaba
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 1 deletion.
17 changes: 16 additions & 1 deletion frame/support/src/storage/bounded_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use sp_std::{convert::TryFrom, marker::PhantomData};
use codec::{FullCodec, Encode, EncodeLike, Decode};
use core::{ops::{Index, IndexMut}, slice::SliceIndex};
use crate::{
traits::Get,
traits::{Get, MaxEncodedLen},
storage::{generator, StorageDecodeLength, StorageValue, StorageMap, StorageDoubleMap},
};

Expand Down Expand Up @@ -347,6 +347,21 @@ impl<
}
}

impl<T, S> MaxEncodedLen for BoundedVec<T, S>
where
T: BoundedVecValue + MaxEncodedLen,
S: Get<u32>,
BoundedVec<T, S>: Encode,
{
fn max_encoded_len() -> usize {
// BoundedVec<T, S> encodes like Vec<T> which encodes like [T], which is a compact u32
// plus each item in the slice:
// https://substrate.dev/rustdocs/v3.0.0/src/parity_scale_codec/codec.rs.html#798-808
codec::Compact::<u32>::max_encoded_len()
.saturating_add(Self::bound().saturating_mul(T::max_encoded_len()))
}
}

#[cfg(test)]
pub mod test {
use super::*;
Expand Down
3 changes: 3 additions & 0 deletions frame/support/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,6 @@ pub use dispatch::{EnsureOrigin, OriginTrait, UnfilteredDispatchable};

mod voting;
pub use voting::{CurrencyToVote, SaturatingCurrencyToVote, U128CurrencyToVote};

mod max_encoded_len;
pub use max_encoded_len::MaxEncodedLen;
132 changes: 132 additions & 0 deletions frame/support/src/traits/max_encoded_len.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// This file is part of Substrate.

// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// 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 codec::{Compact, Encode};
use impl_trait_for_tuples::impl_for_tuples;
use sp_std::{mem, marker::PhantomData};

/// Items implementing `MaxEncodedLen` 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 MaxEncodedLen: 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 MaxEncodedLen for $t {
fn max_encoded_len() -> usize {
mem::size_of::<$t>()
}
}
)+
};
}

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

macro_rules! impl_compact {
($( $t:ty => $e:expr; )*) => {
$(
impl MaxEncodedLen for Compact<$t> {
fn max_encoded_len() -> usize {
$e
}
}
)*
};
}

impl_compact!(
// https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L261
u8 => 2;
// https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L291
u16 => 4;
// https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L326
u32 => 5;
// https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L369
u64 => 9;
// https://github.com/paritytech/parity-scale-codec/blob/f0341dabb01aa9ff0548558abb6dcc5c31c669a1/src/compact.rs#L413
u128 => 17;
);

// impl_for_tuples for values 19 and higher fails because that's where the WrapperTypeEncode impl stops.
#[impl_for_tuples(18)]
impl MaxEncodedLen for Tuple {
fn max_encoded_len() -> usize {
let mut len: usize = 0;
for_tuples!( #( len = len.saturating_add(Tuple::max_encoded_len()); )* );
len
}
}

impl<T: MaxEncodedLen, const N: usize> MaxEncodedLen for [T; N] {
fn max_encoded_len() -> usize {
T::max_encoded_len().saturating_mul(N)
}
}

impl<T: MaxEncodedLen> MaxEncodedLen for Option<T> {
fn max_encoded_len() -> usize {
T::max_encoded_len().saturating_add(1)
}
}

impl<T, E> MaxEncodedLen for Result<T, E>
where
T: MaxEncodedLen,
E: MaxEncodedLen,
{
fn max_encoded_len() -> usize {
T::max_encoded_len().max(E::max_encoded_len()).saturating_add(1)
}
}

impl<T> MaxEncodedLen for PhantomData<T> {
fn max_encoded_len() -> usize {
0
}
}

#[cfg(test)]
mod tests {
use super::*;

macro_rules! test_compact_length {
($(fn $name:ident($t:ty);)*) => {
$(
#[test]
fn $name() {
assert_eq!(Compact(<$t>::MAX).encode().len(), Compact::<$t>::max_encoded_len());
}
)*
};
}

test_compact_length!(
fn compact_u8(u8);
fn compact_u16(u16);
fn compact_u32(u32);
fn compact_u64(u64);
fn compact_u128(u128);
);
}

0 comments on commit 6bfdaba

Please sign in to comment.