Optimize decode_len (#5975)

* Optimize `decode_len`

Instead of reading the full storage value into the runtime, we only read
at maximum `5bytes` from the storage into the runtime. Furthermore this
drops any handling with regards to set default values in
`decl_storage!`. If the value does not exists or the decoding of the
length fails, it will return `None`. To prevent people from messing
stuff up, this feature relies on the `StorageDecodeLength` trait that is
sealed by `frame-support` (aka only implementable inside this crate).

* Some clean ups

* Update frame/support/src/storage/mod.rs

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>

Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>
This commit is contained in:
Bastian Köcher
2020-05-12 12:13:28 +02:00
committed by GitHub
parent 0690bb51a8
commit 22db788c08
7 changed files with 91 additions and 87 deletions
+68 -20
View File
@@ -18,7 +18,7 @@
use sp_std::{prelude::*, marker::PhantomData};
use codec::{FullCodec, FullEncode, Encode, EncodeLike, Decode};
use crate::{traits::Len, hash::{Twox128, StorageHasher}};
use crate::hash::{Twox128, StorageHasher};
use sp_runtime::generic::{Digest, DigestItem};
pub mod unhashed;
@@ -105,11 +105,20 @@ pub trait StorageValue<T: FullCodec> {
EncodeLikeItem: EncodeLike<Item>,
T: StorageAppend<Item>;
/// Read the length of the value in a fast way, without decoding the entire value.
/// Read the length of the storage value without decoding the entire value.
///
/// `T` is required to implement `Codec::DecodeLength`.
fn decode_len() -> Result<usize, &'static str>
where T: codec::DecodeLength + Len;
/// `T` is required to implement [`StorageDecodeLength`].
///
/// If the value does not exists or it fails to decode the length, `None` is returned.
/// Otherwise `Some(len)` is returned.
///
/// # Warning
///
/// `None` does not mean that `get()` does not return a value. The default value is completly
/// ignored by this function.
fn decode_len() -> Option<usize> where T: StorageDecodeLength {
T::decode_len(&Self::hashed_key())
}
}
/// A strongly-typed map in storage.
@@ -175,15 +184,23 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
EncodeLikeItem: EncodeLike<Item>,
V: StorageAppend<Item>;
/// Read the length of the value in a fast way, without decoding the entire value.
/// Read the length of the storage value without decoding the entire value under the
/// given `key`.
///
/// `T` is required to implement `Codec::DecodeLength`.
/// `V` is required to implement [`StorageDecodeLength`].
///
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
/// Therefore, this function cannot be used as a sign of _existence_. use the `::contains_key()`
/// function for this purpose.
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
where V: codec::DecodeLength + Len;
/// If the value does not exists or it fails to decode the length, `None` is returned.
/// Otherwise `Some(len)` is returned.
///
/// # Warning
///
/// `None` does not mean that `get()` does not return a value. The default value is completly
/// ignored by this function.
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Option<usize>
where V: StorageDecodeLength,
{
V::decode_len(&Self::hashed_key_for(key))
}
/// Migrate an item with the given `key` from a defunct `OldHasher` to the current hasher.
///
@@ -348,18 +365,26 @@ pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
EncodeLikeItem: EncodeLike<Item>,
V: StorageAppend<Item>;
/// Read the length of the value in a fast way, without decoding the entire value.
/// Read the length of the storage value without decoding the entire value under the
/// given `key1` and `key2`.
///
/// `V` is required to implement `Codec::DecodeLength`.
/// `V` is required to implement [`StorageDecodeLength`].
///
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
/// Therefore, this function cannot be used as a sign of _existence_. use the `::contains_key()`
/// function for this purpose.
fn decode_len<KArg1, KArg2>(key1: KArg1, key2: KArg2) -> Result<usize, &'static str>
/// If the value does not exists or it fails to decode the length, `None` is returned.
/// Otherwise `Some(len)` is returned.
///
/// # Warning
///
/// `None` does not mean that `get()` does not return a value. The default value is completly
/// ignored by this function.
fn decode_len<KArg1, KArg2>(key1: KArg1, key2: KArg2) -> Option<usize>
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
V: codec::DecodeLength + Len;
V: StorageDecodeLength,
{
V::decode_len(&Self::hashed_key_for(key1, key2))
}
/// Migrate an item with the given `key1` and `key2` from defunct `OldHasher1` and
/// `OldHasher2` to the current hashers.
@@ -493,7 +518,29 @@ pub trait StoragePrefixedMap<Value: FullCodec> {
/// This trait is sealed.
pub trait StorageAppend<Item: Encode>: private::Sealed {}
/// Provides `Sealed` trait to prevent implementing trait `StorageAppend` outside of this crate.
/// Marker trait that will be implemented for types that support to decode their length in an
/// effificent way. It is expected that the length is at the beginning of the encoded object
/// and that the length is a `Compact<u32>`.
///
/// This trait is sealed.
pub trait StorageDecodeLength: private::Sealed + codec::DecodeLength {
/// Decode the length of the storage value at `key`.
///
/// This function assumes that the length is at the beginning of the encoded object
/// and is a `Compact<u32>`.
///
/// Returns `None` if the storage value does not exist or the decoding failed.
fn decode_len(key: &[u8]) -> Option<usize> {
// `Compact<u32>` is 5 bytes in maximum.
let mut data = [0u8; 5];
let len = sp_io::storage::read(key, &mut data, 0)?;
let len = data.len().min(len as usize);
<Self as codec::DecodeLength>::len(&data[..len]).ok()
}
}
/// Provides `Sealed` trait to prevent implementing trait `StorageAppend` & `StorageDecodeLength`
/// outside of this crate.
mod private {
use super::*;
@@ -504,6 +551,7 @@ mod private {
}
impl<T: Encode> StorageAppend<T> for Vec<T> {}
impl<T: Encode> StorageDecodeLength for Vec<T> {}
/// We abuse the fact that SCALE does not put any marker into the encoding, i.e.
/// we only encode the internal vec and we can append to this vec. We have a test that ensures