mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-24 09:01:08 +00:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user