diff --git a/srml/support/src/lib.rs b/srml/support/src/lib.rs index cfe6487203ddd..c3649dd6ce553 100644 --- a/srml/support/src/lib.rs +++ b/srml/support/src/lib.rs @@ -327,6 +327,36 @@ mod tests { }); } + #[test] + fn double_map_swap_works() { + new_test_ext().execute_with(|| { + type DoubleMap = DataDM; + DataDM::insert(0, 1, 0); + DataDM::insert(0, 2, 1); + DataDM::insert(0, 3, 2); + DataDM::insert(0, 4, 3); + + let collect = || DataDM::collect::>(); + assert_eq!(collect(), vec![(3, 3), (2, 2), (1, 1), (0, 0)]); + + // Two existing + DataDM::swap(0, 1, 0, 2); + assert_eq!(collect(), vec![(3, 3), (2, 1), (1, 2), (0, 0)]); + + // Back to normal + DataDM::swap(0, 2, 0, 1); + assert_eq!(collect(), vec![(3, 3), (2, 2), (1, 1), (0, 0)]); + + // Left existing + DataDM::swap(0, 2, 0, 5); + assert_eq!(collect(), vec![(5, 2), (3, 3), (1, 1), (0, 0)]); + + // Right existing + DataDM::swap(0, 5, 0, 2); + assert_eq!(collect(), vec![(2, 2), (3, 3), (1, 1), (0, 0)]); + }); + } + #[test] fn linked_map_basic_insert_remove_should_work() { new_test_ext().execute_with(|| { diff --git a/srml/support/src/storage/generator/double_map.rs b/srml/support/src/storage/generator/double_map.rs index cac8dbd034e85..95295d203d78e 100644 --- a/srml/support/src/storage/generator/double_map.rs +++ b/srml/support/src/storage/generator/double_map.rs @@ -17,7 +17,7 @@ use rstd::prelude::*; use rstd::borrow::Borrow; use codec::{Ref, FullCodec, FullEncode, Encode, EncodeLike, EncodeAppend}; -use crate::{storage::{self, unhashed}, hash::StorageHasher}; +use crate::{storage::{self, unhashed}, hash::StorageHasher, traits::Len}; /// Generator for `StorageDoubleMap` used by `decl_storage`. /// @@ -116,6 +116,29 @@ where G::from_optional_value_to_query(value) } + fn swap(key11: KArg1, key12: KArg2, key21: KArg3, key22: KArg4) + where + KArg1: EncodeLike, + KArg2: EncodeLike, + KArg3: EncodeLike, + KArg4: EncodeLike, + { + let final_k1 = Self::storage_double_map_final_key(key11, key12); + let final_k2 = Self::storage_double_map_final_key(key21, key22); + + let v1 = unhashed::get_raw(&final_k1); + if let Some(val) = unhashed::get_raw(&final_k2) { + unhashed::put_raw(&final_k1, &val); + } else { + unhashed::kill(&final_k1) + } + if let Some(val) = v1 { + unhashed::put_raw(&final_k2, &val); + } else { + unhashed::kill(&final_k2) + } + } + fn insert(k1: KArg1, k2: KArg2, val: VArg) where KArg1: EncodeLike, @@ -204,4 +227,21 @@ where Self::append(Ref::from(&k1), Ref::from(&k2), items.clone()) .unwrap_or_else(|_| Self::insert(k1, k2, items)); } + + fn decode_len(key1: KArg1, key2: KArg2) -> Result + where KArg1: EncodeLike, + KArg2: EncodeLike, + V: codec::DecodeLength + Len, + { + let final_key = Self::storage_double_map_final_key(key1, key2); + if let Some(v) = unhashed::get_raw(&final_key) { + ::len(&v).map_err(|e| e.what()) + } else { + let len = G::from_query_to_optional_value(G::from_optional_value_to_query(None)) + .map(|v| v.len()) + .unwrap_or(0); + + Ok(len) + } + } } diff --git a/srml/support/src/storage/mod.rs b/srml/support/src/storage/mod.rs index f10deb93d241a..df8a73708cdb7 100644 --- a/srml/support/src/storage/mod.rs +++ b/srml/support/src/storage/mod.rs @@ -258,6 +258,14 @@ pub trait StorageDoubleMap { KArg1: EncodeLike, KArg2: EncodeLike; + /// Swap the values of two key-pairs. + fn swap(key11: KArg1, key12: KArg2, key21: KArg3, key22: KArg4) + where + KArg1: EncodeLike, + KArg2: EncodeLike, + KArg3: EncodeLike, + KArg4: EncodeLike; + fn insert(k1: KArg1, k2: KArg2, val: VArg) where KArg1: EncodeLike, @@ -304,4 +312,17 @@ pub trait StorageDoubleMap { V: EncodeAppend, Items: IntoIterator + Clone + EncodeLike, Items::IntoIter: ExactSizeIterator; + + /// Read the length of the value in a fast way, without decoding the entire value. + /// + /// `V` is required to implement `Codec::DecodeLength`. + /// + /// 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 `::exists()` + /// function for this purpose. + fn decode_len(key1: KArg1, key2: KArg2) -> Result + where + KArg1: EncodeLike, + KArg2: EncodeLike, + V: codec::DecodeLength + Len; } diff --git a/srml/support/test/tests/decl_storage.rs b/srml/support/test/tests/decl_storage.rs index c9dd96791b164..631cb27bc42f1 100644 --- a/srml/support/test/tests/decl_storage.rs +++ b/srml/support/test/tests/decl_storage.rs @@ -522,6 +522,10 @@ mod test_append_and_len { MapVecWithDefault: map u32 => Vec = vec![6, 9]; OptionMapVec: map u32 => Option>; + DoubleMapVec: double_map u32, u32 => Vec; + DoubleMapVecWithDefault: double_map u32, u32 => Vec = vec![6, 9]; + OptionDoubleMapVec: double_map u32, u32 => Option>; + LinkedMapVec: linked_map u32 => Vec; LinkedMapVecWithDefault: linked_map u32 => Vec = vec![6, 9]; OptionLinkedMapVec: linked_map u32 => Option>; @@ -597,11 +601,13 @@ mod test_append_and_len { OptionVec::put(&vec![1, 2, 3, 4, 5]); MapVec::insert(1, &vec![1, 2, 3, 4, 5, 6]); LinkedMapVec::insert(2, &vec![1, 2, 3]); + DoubleMapVec::insert(0, 1, &vec![1, 2]); assert_eq!(JustVec::decode_len().unwrap(), 4); assert_eq!(OptionVec::decode_len().unwrap(), 5); assert_eq!(MapVec::decode_len(1).unwrap(), 6); assert_eq!(LinkedMapVec::decode_len(2).unwrap(), 3); + assert_eq!(DoubleMapVec::decode_len(0, 1).unwrap(), 2); }); } @@ -637,6 +643,16 @@ mod test_append_and_len { assert_eq!(OptionLinkedMapVec::get(0), None); assert_eq!(OptionLinkedMapVec::decode_len(0), Ok(0)); + + // Double map + assert_eq!(DoubleMapVec::get(0, 0), vec![]); + assert_eq!(DoubleMapVec::decode_len(0, 1), Ok(0)); + + assert_eq!(DoubleMapVecWithDefault::get(0, 0), vec![6, 9]); + assert_eq!(DoubleMapVecWithDefault::decode_len(0, 1), Ok(2)); + + assert_eq!(OptionDoubleMapVec::get(0, 0), None); + assert_eq!(OptionDoubleMapVec::decode_len(0, 1), Ok(0)); }); } }