mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 19:11:04 +00:00
Translation for linked map (#4052)
* make linked_map generic functions better * implement translation of linked map * proc-macro for linked map updated * test linked map migration * account for instances * address grumbles * cut map short if migration fails
This commit is contained in:
committed by
Gavin Wood
parent
05391f7e04
commit
2eac9f5b1b
@@ -86,6 +86,10 @@ use proc_macro::TokenStream;
|
|||||||
/// * `head_key`: `"head of " ++ $module_prefix ++ " " ++ $storage_name`
|
/// * `head_key`: `"head of " ++ $module_prefix ++ " " ++ $storage_name`
|
||||||
/// * `Hasher`: $hash
|
/// * `Hasher`: $hash
|
||||||
///
|
///
|
||||||
|
/// All key formatting logic can be accessed in a type-agnostic format via the
|
||||||
|
/// [`KeyFormat`](../srml_support/storage/generator/trait.KeyFormat.html) trait, which
|
||||||
|
/// is implemented for the storage linked map type as well.
|
||||||
|
///
|
||||||
/// * Double map: `Foo: double_map hasher($hash1) u32, $hash2(u32) => u32`: Implements the
|
/// * Double map: `Foo: double_map hasher($hash1) u32, $hash2(u32) => u32`: Implements the
|
||||||
/// [`StorageDoubleMap`](../srml_support/storage/trait.StorageDoubleMap.html) trait using the
|
/// [`StorageDoubleMap`](../srml_support/storage/trait.StorageDoubleMap.html) trait using the
|
||||||
/// [`StorageDoubleMap generator`](../srml_support/storage/generator/trait.StorageDoubleMap.html).
|
/// [`StorageDoubleMap generator`](../srml_support/storage/generator/trait.StorageDoubleMap.html).
|
||||||
|
|||||||
@@ -162,15 +162,12 @@ pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStre
|
|||||||
{
|
{
|
||||||
type Query = #query_type;
|
type Query = #query_type;
|
||||||
type Hasher = #scrate::#hasher;
|
type Hasher = #scrate::#hasher;
|
||||||
|
type KeyFormat = Self;
|
||||||
|
|
||||||
fn prefix() -> &'static [u8] {
|
fn prefix() -> &'static [u8] {
|
||||||
#final_prefix
|
#final_prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
fn head_key() -> &'static [u8] {
|
|
||||||
#head_key
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query {
|
fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query {
|
||||||
#from_optional_value_to_query
|
#from_optional_value_to_query
|
||||||
}
|
}
|
||||||
@@ -179,6 +176,14 @@ pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStre
|
|||||||
#from_query_to_optional_value
|
#from_query_to_optional_value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<#impl_trait> #scrate::storage::generator::LinkedMapKeyFormat for #storage_struct {
|
||||||
|
type Hasher = #scrate::#hasher;
|
||||||
|
|
||||||
|
fn head_key() -> &'static [u8] {
|
||||||
|
#head_key
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
StorageLineTypeDef::DoubleMap(map) => {
|
StorageLineTypeDef::DoubleMap(map) => {
|
||||||
|
|||||||
@@ -47,16 +47,21 @@ pub trait StorageLinkedMap<K: FullCodec, V: FullCodec> {
|
|||||||
/// Hasher used to insert into storage.
|
/// Hasher used to insert into storage.
|
||||||
type Hasher: StorageHasher;
|
type Hasher: StorageHasher;
|
||||||
|
|
||||||
|
/// The family of key formats used for this map.
|
||||||
|
type KeyFormat: KeyFormat<Hasher=Self::Hasher>;
|
||||||
|
|
||||||
/// Prefix used to prepend each key.
|
/// Prefix used to prepend each key.
|
||||||
fn prefix() -> &'static [u8];
|
fn prefix() -> &'static [u8];
|
||||||
|
|
||||||
/// Key used to store linked map head.
|
/// The head key of the linked-map.
|
||||||
fn head_key() -> &'static [u8];
|
fn head_key() -> &'static [u8] {
|
||||||
|
<Self::KeyFormat as KeyFormat>::head_key()
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert an optionnal value retrieved from storage to the type queried.
|
/// Convert an optional value retrieved from storage to the type queried.
|
||||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||||
|
|
||||||
/// Convert a query to an optionnal value into storage.
|
/// Convert a query to an optional value into storage.
|
||||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||||
|
|
||||||
/// Generate the full key used in top storage.
|
/// Generate the full key used in top storage.
|
||||||
@@ -64,14 +69,37 @@ pub trait StorageLinkedMap<K: FullCodec, V: FullCodec> {
|
|||||||
where
|
where
|
||||||
KeyArg: EncodeLike<K>,
|
KeyArg: EncodeLike<K>,
|
||||||
{
|
{
|
||||||
let mut final_key = Self::prefix().to_vec();
|
<Self::KeyFormat as KeyFormat>::storage_linked_map_final_key::<KeyArg>(Self::prefix(), &key)
|
||||||
key.encode_to(&mut final_key);
|
|
||||||
Self::Hasher::hash(&final_key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate the hashed key for head
|
/// Generate the hashed key for head
|
||||||
fn storage_linked_map_final_head_key() -> <Self::Hasher as StorageHasher>::Output {
|
fn storage_linked_map_final_head_key() -> <Self::Hasher as StorageHasher>::Output {
|
||||||
Self::Hasher::hash(Self::head_key())
|
<Self::KeyFormat as KeyFormat>::storage_linked_map_final_head_key()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type-abstracted key format used for a family of linked-map types.
|
||||||
|
pub trait KeyFormat {
|
||||||
|
type Hasher: StorageHasher;
|
||||||
|
|
||||||
|
/// Key used to store linked map head.
|
||||||
|
fn head_key() -> &'static [u8];
|
||||||
|
|
||||||
|
/// Generate the full key used in top storage.
|
||||||
|
fn storage_linked_map_final_key<K>(prefix: &[u8], key: &K)
|
||||||
|
-> <Self::Hasher as StorageHasher>::Output
|
||||||
|
where
|
||||||
|
K: Encode,
|
||||||
|
{
|
||||||
|
let mut final_key = prefix.to_vec();
|
||||||
|
key.encode_to(&mut final_key);
|
||||||
|
<Self::Hasher as StorageHasher>::hash(&final_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn storage_linked_map_final_head_key()
|
||||||
|
-> <Self::Hasher as StorageHasher>::Output
|
||||||
|
{
|
||||||
|
<Self::Hasher as StorageHasher>::hash(Self::head_key())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,25 +133,39 @@ struct EncodeLikeLinkage<PKey: EncodeLike<Key>, NKey: EncodeLike<Key>, Key: Enco
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A key-value pair iterator for enumerable map.
|
/// A key-value pair iterator for enumerable map.
|
||||||
pub struct Enumerator<K: FullCodec, V: FullCodec, G: StorageLinkedMap<K, V>> {
|
pub struct Enumerator<K, V, F> {
|
||||||
next: Option<K>,
|
next: Option<K>,
|
||||||
_phantom: PhantomData<(G, V)>,
|
prefix: &'static [u8],
|
||||||
|
_phantom: PhantomData<(V, F)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, V, G> Iterator for Enumerator<K, V, G>
|
impl<K, V, F> Enumerator<K, V, F> {
|
||||||
|
/// Create an explicit enumerator for testing.
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn from_head(head: K, prefix: &'static [u8]) -> Self {
|
||||||
|
Enumerator {
|
||||||
|
next: Some(head),
|
||||||
|
prefix,
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V, F> Iterator for Enumerator<K, V, F>
|
||||||
where
|
where
|
||||||
K: FullCodec,
|
K: FullCodec,
|
||||||
V: FullCodec,
|
V: FullCodec,
|
||||||
G: StorageLinkedMap<K, V>,
|
F: KeyFormat,
|
||||||
{
|
{
|
||||||
type Item = (K, V);
|
type Item = (K, V);
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let next = self.next.take()?;
|
let next = self.next.take()?;
|
||||||
|
|
||||||
let (val, linkage): (V, Linkage<K>) = {
|
let (val, linkage): (V, Linkage<K>) = {
|
||||||
let next_full_key = G::storage_linked_map_final_key(&next);
|
let next_full_key = F::storage_linked_map_final_key(self.prefix, &next);
|
||||||
unhashed::get(next_full_key.as_ref())
|
read_with_linkage::<K, V>(next_full_key.as_ref())
|
||||||
.expect("previous/next only contain existing entires;
|
.expect("previous/next only contains existing entries;
|
||||||
we enumerate using next; entry exists; qed")
|
we enumerate using next; entry exists; qed")
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -136,33 +178,33 @@ where
|
|||||||
///
|
///
|
||||||
/// Takes care of updating previous and next elements points
|
/// Takes care of updating previous and next elements points
|
||||||
/// as well as updates head if the element is first or last.
|
/// as well as updates head if the element is first or last.
|
||||||
fn remove_linkage<K, V, G>(linkage: Linkage<K>)
|
fn remove_linkage<K, V, F>(linkage: Linkage<K>, prefix: &[u8])
|
||||||
where
|
where
|
||||||
K: FullCodec,
|
K: FullCodec,
|
||||||
V: FullCodec,
|
V: FullCodec,
|
||||||
G: StorageLinkedMap<K, V>,
|
F: KeyFormat,
|
||||||
{
|
{
|
||||||
let next_key = linkage.next.as_ref()
|
let next_key = linkage.next.as_ref()
|
||||||
.map(G::storage_linked_map_final_key)
|
.map(|k| F::storage_linked_map_final_key(prefix, k))
|
||||||
.map(|x| x.as_ref().to_vec());
|
.map(|x| x.as_ref().to_vec());
|
||||||
let prev_key = linkage.previous.as_ref()
|
let prev_key = linkage.previous.as_ref()
|
||||||
.map(G::storage_linked_map_final_key)
|
.map(|k| F::storage_linked_map_final_key(prefix, k))
|
||||||
.map(|x| x.as_ref().to_vec());
|
.map(|x| x.as_ref().to_vec());
|
||||||
|
|
||||||
if let Some(prev_key) = prev_key {
|
if let Some(prev_key) = prev_key {
|
||||||
// Retrieve previous element and update `next`
|
// Retrieve previous element and update `next`
|
||||||
let mut res = read_with_linkage::<_, _, G>(prev_key.as_ref())
|
let mut res = read_with_linkage::<K, V>(prev_key.as_ref())
|
||||||
.expect("Linkage is updated in case entry is removed;
|
.expect("Linkage is updated in case entry is removed;
|
||||||
it always points to existing keys; qed");
|
it always points to existing keys; qed");
|
||||||
res.1.next = linkage.next;
|
res.1.next = linkage.next;
|
||||||
unhashed::put(prev_key.as_ref(), &res);
|
unhashed::put(prev_key.as_ref(), &res);
|
||||||
} else {
|
} else {
|
||||||
// we were first so let's update the head
|
// we were first so let's update the head
|
||||||
write_head::<_, _, _, G>(linkage.next.as_ref());
|
write_head::<&K, K, F>(linkage.next.as_ref());
|
||||||
}
|
}
|
||||||
if let Some(next_key) = next_key {
|
if let Some(next_key) = next_key {
|
||||||
// Update previous of next element
|
// Update previous of next element
|
||||||
let mut res = read_with_linkage::<_, _, G>(next_key.as_ref())
|
let mut res = read_with_linkage::<K, V>(next_key.as_ref())
|
||||||
.expect("Linkage is updated in case entry is removed;
|
.expect("Linkage is updated in case entry is removed;
|
||||||
it always points to existing keys; qed");
|
it always points to existing keys; qed");
|
||||||
res.1.previous = linkage.previous;
|
res.1.previous = linkage.previous;
|
||||||
@@ -170,12 +212,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the contained data and it's linkage.
|
/// Read the contained data and its linkage.
|
||||||
fn read_with_linkage<K, V, G>(key: &[u8]) -> Option<(V, Linkage<K>)>
|
pub(super) fn read_with_linkage<K, V>(key: &[u8]) -> Option<(V, Linkage<K>)>
|
||||||
where
|
where
|
||||||
K: FullCodec,
|
K: Decode,
|
||||||
V: FullCodec,
|
V: Decode,
|
||||||
G: StorageLinkedMap<K, V>,
|
|
||||||
{
|
{
|
||||||
unhashed::get(key)
|
unhashed::get(key)
|
||||||
}
|
}
|
||||||
@@ -183,18 +224,18 @@ where
|
|||||||
/// Generate linkage for newly inserted element.
|
/// Generate linkage for newly inserted element.
|
||||||
///
|
///
|
||||||
/// Takes care of updating head and previous head's pointer.
|
/// Takes care of updating head and previous head's pointer.
|
||||||
fn new_head_linkage<KeyArg, K, V, G>(key: KeyArg) -> Linkage<K>
|
pub(super) fn new_head_linkage<KeyArg, K, V, F>(key: KeyArg, prefix: &[u8]) -> Linkage<K>
|
||||||
where
|
where
|
||||||
KeyArg: EncodeLike<K>,
|
KeyArg: EncodeLike<K>,
|
||||||
K: FullCodec,
|
K: FullCodec,
|
||||||
V: FullCodec,
|
V: FullCodec,
|
||||||
G: StorageLinkedMap<K, V>,
|
F: KeyFormat,
|
||||||
{
|
{
|
||||||
if let Some(head) = read_head::<_, _, G>() {
|
if let Some(head) = read_head::<K, F>() {
|
||||||
// update previous head predecessor
|
// update previous head predecessor
|
||||||
{
|
{
|
||||||
let head_key = G::storage_linked_map_final_key(&head);
|
let head_key = F::storage_linked_map_final_key(prefix, &head);
|
||||||
let (data, linkage) = read_with_linkage::<_, _, G>(head_key.as_ref())
|
let (data, linkage) = read_with_linkage::<K, V>(head_key.as_ref())
|
||||||
.expect("head is set when first element is inserted
|
.expect("head is set when first element is inserted
|
||||||
and unset when last element is removed;
|
and unset when last element is removed;
|
||||||
if head is Some then it points to existing key; qed");
|
if head is Some then it points to existing key; qed");
|
||||||
@@ -206,41 +247,39 @@ where
|
|||||||
unhashed::put(head_key.as_ref(), &(data, new_linkage));
|
unhashed::put(head_key.as_ref(), &(data, new_linkage));
|
||||||
}
|
}
|
||||||
// update to current head
|
// update to current head
|
||||||
write_head::<_, _, _, G>(Some(key));
|
write_head::<_, _, F>(Some(key));
|
||||||
// return linkage with pointer to previous head
|
// return linkage with pointer to previous head
|
||||||
let mut linkage = Linkage::default();
|
let mut linkage = Linkage::default();
|
||||||
linkage.next = Some(head);
|
linkage.next = Some(head);
|
||||||
linkage
|
linkage
|
||||||
} else {
|
} else {
|
||||||
// we are first - update the head and produce empty linkage
|
// we are first - update the head and produce empty linkage
|
||||||
write_head::<_, _, _, G>(Some(key));
|
write_head::<_, _, F>(Some(key));
|
||||||
Linkage::default()
|
Linkage::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read current head pointer.
|
/// Read current head pointer.
|
||||||
fn read_head<K, V, G>() -> Option<K>
|
pub(crate) fn read_head<K, F>() -> Option<K>
|
||||||
where
|
where
|
||||||
K: FullCodec,
|
K: Decode,
|
||||||
V: FullCodec,
|
F: KeyFormat,
|
||||||
G: StorageLinkedMap<K, V>,
|
|
||||||
{
|
{
|
||||||
unhashed::get(G::storage_linked_map_final_head_key().as_ref())
|
unhashed::get(F::storage_linked_map_final_head_key().as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overwrite current head pointer.
|
/// Overwrite current head pointer.
|
||||||
///
|
///
|
||||||
/// If `None` is given head is removed from storage.
|
/// If `None` is given head is removed from storage.
|
||||||
fn write_head<KeyArg, K, V, G>(head: Option<KeyArg>)
|
pub(super) fn write_head<KeyArg, K, F>(head: Option<KeyArg>)
|
||||||
where
|
where
|
||||||
KeyArg: EncodeLike<K>,
|
KeyArg: EncodeLike<K>,
|
||||||
K: FullCodec,
|
K: FullCodec,
|
||||||
V: FullCodec,
|
F: KeyFormat,
|
||||||
G: StorageLinkedMap<K, V>,
|
|
||||||
{
|
{
|
||||||
match head.as_ref() {
|
match head.as_ref() {
|
||||||
Some(head) => unhashed::put(G::storage_linked_map_final_head_key().as_ref(), head),
|
Some(head) => unhashed::put(F::storage_linked_map_final_head_key().as_ref(), head),
|
||||||
None => unhashed::kill(G::storage_linked_map_final_head_key().as_ref()),
|
None => unhashed::kill(F::storage_linked_map_final_head_key().as_ref()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +291,7 @@ where
|
|||||||
{
|
{
|
||||||
type Query = G::Query;
|
type Query = G::Query;
|
||||||
|
|
||||||
type Enumerator = Enumerator<K, V, Self>;
|
type Enumerator = Enumerator<K, V, G::KeyFormat>;
|
||||||
|
|
||||||
fn exists<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool {
|
fn exists<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool {
|
||||||
unhashed::exists(Self::storage_linked_map_final_key(key).as_ref())
|
unhashed::exists(Self::storage_linked_map_final_key(key).as_ref())
|
||||||
@@ -264,10 +303,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2) {
|
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2) {
|
||||||
|
let prefix = Self::prefix();
|
||||||
let final_key1 = Self::storage_linked_map_final_key(Ref::from(&key1));
|
let final_key1 = Self::storage_linked_map_final_key(Ref::from(&key1));
|
||||||
let final_key2 = Self::storage_linked_map_final_key(Ref::from(&key2));
|
let final_key2 = Self::storage_linked_map_final_key(Ref::from(&key2));
|
||||||
let full_value_1 = read_with_linkage::<_, _, G>(final_key1.as_ref());
|
let full_value_1 = read_with_linkage::<K, V>(final_key1.as_ref());
|
||||||
let full_value_2 = read_with_linkage::<_, _, G>(final_key2.as_ref());
|
let full_value_2 = read_with_linkage::<K, V>(final_key2.as_ref());
|
||||||
|
|
||||||
match (full_value_1, full_value_2) {
|
match (full_value_1, full_value_2) {
|
||||||
// Just keep linkage in order and only swap values.
|
// Just keep linkage in order and only swap values.
|
||||||
@@ -278,13 +318,13 @@ where
|
|||||||
// Remove key and insert the new one.
|
// Remove key and insert the new one.
|
||||||
(Some((value, _linkage)), None) => {
|
(Some((value, _linkage)), None) => {
|
||||||
Self::remove(key1);
|
Self::remove(key1);
|
||||||
let linkage = new_head_linkage::<_, _, _, G>(key2);
|
let linkage = new_head_linkage::<_, _, V, G::KeyFormat>(key2, prefix);
|
||||||
unhashed::put(final_key2.as_ref(), &(value, linkage));
|
unhashed::put(final_key2.as_ref(), &(value, linkage));
|
||||||
}
|
}
|
||||||
// Remove key and insert the new one.
|
// Remove key and insert the new one.
|
||||||
(None, Some((value, _linkage))) => {
|
(None, Some((value, _linkage))) => {
|
||||||
Self::remove(key2);
|
Self::remove(key2);
|
||||||
let linkage = new_head_linkage::<_, _, _, G>(key1);
|
let linkage = new_head_linkage::<_, _, V, G::KeyFormat>(key1, prefix);
|
||||||
unhashed::put(final_key1.as_ref(), &(value, linkage));
|
unhashed::put(final_key1.as_ref(), &(value, linkage));
|
||||||
}
|
}
|
||||||
// No-op.
|
// No-op.
|
||||||
@@ -294,11 +334,11 @@ where
|
|||||||
|
|
||||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg) {
|
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg) {
|
||||||
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
||||||
let linkage = match read_with_linkage::<_, _, G>(final_key.as_ref()) {
|
let linkage = match read_with_linkage::<_, V>(final_key.as_ref()) {
|
||||||
// overwrite but reuse existing linkage
|
// overwrite but reuse existing linkage
|
||||||
Some((_data, linkage)) => linkage,
|
Some((_data, linkage)) => linkage,
|
||||||
// create new linkage
|
// create new linkage
|
||||||
None => new_head_linkage::<_, _, _, G>(key),
|
None => new_head_linkage::<_, _, V, G::KeyFormat>(key, Self::prefix()),
|
||||||
};
|
};
|
||||||
unhashed::put(final_key.as_ref(), &(val, linkage))
|
unhashed::put(final_key.as_ref(), &(val, linkage))
|
||||||
}
|
}
|
||||||
@@ -310,7 +350,7 @@ where
|
|||||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R {
|
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R {
|
||||||
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
||||||
|
|
||||||
let (mut val, _linkage) = read_with_linkage::<_, _, G>(final_key.as_ref())
|
let (mut val, _linkage) = read_with_linkage::<K, V>(final_key.as_ref())
|
||||||
.map(|(data, linkage)| (G::from_optional_value_to_query(Some(data)), Some(linkage)))
|
.map(|(data, linkage)| (G::from_optional_value_to_query(Some(data)), Some(linkage)))
|
||||||
.unwrap_or_else(|| (G::from_optional_value_to_query(None), None));
|
.unwrap_or_else(|| (G::from_optional_value_to_query(None), None));
|
||||||
|
|
||||||
@@ -328,7 +368,7 @@ where
|
|||||||
let full_value: Option<(V, Linkage<K>)> = unhashed::take(final_key.as_ref());
|
let full_value: Option<(V, Linkage<K>)> = unhashed::take(final_key.as_ref());
|
||||||
|
|
||||||
let value = full_value.map(|(data, linkage)| {
|
let value = full_value.map(|(data, linkage)| {
|
||||||
remove_linkage::<_, _, G>(linkage);
|
remove_linkage::<K, V, G::KeyFormat>(linkage, Self::prefix());
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -336,14 +376,15 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn enumerate() -> Self::Enumerator {
|
fn enumerate() -> Self::Enumerator {
|
||||||
Enumerator::<_, _, G> {
|
Enumerator::<_, _, G::KeyFormat> {
|
||||||
next: read_head::<_, _, G>(),
|
next: read_head::<_, G::KeyFormat>(),
|
||||||
|
prefix: Self::prefix(),
|
||||||
_phantom: Default::default(),
|
_phantom: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn head() -> Option<K> {
|
fn head() -> Option<K> {
|
||||||
read_head::<_, _, G>()
|
read_head::<_, G::KeyFormat>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||||
@@ -360,4 +401,67 @@ where
|
|||||||
Ok(len)
|
Ok(len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn translate<K2, V2, TK, TV>(translate_key: TK, translate_val: TV) -> Result<(), Option<K2>>
|
||||||
|
where K2: FullCodec + Clone, V2: Decode, TK: Fn(K2) -> K, TV: Fn(V2) -> V
|
||||||
|
{
|
||||||
|
let head_key = read_head::<K2, G::KeyFormat>().ok_or(None)?;
|
||||||
|
let prefix = G::prefix();
|
||||||
|
|
||||||
|
let mut last_key = None;
|
||||||
|
let mut current_key = head_key.clone();
|
||||||
|
|
||||||
|
write_head::<&K, K, G::KeyFormat>(Some(&translate_key(head_key)));
|
||||||
|
|
||||||
|
let translate_linkage = |old: Linkage<K2>| -> Linkage<K> {
|
||||||
|
Linkage {
|
||||||
|
previous: old.previous.map(&translate_key),
|
||||||
|
next: old.next.map(&translate_key),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let old_raw_key = G::KeyFormat::storage_linked_map_final_key(prefix, ¤t_key);
|
||||||
|
let x = unhashed::take(old_raw_key.as_ref());
|
||||||
|
let (val, linkage): (V2, Linkage<K2>) = match x {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
// we failed to read value and linkage. Update the last key's linkage
|
||||||
|
// to end the map early, since it's impossible to iterate further.
|
||||||
|
if let Some(last_key) = last_key {
|
||||||
|
let last_raw_key = G::storage_linked_map_final_key(&last_key);
|
||||||
|
if let Some((val, mut linkage))
|
||||||
|
= read_with_linkage::<K, V>(last_raw_key.as_ref())
|
||||||
|
{
|
||||||
|
// defensive: should always happen, since it was just written
|
||||||
|
// in the last iteration of the loop.
|
||||||
|
linkage.next = None;
|
||||||
|
unhashed::put(last_raw_key.as_ref(), &(&val, &linkage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(Some(current_key));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let next = linkage.next.clone();
|
||||||
|
|
||||||
|
let val = translate_val(val);
|
||||||
|
let linkage = translate_linkage(linkage);
|
||||||
|
|
||||||
|
// and write in the value and linkage under the new key.
|
||||||
|
let new_key = translate_key(current_key.clone());
|
||||||
|
let new_raw_key = G::storage_linked_map_final_key(&new_key);
|
||||||
|
unhashed::put(new_raw_key.as_ref(), &(&val, &linkage));
|
||||||
|
|
||||||
|
match next {
|
||||||
|
None => break,
|
||||||
|
Some(next) => {
|
||||||
|
last_key = Some(new_key);
|
||||||
|
current_key = next
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ mod map;
|
|||||||
mod double_map;
|
mod double_map;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
pub use linked_map::{StorageLinkedMap, Enumerator, Linkage};
|
pub use linked_map::{StorageLinkedMap, Enumerator, Linkage, KeyFormat as LinkedMapKeyFormat};
|
||||||
pub use map::StorageMap;
|
pub use map::StorageMap;
|
||||||
pub use double_map::StorageDoubleMap;
|
pub use double_map::StorageDoubleMap;
|
||||||
pub use value::StorageValue;
|
pub use value::StorageValue;
|
||||||
@@ -36,8 +36,8 @@ pub use value::StorageValue;
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use runtime_io::TestExternalities;
|
use runtime_io::TestExternalities;
|
||||||
use codec::Encode;
|
use codec::{Encode, Decode};
|
||||||
use crate::storage::{unhashed, generator::StorageValue};
|
use crate::storage::{unhashed, generator::{StorageValue, StorageLinkedMap}};
|
||||||
|
|
||||||
struct Runtime {}
|
struct Runtime {}
|
||||||
pub trait Trait {
|
pub trait Trait {
|
||||||
@@ -54,9 +54,16 @@ mod tests {
|
|||||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Encode, Decode, Clone, Debug, Eq, PartialEq)]
|
||||||
|
struct NumberNumber {
|
||||||
|
a: u32,
|
||||||
|
b: u32,
|
||||||
|
}
|
||||||
|
|
||||||
crate::decl_storage! {
|
crate::decl_storage! {
|
||||||
trait Store for Module<T: Trait> as Runtime {
|
trait Store for Module<T: Trait> as Runtime {
|
||||||
Value get(fn value) config(): (u64, u64);
|
Value get(fn value) config(): (u64, u64);
|
||||||
|
NumberMap: linked_map NumberNumber => u64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,4 +85,48 @@ mod tests {
|
|||||||
assert_eq!(Value::get(), (1111, 2222));
|
assert_eq!(Value::get(), (1111, 2222));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn linked_map_translate_works() {
|
||||||
|
use super::linked_map::{self, Enumerator, KeyFormat};
|
||||||
|
|
||||||
|
type Format = <NumberMap as StorageLinkedMap<NumberNumber, u64>>::KeyFormat;
|
||||||
|
|
||||||
|
let t = GenesisConfig::default().build_storage().unwrap();
|
||||||
|
TestExternalities::new(t).execute_with(|| {
|
||||||
|
let prefix = NumberMap::prefix();
|
||||||
|
|
||||||
|
// start with a map of u32 -> u32.
|
||||||
|
for i in 0u32..100u32 {
|
||||||
|
let final_key = <Format as KeyFormat>::storage_linked_map_final_key(
|
||||||
|
prefix, &i,
|
||||||
|
);
|
||||||
|
|
||||||
|
let linkage = linked_map::new_head_linkage::<_, u32, u32, Format>(&i, prefix);
|
||||||
|
unhashed::put(final_key.as_ref(), &(&i, linkage));
|
||||||
|
}
|
||||||
|
|
||||||
|
let head = linked_map::read_head::<u32, Format>().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Enumerator::<u32, u32, Format>::from_head(head, prefix).collect::<Vec<_>>(),
|
||||||
|
(0..100).rev().map(|x| (x, x)).collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// do translation.
|
||||||
|
NumberMap::translate(
|
||||||
|
|k: u32| NumberNumber { a: k, b: k },
|
||||||
|
|v: u32| (v as u64) << 32 | v as u64,
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
assert!(linked_map::read_head::<NumberNumber, Format>().is_some());
|
||||||
|
assert_eq!(
|
||||||
|
NumberMap::enumerate().collect::<Vec<_>>(),
|
||||||
|
(0..100u32).rev().map(|x| (
|
||||||
|
NumberNumber { a: x, b: x },
|
||||||
|
(x as u64) << 32 | x as u64,
|
||||||
|
)).collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,6 +230,27 @@ pub trait StorageLinkedMap<K: FullCodec, V: FullCodec> {
|
|||||||
/// function for this purpose.
|
/// function for this purpose.
|
||||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||||
where V: codec::DecodeLength + Len;
|
where V: codec::DecodeLength + Len;
|
||||||
|
|
||||||
|
/// Translate the keys and values from some previous `(K2, V2)` to the current type.
|
||||||
|
///
|
||||||
|
/// `TK` translates keys from the old type, and `TV` translates values.
|
||||||
|
///
|
||||||
|
/// Returns `Err` if the map could not be interpreted as the old type, and Ok if it could.
|
||||||
|
/// The `Err` contains the first key which could not be migrated, or `None` if the
|
||||||
|
/// head of the list could not be read.
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// This function must be used with care, before being updated the storage still contains the
|
||||||
|
/// old type, thus other calls (such as `get`) will fail at decoding it.
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
///
|
||||||
|
/// This would typically be called inside the module implementation of on_initialize, while
|
||||||
|
/// ensuring **no usage of this storage are made before the call to `on_initialize`**. (More
|
||||||
|
/// precisely prior initialized modules doesn't make use of this storage).
|
||||||
|
fn translate<K2, V2, TK, TV>(translate_key: TK, translate_val: TV) -> Result<(), Option<K2>>
|
||||||
|
where K2: FullCodec + Clone, V2: Decode, TK: Fn(K2) -> K, TV: Fn(V2) -> V;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An implementation of a map with a two keys.
|
/// An implementation of a map with a two keys.
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ fn final_keys_default_instance() {
|
|||||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&k)), Some(2u32));
|
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&k)), Some(2u32));
|
||||||
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&head)), Some(1u32));
|
assert_eq!(unhashed::get::<u32>(&hashing::blake2_256(&head)), Some(1u32));
|
||||||
|
|
||||||
< instance::LinkedMap2<instance::DefaultInstance>>::insert(1, 2);
|
<instance::LinkedMap2<instance::DefaultInstance>>::insert(1, 2);
|
||||||
let mut k = b"FinalKeysSome LinkedMap2".to_vec();
|
let mut k = b"FinalKeysSome LinkedMap2".to_vec();
|
||||||
k.extend(1u32.encode());
|
k.extend(1u32.encode());
|
||||||
assert_eq!(unhashed::get::<u32>(&hashing::twox_128(&k)), Some(2u32));
|
assert_eq!(unhashed::get::<u32>(&hashing::twox_128(&k)), Some(2u32));
|
||||||
|
|||||||
Reference in New Issue
Block a user