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

Commit

Permalink
frame-support Add translate_next (#14043)
Browse files Browse the repository at this point in the history
* 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 `?`
  • Loading branch information
pgherveou authored Jun 2, 2023
1 parent a6a1c02 commit 28f56b6
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 26 deletions.
64 changes: 39 additions & 25 deletions frame/support/src/storage/generator/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,34 +178,48 @@ where
}

fn translate<O: Decode, F: FnMut(K, O) -> Option<V>>(mut f: F) {
let mut previous_key = None;
loop {
previous_key = Self::translate_next(previous_key, &mut f);
if previous_key.is_none() {
break
}
}
}

fn translate_next<O: Decode, F: FnMut(K, O) -> Option<V>>(
previous_key: Option<Vec<u8>>,
mut f: F,
) -> Option<Vec<u8>> {
let prefix = G::prefix_hash();
let mut previous_key = prefix.clone();
while let Some(next) =
sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix))
{
previous_key = next;
let value = match unhashed::get::<O>(&previous_key) {
Some(value) => value,
None => {
log::error!("Invalid translate: fail to decode old value");
continue
},
};

let mut key_material = G::Hasher::reverse(&previous_key[prefix.len()..]);
let key = match K::decode(&mut key_material) {
Ok(key) => key,
Err(_) => {
log::error!("Invalid translate: fail to decode key");
continue
},
};
let previous_key = previous_key.unwrap_or_else(|| prefix.clone());

match f(key, value) {
Some(new) => unhashed::put::<V>(&previous_key, &new),
None => unhashed::kill(&previous_key),
}
let current_key =
sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix))?;

let value = match unhashed::get::<O>(&current_key) {
Some(value) => value,
None => {
log::error!("Invalid translate: fail to decode old value");
return Some(current_key)
},
};

let mut key_material = G::Hasher::reverse(&current_key[prefix.len()..]);
let key = match K::decode(&mut key_material) {
Ok(key) => key,
Err(_) => {
log::error!("Invalid translate: fail to decode key");
return Some(current_key)
},
};

match f(key, value) {
Some(new) => unhashed::put::<V>(&current_key, &new),
None => unhashed::kill(&current_key),
}

Some(current_key)
}
}

Expand Down
9 changes: 9 additions & 0 deletions frame/support/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,15 @@ pub trait IterableStorageMap<K: FullEncode, V: FullCodec>: StorageMap<K, V> {
///
/// NOTE: If a value fail to decode because storage is corrupted then it is skipped.
fn translate<O: Decode, F: FnMut(K, O) -> Option<V>>(f: F);

/// Translate the next entry following `previous_key` by a function `f`.
/// By returning `None` from `f` for an element, you'll remove it from the map.
///
/// Returns the next key to iterate from in lexicographical order of the encoded key.
fn translate_next<O: Decode, F: FnMut(K, O) -> Option<V>>(
previous_key: Option<Vec<u8>>,
f: F,
) -> Option<Vec<u8>>;
}

/// A strongly-typed double map in storage whose secondary keys and values can be iterated over.
Expand Down
11 changes: 10 additions & 1 deletion frame/support/src/storage/types/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ mod test {
use crate::{
hash::*,
metadata_ir::{StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR},
storage::types::ValueQuery,
storage::{types::ValueQuery, IterableStorageMap},
};
use sp_io::{hashing::twox_128, TestExternalities};

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

let translate_next = |k: u16, v: u8| Some((v as u16 / k).into());
let k = A::translate_next::<u8, _>(None, translate_next);
let k = A::translate_next::<u8, _>(k, translate_next);
assert_eq!(None, A::translate_next::<u8, _>(k, translate_next));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 10), (3, 10)]);

let _ = A::translate_next::<u8, _>(None, |_, _| None);
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(3, 10)]);

let mut entries = vec![];
A::build_metadata(vec![], &mut entries);
AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries);
Expand Down

0 comments on commit 28f56b6

Please sign in to comment.