diff --git a/frame/support/src/storage/generator/map.rs b/frame/support/src/storage/generator/map.rs index f6c8eaa270bb3..f46ed1e2ec7b6 100644 --- a/frame/support/src/storage/generator/map.rs +++ b/frame/support/src/storage/generator/map.rs @@ -17,7 +17,10 @@ use crate::{ hash::{ReversibleStorageHasher, StorageHasher}, - storage::{self, storage_prefix, unhashed, KeyPrefixIterator, PrefixIterator, StorageAppend}, + storage::{ + self, storage_prefix, unhashed, KeyPrefixIterator, PrefixIterator, StorageAppend, + TranslateResult, + }, Never, }; use codec::{Decode, Encode, EncodeLike, FullCodec, FullEncode}; @@ -207,6 +210,76 @@ where } } } + + fn single_translate Option>( + last_processed_key: Option>, + mut f: F, + ) -> TranslateResult { + let prefix = G::prefix_hash(); + let previous_key = last_processed_key.unwrap_or(prefix.clone()); + + let mut result = TranslateResult::default(); + + if let Some(next) = + sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix)) + { + result.previous_key = Some(next.clone()); + result.number += 1; + + let value = match unhashed::get::(&next) { + Some(value) => value, + None => { + log::error!("Invalid translate: fail to decode old value"); + return result + }, + }; + + let mut key_material = G::Hasher::reverse(&next[prefix.len()..]); + let key = match K::decode(&mut key_material) { + Ok(key) => key, + Err(_) => { + log::error!("Invalid translate: fail to decode key"); + return result + }, + }; + + match f(key, value) { + Some(new) => unhashed::put::(&previous_key, &new), + None => unhashed::kill(&previous_key), + } + } else { + result.is_finalized = true; + } + + result + } + + fn limited_translate Option>( + limit: Option, + mut last_processed_key: Option>, + mut f: F, + ) -> TranslateResult { + let limit = limit.unwrap_or(u32::MAX); + let mut result = TranslateResult::default(); + + for _ in 0..limit { + let temp_res = Self::single_translate(last_processed_key, &mut f); + + // TODO: add custom methods to the result object and make this code cleaner! + + if temp_res.is_finalized { + result.is_finalized = true; + break + } + + result.previous_key = temp_res.previous_key.clone(); + result.number += 1; + + last_processed_key = temp_res.previous_key; + } + + result + } } impl> storage::StorageMap for G { diff --git a/frame/support/src/storage/mod.rs b/frame/support/src/storage/mod.rs index 333f4382557b1..32ad52e3ba16d 100644 --- a/frame/support/src/storage/mod.rs +++ b/frame/support/src/storage/mod.rs @@ -295,6 +295,38 @@ pub trait IterableStorageMap: StorageMap { /// /// NOTE: If a value fail to decode because storage is corrupted then it is skipped. fn translate Option>(f: F); + + /// limit - If Some(x), limits number of translations that can be done by up to **x** + /// last_processed_key - If Some(k), starts iteration from key after **k** + /// + /// returns report on how much values were processed + fn limited_translate Option>( + limit: Option, + last_processed_key: Option>, + f: F, + ) -> TranslateResult; + + /// last_processed_key - If Some(k), starts iteration from key after **k** + /// + /// returns report on whether the value was processed and if any more remain + fn single_translate Option>( + last_processed_key: Option>, + f: F, + ) -> TranslateResult; +} + +#[derive(Default)] +pub struct TranslateResult { + /// Number of successfully translated values + pub number: u32, + /// `true` in case no more values remain + pub is_finalized: bool, + /// Previous processed key, if any. + pub previous_key: Option>, +} + +impl TranslateResult { + // TODO } /// A strongly-typed double map in storage whose secondary keys and values can be iterated over.