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

Commit 28f56b6

Browse files
authored
frame-support Add translate_next (#14043)
* Frame Add translate_next This works similarly to to `translate` but only translate a single entry. This function will be useful in the context of multi-block migration. * Add test * add None return case * fixes * PR comment use `?`
1 parent a6a1c02 commit 28f56b6

File tree

3 files changed

+58
-26
lines changed

3 files changed

+58
-26
lines changed

frame/support/src/storage/generator/map.rs

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -178,34 +178,48 @@ where
178178
}
179179

180180
fn translate<O: Decode, F: FnMut(K, O) -> Option<V>>(mut f: F) {
181+
let mut previous_key = None;
182+
loop {
183+
previous_key = Self::translate_next(previous_key, &mut f);
184+
if previous_key.is_none() {
185+
break
186+
}
187+
}
188+
}
189+
190+
fn translate_next<O: Decode, F: FnMut(K, O) -> Option<V>>(
191+
previous_key: Option<Vec<u8>>,
192+
mut f: F,
193+
) -> Option<Vec<u8>> {
181194
let prefix = G::prefix_hash();
182-
let mut previous_key = prefix.clone();
183-
while let Some(next) =
184-
sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix))
185-
{
186-
previous_key = next;
187-
let value = match unhashed::get::<O>(&previous_key) {
188-
Some(value) => value,
189-
None => {
190-
log::error!("Invalid translate: fail to decode old value");
191-
continue
192-
},
193-
};
194-
195-
let mut key_material = G::Hasher::reverse(&previous_key[prefix.len()..]);
196-
let key = match K::decode(&mut key_material) {
197-
Ok(key) => key,
198-
Err(_) => {
199-
log::error!("Invalid translate: fail to decode key");
200-
continue
201-
},
202-
};
195+
let previous_key = previous_key.unwrap_or_else(|| prefix.clone());
203196

204-
match f(key, value) {
205-
Some(new) => unhashed::put::<V>(&previous_key, &new),
206-
None => unhashed::kill(&previous_key),
207-
}
197+
let current_key =
198+
sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix))?;
199+
200+
let value = match unhashed::get::<O>(&current_key) {
201+
Some(value) => value,
202+
None => {
203+
log::error!("Invalid translate: fail to decode old value");
204+
return Some(current_key)
205+
},
206+
};
207+
208+
let mut key_material = G::Hasher::reverse(&current_key[prefix.len()..]);
209+
let key = match K::decode(&mut key_material) {
210+
Ok(key) => key,
211+
Err(_) => {
212+
log::error!("Invalid translate: fail to decode key");
213+
return Some(current_key)
214+
},
215+
};
216+
217+
match f(key, value) {
218+
Some(new) => unhashed::put::<V>(&current_key, &new),
219+
None => unhashed::kill(&current_key),
208220
}
221+
222+
Some(current_key)
209223
}
210224
}
211225

frame/support/src/storage/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,15 @@ pub trait IterableStorageMap<K: FullEncode, V: FullCodec>: StorageMap<K, V> {
303303
///
304304
/// NOTE: If a value fail to decode because storage is corrupted then it is skipped.
305305
fn translate<O: Decode, F: FnMut(K, O) -> Option<V>>(f: F);
306+
307+
/// Translate the next entry following `previous_key` by a function `f`.
308+
/// By returning `None` from `f` for an element, you'll remove it from the map.
309+
///
310+
/// Returns the next key to iterate from in lexicographical order of the encoded key.
311+
fn translate_next<O: Decode, F: FnMut(K, O) -> Option<V>>(
312+
previous_key: Option<Vec<u8>>,
313+
f: F,
314+
) -> Option<Vec<u8>>;
306315
}
307316

308317
/// A strongly-typed double map in storage whose secondary keys and values can be iterated over.

frame/support/src/storage/types/map.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ mod test {
484484
use crate::{
485485
hash::*,
486486
metadata_ir::{StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR},
487-
storage::types::ValueQuery,
487+
storage::{types::ValueQuery, IterableStorageMap},
488488
};
489489
use sp_io::{hashing::twox_128, TestExternalities};
490490

@@ -700,6 +700,15 @@ mod test {
700700
A::translate::<u8, _>(|k, v| Some((k * v as u16).into()));
701701
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40), (3, 30)]);
702702

703+
let translate_next = |k: u16, v: u8| Some((v as u16 / k).into());
704+
let k = A::translate_next::<u8, _>(None, translate_next);
705+
let k = A::translate_next::<u8, _>(k, translate_next);
706+
assert_eq!(None, A::translate_next::<u8, _>(k, translate_next));
707+
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 10), (3, 10)]);
708+
709+
let _ = A::translate_next::<u8, _>(None, |_, _| None);
710+
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(3, 10)]);
711+
703712
let mut entries = vec![];
704713
A::build_metadata(vec![], &mut entries);
705714
AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries);

0 commit comments

Comments
 (0)