From 0f7b0e1a76bc5739db36cec92fc2fa3f15dff4bf Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 4 Feb 2020 20:12:38 +0100 Subject: [PATCH 001/142] [core] initial implementation and setup of the new storage traits --- core/src/storage/mod.rs | 7 + core/src/storage/traits/mod.rs | 61 ++++++++ core/src/storage/traits/pull.rs | 177 ++++++++++++++++++++++++ core/src/storage/traits/push.rs | 171 +++++++++++++++++++++++ core/src/storage/traits/storage_size.rs | 95 +++++++++++++ 5 files changed, 511 insertions(+) create mode 100644 core/src/storage/traits/mod.rs create mode 100644 core/src/storage/traits/pull.rs create mode 100644 core/src/storage/traits/push.rs create mode 100644 core/src/storage/traits/storage_size.rs diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 6b56d95bd09..dd88f7624ae 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -74,6 +74,7 @@ pub mod cell; pub mod chunk; mod collections; mod flush; +mod traits; mod value; pub use self::{ @@ -100,6 +101,12 @@ pub use self::{ }, }, flush::Flush, + traits::{ + KeyPtr, + Pull, + Push, + StorageSize, + }, }; #[doc(inline)] diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs new file mode 100644 index 00000000000..f47eff23d5a --- /dev/null +++ b/core/src/storage/traits/mod.rs @@ -0,0 +1,61 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[rustfmt::skip] +macro_rules! forward_supported_array_lens { + ( $mac:ident ) => { + $mac! { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, + 64, 96, 128, 256, 512, 1024, 2048, 4096, + 8192, 16384, + } + }; +} + +mod push; +mod pull; +mod storage_size; + +use ink_primitives::Key; + +pub use self::{ + storage_size::StorageSize, + push::Push, + pull::Pull, +}; + +/// A key pointer. +/// +/// Mainly used by [`Pull`] and [`Push`] traits in order to provide +/// a streamlined interface for accessing the underlying [`Key`]. +pub struct KeyPtr { + /// The underlying key. + key: Key, +} + +impl KeyPtr { + /// Advances the key by the given amount derive by `T` and its `StorageSize` + /// and returns the next `Key` for usage by the storage element. + pub fn next_for(&mut self) -> Key + where + T: StorageSize, + { + let copy = self.key; + self.key += ::SIZE; + copy + } +} diff --git a/core/src/storage/traits/pull.rs b/core/src/storage/traits/pull.rs new file mode 100644 index 00000000000..6adddfff904 --- /dev/null +++ b/core/src/storage/traits/pull.rs @@ -0,0 +1,177 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{ + KeyPtr, + StorageSize, +}; + +/// Pulls the associated key values from the contract storage and forms `Self`. +pub trait Pull { + fn pull(key_ptr: &mut KeyPtr) -> Self; +} + +fn pull_single_cell(key_ptr: &mut KeyPtr) -> T +where + T: StorageSize + scale::Decode, +{ + crate::env::get_contract_storage::(key_ptr.next_for::()) + .expect("storage entry was empty") + .expect("could not properly decode storage entry") +} + +macro_rules! impl_pull_for_primitive { + ( $($ty:ty),* $(,)? ) => { + $( + impl Pull for $ty { + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::<$ty>(key_ptr) + } + } + )* + }; +} +impl_pull_for_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + +macro_rules! impl_pull_for_array { + ( $($len:literal),* $(,)? ) => { + $( + impl Pull for [T; $len] + where + [T; $len]: scale::Decode, + { + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::<[T; $len]>(key_ptr) + } + } + )* + } +} +forward_supported_array_lens!(impl_pull_for_array); + +macro_rules! impl_pull_tuple { + ( $($frag:ident),* $(,)? ) => { + impl<$($frag),*> Pull for ($($frag),* ,) + where + ( $($frag),* ,): scale::Decode, + { + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::<($($frag),* ,)>(key_ptr) + } + } + } +} +impl_pull_tuple!(A); +impl_pull_tuple!(A, B); +impl_pull_tuple!(A, B, C); +impl_pull_tuple!(A, B, C, D); +impl_pull_tuple!(A, B, C, D, E); +impl_pull_tuple!(A, B, C, D, E, F); +impl_pull_tuple!(A, B, C, D, E, F, G); +impl_pull_tuple!(A, B, C, D, E, F, G, H); +impl_pull_tuple!(A, B, C, D, E, F, G, H, I); +impl_pull_tuple!(A, B, C, D, E, F, G, H, I, J); + +impl Pull for () { + fn pull(_key_ptr: &mut KeyPtr) -> Self { + () + } +} + +impl Pull for core::marker::PhantomData { + fn pull(_key_ptr: &mut KeyPtr) -> Self { + Default::default() + } +} + +impl Pull for Option +where + Self: scale::Decode, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::(key_ptr) + } +} + +impl Pull for Result +where + Self: scale::Decode, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::(key_ptr) + } +} + +impl Pull for ink_prelude::vec::Vec +where + Self: scale::Decode, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::(key_ptr) + } +} + +impl Pull for ink_prelude::string::String +where + Self: scale::Decode, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::(key_ptr) + } +} + +impl Pull for ink_prelude::boxed::Box +where + Self: scale::Decode, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::(key_ptr) + } +} + +impl Pull for ink_prelude::collections::BTreeSet +where + Self: scale::Decode, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::(key_ptr) + } +} + +impl Pull for ink_prelude::collections::BinaryHeap +where + Self: scale::Decode, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::(key_ptr) + } +} + +impl Pull for ink_prelude::collections::LinkedList +where + Self: scale::Decode, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::(key_ptr) + } +} + +impl Pull for ink_prelude::collections::VecDeque +where + Self: scale::Decode, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + pull_single_cell::(key_ptr) + } +} diff --git a/core/src/storage/traits/push.rs b/core/src/storage/traits/push.rs new file mode 100644 index 00000000000..c38735d72f6 --- /dev/null +++ b/core/src/storage/traits/push.rs @@ -0,0 +1,171 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{ + KeyPtr, + StorageSize, +}; + +/// Pushes the associated key values of `Self` to the contract storage. +pub trait Push { + fn push(&self, key_ptr: &mut KeyPtr); +} + +fn push_single_cell(value: &T, key_ptr: &mut KeyPtr) +where + T: StorageSize + scale::Encode, +{ + crate::env::set_contract_storage::(key_ptr.next_for::(), value) +} + +macro_rules! impl_push_for_primitive { + ( $($ty:ty),* $(,)? ) => { + $( + impl Push for $ty { + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } + } + )* + }; +} +impl_push_for_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + +macro_rules! impl_push_for_array { + ( $($len:literal),* $(,)? ) => { + $( + impl Push for [T; $len] + where + [T; $len]: scale::Encode, + { + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } + } + )* + } +} +forward_supported_array_lens!(impl_push_for_array); + +macro_rules! impl_push_tuple { + ( $($frag:ident),* $(,)? ) => { + impl<$($frag),*> Push for ($($frag),* ,) + where + ( $($frag),* ,): scale::Encode, + { + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } + } + } +} +impl_push_tuple!(A); +impl_push_tuple!(A, B); +impl_push_tuple!(A, B, C); +impl_push_tuple!(A, B, C, D); +impl_push_tuple!(A, B, C, D, E); +impl_push_tuple!(A, B, C, D, E, F); +impl_push_tuple!(A, B, C, D, E, F, G); +impl_push_tuple!(A, B, C, D, E, F, G, H); +impl_push_tuple!(A, B, C, D, E, F, G, H, I); +impl_push_tuple!(A, B, C, D, E, F, G, H, I, J); + +impl Push for () { + fn push(&self, _key_ptr: &mut KeyPtr) {} +} + +impl Push for core::marker::PhantomData { + fn push(&self, _key_ptr: &mut KeyPtr) {} +} + +impl Push for Option +where + Self: scale::Encode, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } +} + +impl Push for Result +where + Self: scale::Encode, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } +} + +impl Push for ink_prelude::vec::Vec +where + Self: scale::Encode, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } +} + +impl Push for ink_prelude::string::String +where + Self: scale::Encode, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } +} + +impl Push for ink_prelude::boxed::Box +where + Self: scale::Encode, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } +} + +impl Push for ink_prelude::collections::BTreeSet +where + Self: scale::Encode, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } +} + +impl Push for ink_prelude::collections::BinaryHeap +where + Self: scale::Encode, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } +} + +impl Push for ink_prelude::collections::LinkedList +where + Self: scale::Encode, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } +} + +impl Push for ink_prelude::collections::VecDeque +where + Self: scale::Encode, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + push_single_cell(self, key_ptr) + } +} diff --git a/core/src/storage/traits/storage_size.rs b/core/src/storage/traits/storage_size.rs new file mode 100644 index 00000000000..a79d9ec6776 --- /dev/null +++ b/core/src/storage/traits/storage_size.rs @@ -0,0 +1,95 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Implemented by types that can be stored on contract storage. +/// +/// Tells the amount of storage cells the type requires to be stored. +pub trait StorageSize { + /// The number of storage cells required by `Self` to be stored + /// on the contract storage. + const SIZE: u64; +} + +macro_rules! impl_storage_size_1 { + ( $($ty:ty),* ) => { + $( + impl StorageSize for $ty { const SIZE: u64 = 1; } + )* + }; +} +impl_storage_size_1!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + +macro_rules! impl_storage_size_array { + ( $($n:literal),* $(,)? ) => { + $( + impl StorageSize for [T; $n] { const SIZE: u64 = 1; } + )* + }; +} +forward_supported_array_lens!(impl_storage_size_array); + +macro_rules! impl_storage_size_tuple { + ( $($frag:ident),* $(,)? ) => { + #[allow(unused_parens)] + impl<$($frag),*> StorageSize for ($($frag),* ,) { const SIZE: u64 = 1; } + } +} +impl_storage_size_tuple!(A); +impl_storage_size_tuple!(A, B); +impl_storage_size_tuple!(A, B, C); +impl_storage_size_tuple!(A, B, C, D); +impl_storage_size_tuple!(A, B, C, D, E); +impl_storage_size_tuple!(A, B, C, D, E, F); +impl_storage_size_tuple!(A, B, C, D, E, F, G); +impl_storage_size_tuple!(A, B, C, D, E, F, G, H); +impl_storage_size_tuple!(A, B, C, D, E, F, G, H, I); +impl_storage_size_tuple!(A, B, C, D, E, F, G, H, I, J); + +impl StorageSize for () { + const SIZE: u64 = 0; +} +impl StorageSize for core::marker::PhantomData { + const SIZE: u64 = 0; +} +impl StorageSize for Option { + const SIZE: u64 = 1; +} +impl StorageSize for Result { + const SIZE: u64 = 1; +} +impl StorageSize for ink_prelude::vec::Vec { + const SIZE: u64 = 1; +} +impl StorageSize for ink_prelude::string::String { + const SIZE: u64 = 1; +} +impl StorageSize for ink_prelude::boxed::Box { + const SIZE: u64 = 1; +} + +impl StorageSize for ink_prelude::collections::BTreeSet { + const SIZE: u64 = 1; +} + +impl StorageSize for ink_prelude::collections::BinaryHeap { + const SIZE: u64 = 1; +} + +impl StorageSize for ink_prelude::collections::LinkedList { + const SIZE: u64 = 1; +} + +impl StorageSize for ink_prelude::collections::VecDeque { + const SIZE: u64 = 1; +} From 823b1015e96d3b360c9bca677f3b19fc82480c12 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 5 Feb 2020 02:09:28 +0100 Subject: [PATCH 002/142] [core] add initial implementation of the storage::Lazy abstraction --- core/src/storage/lazy.rs | 179 +++++++++++++++++++++++++++++++++++++++ core/src/storage/mod.rs | 2 + 2 files changed, 181 insertions(+) create mode 100644 core/src/storage/lazy.rs diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy.rs new file mode 100644 index 00000000000..aacba364afa --- /dev/null +++ b/core/src/storage/lazy.rs @@ -0,0 +1,179 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use ink_primitives::Key; +use core::cell::UnsafeCell; + +/// A lazy storage entity. +/// +/// This loads its value from storage upon first use. +/// +/// # Note +/// +/// Use this if the storage field doesn't need to be loaded in some or most cases. +pub struct Lazy { + kind: UnsafeCell>, +} + +impl Lazy { + /// Creates an eagerly populated lazy storage value. + pub fn eager(value: T) -> Self { + Self { + kind: UnsafeCell::new(LazyKind::Occupied(OccupiedLazy::new(value))), + } + } + + /// Creates a true lazy storage value for the given key. + pub fn lazy(key: Key) -> Self { + Self { + kind: UnsafeCell::new(LazyKind::Vacant(VacantLazy::new(key))), + } + } + + /// Returns a shared reference to the inner lazy kind. + fn kind(&self) -> &LazyKind { + unsafe { &*self.kind.get() } + } + + /// Returns an exclusive reference to the inner lazy kind. + fn kind_mut(&mut self) -> &mut LazyKind { + unsafe { &mut *self.kind.get() } + } + + /// Performs the given closure on the mutable lazy kind. + /// + /// # Note + /// + /// Actions on the mutable lazy kind are performed within the closure + /// to not leak exclusive references to it to the outside. This is important + /// since the `for_kind` method itself operates only on `&self`. + fn for_kind(&self, f: F) -> R + where + F: FnOnce(&mut LazyKind) -> R, + { + f(unsafe { &mut *self.kind.get() }) + } +} + +impl Lazy +where + T: scale::Decode, +{ + /// Loads the value lazily from contract storage. + /// + /// Does nothing if value has already been loaded. + fn load_value_lazily(&self) { + self.for_kind(|kind| { + if let LazyKind::Vacant(vacant) = kind { + let value = crate::env::get_contract_storage::(vacant.key) + .expect("couldn't find contract storage entry") + .expect("couldn't properly decode contract storage entry"); + *kind = LazyKind::Occupied(OccupiedLazy::new(value)); + } + }); + } + + /// Returns a shared reference to the lazily loaded value. + /// + /// # Note + /// + /// This loads the value from the contract storage if this did not happed before. + /// + /// # Panics + /// + /// If loading from contract storage failed. + pub fn get(&self) -> &T { + self.load_value_lazily(); + match self.kind() { + LazyKind::Vacant(_) => panic!("expect occupied lazy here"), + LazyKind::Occupied(occupied) => &occupied.value, + } + } + + /// Returns an exclusive reference to the lazily loaded value. + /// + /// # Note + /// + /// This loads the value from the contract storage if this did not happed before. + /// + /// # Panics + /// + /// If loading from contract storage failed. + pub fn get_mut(&mut self) -> &mut T { + self.load_value_lazily(); + match self.kind_mut() { + LazyKind::Vacant(_) => panic!("expect occupied lazy here"), + LazyKind::Occupied(occupied) => &mut occupied.value, + } + } +} + +impl core::ops::Deref for Lazy +where + T: scale::Decode, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl core::ops::DerefMut for Lazy +where + T: scale::Decode, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.get_mut() + } +} + +/// The lazy storage entity can be in either of two states. +/// +/// 1. It either is vacant and thus a real lazy storage entity that +/// waits until it is used for the first time in order to load its value +/// from the contract storage. +/// 2. It is actually an already occupied eager lazy. +pub enum LazyKind { + /// A true lazy storage entity that loads its contract storage value upon first use. + Vacant(VacantLazy), + /// An already loaded eager lazy storage entity. + Occupied(OccupiedLazy), +} + +/// The lazy storage entity is in a lazy state. +pub struct VacantLazy { + /// The key to load the value from contract storage upon first use. + pub key: Key, +} + +impl VacantLazy { + /// Creates a new truly lazy storage entity for the given key. + pub fn new(key: Key) -> Self { + Self { key } + } +} + +/// An already loaded or otherwise occupied eager lazy storage entity. +pub struct OccupiedLazy { + /// The loaded value. + pub value: T, +} + +impl OccupiedLazy { + /// Creates a new eager lazy storage entity with the given value. + pub fn new(value: T) -> Self { + Self { value } + } +} diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index dd88f7624ae..1c4ca7f1f82 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -76,6 +76,7 @@ mod collections; mod flush; mod traits; mod value; +mod lazy; pub use self::{ collections::{ @@ -107,6 +108,7 @@ pub use self::{ Push, StorageSize, }, + lazy::Lazy, }; #[doc(inline)] From 7632f1394f5e9d43496f79519c87fba431e3eb9e Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 5 Feb 2020 13:48:05 +0100 Subject: [PATCH 003/142] [core] rename Lazy::eager -> Lazy::new --- core/src/storage/lazy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy.rs index aacba364afa..9324806b96e 100644 --- a/core/src/storage/lazy.rs +++ b/core/src/storage/lazy.rs @@ -28,7 +28,7 @@ pub struct Lazy { impl Lazy { /// Creates an eagerly populated lazy storage value. - pub fn eager(value: T) -> Self { + pub fn new(value: T) -> Self { Self { kind: UnsafeCell::new(LazyKind::Occupied(OccupiedLazy::new(value))), } From 5fdd886c5c433c45193ab5e72733a3837b20861a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 5 Feb 2020 13:49:02 +0100 Subject: [PATCH 004/142] [core] implement Pull, Push & StorageSize for storage::Lazy --- core/src/storage/lazy.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy.rs index 9324806b96e..2734fca682b 100644 --- a/core/src/storage/lazy.rs +++ b/core/src/storage/lazy.rs @@ -14,6 +14,7 @@ use ink_primitives::Key; use core::cell::UnsafeCell; +use super::{KeyPtr, StorageSize, Pull, Push}; /// A lazy storage entity. /// @@ -26,6 +27,34 @@ pub struct Lazy { kind: UnsafeCell>, } +impl StorageSize for Lazy +where + T: StorageSize, +{ + const SIZE: u64 = ::SIZE; +} + +impl Pull for Lazy +where + T: StorageSize + scale::Decode, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + Self::lazy(key_ptr.next_for::()) + } +} + +impl Push for Lazy +where + T: Push, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + // We skip pushing to contract storage if we are still in unloaded form. + if let LazyKind::Occupied(occupied) = self.kind() { + occupied.value.push(key_ptr) + } + } +} + impl Lazy { /// Creates an eagerly populated lazy storage value. pub fn new(value: T) -> Self { From 8bf99a45f48edf64581f452af76fbdfdec90e63d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 5 Feb 2020 14:18:37 +0100 Subject: [PATCH 005/142] [core] add some #[must_use] attributes to Lazy methods --- core/src/storage/lazy.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy.rs index 2734fca682b..38680daa5bd 100644 --- a/core/src/storage/lazy.rs +++ b/core/src/storage/lazy.rs @@ -57,6 +57,7 @@ where impl Lazy { /// Creates an eagerly populated lazy storage value. + #[must_use] pub fn new(value: T) -> Self { Self { kind: UnsafeCell::new(LazyKind::Occupied(OccupiedLazy::new(value))), @@ -64,6 +65,7 @@ impl Lazy { } /// Creates a true lazy storage value for the given key. + #[must_use] pub fn lazy(key: Key) -> Self { Self { kind: UnsafeCell::new(LazyKind::Vacant(VacantLazy::new(key))), @@ -71,11 +73,13 @@ impl Lazy { } /// Returns a shared reference to the inner lazy kind. + #[must_use] fn kind(&self) -> &LazyKind { unsafe { &*self.kind.get() } } /// Returns an exclusive reference to the inner lazy kind. + #[must_use] fn kind_mut(&mut self) -> &mut LazyKind { unsafe { &mut *self.kind.get() } } @@ -122,6 +126,7 @@ where /// # Panics /// /// If loading from contract storage failed. + #[must_use] pub fn get(&self) -> &T { self.load_value_lazily(); match self.kind() { @@ -139,6 +144,7 @@ where /// # Panics /// /// If loading from contract storage failed. + #[must_use] pub fn get_mut(&mut self) -> &mut T { self.load_value_lazily(); match self.kind_mut() { From 5372afdb4f5613c843f169bb1477f989e18d87fe Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 5 Feb 2020 15:04:51 +0100 Subject: [PATCH 006/142] [core] implement the rest of default Rust traits for storage::Lazy --- core/src/storage/lazy.rs | 127 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 2 deletions(-) diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy.rs index 38680daa5bd..00eb8552661 100644 --- a/core/src/storage/lazy.rs +++ b/core/src/storage/lazy.rs @@ -12,9 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use ink_primitives::Key; +use super::{ + KeyPtr, + Pull, + Push, + StorageSize, +}; use core::cell::UnsafeCell; -use super::{KeyPtr, StorageSize, Pull, Push}; +use ink_primitives::Key; /// A lazy storage entity. /// @@ -23,6 +28,7 @@ use super::{KeyPtr, StorageSize, Pull, Push}; /// # Note /// /// Use this if the storage field doesn't need to be loaded in some or most cases. +#[derive(Debug)] pub struct Lazy { kind: UnsafeCell>, } @@ -154,6 +160,120 @@ where } } +impl From for Lazy { + fn from(value: T) -> Self { + Self::new(value) + } +} + +impl Default for Lazy +where + T: Default, +{ + fn default() -> Self { + Self::new(Default::default()) + } +} + +impl core::cmp::PartialEq for Lazy +where + T: PartialEq + scale::Decode, +{ + fn eq(&self, other: &Self) -> bool { + PartialEq::eq(self.get(), other.get()) + } + + fn ne(&self, other: &Self) -> bool { + PartialEq::ne(self.get(), other.get()) + } +} + +impl core::cmp::Eq for Lazy where T: Eq + scale::Decode {} + +impl core::cmp::PartialOrd for Lazy +where + T: PartialOrd + scale::Decode, +{ + fn partial_cmp(&self, other: &Self) -> Option { + PartialOrd::partial_cmp(self.get(), other.get()) + } + fn lt(&self, other: &Self) -> bool { + PartialOrd::lt(self.get(), other.get()) + } + fn le(&self, other: &Self) -> bool { + PartialOrd::le(self.get(), other.get()) + } + fn ge(&self, other: &Self) -> bool { + PartialOrd::ge(self.get(), other.get()) + } + fn gt(&self, other: &Self) -> bool { + PartialOrd::gt(self.get(), other.get()) + } +} + +impl core::cmp::Ord for Lazy +where + T: core::cmp::Ord + scale::Decode, +{ + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + Ord::cmp(self.get(), other.get()) + } +} + +impl core::fmt::Display for Lazy +where + T: scale::Decode + core::fmt::Display, +{ + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Display::fmt(self.get(), f) + } +} + +impl core::hash::Hash for Lazy +where + T: core::hash::Hash + scale::Decode, +{ + fn hash(&self, state: &mut H) { + self.get().hash(state); + } +} + +impl core::convert::AsRef for Lazy +where + T: scale::Decode, +{ + fn as_ref(&self) -> &T { + self.get() + } +} + +impl core::convert::AsMut for Lazy +where + T: scale::Decode, +{ + fn as_mut(&mut self) -> &mut T { + self.get_mut() + } +} + +impl ink_prelude::borrow::Borrow for Lazy +where + T: scale::Decode, +{ + fn borrow(&self) -> &T { + self.get() + } +} + +impl ink_prelude::borrow::BorrowMut for Lazy +where + T: scale::Decode, +{ + fn borrow_mut(&mut self) -> &mut T { + self.get_mut() + } +} + impl core::ops::Deref for Lazy where T: scale::Decode, @@ -180,6 +300,7 @@ where /// waits until it is used for the first time in order to load its value /// from the contract storage. /// 2. It is actually an already occupied eager lazy. +#[derive(Debug)] pub enum LazyKind { /// A true lazy storage entity that loads its contract storage value upon first use. Vacant(VacantLazy), @@ -188,6 +309,7 @@ pub enum LazyKind { } /// The lazy storage entity is in a lazy state. +#[derive(Debug)] pub struct VacantLazy { /// The key to load the value from contract storage upon first use. pub key: Key, @@ -201,6 +323,7 @@ impl VacantLazy { } /// An already loaded or otherwise occupied eager lazy storage entity. +#[derive(Debug)] pub struct OccupiedLazy { /// The loaded value. pub value: T, From ed5243ed5c5142dd2dd95cd68ee1a46579cbd2ae Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 5 Feb 2020 15:13:51 +0100 Subject: [PATCH 007/142] [core] add rationals for the unsafe blocks in storage::Lazy --- core/src/storage/lazy.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy.rs index 00eb8552661..b9ff4b705df 100644 --- a/core/src/storage/lazy.rs +++ b/core/src/storage/lazy.rs @@ -81,12 +81,18 @@ impl Lazy { /// Returns a shared reference to the inner lazy kind. #[must_use] fn kind(&self) -> &LazyKind { + // SAFETY: We just return a shared reference while the method receiver + // is a shared reference (&self) itself. So we respect normal + // Rust rules. unsafe { &*self.kind.get() } } /// Returns an exclusive reference to the inner lazy kind. #[must_use] fn kind_mut(&mut self) -> &mut LazyKind { + // SAFETY: We just return an exclusive reference while the method receiver + // is an exclusive reference (&mut self) itself. So we respect normal + // Rust rules. unsafe { &mut *self.kind.get() } } @@ -101,6 +107,11 @@ impl Lazy { where F: FnOnce(&mut LazyKind) -> R, { + // SAFETY: We operate on an exclusive reference on `LazyKind` within the + // given closure while our method receiver is only a shared reference. + // However, due to encapsulation of the exclusive reference within + // the given closure we cannot leak the exclusive reference outside + // of the closure. So the below action is safe in this regard. f(unsafe { &mut *self.kind.get() }) } } From fcf7ee87d9d099152c65a6a2e28d281b9eb96e68 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 12:04:59 +0100 Subject: [PATCH 008/142] [core] impl From for KeyPtr --- core/src/storage/traits/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index f47eff23d5a..c850c6dda03 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -47,6 +47,12 @@ pub struct KeyPtr { key: Key, } +impl From for KeyPtr { + fn from(key: Key) -> Self { + Self { key } + } +} + impl KeyPtr { /// Advances the key by the given amount derive by `T` and its `StorageSize` /// and returns the next `Key` for usage by the storage element. From 9dfd688c69141cb1ad693dadc4cf50745249ce47 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 12:05:25 +0100 Subject: [PATCH 009/142] [core] add initial implementation of storage::LazyChunk --- core/src/storage/lazy_chunk.rs | 425 +++++++++++++++++++++++++++++++++ core/src/storage/mod.rs | 6 +- 2 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 core/src/storage/lazy_chunk.rs diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs new file mode 100644 index 00000000000..ac3fa20cc2f --- /dev/null +++ b/core/src/storage/lazy_chunk.rs @@ -0,0 +1,425 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{ + KeyPtr, + Pull, + Push, + StorageSize, +}; +use core::{ + cell::UnsafeCell, + pin::Pin, +}; +use ink_primitives::Key; + +use ink_prelude::{ + boxed::Box, + collections::BTreeMap, +}; + +/// The index type used in the lazy storage chunk. +pub type Index = u32; + +/// A lazy storage chunk that spans over a whole chunk of storage cells. +/// +/// # Note +/// +/// This is mainly used as low-level storage primitives by other high-level +/// storage primitives in order to manage the contract storage for a whole +/// chunk of storage cells. +/// +/// A chunk of storage cells is a contiguous range of 2^32 storage cells. +#[derive(Debug, Default)] +pub struct LazyChunk { + /// The offset key for the chunk of cells. + /// + /// If the lazy chunk has been initialized during contract initialization + /// the key will be `None` since there won't be a storage region associated + /// to the lazy chunk which prevents it from lazily loading elements. This, + /// however, is only checked at contract runtime. We might incorporate + /// compile-time checks for this particular use case later on. + key: Option, + /// The subset of currently cached entries of the lazy storage chunk. + /// + /// An entry is cached as soon as it is loaded or written. + cached_entries: UnsafeCell>, +} + +/// The map for the contract storage entries. +pub type EntryMap = BTreeMap>; + +/// An entry within the lazy chunk +#[derive(Debug)] +pub struct Entry { + /// The current value of the entry. + /// + /// We keep the value in a `Pin>` in order to prevent pointer + /// invalidation upon updating the cache through `&self` methods as in + /// [`LazyChunk::get`]. + value: Option>>, + /// This is `true` if the `value` has been mutated and is potentially + /// out-of-sync with the contract storage. + mutated: bool, +} + +impl Entry { + /// Returns a shared reference to the value of the entry. + pub fn value(&self) -> Option<&T> { + match &self.value { + Some(value) => Some(value.as_ref().get_ref()), + None => None, + } + } + + /// Returns an exclusive reference to the value of the entry. + /// + /// # Note + /// + /// This changes the `mutate` state of the entry if the entry was occupied + /// since the caller could potentially change the returned value. + pub fn value_mut(&mut self) -> Option<&mut T> + where + T: Unpin, + { + match &mut self.value { + Some(value) => { + self.mutated = true; + Some(value.as_mut().get_mut()) + } + None => None, + } + } + + /// Takes the value from the entry and returns it. + /// + /// # Note + /// + /// This changes the `mutate` state of the entry if the entry was occupied. + pub fn take_value(&mut self) -> Option + where + T: Unpin, + { + if self.value.is_some() { + self.mutated = true; + } + self.value.take().map(|pin| *Pin::into_inner(pin)) + } + + /// Puts the new value into the entry and returns the old value. + /// + /// # Note + /// + /// This changes the `mutate` state of the entry to `true` as long as at + /// least one of `old_value` and `new_value` is `Some`. + pub fn put(&mut self, new_value: Option) -> Option + where + T: Unpin, + { + if self.value.is_some() || new_value.is_some() { + self.mutated = true; + } + // Note: This implementation is a bit more complex than it could be + // because we want to re-use the eventually already heap allocated + // `Pin>`. + match &mut self.value { + old_value @ Some(_) => { + match new_value { + Some(new_value) => { + // Re-use the heap allocation. + let old_value = old_value + .as_mut() + .expect("we asserted to have a value") + .as_mut() + .get_mut(); + Some(core::mem::replace::(old_value, new_value)) + } + None => { + // Throw-away the heap allocation. + let old_value = + old_value.take().expect("we asserted to have a value"); + Some(*Pin::into_inner(old_value)) + } + } + } + old_value @ None => { + match new_value { + Some(new_value) => { + // Create a new heap allocation. + old_value.replace(Box::pin(new_value)); + None + } + None => { + // We do nothing. + None + } + } + } + } + } +} + +impl LazyChunk { + /// Creates a new empty lazy chunk that cannot be mutated. + pub fn new() -> Self { + Self { + key: None, + cached_entries: UnsafeCell::new(EntryMap::new()), + } + } + + /// Returns a shared reference to the cached entries. + fn cached_entries(&self) -> &EntryMap { + // SAFETY: We just return a shared reference while the method receiver + // is a shared reference (&self) itself. So we respect normal + // Rust rules. + unsafe { &*self.cached_entries.get() } + } + + /// Returns a shared reference to the cached entries. + fn cached_entries_mut(&mut self) -> &mut EntryMap { + // SAFETY: We just return an exclusive reference while the method receiver + // is an exclusive reference (&mut self) itself. So we respect normal + // Rust rules. + unsafe { &mut *self.cached_entries.get() } + } + + /// Performs the given closure on the mutable cached entries. + /// + /// # Note + /// + /// Actions on the mutable lazy entries are performed within the closure + /// to not leak exclusive references to it to the outside. This is important + /// since the `for_entries` method itself operates only on `&self`. + fn for_cached_entries(&self, f: F) -> R + where + F: FnOnce(&mut EntryMap) -> R, + { + // SAFETY: We operate on an exclusive reference on `BTreeMap` within the + // given closure while our method receiver is only a shared reference. + // However, due to encapsulation of the exclusive reference within + // the given closure we cannot leak the exclusive reference outside + // of the closure. So the below action is safe in this regard. + f(unsafe { &mut *self.cached_entries.get() }) + } +} + +impl StorageSize for LazyChunk { + const SIZE: u64 = core::u32::MAX as u64; +} + +impl Pull for LazyChunk +where + Self: StorageSize, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + Self { + key: Some(key_ptr.next_for::()), + cached_entries: UnsafeCell::new(BTreeMap::new()), + } + } +} + +impl Push for LazyChunk +where + T: Push + scale::Encode, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + // Simply increment `key_ptr` for the next storage entities. + let next_key = key_ptr.next_for::(); + match self.key { + None => (), + Some(key) => { + assert_eq!( + key, next_key, + // Panic if we would push to some other place than we'd pull from. + // This is just us being overly assertive. + "pull and push keys do not match" + ); + self.for_cached_entries(|entries| { + for (&index, entry) in entries.iter_mut().filter(|(_, entry)| entry.mutated) { + let offset: Key = key + index; + let mut ptr = KeyPtr::from(offset); + match &entry.value { + Some(value) => { + // Forward push to the inner value on the computed key. + Push::push(&**value, &mut ptr); + } + None => { + // Clear the storage at the given index. + crate::env::clear_contract_storage(offset); + } + } + // Reset the mutated entry flag because we just synced. + entry.mutated = false; + } + }); + } + } + } +} + +impl LazyChunk +where + T: scale::Decode, +{ + /// Lazily loads the value at the given index. + /// + /// # Note + /// + /// Only loads a value if `key` is set and if the value has not been loaded yet. + /// Returns the freshly loaded or already loaded entry of the value. + /// + /// # Safety + /// + /// This function has a `&self` receiver while returning an `Option<*mut T>` + /// which is unsafe in isolation. The caller has to determine how to forward + /// the returned `*mut T`. + /// + /// # Panics + /// + /// If the lazy chunk is not in a state that allows lazy loading. + fn lazily_load(&self, index: Index) -> *mut Entry + where + T: Unpin, + { + let key = match self.key { + Some(key) => key, + None => panic!("cannot load lazily in this state"), + }; + // SAFETY: We have put the whole `cached_entries` mapping into an + // `UnsafeCell` because of this caching functionality. The + // trick here is that due to using `Pin>` internally + // we are able to return references to the cached entries + // while maintaining the invariant that mutating the caching + // `BTreeMap` will never invalidate those references. + // By returning a raw pointer we enforce an `unsafe` block at + // the caller site to underline that guarantees are given by the + // caller. + let cached_entries = unsafe { &mut *self.cached_entries.get() }; + use ink_prelude::collections::btree_map::Entry as BTreeMapEntry; + match cached_entries.entry(index) { + BTreeMapEntry::Occupied(occupied) => occupied.into_mut(), + BTreeMapEntry::Vacant(vacant) => { + let loaded_value = + match crate::env::get_contract_storage::(key + index) { + Some(new_value) => { + Some(new_value.expect("could not decode lazily loaded value")) + } + None => None, + }; + let value = loaded_value.map(|value| Box::pin(value)); + vacant.insert(Entry { + value, + mutated: false, + }) + } + } + } + + /// Returns a shared reference to the element at the given index if any. + /// + /// # Panics + /// + /// If the decoding of the element at the given index failed. + pub fn get(&self, index: Index) -> Option<&T> + where + T: Unpin, + { + // SAFETY: Dereferencing the `*mut T` pointer into a `&T` is safe + // since this method's receiver is `&self` so we do not + // leak non-shared references to the outside. + let entry: &Entry = unsafe { &*self.lazily_load(index) }; + entry.value() + } + + /// Returns an exclusive reference to the element at the given index if any. + /// + /// # Panics + /// + /// If the decoding of the element at the given index failed. + pub fn get_mut(&mut self, index: Index) -> Option<&mut T> + where + T: Unpin, + { + // SAFETY: Dereferencing the `*mut T` pointer into a `&mut T` is + // safe since this method's receiver is `&mut self` so we + // are allowed to return `&mut T` references to internals. + let entry: &mut Entry = unsafe { &mut *self.lazily_load(index) }; + entry.value_mut() + } + + /// Takes and returns the element at the given index if any. + /// + /// # Note + /// + /// This removes the element at the given index from the storage. + /// + /// # Panics + /// + /// If the decoding of the element at the given index failed. + pub fn take(&mut self, index: Index) -> Option + where + T: Unpin, + { + // SAFETY: Dereferencing the `*mut T` pointer into a `&mut T` is + // safe since this method's receiver is `&mut self` so we + // are allowed to return `&mut T` references to internals. + let entry: &mut Entry = unsafe { &mut *self.lazily_load(index) }; + entry.take_value() + } +} + +impl LazyChunk +where + T: scale::Encode, +{ + /// Puts the new value at the given index and returns the old value if any. + /// + /// # Note + /// + /// - Use [`LazyChunk::put`]`(None)` in order to remove an element. + /// - Prefer this method over [`LazyChunk::put_get`] if you are not interested + /// in the old value of the same cell index. + /// + /// # Panics + /// + /// If the decoding of the old element at the given index failed. + pub fn put(&mut self, _index: Index, _new_value: Option) { + todo!() + } +} + +impl LazyChunk +where + T: Unpin + scale::Codec, +{ + /// Puts the new value at the given index and returns the old value if any. + /// + /// # Note + /// + /// - Use [`LazyChunk::put_get`]`(None)` in order to remove an element + /// and retrieve the old element back. + /// + /// # Panics + /// + /// If the decoding of the old element at the given index failed. + pub fn put_get(&mut self, index: Index, new_value: Option) -> Option { + // SAFETY: Dereferencing the `*mut T` pointer into a `&mut T` is + // safe since this method's receiver is `&mut self` so we + // are allowed to return `&mut T` references to internals. + let entry: &mut Entry = unsafe { &mut *self.lazily_load(index) }; + entry.put(new_value) + } +} diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 1c4ca7f1f82..45a5c0b6f86 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -74,9 +74,10 @@ pub mod cell; pub mod chunk; mod collections; mod flush; +mod lazy; +mod lazy_chunk; mod traits; mod value; -mod lazy; pub use self::{ collections::{ @@ -102,13 +103,14 @@ pub use self::{ }, }, flush::Flush, + lazy::Lazy, + lazy_chunk::LazyChunk, traits::{ KeyPtr, Pull, Push, StorageSize, }, - lazy::Lazy, }; #[doc(inline)] From 8fc1a3c02e9493739d90a8a4018e6428243be263 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 12:08:58 +0100 Subject: [PATCH 010/142] [core] remove unused cache_entries getters --- core/src/storage/lazy_chunk.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index ac3fa20cc2f..62e173464ae 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -179,22 +179,6 @@ impl LazyChunk { } } - /// Returns a shared reference to the cached entries. - fn cached_entries(&self) -> &EntryMap { - // SAFETY: We just return a shared reference while the method receiver - // is a shared reference (&self) itself. So we respect normal - // Rust rules. - unsafe { &*self.cached_entries.get() } - } - - /// Returns a shared reference to the cached entries. - fn cached_entries_mut(&mut self) -> &mut EntryMap { - // SAFETY: We just return an exclusive reference while the method receiver - // is an exclusive reference (&mut self) itself. So we respect normal - // Rust rules. - unsafe { &mut *self.cached_entries.get() } - } - /// Performs the given closure on the mutable cached entries. /// /// # Note From be5fd6f0d14f75807e659bfb4cc2874c78ac4b88 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 12:13:50 +0100 Subject: [PATCH 011/142] [core] refactor to reduce number of unsafe blocks --- core/src/storage/lazy_chunk.rs | 37 ++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index 62e173464ae..54da83a5e12 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -312,6 +312,25 @@ where } } + /// Lazily loads the value at the given index. + /// + /// # Note + /// + /// Only loads a value if `key` is set and if the value has not been loaded yet. + /// Returns the freshly loaded or already loaded entry of the value. + /// + /// # Panics + /// + /// If the lazy chunk is not in a state that allows lazy loading. + fn lazily_load_mut(&mut self, index: Index) -> &mut Entry + where + T: Unpin, + { + // SAFETY: Dereferencing the raw-pointer here is safe since we + // encapsulated this whole call with a `&mut self` receiver. + unsafe { &mut *self.lazily_load(index) } + } + /// Returns a shared reference to the element at the given index if any. /// /// # Panics @@ -337,11 +356,7 @@ where where T: Unpin, { - // SAFETY: Dereferencing the `*mut T` pointer into a `&mut T` is - // safe since this method's receiver is `&mut self` so we - // are allowed to return `&mut T` references to internals. - let entry: &mut Entry = unsafe { &mut *self.lazily_load(index) }; - entry.value_mut() + self.lazily_load_mut(index).value_mut() } /// Takes and returns the element at the given index if any. @@ -357,11 +372,7 @@ where where T: Unpin, { - // SAFETY: Dereferencing the `*mut T` pointer into a `&mut T` is - // safe since this method's receiver is `&mut self` so we - // are allowed to return `&mut T` references to internals. - let entry: &mut Entry = unsafe { &mut *self.lazily_load(index) }; - entry.take_value() + self.lazily_load_mut(index).take_value() } } @@ -400,10 +411,6 @@ where /// /// If the decoding of the old element at the given index failed. pub fn put_get(&mut self, index: Index, new_value: Option) -> Option { - // SAFETY: Dereferencing the `*mut T` pointer into a `&mut T` is - // safe since this method's receiver is `&mut self` so we - // are allowed to return `&mut T` references to internals. - let entry: &mut Entry = unsafe { &mut *self.lazily_load(index) }; - entry.put(new_value) + self.lazily_load_mut(index).put(new_value) } } From d7ee2bbae2ad28943a613b35eb2294bd13ba8316 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 12:27:06 +0100 Subject: [PATCH 012/142] [core] unify Cell and Chunk StorazeSize definitions --- core/src/storage/lazy_chunk.rs | 3 +- core/src/storage/mod.rs | 4 +++ core/src/storage/traits/mod.rs | 10 ++++-- core/src/storage/traits/storage_size.rs | 44 ++++++++++++++++++------- 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index 54da83a5e12..9cbd46b1843 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -13,6 +13,7 @@ // limitations under the License. use super::{ + storage_size::Chunk, KeyPtr, Pull, Push, @@ -200,7 +201,7 @@ impl LazyChunk { } impl StorageSize for LazyChunk { - const SIZE: u64 = core::u32::MAX as u64; + const SIZE: u64 = ::SIZE; } impl Pull for LazyChunk diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 45a5c0b6f86..0f8bb297de6 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -79,6 +79,10 @@ mod lazy_chunk; mod traits; mod value; +pub mod storage_size { + pub use super::traits::{Cell, Chunk}; +} + pub use self::{ collections::{ binary_heap::{ diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index c850c6dda03..659aab07c42 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -26,16 +26,20 @@ macro_rules! forward_supported_array_lens { }; } -mod push; mod pull; +mod push; mod storage_size; use ink_primitives::Key; pub use self::{ - storage_size::StorageSize, - push::Push, pull::Pull, + push::Push, + storage_size::{ + Cell, + Chunk, + StorageSize, + }, }; /// A key pointer. diff --git a/core/src/storage/traits/storage_size.rs b/core/src/storage/traits/storage_size.rs index a79d9ec6776..4d0dbb5e9b6 100644 --- a/core/src/storage/traits/storage_size.rs +++ b/core/src/storage/traits/storage_size.rs @@ -21,10 +21,26 @@ pub trait StorageSize { const SIZE: u64; } +/// A single cell. +pub enum Cell {} + +impl StorageSize for Cell { + const SIZE: u64 = 1; +} + +/// A chunk of cells. +pub enum Chunk {} + +impl StorageSize for Chunk { + const SIZE: u64 = core::u32::MAX as u64; +} + macro_rules! impl_storage_size_1 { ( $($ty:ty),* ) => { $( - impl StorageSize for $ty { const SIZE: u64 = 1; } + impl StorageSize for $ty { + const SIZE: u64 = ::SIZE; + } )* }; } @@ -33,7 +49,9 @@ impl_storage_size_1!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); macro_rules! impl_storage_size_array { ( $($n:literal),* $(,)? ) => { $( - impl StorageSize for [T; $n] { const SIZE: u64 = 1; } + impl StorageSize for [T; $n] { + const SIZE: u64 = ::SIZE; + } )* }; } @@ -42,7 +60,9 @@ forward_supported_array_lens!(impl_storage_size_array); macro_rules! impl_storage_size_tuple { ( $($frag:ident),* $(,)? ) => { #[allow(unused_parens)] - impl<$($frag),*> StorageSize for ($($frag),* ,) { const SIZE: u64 = 1; } + impl<$($frag),*> StorageSize for ($($frag),* ,) { + const SIZE: u64 = ::SIZE; + } } } impl_storage_size_tuple!(A); @@ -63,33 +83,33 @@ impl StorageSize for core::marker::PhantomData { const SIZE: u64 = 0; } impl StorageSize for Option { - const SIZE: u64 = 1; + const SIZE: u64 = ::SIZE; } impl StorageSize for Result { - const SIZE: u64 = 1; + const SIZE: u64 = ::SIZE; } impl StorageSize for ink_prelude::vec::Vec { - const SIZE: u64 = 1; + const SIZE: u64 = ::SIZE; } impl StorageSize for ink_prelude::string::String { - const SIZE: u64 = 1; + const SIZE: u64 = ::SIZE; } impl StorageSize for ink_prelude::boxed::Box { - const SIZE: u64 = 1; + const SIZE: u64 = ::SIZE; } impl StorageSize for ink_prelude::collections::BTreeSet { - const SIZE: u64 = 1; + const SIZE: u64 = ::SIZE; } impl StorageSize for ink_prelude::collections::BinaryHeap { - const SIZE: u64 = 1; + const SIZE: u64 = ::SIZE; } impl StorageSize for ink_prelude::collections::LinkedList { - const SIZE: u64 = 1; + const SIZE: u64 = ::SIZE; } impl StorageSize for ink_prelude::collections::VecDeque { - const SIZE: u64 = 1; + const SIZE: u64 = ::SIZE; } From 1fa413176d7da14aba157dc9654e9c8ccd8779b8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 12:37:53 +0100 Subject: [PATCH 013/142] [core] improve doc comments of LazyChunk methods --- core/src/storage/lazy_chunk.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index 9cbd46b1843..04276e15ee7 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -274,7 +274,8 @@ where /// /// # Panics /// - /// If the lazy chunk is not in a state that allows lazy loading. + /// - If the lazy chunk is in an invalid state that forbids interaction. + /// - If the lazy chunk is not in a state that allows lazy loading. fn lazily_load(&self, index: Index) -> *mut Entry where T: Unpin, @@ -322,7 +323,8 @@ where /// /// # Panics /// - /// If the lazy chunk is not in a state that allows lazy loading. + /// - If the lazy chunk is in an invalid state that forbids interaction. + /// - If the lazy chunk is not in a state that allows lazy loading. fn lazily_load_mut(&mut self, index: Index) -> &mut Entry where T: Unpin, @@ -336,7 +338,8 @@ where /// /// # Panics /// - /// If the decoding of the element at the given index failed. + /// - If the lazy chunk is in an invalid state that forbids interaction. + /// - If the decoding of the element at the given index failed. pub fn get(&self, index: Index) -> Option<&T> where T: Unpin, @@ -352,7 +355,8 @@ where /// /// # Panics /// - /// If the decoding of the element at the given index failed. + /// - If the lazy chunk is in an invalid state that forbids interaction. + /// - If the decoding of the element at the given index failed. pub fn get_mut(&mut self, index: Index) -> Option<&mut T> where T: Unpin, @@ -368,7 +372,8 @@ where /// /// # Panics /// - /// If the decoding of the element at the given index failed. + /// - If the lazy chunk is in an invalid state that forbids interaction. + /// - If the decoding of the element at the given index failed. pub fn take(&mut self, index: Index) -> Option where T: Unpin, @@ -391,7 +396,8 @@ where /// /// # Panics /// - /// If the decoding of the old element at the given index failed. + /// - If the lazy chunk is in an invalid state that forbids interaction. + /// - If the decoding of the old element at the given index failed. pub fn put(&mut self, _index: Index, _new_value: Option) { todo!() } @@ -410,7 +416,8 @@ where /// /// # Panics /// - /// If the decoding of the old element at the given index failed. + /// - If the lazy chunk is in an invalid state that forbids interaction. + /// - If the decoding of the old element at the given index failed. pub fn put_get(&mut self, index: Index, new_value: Option) -> Option { self.lazily_load_mut(index).put(new_value) } From 49a63c70e05b6ffeb1902c605f723a308f6f3167 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 12:38:10 +0100 Subject: [PATCH 014/142] [core] add stub for future LazyChunk method --- core/src/storage/lazy_chunk.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index 04276e15ee7..0a6dbce7c1b 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -421,4 +421,16 @@ where pub fn put_get(&mut self, index: Index, new_value: Option) -> Option { self.lazily_load_mut(index).put(new_value) } + + /// Swaps the values at indices `x` and `y`. + /// + /// This operation tries to be as efficient as possible and reuse allocations. + /// + /// # Panics + /// + /// - If the lazy chunk is in an invalid state that forbids interaction. + /// - If the decoding of one of the elements failed. + pub fn swap(&mut self, x: Index, y: Index) { + todo!() + } } From 0d05b585d0611d459a517650685acaaedcc6b494 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 16:25:31 +0100 Subject: [PATCH 015/142] [core] wrap entire Entry in Pin> instead of just Some value for LazyChunk --- core/src/storage/lazy_chunk.rs | 178 +++++++++------------------------ 1 file changed, 49 insertions(+), 129 deletions(-) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index 0a6dbce7c1b..c1b91816717 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -59,7 +59,7 @@ pub struct LazyChunk { } /// The map for the contract storage entries. -pub type EntryMap = BTreeMap>; +pub type EntryMap = BTreeMap>>>; /// An entry within the lazy chunk #[derive(Debug)] @@ -69,7 +69,7 @@ pub struct Entry { /// We keep the value in a `Pin>` in order to prevent pointer /// invalidation upon updating the cache through `&self` methods as in /// [`LazyChunk::get`]. - value: Option>>, + value: Option, /// This is `true` if the `value` has been mutated and is potentially /// out-of-sync with the contract storage. mutated: bool, @@ -78,10 +78,7 @@ pub struct Entry { impl Entry { /// Returns a shared reference to the value of the entry. pub fn value(&self) -> Option<&T> { - match &self.value { - Some(value) => Some(value.as_ref().get_ref()), - None => None, - } + self.value.as_ref() } /// Returns an exclusive reference to the value of the entry. @@ -90,17 +87,9 @@ impl Entry { /// /// This changes the `mutate` state of the entry if the entry was occupied /// since the caller could potentially change the returned value. - pub fn value_mut(&mut self) -> Option<&mut T> - where - T: Unpin, - { - match &mut self.value { - Some(value) => { - self.mutated = true; - Some(value.as_mut().get_mut()) - } - None => None, - } + pub fn value_mut(&mut self) -> Option<&mut T> { + self.mutated = self.value.is_some(); + self.value.as_mut() } /// Takes the value from the entry and returns it. @@ -108,14 +97,9 @@ impl Entry { /// # Note /// /// This changes the `mutate` state of the entry if the entry was occupied. - pub fn take_value(&mut self) -> Option - where - T: Unpin, - { - if self.value.is_some() { - self.mutated = true; - } - self.value.take().map(|pin| *Pin::into_inner(pin)) + pub fn take_value(&mut self) -> Option { + self.mutated = self.value.is_some(); + self.value.take() } /// Puts the new value into the entry and returns the old value. @@ -124,49 +108,13 @@ impl Entry { /// /// This changes the `mutate` state of the entry to `true` as long as at /// least one of `old_value` and `new_value` is `Some`. - pub fn put(&mut self, new_value: Option) -> Option - where - T: Unpin, - { - if self.value.is_some() || new_value.is_some() { - self.mutated = true; - } - // Note: This implementation is a bit more complex than it could be - // because we want to re-use the eventually already heap allocated - // `Pin>`. - match &mut self.value { - old_value @ Some(_) => { - match new_value { - Some(new_value) => { - // Re-use the heap allocation. - let old_value = old_value - .as_mut() - .expect("we asserted to have a value") - .as_mut() - .get_mut(); - Some(core::mem::replace::(old_value, new_value)) - } - None => { - // Throw-away the heap allocation. - let old_value = - old_value.take().expect("we asserted to have a value"); - Some(*Pin::into_inner(old_value)) - } - } - } - old_value @ None => { - match new_value { - Some(new_value) => { - // Create a new heap allocation. - old_value.replace(Box::pin(new_value)); - None - } - None => { - // We do nothing. - None - } - } + pub fn put(&mut self, new_value: Option) -> Option { + match new_value { + Some(new_value) => { + self.mutated = true; + self.value.replace(new_value) } + None => self.take_value(), } } } @@ -218,46 +166,35 @@ where impl Push for LazyChunk where - T: Push + scale::Encode, + T: Unpin + Push + scale::Encode, { fn push(&self, key_ptr: &mut KeyPtr) { - // Simply increment `key_ptr` for the next storage entities. - let next_key = key_ptr.next_for::(); - match self.key { - None => (), - Some(key) => { - assert_eq!( - key, next_key, - // Panic if we would push to some other place than we'd pull from. - // This is just us being overly assertive. - "pull and push keys do not match" - ); - self.for_cached_entries(|entries| { - for (&index, entry) in entries.iter_mut().filter(|(_, entry)| entry.mutated) { - let offset: Key = key + index; - let mut ptr = KeyPtr::from(offset); - match &entry.value { - Some(value) => { - // Forward push to the inner value on the computed key. - Push::push(&**value, &mut ptr); - } - None => { - // Clear the storage at the given index. - crate::env::clear_contract_storage(offset); - } - } - // Reset the mutated entry flag because we just synced. - entry.mutated = false; + let key = key_ptr.next_for::(); + assert_eq!(self.key, Some(key)); + self.for_cached_entries(|entries| { + for (&index, entry) in entries.iter_mut().filter(|(_, entry)| entry.mutated) { + let offset: Key = key + index; + let mut ptr = KeyPtr::from(offset); + match entry.value() { + Some(value) => { + // Forward push to the inner value on the computed key. + Push::push(value, &mut ptr); + } + None => { + // Clear the storage at the given index. + crate::env::clear_contract_storage(offset); } - }); + } + // Reset the mutated entry flag because we just synced. + entry.mutated = false; } - } + }); } } impl LazyChunk where - T: scale::Decode, + T: Unpin + scale::Decode, { /// Lazily loads the value at the given index. /// @@ -276,10 +213,7 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the lazy chunk is not in a state that allows lazy loading. - fn lazily_load(&self, index: Index) -> *mut Entry - where - T: Unpin, - { + fn lazily_load(&self, index: Index) -> *mut Entry { let key = match self.key { Some(key) => key, None => panic!("cannot load lazily in this state"), @@ -296,20 +230,18 @@ where let cached_entries = unsafe { &mut *self.cached_entries.get() }; use ink_prelude::collections::btree_map::Entry as BTreeMapEntry; match cached_entries.entry(index) { - BTreeMapEntry::Occupied(occupied) => occupied.into_mut(), + BTreeMapEntry::Occupied(occupied) => &mut **occupied.into_mut(), BTreeMapEntry::Vacant(vacant) => { - let loaded_value = - match crate::env::get_contract_storage::(key + index) { - Some(new_value) => { - Some(new_value.expect("could not decode lazily loaded value")) - } - None => None, - }; - let value = loaded_value.map(|value| Box::pin(value)); - vacant.insert(Entry { + let value = match crate::env::get_contract_storage::(key + index) { + Some(new_value) => { + Some(new_value.expect("could not decode lazily loaded value")) + } + None => None, + }; + &mut **vacant.insert(Box::pin(Entry { value, mutated: false, - }) + })) } } } @@ -325,10 +257,7 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the lazy chunk is not in a state that allows lazy loading. - fn lazily_load_mut(&mut self, index: Index) -> &mut Entry - where - T: Unpin, - { + fn lazily_load_mut(&mut self, index: Index) -> &mut Entry { // SAFETY: Dereferencing the raw-pointer here is safe since we // encapsulated this whole call with a `&mut self` receiver. unsafe { &mut *self.lazily_load(index) } @@ -340,10 +269,7 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the decoding of the element at the given index failed. - pub fn get(&self, index: Index) -> Option<&T> - where - T: Unpin, - { + pub fn get(&self, index: Index) -> Option<&T> { // SAFETY: Dereferencing the `*mut T` pointer into a `&T` is safe // since this method's receiver is `&self` so we do not // leak non-shared references to the outside. @@ -357,10 +283,7 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the decoding of the element at the given index failed. - pub fn get_mut(&mut self, index: Index) -> Option<&mut T> - where - T: Unpin, - { + pub fn get_mut(&mut self, index: Index) -> Option<&mut T> { self.lazily_load_mut(index).value_mut() } @@ -374,10 +297,7 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the decoding of the element at the given index failed. - pub fn take(&mut self, index: Index) -> Option - where - T: Unpin, - { + pub fn take(&mut self, index: Index) -> Option { self.lazily_load_mut(index).take_value() } } From 8bda91ff4ab630ffd8629ae6f9558f48c037b771 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 16:25:47 +0100 Subject: [PATCH 016/142] [core] implement LazyChunk::swap --- core/src/storage/lazy_chunk.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index c1b91816717..1a8f072eb4f 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -351,6 +351,25 @@ where /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the decoding of one of the elements failed. pub fn swap(&mut self, x: Index, y: Index) { - todo!() + if x == y { + // Bail out early if both indices are the same. + return + } + let (loaded_x, loaded_y) = + // SAFETY: The loaded `x` and `y` entries are distinct from each + // other guaranteed by the previous check. Also `lazily_load` + // guarantees to return a pointer to a pinned entity + // so that the returned references do not conflict with + // each other. + unsafe { (&mut *self.lazily_load(x), &mut *self.lazily_load(y)) }; + if loaded_x.value.is_none() && loaded_y.value.is_none() { + // Bail out since nothing has to be swapped if both values are `None`. + return + } + // Set the `mutate` flag since at this point at least one of the loaded + // values is guaranteed to be `Some`. + loaded_x.mutated = true; + loaded_y.mutated = true; + core::mem::swap(&mut loaded_x.value, &mut loaded_y.value); } } From 20bda31dc9f75c7925ab95f9023f24653d225933 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 16:36:09 +0100 Subject: [PATCH 017/142] [core] refactoring and follow-ups for storage::LazyChunk --- core/src/storage/lazy_chunk.rs | 64 +++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index 1a8f072eb4f..0e5c3901d97 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -59,23 +59,48 @@ pub struct LazyChunk { } /// The map for the contract storage entries. +/// +/// We keep the whole entry in a `Pin>` in order to prevent pointer +/// invalidation upon updating the cache through `&self` methods as in +/// [`LazyChunk::get`]. pub type EntryMap = BTreeMap>>>; /// An entry within the lazy chunk #[derive(Debug)] pub struct Entry { /// The current value of the entry. - /// - /// We keep the value in a `Pin>` in order to prevent pointer - /// invalidation upon updating the cache through `&self` methods as in - /// [`LazyChunk::get`]. value: Option, /// This is `true` if the `value` has been mutated and is potentially /// out-of-sync with the contract storage. - mutated: bool, + mutated: core::cell::Cell, +} + +impl Push for Entry +where + T: Push + StorageSize, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + // Reset the mutated entry flag because we just synced. + self.mutated.set(false); + match self.value() { + Some(value) => { + // Forward push to the inner value on the computed key. + Push::push(value, key_ptr); + } + None => { + // Clear the storage at the given index. + crate::env::clear_contract_storage(key_ptr.next_for::()); + } + } + } } impl Entry { + /// Returns `true` if the cached value of the entry has potentially been mutated. + pub fn mutated(&self) -> bool { + self.mutated.get() + } + /// Returns a shared reference to the value of the entry. pub fn value(&self) -> Option<&T> { self.value.as_ref() @@ -88,7 +113,7 @@ impl Entry { /// This changes the `mutate` state of the entry if the entry was occupied /// since the caller could potentially change the returned value. pub fn value_mut(&mut self) -> Option<&mut T> { - self.mutated = self.value.is_some(); + self.mutated.set(self.value.is_some()); self.value.as_mut() } @@ -98,7 +123,7 @@ impl Entry { /// /// This changes the `mutate` state of the entry if the entry was occupied. pub fn take_value(&mut self) -> Option { - self.mutated = self.value.is_some(); + self.mutated.set(self.value.is_some()); self.value.take() } @@ -111,7 +136,7 @@ impl Entry { pub fn put(&mut self, new_value: Option) -> Option { match new_value { Some(new_value) => { - self.mutated = true; + self.mutated.set(true); self.value.replace(new_value) } None => self.take_value(), @@ -166,27 +191,16 @@ where impl Push for LazyChunk where - T: Unpin + Push + scale::Encode, + T: Unpin + Push + StorageSize + scale::Encode, { fn push(&self, key_ptr: &mut KeyPtr) { let key = key_ptr.next_for::(); assert_eq!(self.key, Some(key)); self.for_cached_entries(|entries| { - for (&index, entry) in entries.iter_mut().filter(|(_, entry)| entry.mutated) { + for (&index, entry) in entries.iter_mut().filter(|(_, entry)| entry.mutated()) { let offset: Key = key + index; let mut ptr = KeyPtr::from(offset); - match entry.value() { - Some(value) => { - // Forward push to the inner value on the computed key. - Push::push(value, &mut ptr); - } - None => { - // Clear the storage at the given index. - crate::env::clear_contract_storage(offset); - } - } - // Reset the mutated entry flag because we just synced. - entry.mutated = false; + Push::push(&**entry, &mut ptr); } }); } @@ -240,7 +254,7 @@ where }; &mut **vacant.insert(Box::pin(Entry { value, - mutated: false, + mutated: core::cell::Cell::new(false), })) } } @@ -368,8 +382,8 @@ where } // Set the `mutate` flag since at this point at least one of the loaded // values is guaranteed to be `Some`. - loaded_x.mutated = true; - loaded_y.mutated = true; + loaded_x.mutated.set(true); + loaded_y.mutated.set(true); core::mem::swap(&mut loaded_x.value, &mut loaded_y.value); } } From fc7c21180676241788079dab6437e2adb93229d9 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 16:42:59 +0100 Subject: [PATCH 018/142] [core] update docs --- core/src/storage/lazy_chunk.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index 0e5c3901d97..d8324ea949e 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -274,6 +274,9 @@ where fn lazily_load_mut(&mut self, index: Index) -> &mut Entry { // SAFETY: Dereferencing the raw-pointer here is safe since we // encapsulated this whole call with a `&mut self` receiver. + // Also `Entry` instances are all pinned, so mutating the + // `BTreeMap` that stores tham won't invalidate references + // to them. unsafe { &mut *self.lazily_load(index) } } From c744a669e90668c057d08067431a4ebf60f4c880 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 17:14:59 +0100 Subject: [PATCH 019/142] [core] fix some clippy warnings --- core/src/storage/lazy.rs | 4 ---- core/src/storage/traits/pull.rs | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy.rs index b9ff4b705df..cab27265663 100644 --- a/core/src/storage/lazy.rs +++ b/core/src/storage/lazy.rs @@ -193,10 +193,6 @@ where fn eq(&self, other: &Self) -> bool { PartialEq::eq(self.get(), other.get()) } - - fn ne(&self, other: &Self) -> bool { - PartialEq::ne(self.get(), other.get()) - } } impl core::cmp::Eq for Lazy where T: Eq + scale::Decode {} diff --git a/core/src/storage/traits/pull.rs b/core/src/storage/traits/pull.rs index 6adddfff904..dfe521c2449 100644 --- a/core/src/storage/traits/pull.rs +++ b/core/src/storage/traits/pull.rs @@ -84,9 +84,7 @@ impl_pull_tuple!(A, B, C, D, E, F, G, H, I); impl_pull_tuple!(A, B, C, D, E, F, G, H, I, J); impl Pull for () { - fn pull(_key_ptr: &mut KeyPtr) -> Self { - () - } + fn pull(_key_ptr: &mut KeyPtr) -> Self {} } impl Pull for core::marker::PhantomData { From 96116048d9070ceb38796dbbbb4b1615fe68cf49 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 17:15:24 +0100 Subject: [PATCH 020/142] [core] apply rustfmt --- core/src/storage/lazy_chunk.rs | 3 ++- core/src/storage/mod.rs | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index d8324ea949e..3d22b603cdf 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -197,7 +197,8 @@ where let key = key_ptr.next_for::(); assert_eq!(self.key, Some(key)); self.for_cached_entries(|entries| { - for (&index, entry) in entries.iter_mut().filter(|(_, entry)| entry.mutated()) { + for (&index, entry) in entries.iter_mut().filter(|(_, entry)| entry.mutated()) + { let offset: Key = key + index; let mut ptr = KeyPtr::from(offset); Push::push(&**entry, &mut ptr); diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 0f8bb297de6..96807418f42 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -80,7 +80,10 @@ mod traits; mod value; pub mod storage_size { - pub use super::traits::{Cell, Chunk}; + pub use super::traits::{ + Cell, + Chunk, + }; } pub use self::{ From 9a3959da3a77b95ecd575286649f09148f11114a Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Thu, 6 Feb 2020 19:47:23 +0100 Subject: [PATCH 021/142] add for-lifetime to FnOnce Co-Authored-By: Robert Habermeier --- core/src/storage/lazy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy.rs index cab27265663..0cf706502ad 100644 --- a/core/src/storage/lazy.rs +++ b/core/src/storage/lazy.rs @@ -105,7 +105,7 @@ impl Lazy { /// since the `for_kind` method itself operates only on `&self`. fn for_kind(&self, f: F) -> R where - F: FnOnce(&mut LazyKind) -> R, + F: for<'a> FnOnce(&'a mut LazyKind) -> R, { // SAFETY: We operate on an exclusive reference on `LazyKind` within the // given closure while our method receiver is only a shared reference. From eebdb151668fa2532a30f8e2e0fb9d6dd32b0ca6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 19:57:05 +0100 Subject: [PATCH 022/142] [core] replace LazyChunk::for_cached_entries --- core/src/storage/lazy_chunk.rs | 37 +++++++++++----------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index 3d22b603cdf..aa2accd73e6 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -153,23 +153,12 @@ impl LazyChunk { } } - /// Performs the given closure on the mutable cached entries. - /// - /// # Note - /// - /// Actions on the mutable lazy entries are performed within the closure - /// to not leak exclusive references to it to the outside. This is important - /// since the `for_entries` method itself operates only on `&self`. - fn for_cached_entries(&self, f: F) -> R - where - F: FnOnce(&mut EntryMap) -> R, - { - // SAFETY: We operate on an exclusive reference on `BTreeMap` within the - // given closure while our method receiver is only a shared reference. - // However, due to encapsulation of the exclusive reference within - // the given closure we cannot leak the exclusive reference outside - // of the closure. So the below action is safe in this regard. - f(unsafe { &mut *self.cached_entries.get() }) + /// Returns a shared reference to the entries. + fn entries(&self) -> &EntryMap { + // SAFETY: We return a shared reference while `entries` is a method + // with a `&self` receiver so we don't break lifetime or borrow + // rules in isolation. + unsafe { &*self.cached_entries.get() } } } @@ -196,14 +185,12 @@ where fn push(&self, key_ptr: &mut KeyPtr) { let key = key_ptr.next_for::(); assert_eq!(self.key, Some(key)); - self.for_cached_entries(|entries| { - for (&index, entry) in entries.iter_mut().filter(|(_, entry)| entry.mutated()) - { - let offset: Key = key + index; - let mut ptr = KeyPtr::from(offset); - Push::push(&**entry, &mut ptr); - } - }); + for (&index, entry) in self.entries().iter().filter(|(_, entry)| entry.mutated()) + { + let offset: Key = key + index; + let mut ptr = KeyPtr::from(offset); + Push::push(&**entry, &mut ptr); + } } } From 88a8b9f48385f81cac93b8ca7b4148a05e3d7b15 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 23:17:50 +0100 Subject: [PATCH 023/142] [core] remove Lazy::for_kind --- core/src/storage/lazy.rs | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy.rs index 0cf706502ad..f94640095fe 100644 --- a/core/src/storage/lazy.rs +++ b/core/src/storage/lazy.rs @@ -95,25 +95,6 @@ impl Lazy { // Rust rules. unsafe { &mut *self.kind.get() } } - - /// Performs the given closure on the mutable lazy kind. - /// - /// # Note - /// - /// Actions on the mutable lazy kind are performed within the closure - /// to not leak exclusive references to it to the outside. This is important - /// since the `for_kind` method itself operates only on `&self`. - fn for_kind(&self, f: F) -> R - where - F: for<'a> FnOnce(&'a mut LazyKind) -> R, - { - // SAFETY: We operate on an exclusive reference on `LazyKind` within the - // given closure while our method receiver is only a shared reference. - // However, due to encapsulation of the exclusive reference within - // the given closure we cannot leak the exclusive reference outside - // of the closure. So the below action is safe in this regard. - f(unsafe { &mut *self.kind.get() }) - } } impl Lazy @@ -124,14 +105,16 @@ where /// /// Does nothing if value has already been loaded. fn load_value_lazily(&self) { - self.for_kind(|kind| { - if let LazyKind::Vacant(vacant) = kind { - let value = crate::env::get_contract_storage::(vacant.key) - .expect("couldn't find contract storage entry") - .expect("couldn't properly decode contract storage entry"); - *kind = LazyKind::Occupied(OccupiedLazy::new(value)); - } - }); + // SAFETY: We mutate the kind only if it is vacant. + // So if there is an actual value (Occupied) we leave the + // entire entity as it is not to invalidate references to it. + let kind = unsafe { &mut *self.kind.get() }; + if let LazyKind::Vacant(vacant) = kind { + let value = crate::env::get_contract_storage::(vacant.key) + .expect("couldn't find contract storage entry") + .expect("couldn't properly decode contract storage entry"); + *kind = LazyKind::Occupied(OccupiedLazy::new(value)); + } } /// Returns a shared reference to the lazily loaded value. From 7c63df5975f26352019d2a26ca5c79d69abf1487 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 6 Feb 2020 23:20:08 +0100 Subject: [PATCH 024/142] [core] remove Unpin trait bounds --- core/src/storage/lazy_chunk.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy_chunk.rs index aa2accd73e6..987dfb75a1c 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy_chunk.rs @@ -75,6 +75,8 @@ pub struct Entry { mutated: core::cell::Cell, } +impl Unpin for Entry {} + impl Push for Entry where T: Push + StorageSize, @@ -180,7 +182,7 @@ where impl Push for LazyChunk where - T: Unpin + Push + StorageSize + scale::Encode, + T: Push + StorageSize + scale::Encode, { fn push(&self, key_ptr: &mut KeyPtr) { let key = key_ptr.next_for::(); @@ -196,7 +198,7 @@ where impl LazyChunk where - T: Unpin + scale::Decode, + T: scale::Decode, { /// Lazily loads the value at the given index. /// @@ -330,7 +332,7 @@ where impl LazyChunk where - T: Unpin + scale::Codec, + T: scale::Codec, { /// Puts the new value at the given index and returns the old value if any. /// From a08f283dfb63290b6bcd4d068b18a7b974103f8a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 12 Feb 2020 15:09:45 -0800 Subject: [PATCH 025/142] [core] add LazyCell --- core/src/storage/lazy_cell.rs | 247 ++++++++++++++++++++++++++++++++++ core/src/storage/mod.rs | 2 + 2 files changed, 249 insertions(+) create mode 100644 core/src/storage/lazy_cell.rs diff --git a/core/src/storage/lazy_cell.rs b/core/src/storage/lazy_cell.rs new file mode 100644 index 00000000000..e09b2117bfe --- /dev/null +++ b/core/src/storage/lazy_cell.rs @@ -0,0 +1,247 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{ + KeyPtr, + Pull, + Push, + StorageSize, +}; +use core::cell::UnsafeCell; +use ink_primitives::Key; + +/// The lazy storage entry can be in either of two states. +/// +/// - Vacant: No value has been loaded and the next access to the lazy cell +/// will lazily load and decode the value from contract storage. +/// - Occupied: There already is a value that has been loaded or that has been +/// simply set upon initialization. +#[derive(Debug)] +pub enum LazyCellEntry { + /// A true lazy storage entity that loads its contract storage value upon first use. + Vacant(VacantEntry), + /// An already loaded eager lazy storage entity. + Occupied(OccupiedEntry), +} + +/// The lazy storage entity is in a lazy state. +#[derive(Debug)] +pub struct VacantEntry { + /// The key to load the value from contract storage upon first use. + pub key: Key, +} + +impl VacantEntry { + /// Creates a new truly lazy storage entity for the given key. + pub fn new(key: Key) -> Self { + Self { key } + } +} + +/// An already loaded or otherwise occupied eager lazy storage entity. +#[derive(Debug)] +pub struct OccupiedEntry { + /// The loaded value. + pub value: Option, +} + +impl OccupiedEntry { + /// Creates a new eager lazy storage entity with the given value. + pub fn new(value: Option) -> Self { + Self { + value, + } + } +} + +/// A lazy storage entity. +/// +/// This loads its value from storage upon first use. +/// +/// # Note +/// +/// Use this if the storage field doesn't need to be loaded in some or most cases. +#[derive(Debug)] +pub struct LazyCell { + // SAFETY: We use `UnsafeCell` instead of `RefCell` because + // the intended use-case is to hand out references (`&` and `&mut`) + // to the callers of `Lazy`. This cannot be done without `unsafe` + // code even with `RefCell`. Also `RefCell` has a larger footprint + // and has additional overhead that we can avoid by the interface + // and the fact that ink! code is always run single-threaded. + // Being efficient is important here because this is intended to be + // a low-level primitive with lots of dependencies. + kind: UnsafeCell>, +} + +impl StorageSize for LazyCell +where + T: StorageSize, +{ + const SIZE: u64 = ::SIZE; +} + +impl Pull for LazyCell +where + T: StorageSize + scale::Decode, +{ + fn pull(key_ptr: &mut KeyPtr) -> Self { + Self::lazy(key_ptr.next_for::()) + } +} + +impl Push for LazyCell +where + T: Push, +{ + fn push(&self, key_ptr: &mut KeyPtr) { + // We skip pushing to contract storage if we are still in unloaded form. + if let LazyCellEntry::Occupied(occupied) = self.kind() { + if let Some(value) = &occupied.value { + Push::push(value, key_ptr) + } + } + } +} + +impl LazyCell { + /// Creates an already populated lazy storage cell. + /// + /// # Note + /// + /// Since this already has a value it will never actually load from + /// the contract storage. + #[must_use] + pub fn new(value: I) -> Self + where + I: Into>, + { + Self { + kind: UnsafeCell::new(LazyCellEntry::Occupied(OccupiedEntry::new( + value.into(), + ))), + } + } + + /// Creates a lazy storage cell for the given key. + /// + /// # Note + /// + /// This will actually lazily load from the associated storage cell + /// upon access. + #[must_use] + pub fn lazy(key: Key) -> Self { + Self { + kind: UnsafeCell::new(LazyCellEntry::Vacant(VacantEntry::new(key))), + } + } + + /// Returns a shared reference to the inner lazy kind. + #[must_use] + fn kind(&self) -> &LazyCellEntry { + // SAFETY: We just return a shared reference while the method receiver + // is a shared reference (&self) itself. So we respect normal + // Rust rules. + unsafe { &*self.kind.get() } + } + + /// Returns an exclusive reference to the inner lazy kind. + #[must_use] + fn kind_mut(&mut self) -> &mut LazyCellEntry { + // SAFETY: We just return an exclusive reference while the method receiver + // is an exclusive reference (&mut self) itself. So we respect normal + // Rust rules. + unsafe { &mut *self.kind.get() } + } +} + +impl LazyCell +where + T: scale::Decode, +{ + /// Loads the value lazily from contract storage. + /// + /// # Note + /// + /// - After a successful call there will be an occupied value in the entry. + /// - Does nothing if value has already been loaded. + /// + /// # Panics + /// + /// If a value has been loaded that failed to decode into `T`. + fn load_value_lazily(&self) { + // SAFETY: This is critical because we mutably access the entry. + // However, we mutate the entry only if it is vacant. + // If the entry is occupied by a value we return early. + // This way we do not invalidate pointers to this value. + let kind = unsafe { &mut *self.kind.get() }; + if let LazyCellEntry::Vacant(vacant) = kind { + let value = crate::env::get_contract_storage::(vacant.key).map(|some| { + some.expect("couldn't properly decode contract storage entry") + }); + *kind = LazyCellEntry::Occupied(OccupiedEntry::new(value)); + } + } + + /// Returns a shared reference to the value. + /// + /// # Note + /// + /// This eventually lazily loads the value from the contract storage. + /// + /// # Panics + /// + /// If decoding the loaded value to `T` failed. + #[must_use] + pub fn get(&self) -> Option<&T> { + self.load_value_lazily(); + match self.kind() { + LazyCellEntry::Vacant(_) => unreachable!("assumed occupied value here"), + LazyCellEntry::Occupied(occupied) => occupied.value.as_ref(), + } + } + + /// Returns an exclusive reference to the value. + /// + /// # Note + /// + /// This eventually lazily loads the value from the contract storage. + /// + /// # Panics + /// + /// If decoding the loaded value to `T` failed. + #[must_use] + pub fn get_mut(&mut self) -> Option<&mut T> { + self.load_value_lazily(); + match self.kind_mut() { + LazyCellEntry::Vacant(_) => unreachable!("assumed occupied value here"), + LazyCellEntry::Occupied(occupied) => occupied.value.as_mut(), + } + } +} + +impl From for LazyCell { + fn from(value: T) -> Self { + Self::new(Some(value)) + } +} + +impl Default for LazyCell +where + T: Default, +{ + fn default() -> Self { + Self::new(Some(Default::default())) + } +} diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 96807418f42..afe48740f03 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -75,6 +75,7 @@ pub mod chunk; mod collections; mod flush; mod lazy; +mod lazy_cell; mod lazy_chunk; mod traits; mod value; @@ -111,6 +112,7 @@ pub use self::{ }, flush::Flush, lazy::Lazy, + lazy_cell::LazyCell, lazy_chunk::LazyChunk, traits::{ KeyPtr, From a2e9c925b3b924e642f3c7db8d8dfaf960344e92 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 12 Feb 2020 15:12:20 -0800 Subject: [PATCH 026/142] [core] add Lazy kind field comment --- core/src/storage/lazy.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy.rs index f94640095fe..0435dcef2b8 100644 --- a/core/src/storage/lazy.rs +++ b/core/src/storage/lazy.rs @@ -30,6 +30,14 @@ use ink_primitives::Key; /// Use this if the storage field doesn't need to be loaded in some or most cases. #[derive(Debug)] pub struct Lazy { + // SAFETY: We use `UnsafeCell` instead of `RefCell` because + // the intended use-case is to hand out references (`&` and `&mut`) + // to the callers of `Lazy`. This cannot be done without `unsafe` + // code even with `RefCell`. Also `RefCell` has a larger footprint + // and has additional overhead that we can avoid by the interface + // and the fact that ink! code is always run single-threaded. + // Being efficient is important here because this is intended to be + // a low-level primitive with lots of dependencies. kind: UnsafeCell>, } From 2c8d08b1a667f99dde881e8f82a72bbdd86cb592 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 12 Feb 2020 15:29:48 -0800 Subject: [PATCH 027/142] [core] base Lazy on LazyCell --- core/src/storage/lazy.rs | 117 ++++----------------------------------- 1 file changed, 12 insertions(+), 105 deletions(-) diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy.rs index 0435dcef2b8..b9ba806b107 100644 --- a/core/src/storage/lazy.rs +++ b/core/src/storage/lazy.rs @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{ +use super::super::{ KeyPtr, + LazyCell, Pull, Push, StorageSize, }; -use core::cell::UnsafeCell; use ink_primitives::Key; /// A lazy storage entity. @@ -30,22 +30,14 @@ use ink_primitives::Key; /// Use this if the storage field doesn't need to be loaded in some or most cases. #[derive(Debug)] pub struct Lazy { - // SAFETY: We use `UnsafeCell` instead of `RefCell` because - // the intended use-case is to hand out references (`&` and `&mut`) - // to the callers of `Lazy`. This cannot be done without `unsafe` - // code even with `RefCell`. Also `RefCell` has a larger footprint - // and has additional overhead that we can avoid by the interface - // and the fact that ink! code is always run single-threaded. - // Being efficient is important here because this is intended to be - // a low-level primitive with lots of dependencies. - kind: UnsafeCell>, + cell: LazyCell, } impl StorageSize for Lazy where T: StorageSize, { - const SIZE: u64 = ::SIZE; + const SIZE: u64 = as StorageSize>::SIZE; } impl Pull for Lazy @@ -53,7 +45,9 @@ where T: StorageSize + scale::Decode, { fn pull(key_ptr: &mut KeyPtr) -> Self { - Self::lazy(key_ptr.next_for::()) + Self { + cell: as Pull>::pull(key_ptr), + } } } @@ -62,10 +56,7 @@ where T: Push, { fn push(&self, key_ptr: &mut KeyPtr) { - // We skip pushing to contract storage if we are still in unloaded form. - if let LazyKind::Occupied(occupied) = self.kind() { - occupied.value.push(key_ptr) - } + Push::push(&self.cell, key_ptr) } } @@ -74,7 +65,7 @@ impl Lazy { #[must_use] pub fn new(value: T) -> Self { Self { - kind: UnsafeCell::new(LazyKind::Occupied(OccupiedLazy::new(value))), + cell: LazyCell::new(Some(value)), } } @@ -82,49 +73,15 @@ impl Lazy { #[must_use] pub fn lazy(key: Key) -> Self { Self { - kind: UnsafeCell::new(LazyKind::Vacant(VacantLazy::new(key))), + cell: LazyCell::lazy(key), } } - - /// Returns a shared reference to the inner lazy kind. - #[must_use] - fn kind(&self) -> &LazyKind { - // SAFETY: We just return a shared reference while the method receiver - // is a shared reference (&self) itself. So we respect normal - // Rust rules. - unsafe { &*self.kind.get() } - } - - /// Returns an exclusive reference to the inner lazy kind. - #[must_use] - fn kind_mut(&mut self) -> &mut LazyKind { - // SAFETY: We just return an exclusive reference while the method receiver - // is an exclusive reference (&mut self) itself. So we respect normal - // Rust rules. - unsafe { &mut *self.kind.get() } - } } impl Lazy where T: scale::Decode, { - /// Loads the value lazily from contract storage. - /// - /// Does nothing if value has already been loaded. - fn load_value_lazily(&self) { - // SAFETY: We mutate the kind only if it is vacant. - // So if there is an actual value (Occupied) we leave the - // entire entity as it is not to invalidate references to it. - let kind = unsafe { &mut *self.kind.get() }; - if let LazyKind::Vacant(vacant) = kind { - let value = crate::env::get_contract_storage::(vacant.key) - .expect("couldn't find contract storage entry") - .expect("couldn't properly decode contract storage entry"); - *kind = LazyKind::Occupied(OccupiedLazy::new(value)); - } - } - /// Returns a shared reference to the lazily loaded value. /// /// # Note @@ -136,11 +93,7 @@ where /// If loading from contract storage failed. #[must_use] pub fn get(&self) -> &T { - self.load_value_lazily(); - match self.kind() { - LazyKind::Vacant(_) => panic!("expect occupied lazy here"), - LazyKind::Occupied(occupied) => &occupied.value, - } + self.cell.get().expect("expected Some value") } /// Returns an exclusive reference to the lazily loaded value. @@ -154,11 +107,7 @@ where /// If loading from contract storage failed. #[must_use] pub fn get_mut(&mut self) -> &mut T { - self.load_value_lazily(); - match self.kind_mut() { - LazyKind::Vacant(_) => panic!("expect occupied lazy here"), - LazyKind::Occupied(occupied) => &mut occupied.value, - } + self.cell.get_mut().expect("expected Some value") } } @@ -291,45 +240,3 @@ where self.get_mut() } } - -/// The lazy storage entity can be in either of two states. -/// -/// 1. It either is vacant and thus a real lazy storage entity that -/// waits until it is used for the first time in order to load its value -/// from the contract storage. -/// 2. It is actually an already occupied eager lazy. -#[derive(Debug)] -pub enum LazyKind { - /// A true lazy storage entity that loads its contract storage value upon first use. - Vacant(VacantLazy), - /// An already loaded eager lazy storage entity. - Occupied(OccupiedLazy), -} - -/// The lazy storage entity is in a lazy state. -#[derive(Debug)] -pub struct VacantLazy { - /// The key to load the value from contract storage upon first use. - pub key: Key, -} - -impl VacantLazy { - /// Creates a new truly lazy storage entity for the given key. - pub fn new(key: Key) -> Self { - Self { key } - } -} - -/// An already loaded or otherwise occupied eager lazy storage entity. -#[derive(Debug)] -pub struct OccupiedLazy { - /// The loaded value. - pub value: T, -} - -impl OccupiedLazy { - /// Creates a new eager lazy storage entity with the given value. - pub fn new(value: T) -> Self { - Self { value } - } -} From 8ce30911dde6065508f16aea473c2d3605696189 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 12 Feb 2020 15:30:17 -0800 Subject: [PATCH 028/142] [core] move all lazy entities to lazy module --- core/src/storage/{ => lazy}/lazy.rs | 0 core/src/storage/{ => lazy}/lazy_cell.rs | 2 +- core/src/storage/{ => lazy}/lazy_chunk.rs | 2 +- core/src/storage/lazy/mod.rs | 23 +++++++++++++++++++++++ core/src/storage/mod.rs | 10 +++++----- 5 files changed, 30 insertions(+), 7 deletions(-) rename core/src/storage/{ => lazy}/lazy.rs (100%) rename core/src/storage/{ => lazy}/lazy_cell.rs (99%) rename core/src/storage/{ => lazy}/lazy_chunk.rs (99%) create mode 100644 core/src/storage/lazy/mod.rs diff --git a/core/src/storage/lazy.rs b/core/src/storage/lazy/lazy.rs similarity index 100% rename from core/src/storage/lazy.rs rename to core/src/storage/lazy/lazy.rs diff --git a/core/src/storage/lazy_cell.rs b/core/src/storage/lazy/lazy_cell.rs similarity index 99% rename from core/src/storage/lazy_cell.rs rename to core/src/storage/lazy/lazy_cell.rs index e09b2117bfe..a5bfa756f33 100644 --- a/core/src/storage/lazy_cell.rs +++ b/core/src/storage/lazy/lazy_cell.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{ +use super::super::{ KeyPtr, Pull, Push, diff --git a/core/src/storage/lazy_chunk.rs b/core/src/storage/lazy/lazy_chunk.rs similarity index 99% rename from core/src/storage/lazy_chunk.rs rename to core/src/storage/lazy/lazy_chunk.rs index 987dfb75a1c..ee1687242bf 100644 --- a/core/src/storage/lazy_chunk.rs +++ b/core/src/storage/lazy/lazy_chunk.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{ +use super::super::{ storage_size::Chunk, KeyPtr, Pull, diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs new file mode 100644 index 00000000000..e15eb03dddb --- /dev/null +++ b/core/src/storage/lazy/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod lazy; +mod lazy_cell; +mod lazy_chunk; + +pub use self::{ + lazy::Lazy, + lazy_cell::LazyCell, + lazy_chunk::LazyChunk, +}; diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index afe48740f03..46cda99efae 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -75,8 +75,6 @@ pub mod chunk; mod collections; mod flush; mod lazy; -mod lazy_cell; -mod lazy_chunk; mod traits; mod value; @@ -111,9 +109,11 @@ pub use self::{ }, }, flush::Flush, - lazy::Lazy, - lazy_cell::LazyCell, - lazy_chunk::LazyChunk, + lazy::{ + Lazy, + LazyCell, + LazyChunk, + }, traits::{ KeyPtr, Pull, From d801d968269297671a9198df83890ffeeb81817f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 12 Feb 2020 15:43:37 -0800 Subject: [PATCH 029/142] [core] use Box instead of Pin --- core/src/storage/lazy/lazy_chunk.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/core/src/storage/lazy/lazy_chunk.rs b/core/src/storage/lazy/lazy_chunk.rs index ee1687242bf..425e8722327 100644 --- a/core/src/storage/lazy/lazy_chunk.rs +++ b/core/src/storage/lazy/lazy_chunk.rs @@ -19,10 +19,7 @@ use super::super::{ Push, StorageSize, }; -use core::{ - cell::UnsafeCell, - pin::Pin, -}; +use core::cell::UnsafeCell; use ink_primitives::Key; use ink_prelude::{ @@ -63,7 +60,7 @@ pub struct LazyChunk { /// We keep the whole entry in a `Pin>` in order to prevent pointer /// invalidation upon updating the cache through `&self` methods as in /// [`LazyChunk::get`]. -pub type EntryMap = BTreeMap>>>; +pub type EntryMap = BTreeMap>>; /// An entry within the lazy chunk #[derive(Debug)] @@ -75,8 +72,6 @@ pub struct Entry { mutated: core::cell::Cell, } -impl Unpin for Entry {} - impl Push for Entry where T: Push + StorageSize, @@ -224,7 +219,7 @@ where }; // SAFETY: We have put the whole `cached_entries` mapping into an // `UnsafeCell` because of this caching functionality. The - // trick here is that due to using `Pin>` internally + // trick here is that due to using `Box` internally // we are able to return references to the cached entries // while maintaining the invariant that mutating the caching // `BTreeMap` will never invalidate those references. @@ -242,7 +237,7 @@ where } None => None, }; - &mut **vacant.insert(Box::pin(Entry { + &mut **vacant.insert(Box::new(Entry { value, mutated: core::cell::Cell::new(false), })) From 4e8eea89508cc0ba10d53fd8b6622fcf39a4c358 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 19 Feb 2020 03:14:38 +0100 Subject: [PATCH 030/142] [core] remodel Pull and Push traits and StorageSize calculations --- core/Cargo.toml | 1 + core/src/storage/lazy/lazy.rs | 20 +- core/src/storage/lazy/lazy_cell.rs | 20 +- core/src/storage/lazy/lazy_chunk.rs | 49 +++-- core/src/storage/mod.rs | 13 +- core/src/storage/traits/mod.rs | 33 ++- core/src/storage/traits/pull.rs | 225 ++++++++++++++------ core/src/storage/traits/push.rs | 261 ++++++++++++++++++------ core/src/storage/traits/storage_size.rs | 92 ++++----- 9 files changed, 495 insertions(+), 219 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 30f01ff27df..dbcd803d591 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -27,6 +27,7 @@ derive_more = { version = "0.99.2", default-features = false, features = ["from" smallvec = { version = "1.0", default-features = false, features = ["union"] } cfg-if = "0.1" num-traits = { version = "0.2.1", default-features = false, features = ["i128"] } +array-init = "0.1" # Only used in the off-chain environment. # diff --git a/core/src/storage/lazy/lazy.rs b/core/src/storage/lazy/lazy.rs index b9ba806b107..a10921925ec 100644 --- a/core/src/storage/lazy/lazy.rs +++ b/core/src/storage/lazy/lazy.rs @@ -15,8 +15,8 @@ use super::super::{ KeyPtr, LazyCell, - Pull, - Push, + PullForward, + PushForward, StorageSize, }; use ink_primitives::Key; @@ -40,23 +40,23 @@ where const SIZE: u64 = as StorageSize>::SIZE; } -impl Pull for Lazy +impl PullForward for Lazy where - T: StorageSize + scale::Decode, + T: StorageSize, { - fn pull(key_ptr: &mut KeyPtr) -> Self { + fn pull_forward(ptr: &mut KeyPtr) -> Self { Self { - cell: as Pull>::pull(key_ptr), + cell: as PullForward>::pull_forward(ptr) } } } -impl Push for Lazy +impl PushForward for Lazy where - T: Push, + T: PushForward, { - fn push(&self, key_ptr: &mut KeyPtr) { - Push::push(&self.cell, key_ptr) + fn push_forward(&self, ptr: &mut KeyPtr) { + as PushForward>::push_forward(&self.cell, ptr) } } diff --git a/core/src/storage/lazy/lazy_cell.rs b/core/src/storage/lazy/lazy_cell.rs index a5bfa756f33..d702a7f7bae 100644 --- a/core/src/storage/lazy/lazy_cell.rs +++ b/core/src/storage/lazy/lazy_cell.rs @@ -14,8 +14,8 @@ use super::super::{ KeyPtr, - Pull, - Push, + PullForward, + PushForward, StorageSize, }; use core::cell::UnsafeCell; @@ -92,24 +92,24 @@ where const SIZE: u64 = ::SIZE; } -impl Pull for LazyCell +impl PullForward for LazyCell where - T: StorageSize + scale::Decode, + T: StorageSize, { - fn pull(key_ptr: &mut KeyPtr) -> Self { - Self::lazy(key_ptr.next_for::()) + fn pull_forward(ptr: &mut KeyPtr) -> Self { + Self::lazy(ptr.next_for::()) } } -impl Push for LazyCell +impl PushForward for LazyCell where - T: Push, + T: PushForward, { - fn push(&self, key_ptr: &mut KeyPtr) { + fn push_forward(&self, ptr: &mut KeyPtr) { // We skip pushing to contract storage if we are still in unloaded form. if let LazyCellEntry::Occupied(occupied) = self.kind() { if let Some(value) = &occupied.value { - Push::push(value, key_ptr) + ::push_forward(value, ptr) } } } diff --git a/core/src/storage/lazy/lazy_chunk.rs b/core/src/storage/lazy/lazy_chunk.rs index 425e8722327..abd90e3bb9c 100644 --- a/core/src/storage/lazy/lazy_chunk.rs +++ b/core/src/storage/lazy/lazy_chunk.rs @@ -13,10 +13,9 @@ // limitations under the License. use super::super::{ - storage_size::Chunk, KeyPtr, - Pull, - Push, + PullForward, + PushForward, StorageSize, }; use core::cell::UnsafeCell; @@ -72,21 +71,25 @@ pub struct Entry { mutated: core::cell::Cell, } -impl Push for Entry +impl PushForward for Entry where - T: Push + StorageSize, + T: PushForward + StorageSize, { - fn push(&self, key_ptr: &mut KeyPtr) { + fn push_forward(&self, ptr: &mut KeyPtr) { // Reset the mutated entry flag because we just synced. self.mutated.set(false); match self.value() { Some(value) => { // Forward push to the inner value on the computed key. - Push::push(value, key_ptr); + ::push_forward(value, ptr); } None => { - // Clear the storage at the given index. - crate::env::clear_contract_storage(key_ptr.next_for::()); + let mut offset = ptr.next_for::(); + for _ in 0..::SIZE { + // Clear the storage at the given index. + crate::env::clear_contract_storage(offset); + offset += 1u64; + } } } } @@ -159,34 +162,38 @@ impl LazyChunk { } } -impl StorageSize for LazyChunk { - const SIZE: u64 = ::SIZE; +impl StorageSize for LazyChunk +where + T: StorageSize, +{ + const SIZE: u64 = ::SIZE * (core::u32::MAX as u64); } -impl Pull for LazyChunk +impl PullForward for LazyChunk where - Self: StorageSize, + T: StorageSize, { - fn pull(key_ptr: &mut KeyPtr) -> Self { + fn pull_forward(ptr: &mut KeyPtr) -> Self { Self { - key: Some(key_ptr.next_for::()), + key: Some(ptr.next_for::()), cached_entries: UnsafeCell::new(BTreeMap::new()), } } } -impl Push for LazyChunk +impl PushForward for LazyChunk where - T: Push + StorageSize + scale::Encode, + T: PushForward + StorageSize, { - fn push(&self, key_ptr: &mut KeyPtr) { - let key = key_ptr.next_for::(); + fn push_forward(&self, ptr: &mut KeyPtr) { + let key = ptr.next_for::(); + let elem_size = ::SIZE; assert_eq!(self.key, Some(key)); for (&index, entry) in self.entries().iter().filter(|(_, entry)| entry.mutated()) { - let offset: Key = key + index; + let offset: Key = key + (index as u64 * elem_size); let mut ptr = KeyPtr::from(offset); - Push::push(&**entry, &mut ptr); + PushForward::push_forward(&**entry, &mut ptr); } } } diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 46cda99efae..af9d678fe4b 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -78,13 +78,6 @@ mod lazy; mod traits; mod value; -pub mod storage_size { - pub use super::traits::{ - Cell, - Chunk, - }; -} - pub use self::{ collections::{ binary_heap::{ @@ -116,8 +109,10 @@ pub use self::{ }, traits::{ KeyPtr, - Pull, - Push, + PullAt, + PullForward, + PushAt, + PushForward, StorageSize, }, }; diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index 659aab07c42..afed2b50441 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -26,6 +26,20 @@ macro_rules! forward_supported_array_lens { }; } +/// Implemented by array of sizes of up to 32. +pub trait ArrayLenLessEquals32 {} +macro_rules! impl_array_len_less_equals_32_for { + ( $($n:literal),* $(,)? ) => { + $( + impl ArrayLenLessEquals32 for [T; $n] {} + )* + } +} +impl_array_len_less_equals_32_for!( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, +); + mod pull; mod push; mod storage_size; @@ -33,13 +47,15 @@ mod storage_size; use ink_primitives::Key; pub use self::{ - pull::Pull, - push::Push, - storage_size::{ - Cell, - Chunk, - StorageSize, + pull::{ + PullAt, + PullForward, }, + push::{ + PushAt, + PushForward, + }, + storage_size::StorageSize, }; /// A key pointer. @@ -58,6 +74,11 @@ impl From for KeyPtr { } impl KeyPtr { + /// Returns the current `Key`. + fn current(&self) -> Key { + self.key + } + /// Advances the key by the given amount derive by `T` and its `StorageSize` /// and returns the next `Key` for usage by the storage element. pub fn next_for(&mut self) -> Key diff --git a/core/src/storage/traits/pull.rs b/core/src/storage/traits/pull.rs index dfe521c2449..454122b01cf 100644 --- a/core/src/storage/traits/pull.rs +++ b/core/src/storage/traits/pull.rs @@ -13,20 +13,47 @@ // limitations under the License. use super::{ + ArrayLenLessEquals32, KeyPtr, StorageSize, }; +use crate::env; +use array_init::{ + array_init, + IsArray, +}; +use core::marker::PhantomData; +use ink_primitives::Key; + +/// Types that implement this trait can pull their fields from the associated +/// contract storage in a distributive manner. +/// +/// # Note +/// +/// This tries to distribute all separate fields of an entity into distinct +/// storage cells. +pub trait PullForward { + /// Pulls `self` distributedly from the associated contract storage. + fn pull_forward(ptr: &mut KeyPtr) -> Self; +} -/// Pulls the associated key values from the contract storage and forms `Self`. -pub trait Pull { - fn pull(key_ptr: &mut KeyPtr) -> Self; +/// Types that implement this trait can pull their fields from an associated +/// contract storage cell. +/// +/// # Note +/// +/// This tries to compactly load the entire entity's fields from a single +/// contract storage cell. +pub trait PullAt { + /// Pulls `self` packed from the contract storage cell determined by `at`. + fn pull_at(at: Key) -> Self; } -fn pull_single_cell(key_ptr: &mut KeyPtr) -> T +fn pull_single_cell(at: Key) -> T where - T: StorageSize + scale::Decode, + T: scale::Decode, { - crate::env::get_contract_storage::(key_ptr.next_for::()) + crate::env::get_contract_storage::(at) .expect("storage entry was empty") .expect("could not properly decode storage entry") } @@ -34,9 +61,14 @@ where macro_rules! impl_pull_for_primitive { ( $($ty:ty),* $(,)? ) => { $( - impl Pull for $ty { - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::<$ty>(key_ptr) + impl PullForward for $ty { + fn pull_forward(ptr: &mut KeyPtr) -> Self { + ::pull_at(ptr.next_for::()) + } + } + impl PullAt for $ty { + fn pull_at(at: Key) -> Self { + pull_single_cell::<$ty>(at) } } )* @@ -47,12 +79,22 @@ impl_pull_for_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); macro_rules! impl_pull_for_array { ( $($len:literal),* $(,)? ) => { $( - impl Pull for [T; $len] + impl PullForward for [T; $len] + where + Self: IsArray + ArrayLenLessEquals32, + ::Item: PullForward, + { + fn pull_forward(ptr: &mut KeyPtr) -> Self { + array_init::(|_| PullForward::pull_forward(ptr)) + } + } + + impl PullAt for [T; $len] where [T; $len]: scale::Decode, { - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::<[T; $len]>(key_ptr) + fn pull_at(at: Key) -> Self { + pull_single_cell::<[T; $len]>(at) } } )* @@ -62,12 +104,27 @@ forward_supported_array_lens!(impl_pull_for_array); macro_rules! impl_pull_tuple { ( $($frag:ident),* $(,)? ) => { - impl<$($frag),*> Pull for ($($frag),* ,) + impl<$($frag),*> PullForward for ($($frag),* ,) where - ( $($frag),* ,): scale::Decode, + $( + $frag: PullForward, + )* { - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::<($($frag),* ,)>(key_ptr) + fn pull_forward(ptr: &mut KeyPtr) -> Self { + ( + $( + <$frag as PullForward>::pull_forward(ptr), + )* + ) + } + } + + impl<$($frag),*> PullAt for ($($frag),* ,) + where + Self: scale::Decode, + { + fn pull_at(at: Key) -> Self { + pull_single_cell::(at) } } } @@ -83,93 +140,147 @@ impl_pull_tuple!(A, B, C, D, E, F, G, H); impl_pull_tuple!(A, B, C, D, E, F, G, H, I); impl_pull_tuple!(A, B, C, D, E, F, G, H, I, J); -impl Pull for () { - fn pull(_key_ptr: &mut KeyPtr) -> Self {} +impl PullForward for () { + fn pull_forward(_ptr: &mut KeyPtr) -> Self { + () + } +} + +impl PullAt for () { + fn pull_at(_at: Key) -> Self { + () + } } -impl Pull for core::marker::PhantomData { - fn pull(_key_ptr: &mut KeyPtr) -> Self { +impl PullForward for PhantomData { + fn pull_forward(_ptr: &mut KeyPtr) -> Self { Default::default() } } -impl Pull for Option -where - Self: scale::Decode, -{ - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::(key_ptr) +impl PullAt for PhantomData { + fn pull_at(_at: Key) -> Self { + Default::default() } } -impl Pull for Result +impl PullForward for Option where - Self: scale::Decode, + T: PullForward + StorageSize, { - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::(key_ptr) + fn pull_forward(ptr: &mut KeyPtr) -> Self { + // We decode as `()` because at this point we are not interested + // in the actual value, we just want to know if there exists a value + // at all. + match env::get_contract_storage::<()>(ptr.current()) { + Some(_) => Some(::pull_forward(ptr)), + None => None, + } } } -impl Pull for ink_prelude::vec::Vec +impl PullAt for Option where Self: scale::Decode, { - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::(key_ptr) + fn pull_at(at: Key) -> Self { + pull_single_cell::(at) } } -impl Pull for ink_prelude::string::String +impl PullForward for Result where - Self: scale::Decode, + T: PullForward, + E: PullForward, { - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::(key_ptr) + fn pull_forward(ptr: &mut KeyPtr) -> Self { + match pull_single_cell::(ptr.next_for::()) { + 0 => Ok(::pull_forward(ptr)), + 1 => Err(::pull_forward(ptr)), + _ => unreachable!("found invalid Result discriminator"), + } } } -impl Pull for ink_prelude::boxed::Box +impl PullAt for Result where Self: scale::Decode, { - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::(key_ptr) + fn pull_at(at: Key) -> Self { + pull_single_cell::(at) } } -impl Pull for ink_prelude::collections::BTreeSet -where - Self: scale::Decode, -{ - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::(key_ptr) +impl PullForward for ink_prelude::string::String { + fn pull_forward(ptr: &mut KeyPtr) -> Self { + ::pull_at(ptr.next_for::()) + } +} + +impl PullAt for ink_prelude::string::String { + fn pull_at(at: Key) -> Self { + pull_single_cell::(at) } } -impl Pull for ink_prelude::collections::BinaryHeap +impl PullForward for ink_prelude::boxed::Box where - Self: scale::Decode, + T: PullForward, { - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::(key_ptr) + fn pull_forward(ptr: &mut KeyPtr) -> Self { + Self::new(::pull_forward(ptr)) } } -impl Pull for ink_prelude::collections::LinkedList +impl PullAt for ink_prelude::boxed::Box where - Self: scale::Decode, + T: PullAt, { - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::(key_ptr) + fn pull_at(at: Key) -> Self { + Self::new(::pull_at(at)) } } -impl Pull for ink_prelude::collections::VecDeque +const _: () = { + use ink_prelude::{ + collections::{ + BTreeSet, + BinaryHeap, + LinkedList, + VecDeque, + }, + }; + #[cfg(not(feature = "std"))] + use ink_prelude::vec::Vec; + + macro_rules! impl_pull_at_for_collection { + ( $($collection:ident),* $(,)? ) => { + $( + impl PullAt for $collection + where + Self: scale::Decode, + { + fn pull_at(at: Key) -> Self { + pull_single_cell::(at) + } + } + )* + }; + } + impl_pull_at_for_collection!( + Vec, + BTreeSet, + BinaryHeap, + LinkedList, + VecDeque, + ); +}; + +impl PullAt for ink_prelude::collections::BTreeMap where Self: scale::Decode, { - fn pull(key_ptr: &mut KeyPtr) -> Self { - pull_single_cell::(key_ptr) + fn pull_at(at: Key) -> Self { + pull_single_cell::(at) } } diff --git a/core/src/storage/traits/push.rs b/core/src/storage/traits/push.rs index c38735d72f6..c03b2f4b04d 100644 --- a/core/src/storage/traits/push.rs +++ b/core/src/storage/traits/push.rs @@ -13,28 +13,50 @@ // limitations under the License. use super::{ + ArrayLenLessEquals32, KeyPtr, StorageSize, }; +use crate::env; +use core::marker::PhantomData; +use ink_primitives::Key; -/// Pushes the associated key values of `Self` to the contract storage. -pub trait Push { - fn push(&self, key_ptr: &mut KeyPtr); +/// Types that implement this trait can push their fields to the associated +/// contract storage in a distributive manner. +/// +/// # Note +/// +/// This tries to distribute all separate fields of an entity into distinct +/// storage cells. +pub trait PushForward { + /// Pushes `self` distributed to the associated contract storage. + fn push_forward(&self, ptr: &mut KeyPtr); } -fn push_single_cell(value: &T, key_ptr: &mut KeyPtr) -where - T: StorageSize + scale::Encode, -{ - crate::env::set_contract_storage::(key_ptr.next_for::(), value) +/// Types that implement this trait can push their fields to an associated +/// contract storage cell. +/// +/// # Note +/// +/// This tries to compactly store the entire entity's fields into a single +/// contract storage cell. +pub trait PushAt { + /// Pushes `self` packed to the contract storage cell determined by `at`. + fn push_at(&self, at: Key); } macro_rules! impl_push_for_primitive { ( $($ty:ty),* $(,)? ) => { $( - impl Push for $ty { - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) + impl PushForward for $ty { + fn push_forward(&self, ptr: &mut KeyPtr) { + <$ty as PushAt>::push_at(self, ptr.next_for::<$ty>()) + } + } + + impl PushAt for $ty { + fn push_at(&self, at: Key) { + env::set_contract_storage::<$ty>(at, self) } } )* @@ -45,12 +67,28 @@ impl_push_for_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); macro_rules! impl_push_for_array { ( $($len:literal),* $(,)? ) => { $( - impl Push for [T; $len] + impl PushForward for [T; $len] + where + Self: ArrayLenLessEquals32, + T: PushForward, + { + fn push_forward(&self, ptr: &mut KeyPtr) { + // TODO: Insert check to assert that the array length is + // 32 or smaller since otherwise this is a rather + // expensive operation that it shouldn't be. + // Arrays should generally be used in packed form. + for elem in self.iter() { + PushForward::push_forward(elem, ptr) + } + } + } + + impl PushAt for [T; $len] where [T; $len]: scale::Encode, { - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) + fn push_at(&self, at: Key) { + env::set_contract_storage::<[T; $len]>(at, self) } } )* @@ -60,12 +98,27 @@ forward_supported_array_lens!(impl_push_for_array); macro_rules! impl_push_tuple { ( $($frag:ident),* $(,)? ) => { - impl<$($frag),*> Push for ($($frag),* ,) + impl<$($frag),*> PushForward for ($($frag),* ,) + where + $( + $frag: PushForward, + )* + { + fn push_forward(&self, ptr: &mut KeyPtr) { + #[allow(non_snake_case)] + let ($($frag),*,) = self; + $( + PushForward::push_forward($frag, ptr); + )* + } + } + + impl<$($frag),*> PushAt for ($($frag),* ,) where - ( $($frag),* ,): scale::Encode, + Self: scale::Encode, { - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) + fn push_at(&self, at: Key) { + env::set_contract_storage::(at, self) } } } @@ -81,91 +134,179 @@ impl_push_tuple!(A, B, C, D, E, F, G, H); impl_push_tuple!(A, B, C, D, E, F, G, H, I); impl_push_tuple!(A, B, C, D, E, F, G, H, I, J); -impl Push for () { - fn push(&self, _key_ptr: &mut KeyPtr) {} +impl PushForward for () { + fn push_forward(&self, _ptr: &mut KeyPtr) {} +} + +impl PushAt for () { + fn push_at(&self, _at: Key) {} +} + +impl PushForward for PhantomData { + fn push_forward(&self, _ptr: &mut KeyPtr) {} } -impl Push for core::marker::PhantomData { - fn push(&self, _key_ptr: &mut KeyPtr) {} +impl PushAt for PhantomData { + fn push_at(&self, _at: Key) {} } -impl Push for Option +impl PushForward for Option where - Self: scale::Encode, + T: PushForward + StorageSize, { - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) + /// We implement `PushForward` for `Option` in an optimized fashion + /// leaving behind a cleared contract storage cell area in case of `None`. + /// + /// This can be tricky and a performance hazard for types that occupy + /// a large amount of storage cells (i.e. their `StorageSize::SIZE` is + /// big.). This implementation needs protection against this sort of hazard. + fn push_forward(&self, ptr: &mut KeyPtr) { + match self { + Some(val) => ::push_forward(val, ptr), + None => { + // We still need to advance the key pointer. + let pos0 = ptr.next_for::(); + // Bail out early if `StorageSize` is too big and the method + // is used even though we have tried to prevent this at compile + // time. + if ::SIZE > 32 { + return + } + // In theory we'd need to clear all subfields of the `Option` + // which are technically `::SIZE` many. + // However, this could be a performance hazard so we should + // really not do that. We should add a check to disable this + // implementation for `StorageSize` of more than 32. (TODO) + for n in 0..::SIZE { + env::clear_contract_storage(pos0 + n); + } + } + } } } -impl Push for Result +impl PushAt for Option where - Self: scale::Encode, + T: PushAt, { - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) + fn push_at(&self, at: Key) { + match self { + Some(val) => ::push_at(val, at), + None => env::clear_contract_storage(at), + } } } -impl Push for ink_prelude::vec::Vec +impl PushForward for Result where - Self: scale::Encode, + T: PushForward, + E: PushForward, { - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) + fn push_forward(&self, ptr: &mut KeyPtr) { + match self { + Ok(val) => { + PushForward::push_forward(&0u8, ptr); + ::push_forward(val, ptr); + } + Err(err) => { + PushForward::push_forward(&1u8, ptr); + ::push_forward(err, ptr); + } + } } } -impl Push for ink_prelude::string::String +impl PushAt for Result where Self: scale::Encode, { - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) + fn push_at(&self, at: Key) { + env::set_contract_storage::(at, self) } } -impl Push for ink_prelude::boxed::Box -where - Self: scale::Encode, -{ - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) +// The `PushForward` and `PullForward` traits are not implemented for +// +// - `ink_prelude::vec::Vec` +// - `ink_prelude::collections::BTreeSet` +// - `ink_prelude::collections::BinaryHeap` +// - `ink_prelude::collections::LinkedList` +// - `ink_prelude::collections::VecDeque` +// +// since their storage sizes cannot be determined during compilation time as +// every element in them would potentially occupy its own storage cell and there +// can be arbitrary many elements at runtime. + +impl PushForward for ink_prelude::string::String { + fn push_forward(&self, ptr: &mut KeyPtr) { + ::push_at(self, ptr.next_for::()) } } -impl Push for ink_prelude::collections::BTreeSet -where - Self: scale::Encode, -{ - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) +impl PushAt for ink_prelude::string::String { + fn push_at(&self, at: Key) { + env::set_contract_storage::(at, self) } } -impl Push for ink_prelude::collections::BinaryHeap +impl PushForward for ink_prelude::boxed::Box where - Self: scale::Encode, + T: PushForward, { - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) + fn push_forward(&self, ptr: &mut KeyPtr) { + ::push_forward(&*self, ptr) } } -impl Push for ink_prelude::collections::LinkedList +impl PushAt for ink_prelude::boxed::Box where - Self: scale::Encode, + T: PushAt, { - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) + fn push_at(&self, at: Key) { + ::push_at(&*self, at) } } -impl Push for ink_prelude::collections::VecDeque +const _: () = { + use ink_prelude::{ + collections::{ + BTreeSet, + BinaryHeap, + LinkedList, + VecDeque, + }, + }; + #[cfg(not(feature = "std"))] + use ink_prelude::vec::Vec; + + macro_rules! impl_push_at_for_collection { + ( $($collection:ident),* $(,)? ) => { + $( + impl PushAt for $collection + where + Self: scale::Encode, + { + fn push_at(&self, at: Key) { + env::set_contract_storage::(at, self) + } + } + )* + }; + } + impl_push_at_for_collection!( + Vec, + BTreeSet, + BinaryHeap, + LinkedList, + VecDeque, + ); +}; + +impl PushAt for ink_prelude::collections::BTreeMap where Self: scale::Encode, { - fn push(&self, key_ptr: &mut KeyPtr) { - push_single_cell(self, key_ptr) + fn push_at(&self, at: Key) { + env::set_contract_storage::(at, self) } } diff --git a/core/src/storage/traits/storage_size.rs b/core/src/storage/traits/storage_size.rs index 4d0dbb5e9b6..965e4ada251 100644 --- a/core/src/storage/traits/storage_size.rs +++ b/core/src/storage/traits/storage_size.rs @@ -21,47 +21,45 @@ pub trait StorageSize { const SIZE: u64; } -/// A single cell. -pub enum Cell {} - -impl StorageSize for Cell { - const SIZE: u64 = 1; -} - -/// A chunk of cells. -pub enum Chunk {} - -impl StorageSize for Chunk { - const SIZE: u64 = core::u32::MAX as u64; -} - -macro_rules! impl_storage_size_1 { +macro_rules! impl_storage_size_for_primitive { ( $($ty:ty),* ) => { $( impl StorageSize for $ty { - const SIZE: u64 = ::SIZE; + const SIZE: u64 = 1; } )* }; } -impl_storage_size_1!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); +impl_storage_size_for_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); -macro_rules! impl_storage_size_array { +macro_rules! impl_storage_size_for_array { ( $($n:literal),* $(,)? ) => { $( - impl StorageSize for [T; $n] { - const SIZE: u64 = ::SIZE; + impl StorageSize for [T; $n] + where + T: StorageSize, + { + const SIZE: u64 = ::SIZE * $n; } )* }; } -forward_supported_array_lens!(impl_storage_size_array); +forward_supported_array_lens!(impl_storage_size_for_array); macro_rules! impl_storage_size_tuple { ( $($frag:ident),* $(,)? ) => { #[allow(unused_parens)] - impl<$($frag),*> StorageSize for ($($frag),* ,) { - const SIZE: u64 = ::SIZE; + impl<$($frag),*> StorageSize for ($($frag),* ,) + where + $( + $frag: StorageSize, + )* + { + const SIZE: u64 = 0 + $( + + <$frag as StorageSize>::SIZE + )* + ; } } } @@ -79,37 +77,39 @@ impl_storage_size_tuple!(A, B, C, D, E, F, G, H, I, J); impl StorageSize for () { const SIZE: u64 = 0; } + impl StorageSize for core::marker::PhantomData { const SIZE: u64 = 0; } -impl StorageSize for Option { - const SIZE: u64 = ::SIZE; -} -impl StorageSize for Result { - const SIZE: u64 = ::SIZE; -} -impl StorageSize for ink_prelude::vec::Vec { - const SIZE: u64 = ::SIZE; -} -impl StorageSize for ink_prelude::string::String { - const SIZE: u64 = ::SIZE; -} -impl StorageSize for ink_prelude::boxed::Box { - const SIZE: u64 = ::SIZE; -} -impl StorageSize for ink_prelude::collections::BTreeSet { - const SIZE: u64 = ::SIZE; +impl StorageSize for Option +where + T: StorageSize, +{ + const SIZE: u64 = ::SIZE; } -impl StorageSize for ink_prelude::collections::BinaryHeap { - const SIZE: u64 = ::SIZE; +impl StorageSize for Result +where + T: StorageSize, + E: StorageSize, +{ + const SIZE: u64 = { + // The following returns the maximum value from the storage + // sizes of type `T` and `E` in a way that enables it to be used + // at compile-time. + [::SIZE, ::SIZE] + [(::SIZE < ::SIZE) as usize] + }; } -impl StorageSize for ink_prelude::collections::LinkedList { - const SIZE: u64 = ::SIZE; +impl StorageSize for ink_prelude::boxed::Box +where + T: StorageSize, +{ + const SIZE: u64 = ::SIZE; } -impl StorageSize for ink_prelude::collections::VecDeque { - const SIZE: u64 = ::SIZE; +impl StorageSize for ink_prelude::string::String { + const SIZE: u64 = 1; } From 0f1a9f3c2fd8458a19671642c6d6bafd05a0e680 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 19 Feb 2020 03:18:48 +0100 Subject: [PATCH 031/142] [core] add trivial StorageSize impls for alloc collections --- core/src/storage/traits/storage_size.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core/src/storage/traits/storage_size.rs b/core/src/storage/traits/storage_size.rs index 965e4ada251..f861f902b8f 100644 --- a/core/src/storage/traits/storage_size.rs +++ b/core/src/storage/traits/storage_size.rs @@ -113,3 +113,27 @@ where impl StorageSize for ink_prelude::string::String { const SIZE: u64 = 1; } + +impl StorageSize for ink_prelude::vec::Vec { + const SIZE: u64 = 1; +} + +impl StorageSize for ink_prelude::collections::BinaryHeap { + const SIZE: u64 = 1; +} + +impl StorageSize for ink_prelude::collections::BTreeSet { + const SIZE: u64 = 1; +} + +impl StorageSize for ink_prelude::collections::BTreeMap { + const SIZE: u64 = 1; +} + +impl StorageSize for ink_prelude::collections::VecDeque { + const SIZE: u64 = 1; +} + +impl StorageSize for ink_prelude::collections::LinkedList { + const SIZE: u64 = 1; +} From aa9af7576d2de337ba8f10c0880d01e3d2347bf8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 19 Feb 2020 03:19:27 +0100 Subject: [PATCH 032/142] [core] fix doc comment links --- core/src/storage/traits/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index afed2b50441..a8a19c72100 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -60,7 +60,7 @@ pub use self::{ /// A key pointer. /// -/// Mainly used by [`Pull`] and [`Push`] traits in order to provide +/// Mainly used by [`PullForward`] and [`PushForward`] traits in order to provide /// a streamlined interface for accessing the underlying [`Key`]. pub struct KeyPtr { /// The underlying key. From 296afdcff62a51e9bd0c63a5ad9c76f3162f7141 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 19 Feb 2020 03:46:04 +0100 Subject: [PATCH 033/142] [core] add Pack modular abstraction --- core/src/storage/mod.rs | 2 + core/src/storage/pack.rs | 230 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 core/src/storage/pack.rs diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index af9d678fe4b..dd0a138314c 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -75,6 +75,7 @@ pub mod chunk; mod collections; mod flush; mod lazy; +mod pack; mod traits; mod value; @@ -107,6 +108,7 @@ pub use self::{ LazyCell, LazyChunk, }, + pack::Pack, traits::{ KeyPtr, PullAt, diff --git a/core/src/storage/pack.rs b/core/src/storage/pack.rs new file mode 100644 index 00000000000..4d970d79766 --- /dev/null +++ b/core/src/storage/pack.rs @@ -0,0 +1,230 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + storage::{ + KeyPtr, + PullAt, + PullForward, + PushAt, + PushForward, + StorageSize, + }, +}; +use ink_primitives::Key; + +/// Packs the inner `T` so that it only occupies a single contract stoage cell. +/// +/// # Note +/// +/// This is an important modular building stone in order to manage contract +/// storage occupation. By default types try to distribute themselves onto +/// their respective contract storage area. However, upon packing them into +/// `Pack` they will be compressed to only ever make use of a single +/// contract storage cell. Sometimes this can be advantageous for performance +/// reasons. +/// +/// # Usage +/// +/// - A `Pack` is equivalent to `i32` in its storage occupation. +/// - A `Pack<(i32, i32)>` will occupy a single cell compared to `(i32, i32)` +/// which occupies a cell per `i32`. +/// - A `Lazy>` lazily loads a `Pack<[u8; 8]>` which occupies +/// a single cell whereas a `[u8; 8]` would occupy 8 cells in total - one for +/// each `u8`. +/// - Rust collections will never use more than a single cell. So +/// `Pack>` and `LinkedList` will occupy the same amount of +/// cells, namely 1. +/// - Packs can be packed. So for example a +/// `Pack<(Pack<(i32, i32)>, Pack<[u8; 8]>)` uses just one cell instead of +/// two cells which is the case for `(Pack<(i32, i32)>, Pack<[u8; 8]>)`. +/// +/// As a general advice pack values together that are frequently used together. +/// Also pack many very small elements (e.g. `u8`, `bool`, `u16`) together. +#[derive(Debug, scale::Encode, scale::Decode)] +pub struct Pack { + /// The packed `T` value. + inner: T, +} + +impl Pack { + /// Creates a new packed value. + pub fn new(value: T) -> Self { + Self { inner: value } + } + + /// Returns a shared reference to the packed value. + fn get(&self) -> &T { + &self.inner + } + + /// Returns an exclusive reference to the packed value. + fn get_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl StorageSize for Pack { + const SIZE: u64 = 1; +} + +impl PullForward for Pack +where + T: PullAt, +{ + fn pull_forward(ptr: &mut KeyPtr) -> Self { + ::pull_at(ptr.next_for::()) + } +} + +impl PullAt for Pack +where + T: PullAt, +{ + fn pull_at(at: Key) -> Self { + Self { + inner: ::pull_at(at), + } + } +} + +impl PushForward for Pack +where + T: PushAt, +{ + fn push_forward(&self, ptr: &mut KeyPtr) { + ::push_at(self, ptr.next_for::()) + } +} + +impl PushAt for Pack +where + T: PushAt, +{ + fn push_at(&self, at: Key) { + ::push_at(self.get(), at) + } +} + +impl From for Pack { + fn from(value: T) -> Self { + Self::new(value) + } +} + +impl Default for Pack +where + T: Default, +{ + fn default() -> Self { + Self::new(Default::default()) + } +} + +impl core::ops::Deref for Pack { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl core::ops::DerefMut for Pack { + fn deref_mut(&mut self) -> &mut Self::Target { + self.get_mut() + } +} + +impl core::cmp::PartialEq for Pack +where + T: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + PartialEq::eq(self.get(), other.get()) + } +} + +impl core::cmp::Eq for Pack where T: Eq {} + +impl core::cmp::PartialOrd for Pack +where + T: PartialOrd, +{ + fn partial_cmp(&self, other: &Self) -> Option { + PartialOrd::partial_cmp(self.get(), other.get()) + } + fn lt(&self, other: &Self) -> bool { + PartialOrd::lt(self.get(), other.get()) + } + fn le(&self, other: &Self) -> bool { + PartialOrd::le(self.get(), other.get()) + } + fn ge(&self, other: &Self) -> bool { + PartialOrd::ge(self.get(), other.get()) + } + fn gt(&self, other: &Self) -> bool { + PartialOrd::gt(self.get(), other.get()) + } +} + +impl core::cmp::Ord for Pack +where + T: core::cmp::Ord, +{ + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + Ord::cmp(self.get(), other.get()) + } +} + +impl core::fmt::Display for Pack +where + T: core::fmt::Display, +{ + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Display::fmt(self.get(), f) + } +} + +impl core::hash::Hash for Pack +where + T: core::hash::Hash, +{ + fn hash(&self, state: &mut H) { + self.get().hash(state); + } +} + +impl core::convert::AsRef for Pack { + fn as_ref(&self) -> &T { + self.get() + } +} + +impl core::convert::AsMut for Pack { + fn as_mut(&mut self) -> &mut T { + self.get_mut() + } +} + +impl ink_prelude::borrow::Borrow for Pack { + fn borrow(&self) -> &T { + self.get() + } +} + +impl ink_prelude::borrow::BorrowMut for Pack { + fn borrow_mut(&mut self) -> &mut T { + self.get_mut() + } +} From bbbe99d26f9383d7c84b398fa9a2d7134b1b7686 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 19 Feb 2020 03:52:21 +0100 Subject: [PATCH 034/142] [core] extend Pack docs --- core/src/storage/pack.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/storage/pack.rs b/core/src/storage/pack.rs index 4d970d79766..3898e708dbf 100644 --- a/core/src/storage/pack.rs +++ b/core/src/storage/pack.rs @@ -49,6 +49,9 @@ use ink_primitives::Key; /// - Packs can be packed. So for example a /// `Pack<(Pack<(i32, i32)>, Pack<[u8; 8]>)` uses just one cell instead of /// two cells which is the case for `(Pack<(i32, i32)>, Pack<[u8; 8]>)`. +/// - Not all `storage` types can be packed. Only those that are implementing +/// `PullAt` and `PushAt`. For example `storage::Vec` does not implement +/// those trait and thus cannot be packed. /// /// As a general advice pack values together that are frequently used together. /// Also pack many very small elements (e.g. `u8`, `bool`, `u16`) together. From 0aa47adad4739dcafd503b35bdb4e86aa7b7675b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 19 Feb 2020 03:59:14 +0100 Subject: [PATCH 035/142] [core] add Copy and Clone derive to Pack --- core/src/storage/pack.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/storage/pack.rs b/core/src/storage/pack.rs index 3898e708dbf..6a363527194 100644 --- a/core/src/storage/pack.rs +++ b/core/src/storage/pack.rs @@ -55,7 +55,7 @@ use ink_primitives::Key; /// /// As a general advice pack values together that are frequently used together. /// Also pack many very small elements (e.g. `u8`, `bool`, `u16`) together. -#[derive(Debug, scale::Encode, scale::Decode)] +#[derive(Debug, Copy, Clone, scale::Encode, scale::Decode)] pub struct Pack { /// The packed `T` value. inner: T, From df60facfe5d9fcbaca7b903767be78e5a1ccacd5 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 19 Feb 2020 14:51:49 +0100 Subject: [PATCH 036/142] [core] put lazy.rs submodule into mod.rs --- core/src/storage/lazy/lazy.rs | 242 ---------------------------------- core/src/storage/lazy/mod.rs | 229 +++++++++++++++++++++++++++++++- 2 files changed, 227 insertions(+), 244 deletions(-) delete mode 100644 core/src/storage/lazy/lazy.rs diff --git a/core/src/storage/lazy/lazy.rs b/core/src/storage/lazy/lazy.rs deleted file mode 100644 index a10921925ec..00000000000 --- a/core/src/storage/lazy/lazy.rs +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::super::{ - KeyPtr, - LazyCell, - PullForward, - PushForward, - StorageSize, -}; -use ink_primitives::Key; - -/// A lazy storage entity. -/// -/// This loads its value from storage upon first use. -/// -/// # Note -/// -/// Use this if the storage field doesn't need to be loaded in some or most cases. -#[derive(Debug)] -pub struct Lazy { - cell: LazyCell, -} - -impl StorageSize for Lazy -where - T: StorageSize, -{ - const SIZE: u64 = as StorageSize>::SIZE; -} - -impl PullForward for Lazy -where - T: StorageSize, -{ - fn pull_forward(ptr: &mut KeyPtr) -> Self { - Self { - cell: as PullForward>::pull_forward(ptr) - } - } -} - -impl PushForward for Lazy -where - T: PushForward, -{ - fn push_forward(&self, ptr: &mut KeyPtr) { - as PushForward>::push_forward(&self.cell, ptr) - } -} - -impl Lazy { - /// Creates an eagerly populated lazy storage value. - #[must_use] - pub fn new(value: T) -> Self { - Self { - cell: LazyCell::new(Some(value)), - } - } - - /// Creates a true lazy storage value for the given key. - #[must_use] - pub fn lazy(key: Key) -> Self { - Self { - cell: LazyCell::lazy(key), - } - } -} - -impl Lazy -where - T: scale::Decode, -{ - /// Returns a shared reference to the lazily loaded value. - /// - /// # Note - /// - /// This loads the value from the contract storage if this did not happed before. - /// - /// # Panics - /// - /// If loading from contract storage failed. - #[must_use] - pub fn get(&self) -> &T { - self.cell.get().expect("expected Some value") - } - - /// Returns an exclusive reference to the lazily loaded value. - /// - /// # Note - /// - /// This loads the value from the contract storage if this did not happed before. - /// - /// # Panics - /// - /// If loading from contract storage failed. - #[must_use] - pub fn get_mut(&mut self) -> &mut T { - self.cell.get_mut().expect("expected Some value") - } -} - -impl From for Lazy { - fn from(value: T) -> Self { - Self::new(value) - } -} - -impl Default for Lazy -where - T: Default, -{ - fn default() -> Self { - Self::new(Default::default()) - } -} - -impl core::cmp::PartialEq for Lazy -where - T: PartialEq + scale::Decode, -{ - fn eq(&self, other: &Self) -> bool { - PartialEq::eq(self.get(), other.get()) - } -} - -impl core::cmp::Eq for Lazy where T: Eq + scale::Decode {} - -impl core::cmp::PartialOrd for Lazy -where - T: PartialOrd + scale::Decode, -{ - fn partial_cmp(&self, other: &Self) -> Option { - PartialOrd::partial_cmp(self.get(), other.get()) - } - fn lt(&self, other: &Self) -> bool { - PartialOrd::lt(self.get(), other.get()) - } - fn le(&self, other: &Self) -> bool { - PartialOrd::le(self.get(), other.get()) - } - fn ge(&self, other: &Self) -> bool { - PartialOrd::ge(self.get(), other.get()) - } - fn gt(&self, other: &Self) -> bool { - PartialOrd::gt(self.get(), other.get()) - } -} - -impl core::cmp::Ord for Lazy -where - T: core::cmp::Ord + scale::Decode, -{ - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - Ord::cmp(self.get(), other.get()) - } -} - -impl core::fmt::Display for Lazy -where - T: scale::Decode + core::fmt::Display, -{ - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - core::fmt::Display::fmt(self.get(), f) - } -} - -impl core::hash::Hash for Lazy -where - T: core::hash::Hash + scale::Decode, -{ - fn hash(&self, state: &mut H) { - self.get().hash(state); - } -} - -impl core::convert::AsRef for Lazy -where - T: scale::Decode, -{ - fn as_ref(&self) -> &T { - self.get() - } -} - -impl core::convert::AsMut for Lazy -where - T: scale::Decode, -{ - fn as_mut(&mut self) -> &mut T { - self.get_mut() - } -} - -impl ink_prelude::borrow::Borrow for Lazy -where - T: scale::Decode, -{ - fn borrow(&self) -> &T { - self.get() - } -} - -impl ink_prelude::borrow::BorrowMut for Lazy -where - T: scale::Decode, -{ - fn borrow_mut(&mut self) -> &mut T { - self.get_mut() - } -} - -impl core::ops::Deref for Lazy -where - T: scale::Decode, -{ - type Target = T; - - fn deref(&self) -> &Self::Target { - self.get() - } -} - -impl core::ops::DerefMut for Lazy -where - T: scale::Decode, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - self.get_mut() - } -} diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index e15eb03dddb..360a713d651 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -12,12 +12,237 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod lazy; mod lazy_cell; mod lazy_chunk; pub use self::{ - lazy::Lazy, lazy_cell::LazyCell, lazy_chunk::LazyChunk, }; +use super::{ + KeyPtr, + PullForward, + PushForward, + StorageSize, +}; +use ink_primitives::Key; + +/// A lazy storage entity. +/// +/// This loads its value from storage upon first use. +/// +/// # Note +/// +/// Use this if the storage field doesn't need to be loaded in some or most cases. +#[derive(Debug)] +pub struct Lazy { + cell: LazyCell, +} + +impl StorageSize for Lazy +where + T: StorageSize, +{ + const SIZE: u64 = as StorageSize>::SIZE; +} + +impl PullForward for Lazy +where + T: StorageSize, +{ + fn pull_forward(ptr: &mut KeyPtr) -> Self { + Self { + cell: as PullForward>::pull_forward(ptr), + } + } +} + +impl PushForward for Lazy +where + T: PushForward, +{ + fn push_forward(&self, ptr: &mut KeyPtr) { + as PushForward>::push_forward(&self.cell, ptr) + } +} + +impl Lazy { + /// Creates an eagerly populated lazy storage value. + #[must_use] + pub fn new(value: T) -> Self { + Self { + cell: LazyCell::new(Some(value)), + } + } + + /// Creates a true lazy storage value for the given key. + #[must_use] + pub fn lazy(key: Key) -> Self { + Self { + cell: LazyCell::lazy(key), + } + } +} + +impl Lazy +where + T: scale::Decode, +{ + /// Returns a shared reference to the lazily loaded value. + /// + /// # Note + /// + /// This loads the value from the contract storage if this did not happed before. + /// + /// # Panics + /// + /// If loading from contract storage failed. + #[must_use] + pub fn get(&self) -> &T { + self.cell.get().expect("expected Some value") + } + + /// Returns an exclusive reference to the lazily loaded value. + /// + /// # Note + /// + /// This loads the value from the contract storage if this did not happed before. + /// + /// # Panics + /// + /// If loading from contract storage failed. + #[must_use] + pub fn get_mut(&mut self) -> &mut T { + self.cell.get_mut().expect("expected Some value") + } +} + +impl From for Lazy { + fn from(value: T) -> Self { + Self::new(value) + } +} + +impl Default for Lazy +where + T: Default, +{ + fn default() -> Self { + Self::new(Default::default()) + } +} + +impl core::cmp::PartialEq for Lazy +where + T: PartialEq + scale::Decode, +{ + fn eq(&self, other: &Self) -> bool { + PartialEq::eq(self.get(), other.get()) + } +} + +impl core::cmp::Eq for Lazy where T: Eq + scale::Decode {} + +impl core::cmp::PartialOrd for Lazy +where + T: PartialOrd + scale::Decode, +{ + fn partial_cmp(&self, other: &Self) -> Option { + PartialOrd::partial_cmp(self.get(), other.get()) + } + fn lt(&self, other: &Self) -> bool { + PartialOrd::lt(self.get(), other.get()) + } + fn le(&self, other: &Self) -> bool { + PartialOrd::le(self.get(), other.get()) + } + fn ge(&self, other: &Self) -> bool { + PartialOrd::ge(self.get(), other.get()) + } + fn gt(&self, other: &Self) -> bool { + PartialOrd::gt(self.get(), other.get()) + } +} + +impl core::cmp::Ord for Lazy +where + T: core::cmp::Ord + scale::Decode, +{ + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + Ord::cmp(self.get(), other.get()) + } +} + +impl core::fmt::Display for Lazy +where + T: scale::Decode + core::fmt::Display, +{ + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Display::fmt(self.get(), f) + } +} + +impl core::hash::Hash for Lazy +where + T: core::hash::Hash + scale::Decode, +{ + fn hash(&self, state: &mut H) { + self.get().hash(state); + } +} + +impl core::convert::AsRef for Lazy +where + T: scale::Decode, +{ + fn as_ref(&self) -> &T { + self.get() + } +} + +impl core::convert::AsMut for Lazy +where + T: scale::Decode, +{ + fn as_mut(&mut self) -> &mut T { + self.get_mut() + } +} + +impl ink_prelude::borrow::Borrow for Lazy +where + T: scale::Decode, +{ + fn borrow(&self) -> &T { + self.get() + } +} + +impl ink_prelude::borrow::BorrowMut for Lazy +where + T: scale::Decode, +{ + fn borrow_mut(&mut self) -> &mut T { + self.get_mut() + } +} + +impl core::ops::Deref for Lazy +where + T: scale::Decode, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl core::ops::DerefMut for Lazy +where + T: scale::Decode, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.get_mut() + } +} From 01ce0498163d989d8c0881b147961a3cb151aee2 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 19 Feb 2020 14:52:03 +0100 Subject: [PATCH 037/142] [core] fix clippy warning --- core/src/storage/traits/pull.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/core/src/storage/traits/pull.rs b/core/src/storage/traits/pull.rs index 454122b01cf..607d6bac711 100644 --- a/core/src/storage/traits/pull.rs +++ b/core/src/storage/traits/pull.rs @@ -141,15 +141,11 @@ impl_pull_tuple!(A, B, C, D, E, F, G, H, I); impl_pull_tuple!(A, B, C, D, E, F, G, H, I, J); impl PullForward for () { - fn pull_forward(_ptr: &mut KeyPtr) -> Self { - () - } + fn pull_forward(_ptr: &mut KeyPtr) -> Self {} } impl PullAt for () { - fn pull_at(_at: Key) -> Self { - () - } + fn pull_at(_at: Key) -> Self {} } impl PullForward for PhantomData { From 2d9b5e974809f7962ee91f5d3c7df9aba0a3ad0a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 19 Feb 2020 14:52:21 +0100 Subject: [PATCH 038/142] [core] apply rustfmt --- core/src/storage/lazy/lazy_cell.rs | 4 +--- core/src/storage/pack.rs | 16 +++++++--------- core/src/storage/traits/pull.rs | 20 ++++++-------------- core/src/storage/traits/push.rs | 20 ++++++-------------- 4 files changed, 20 insertions(+), 40 deletions(-) diff --git a/core/src/storage/lazy/lazy_cell.rs b/core/src/storage/lazy/lazy_cell.rs index d702a7f7bae..ebcde43c305 100644 --- a/core/src/storage/lazy/lazy_cell.rs +++ b/core/src/storage/lazy/lazy_cell.rs @@ -59,9 +59,7 @@ pub struct OccupiedEntry { impl OccupiedEntry { /// Creates a new eager lazy storage entity with the given value. pub fn new(value: Option) -> Self { - Self { - value, - } + Self { value } } } diff --git a/core/src/storage/pack.rs b/core/src/storage/pack.rs index 6a363527194..30f931ddaae 100644 --- a/core/src/storage/pack.rs +++ b/core/src/storage/pack.rs @@ -12,15 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - storage::{ - KeyPtr, - PullAt, - PullForward, - PushAt, - PushForward, - StorageSize, - }, +use crate::storage::{ + KeyPtr, + PullAt, + PullForward, + PushAt, + PushForward, + StorageSize, }; use ink_primitives::Key; diff --git a/core/src/storage/traits/pull.rs b/core/src/storage/traits/pull.rs index 607d6bac711..193152972de 100644 --- a/core/src/storage/traits/pull.rs +++ b/core/src/storage/traits/pull.rs @@ -238,13 +238,11 @@ where } const _: () = { - use ink_prelude::{ - collections::{ - BTreeSet, - BinaryHeap, - LinkedList, - VecDeque, - }, + use ink_prelude::collections::{ + BTreeSet, + BinaryHeap, + LinkedList, + VecDeque, }; #[cfg(not(feature = "std"))] use ink_prelude::vec::Vec; @@ -263,13 +261,7 @@ const _: () = { )* }; } - impl_pull_at_for_collection!( - Vec, - BTreeSet, - BinaryHeap, - LinkedList, - VecDeque, - ); + impl_pull_at_for_collection!(Vec, BTreeSet, BinaryHeap, LinkedList, VecDeque,); }; impl PullAt for ink_prelude::collections::BTreeMap diff --git a/core/src/storage/traits/push.rs b/core/src/storage/traits/push.rs index c03b2f4b04d..bb775233bec 100644 --- a/core/src/storage/traits/push.rs +++ b/core/src/storage/traits/push.rs @@ -268,13 +268,11 @@ where } const _: () = { - use ink_prelude::{ - collections::{ - BTreeSet, - BinaryHeap, - LinkedList, - VecDeque, - }, + use ink_prelude::collections::{ + BTreeSet, + BinaryHeap, + LinkedList, + VecDeque, }; #[cfg(not(feature = "std"))] use ink_prelude::vec::Vec; @@ -293,13 +291,7 @@ const _: () = { )* }; } - impl_push_at_for_collection!( - Vec, - BTreeSet, - BinaryHeap, - LinkedList, - VecDeque, - ); + impl_push_at_for_collection!(Vec, BTreeSet, BinaryHeap, LinkedList, VecDeque,); }; impl PushAt for ink_prelude::collections::BTreeMap From a2a0d3b753cb4249e8ee3e43c5b70aa5425eaa31 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 19 Feb 2020 17:17:07 +0100 Subject: [PATCH 039/142] [core] add new storage::Vec that builds on new traits --- core/src/storage/collections2/mod.rs | 15 ++ core/src/storage/collections2/vec/iter.rs | 76 +++++++++ core/src/storage/collections2/vec/mod.rs | 163 ++++++++++++++++++++ core/src/storage/collections2/vec/traits.rs | 75 +++++++++ core/src/storage/mod.rs | 4 + 5 files changed, 333 insertions(+) create mode 100644 core/src/storage/collections2/mod.rs create mode 100644 core/src/storage/collections2/vec/iter.rs create mode 100644 core/src/storage/collections2/vec/mod.rs create mode 100644 core/src/storage/collections2/vec/traits.rs diff --git a/core/src/storage/collections2/mod.rs b/core/src/storage/collections2/mod.rs new file mode 100644 index 00000000000..c0cf909715d --- /dev/null +++ b/core/src/storage/collections2/mod.rs @@ -0,0 +1,15 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub mod vec; diff --git a/core/src/storage/collections2/vec/iter.rs b/core/src/storage/collections2/vec/iter.rs new file mode 100644 index 00000000000..7398adf6b0f --- /dev/null +++ b/core/src/storage/collections2/vec/iter.rs @@ -0,0 +1,76 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::storage; + +/// An iterator over the values of a storage `Vec`. +#[derive(Debug)] +pub struct Iter<'a, T> { + /// The storage vector to iterate over. + vec: &'a storage::Vec2, + /// The current begin of the iteration. + begin: u32, + /// The current end of the iteration. + end: u32, +} + +impl<'a, T> Iter<'a, T> { + /// Creates a new iterator for the given storage vector. + pub(crate) fn new(vec: &'a storage::Vec2) -> Self { + Self { + vec, + begin: 0, + end: vec.len(), + } + } +} + +impl<'a, T> Iterator for Iter<'a, T> +where + T: scale::Codec, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + let cur = self.begin; + self.begin += 1; + self.vec.get(cur) + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = (self.end - self.begin) as usize; + (remaining, Some(remaining)) + } +} + +impl<'a, T> ExactSizeIterator for Iter<'a, T> where T: scale::Codec {} + +impl<'a, T> DoubleEndedIterator for Iter<'a, T> +where + T: scale::Codec, +{ + fn next_back(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + debug_assert_ne!(self.end, 0); + self.end -= 1; + self.vec.get(self.end) + } +} diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs new file mode 100644 index 00000000000..0a8ede000da --- /dev/null +++ b/core/src/storage/collections2/vec/mod.rs @@ -0,0 +1,163 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod iter; +mod traits; + +pub use self::iter::Iter; +use crate::storage; + +/// A contiguous growable array type, written `Vec` but pronounced 'vector'. +/// +/// # Note +/// +/// Despite the similarity to Rust's `Vec` type this storage `Vec` has many +/// differences in its internal data layout. While it stores its data in contiguous +/// storage slots this does not mean that the data is actually densely stored +/// in memory. +/// +/// Also its technical performance characteristics may be different from Rust's +/// `Vec` due to the differences stated above. +/// +/// Allows to store up to `2^32` elements and is guaranteed to not reallocate +/// upon pushing new elements to it. +#[derive(Debug)] +pub struct Vec { + /// The length of the vector. + len: storage::Lazy, + /// The synchronized cells to operate on the contract storage. + elems: storage::LazyChunk, +} + +impl Vec { + /// Creates a new empty storage vector. + pub fn new() -> Self { + Self { + len: storage::Lazy::new(0), + elems: storage::LazyChunk::new(), + } + } + + /// Returns the number of elements in the vector, also referred to as its 'length'. + pub fn len(&self) -> u32 { + *self.len.get() + } + + /// Returns `true` if the vector contains no elements. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl Vec +where + T: scale::Codec, +{ + /// Appends an element to the back of the vector. + pub fn push(&mut self, value: T) { + assert!( + self.len() < core::u32::MAX, + "cannot push more elements into the storage vector" + ); + let last_index = self.len(); + *self.len += 1; + self.elems.put(last_index, Some(value)); + } + + /// Pops the last element from the vector and returns it. + // + /// Returns `None` if the vector is empty. + pub fn pop(&mut self) -> Option { + if self.is_empty() { + return None + } + let last_index = self.len() - 1; + *self.len = last_index; + self.elems.take(last_index) + } + + /// Returns an iterator over the references of all elements stored in the vector. + /// + /// # Note + /// + /// - It is **not** recommended to iterate over all elements of a storage vector. + /// - Try to avoid this if possible or iterate only over a minimal subset of + /// all elements using e.g. `Iterator::take(n)`. + pub fn iter(&self) -> Iter { + Iter::new(self) + } + + /// Returns the index if it is witihn bounds or `None` otherwise. + fn within_bounds(&self, index: u32) -> Option { + if index < self.len() { + return Some(index) + } + None + } + + /// Returns a shared reference to the indexed element. + /// + /// Returns `None` if `index` is out of bounds. + pub fn get(&self, index: u32) -> Option<&T> { + self.within_bounds(index) + .and_then(|index| self.elems.get(index)) + } + + /// Returns an exclusive reference to the indexed element. + /// + /// Returns `None` if `index` is out of bounds. + pub fn get_mut(&mut self, index: u32) -> Option<&mut T> { + self.within_bounds(index) + .and_then(move |index| self.elems.get_mut(index)) + } + + /// Replaces the element at the given index and returns the old value. + /// + /// Returns `None` if `n` is out of bounds. + pub fn replace(&mut self, index: u32, f: F) -> Option + where + F: FnOnce() -> T, + { + self.within_bounds(index).map(|index| { + self.elems + .put_get(index, Some(f())) + .expect("expected an actual element since access is within bounds") + }) + } + + /// Swaps the elements at the given indices. + /// + /// # Panics + /// + /// If one or both indices are out of bounds. + pub fn swap(&mut self, a: u32, b: u32) { + self.elems.swap(a, b) + } + + /// Removes the indexed element from the vector and returns it. + /// + /// The last element of the vector is put into the indexed slot. + /// Returns `None` and does not mutate the vector if the index is out of bounds. + /// + /// # Note + /// + /// This operation does not preserve ordering but is constant time. + pub fn swap_remove(&mut self, n: u32) -> Option { + if self.is_empty() { + return None + } + self.elems.swap(n, self.len() - 1); + self.pop() + } +} diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs new file mode 100644 index 00000000000..e0f943a70ab --- /dev/null +++ b/core/src/storage/collections2/vec/traits.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + storage, + storage::{ + KeyPtr, + PullForward, + PushForward, + StorageSize, + }, +}; + +impl core::ops::Index for storage::Vec2 +where + T: scale::Codec, +{ + type Output = T; + + fn index(&self, index: u32) -> &Self::Output { + self.get(index) + .expect("index out of bounds") + } +} + +impl core::ops::IndexMut for storage::Vec2 +where + T: scale::Codec, +{ + fn index_mut(&mut self, index: u32) -> &mut Self::Output { + self.get_mut(index) + .expect("index out of bounds") + } +} + +impl StorageSize for storage::Vec2 +where + T: StorageSize, +{ + const SIZE: u64 = + ::SIZE + as StorageSize>::SIZE; +} + +impl PullForward for storage::Vec2 +where + T: StorageSize, +{ + fn pull_forward(ptr: &mut KeyPtr) -> Self { + Self { + len: PullForward::pull_forward(ptr), + elems: PullForward::pull_forward(ptr), + } + } +} + +impl PushForward for storage::Vec2 +where + storage::LazyChunk: PushForward, +{ + fn push_forward(&self, ptr: &mut KeyPtr) { + PushForward::push_forward(&self.len(), ptr); + PushForward::push_forward(&self.elems, ptr); + } +} diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index dd0a138314c..cd74dc85758 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -73,6 +73,7 @@ pub mod alloc; pub mod cell; pub mod chunk; mod collections; +mod collections2; mod flush; mod lazy; mod pack; @@ -102,6 +103,9 @@ pub use self::{ Vec, }, }, + collections2::{ + vec::Vec as Vec2, + }, flush::Flush, lazy::{ Lazy, From eb12f8aca64cf861384ec7f28d81f454b4c0c5b8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 19 Feb 2020 17:44:43 +0100 Subject: [PATCH 040/142] [core] make LazyCell use new traits instead of relying on raw Encode/Decode --- core/src/storage/lazy/lazy_cell.rs | 7 +++---- core/src/storage/lazy/mod.rs | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/core/src/storage/lazy/lazy_cell.rs b/core/src/storage/lazy/lazy_cell.rs index ebcde43c305..608a501a0c2 100644 --- a/core/src/storage/lazy/lazy_cell.rs +++ b/core/src/storage/lazy/lazy_cell.rs @@ -166,7 +166,7 @@ impl LazyCell { impl LazyCell where - T: scale::Decode, + T: StorageSize + PullForward, { /// Loads the value lazily from contract storage. /// @@ -185,9 +185,8 @@ where // This way we do not invalidate pointers to this value. let kind = unsafe { &mut *self.kind.get() }; if let LazyCellEntry::Vacant(vacant) = kind { - let value = crate::env::get_contract_storage::(vacant.key).map(|some| { - some.expect("couldn't properly decode contract storage entry") - }); + let value = + as PullForward>::pull_forward(&mut KeyPtr::from(vacant.key)); *kind = LazyCellEntry::Occupied(OccupiedEntry::new(value)); } } diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index 360a713d651..448180907e1 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -86,7 +86,7 @@ impl Lazy { impl Lazy where - T: scale::Decode, + T: StorageSize + PullForward, { /// Returns a shared reference to the lazily loaded value. /// @@ -134,18 +134,18 @@ where impl core::cmp::PartialEq for Lazy where - T: PartialEq + scale::Decode, + T: PartialEq + StorageSize + PullForward, { fn eq(&self, other: &Self) -> bool { PartialEq::eq(self.get(), other.get()) } } -impl core::cmp::Eq for Lazy where T: Eq + scale::Decode {} +impl core::cmp::Eq for Lazy where T: Eq + StorageSize + PullForward {} impl core::cmp::PartialOrd for Lazy where - T: PartialOrd + scale::Decode, + T: PartialOrd + StorageSize + PullForward, { fn partial_cmp(&self, other: &Self) -> Option { PartialOrd::partial_cmp(self.get(), other.get()) @@ -166,7 +166,7 @@ where impl core::cmp::Ord for Lazy where - T: core::cmp::Ord + scale::Decode, + T: core::cmp::Ord + StorageSize + PullForward, { fn cmp(&self, other: &Self) -> core::cmp::Ordering { Ord::cmp(self.get(), other.get()) @@ -175,7 +175,7 @@ where impl core::fmt::Display for Lazy where - T: scale::Decode + core::fmt::Display, + T: core::fmt::Display + StorageSize + PullForward, { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { core::fmt::Display::fmt(self.get(), f) @@ -184,7 +184,7 @@ where impl core::hash::Hash for Lazy where - T: core::hash::Hash + scale::Decode, + T: core::hash::Hash + StorageSize + PullForward, { fn hash(&self, state: &mut H) { self.get().hash(state); @@ -193,7 +193,7 @@ where impl core::convert::AsRef for Lazy where - T: scale::Decode, + T: StorageSize + PullForward, { fn as_ref(&self) -> &T { self.get() @@ -202,7 +202,7 @@ where impl core::convert::AsMut for Lazy where - T: scale::Decode, + T: StorageSize + PullForward, { fn as_mut(&mut self) -> &mut T { self.get_mut() @@ -211,7 +211,7 @@ where impl ink_prelude::borrow::Borrow for Lazy where - T: scale::Decode, + T: StorageSize + PullForward, { fn borrow(&self) -> &T { self.get() @@ -220,7 +220,7 @@ where impl ink_prelude::borrow::BorrowMut for Lazy where - T: scale::Decode, + T: StorageSize + PullForward, { fn borrow_mut(&mut self) -> &mut T { self.get_mut() @@ -229,7 +229,7 @@ where impl core::ops::Deref for Lazy where - T: scale::Decode, + T: StorageSize + PullForward, { type Target = T; @@ -240,7 +240,7 @@ where impl core::ops::DerefMut for Lazy where - T: scale::Decode, + T: StorageSize + PullForward, { fn deref_mut(&mut self) -> &mut Self::Target { self.get_mut() From 09bbd66b86e105edc4d1319ca4fefd0e2799adcb Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 24 Feb 2020 18:32:03 +0100 Subject: [PATCH 041/142] [core] make LazyChunk::lazily_load unsafe --- core/src/storage/lazy/lazy_chunk.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/storage/lazy/lazy_chunk.rs b/core/src/storage/lazy/lazy_chunk.rs index abd90e3bb9c..7ff0052fde3 100644 --- a/core/src/storage/lazy/lazy_chunk.rs +++ b/core/src/storage/lazy/lazy_chunk.rs @@ -219,7 +219,14 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the lazy chunk is not in a state that allows lazy loading. - fn lazily_load(&self, index: Index) -> *mut Entry { + /// + /// # Safety + /// + /// This is an `unsafe` operation because it has a `&self` receiver but returns + /// a `*mut Entry` pointer that allows for exclusive access. This is safe + /// within internal use only and should never be given outside of the lazy + /// entity for public `&self` methods. + unsafe fn lazily_load(&self, index: Index) -> *mut Entry { let key = match self.key { Some(key) => key, None => panic!("cannot load lazily in this state"), @@ -233,6 +240,7 @@ where // By returning a raw pointer we enforce an `unsafe` block at // the caller site to underline that guarantees are given by the // caller. + #[allow(unused_unsafe)] let cached_entries = unsafe { &mut *self.cached_entries.get() }; use ink_prelude::collections::btree_map::Entry as BTreeMapEntry; match cached_entries.entry(index) { From 34ca2053188b8911680604c3ddb538b995fce2fc Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Mon, 24 Feb 2020 18:35:47 +0100 Subject: [PATCH 042/142] fix license header year MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Michael Müller --- core/src/storage/lazy/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index 448180907e1..8c3e9ac230e 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// Copyright 2018-2020 Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From ce0345037282d3a2ba5906d89697a540f6aa9288 Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Mon, 24 Feb 2020 18:36:27 +0100 Subject: [PATCH 043/142] fix doc comment typo Co-Authored-By: Andrew Jones --- core/src/storage/lazy/lazy_chunk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/storage/lazy/lazy_chunk.rs b/core/src/storage/lazy/lazy_chunk.rs index abd90e3bb9c..f5aa18b3685 100644 --- a/core/src/storage/lazy/lazy_chunk.rs +++ b/core/src/storage/lazy/lazy_chunk.rs @@ -267,7 +267,7 @@ where // SAFETY: Dereferencing the raw-pointer here is safe since we // encapsulated this whole call with a `&mut self` receiver. // Also `Entry` instances are all pinned, so mutating the - // `BTreeMap` that stores tham won't invalidate references + // `BTreeMap` that stores them won't invalidate references // to them. unsafe { &mut *self.lazily_load(index) } } From 087ff778f0fe7d81ede99a5bf341d93d2334aa58 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 27 Feb 2020 19:35:38 +0100 Subject: [PATCH 044/142] [core] implement LazyChunk::put --- core/src/storage/lazy/lazy_chunk.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/src/storage/lazy/lazy_chunk.rs b/core/src/storage/lazy/lazy_chunk.rs index 7ff0052fde3..110c15c1096 100644 --- a/core/src/storage/lazy/lazy_chunk.rs +++ b/core/src/storage/lazy/lazy_chunk.rs @@ -335,8 +335,14 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the decoding of the old element at the given index failed. - pub fn put(&mut self, _index: Index, _new_value: Option) { - todo!() + pub fn put(&mut self, index: Index, new_value: Option) { + self.entries_mut().insert( + index, + Box::new(Entry { + value: new_value, + mutated: core::cell::Cell::new(true), + }), + ); } } From 1d05feceb87a9e4481c7814ee445937e6ea5dfe9 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 27 Feb 2020 19:36:04 +0100 Subject: [PATCH 045/142] [core] update ToDo comment --- core/src/storage/traits/push.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/storage/traits/push.rs b/core/src/storage/traits/push.rs index bb775233bec..a39a705dbbf 100644 --- a/core/src/storage/traits/push.rs +++ b/core/src/storage/traits/push.rs @@ -172,11 +172,11 @@ where if ::SIZE > 32 { return } - // In theory we'd need to clear all subfields of the `Option` - // which are technically `::SIZE` many. - // However, this could be a performance hazard so we should - // really not do that. We should add a check to disable this - // implementation for `StorageSize` of more than 32. (TODO) + // # ToDo + // + // Create a trait bound onto something like + // `ClearForward` and `ClearAt` that have a sole purpose of + // clearing the underlying storage of a storage entity. for n in 0..::SIZE { env::clear_contract_storage(pos0 + n); } From 943c67ba36ce8fbb869077a8348a8c4a56ffdfc5 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 27 Feb 2020 19:36:25 +0100 Subject: [PATCH 046/142] [core] update LazyChunk to make use of new traits instead of directly using scale --- core/src/storage/lazy/lazy_chunk.rs | 53 ++++++++++++----------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/core/src/storage/lazy/lazy_chunk.rs b/core/src/storage/lazy/lazy_chunk.rs index 110c15c1096..8acf02ec549 100644 --- a/core/src/storage/lazy/lazy_chunk.rs +++ b/core/src/storage/lazy/lazy_chunk.rs @@ -56,7 +56,9 @@ pub struct LazyChunk { /// The map for the contract storage entries. /// -/// We keep the whole entry in a `Pin>` in order to prevent pointer +/// # Note +/// +/// We keep the whole entry in a `Box` in order to prevent pointer /// invalidation upon updating the cache through `&self` methods as in /// [`LazyChunk::get`]. pub type EntryMap = BTreeMap>>; @@ -78,20 +80,9 @@ where fn push_forward(&self, ptr: &mut KeyPtr) { // Reset the mutated entry flag because we just synced. self.mutated.set(false); - match self.value() { - Some(value) => { - // Forward push to the inner value on the computed key. - ::push_forward(value, ptr); - } - None => { - let mut offset = ptr.next_for::(); - for _ in 0..::SIZE { - // Clear the storage at the given index. - crate::env::clear_contract_storage(offset); - offset += 1u64; - } - } - } + // Since `self.value` is of type `Option` this will eventually + // clear the underlying storage entry if `self.value` is `None`. + self.value.push_forward(ptr); } } @@ -200,7 +191,7 @@ where impl LazyChunk where - T: scale::Decode, + T: StorageSize + PullForward, { /// Lazily loads the value at the given index. /// @@ -246,16 +237,11 @@ where match cached_entries.entry(index) { BTreeMapEntry::Occupied(occupied) => &mut **occupied.into_mut(), BTreeMapEntry::Vacant(vacant) => { - let value = match crate::env::get_contract_storage::(key + index) { - Some(new_value) => { - Some(new_value.expect("could not decode lazily loaded value")) - } - None => None, - }; - &mut **vacant.insert(Box::new(Entry { - value, - mutated: core::cell::Cell::new(false), - })) + let value = as PullForward>::pull_forward(&mut KeyPtr::from( + key + index, + )); + let mutated = core::cell::Cell::new(false); + &mut **vacant.insert(Box::new(Entry { value, mutated })) } } } @@ -319,11 +305,14 @@ where } } -impl LazyChunk -where - T: scale::Encode, -{ - /// Puts the new value at the given index and returns the old value if any. +impl LazyChunk { + /// Returns an exclusive reference to the cached entry map. + pub fn entries_mut(&mut self) -> &mut EntryMap { + // SAFETY: It is safe to return a `&mut` reference from a `&mut` receiver method. + unsafe { &mut *self.cached_entries.get() } + } + + /// Puts the new value at the given index. /// /// # Note /// @@ -348,7 +337,7 @@ where impl LazyChunk where - T: scale::Codec, + T: StorageSize + PullForward, { /// Puts the new value at the given index and returns the old value if any. /// From 5b37a26cc6904bba5bd60ac3286a18f653e00210 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 27 Feb 2020 19:36:45 +0100 Subject: [PATCH 047/142] [core] adjust new storage::Vec for changes done in storage::LazyChunk --- core/src/storage/collections2/vec/iter.rs | 14 ++++++++++---- core/src/storage/collections2/vec/mod.rs | 10 ++++++++-- core/src/storage/collections2/vec/traits.rs | 4 ++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/core/src/storage/collections2/vec/iter.rs b/core/src/storage/collections2/vec/iter.rs index 7398adf6b0f..7d0e70525b6 100644 --- a/core/src/storage/collections2/vec/iter.rs +++ b/core/src/storage/collections2/vec/iter.rs @@ -12,7 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::storage; +use crate::{ + storage, + storage::{ + PullForward, + StorageSize, + }, +}; /// An iterator over the values of a storage `Vec`. #[derive(Debug)] @@ -38,7 +44,7 @@ impl<'a, T> Iter<'a, T> { impl<'a, T> Iterator for Iter<'a, T> where - T: scale::Codec, + T: StorageSize + PullForward, { type Item = &'a T; @@ -58,11 +64,11 @@ where } } -impl<'a, T> ExactSizeIterator for Iter<'a, T> where T: scale::Codec {} +impl<'a, T> ExactSizeIterator for Iter<'a, T> where T: StorageSize + PullForward {} impl<'a, T> DoubleEndedIterator for Iter<'a, T> where - T: scale::Codec, + T: StorageSize + PullForward, { fn next_back(&mut self) -> Option { debug_assert!(self.begin <= self.end); diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 0a8ede000da..fd745d3c02c 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -16,7 +16,13 @@ mod iter; mod traits; pub use self::iter::Iter; -use crate::storage; +use crate::{ + storage, + storage::{ + PullForward, + StorageSize, + }, +}; /// A contiguous growable array type, written `Vec` but pronounced 'vector'. /// @@ -62,7 +68,7 @@ impl Vec { impl Vec where - T: scale::Codec, + T: StorageSize + PullForward, { /// Appends an element to the back of the vector. pub fn push(&mut self, value: T) { diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index e0f943a70ab..082488b8da6 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -24,7 +24,7 @@ use crate::{ impl core::ops::Index for storage::Vec2 where - T: scale::Codec, + T: StorageSize + PullForward, { type Output = T; @@ -36,7 +36,7 @@ where impl core::ops::IndexMut for storage::Vec2 where - T: scale::Codec, + T: StorageSize + PullForward, { fn index_mut(&mut self, index: u32) -> &mut Self::Output { self.get_mut(index) From f386d447d3631280f3ba414db8d3203814f894c5 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 27 Feb 2020 19:40:10 +0100 Subject: [PATCH 048/142] [core] update SAFETY comment --- core/src/storage/lazy/lazy_chunk.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/storage/lazy/lazy_chunk.rs b/core/src/storage/lazy/lazy_chunk.rs index 6cd960661fa..7508d390e40 100644 --- a/core/src/storage/lazy/lazy_chunk.rs +++ b/core/src/storage/lazy/lazy_chunk.rs @@ -258,11 +258,10 @@ where /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the lazy chunk is not in a state that allows lazy loading. fn lazily_load_mut(&mut self, index: Index) -> &mut Entry { - // SAFETY: Dereferencing the raw-pointer here is safe since we - // encapsulated this whole call with a `&mut self` receiver. - // Also `Entry` instances are all pinned, so mutating the - // `BTreeMap` that stores them won't invalidate references - // to them. + // SAFETY: + // - Returning a `&mut Entry` is safe because entities inside the + // cache are stored within a `Box` to not invalidate references into + // them upon operating on the outer cache. unsafe { &mut *self.lazily_load(index) } } From 4c87978a91c1d297aae47e8ec4e75ae883378b6b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 27 Feb 2020 20:54:51 +0100 Subject: [PATCH 049/142] [core] minor improvements to tuple PushForward, PushAt impl --- core/src/storage/traits/push.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/storage/traits/push.rs b/core/src/storage/traits/push.rs index a39a705dbbf..95974edf1f3 100644 --- a/core/src/storage/traits/push.rs +++ b/core/src/storage/traits/push.rs @@ -78,7 +78,7 @@ macro_rules! impl_push_for_array { // expensive operation that it shouldn't be. // Arrays should generally be used in packed form. for elem in self.iter() { - PushForward::push_forward(elem, ptr) + ::push_forward(elem, ptr) } } } @@ -108,7 +108,7 @@ macro_rules! impl_push_tuple { #[allow(non_snake_case)] let ($($frag),*,) = self; $( - PushForward::push_forward($frag, ptr); + <$frag as PushForward>::push_forward($frag, ptr); )* } } From 305415ecac2bdd65dcedec423dd4ac188362996d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 27 Feb 2020 20:55:31 +0100 Subject: [PATCH 050/142] [core] add ClearForward and ClearAt At this point I am not sure weather we need them. I still add those definitions to make experimentation with them easier. --- core/src/storage/traits/clear.rs | 222 +++++++++++++++++++++++++++++++ core/src/storage/traits/mod.rs | 5 + 2 files changed, 227 insertions(+) create mode 100644 core/src/storage/traits/clear.rs diff --git a/core/src/storage/traits/clear.rs b/core/src/storage/traits/clear.rs new file mode 100644 index 00000000000..8a55fb84519 --- /dev/null +++ b/core/src/storage/traits/clear.rs @@ -0,0 +1,222 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{ + KeyPtr, + StorageSize, +}; +use crate::env; +use core::marker::PhantomData; +use ink_primitives::Key; + +/// Types that implement this trait can clear their state at the associated +/// contract storage in a distributive manner. +/// +/// # Note +/// +/// This tries to distribute all clearing operations of an entity at distinct +/// storage cells. +pub trait ClearForward { + /// Pushes `self` distributed to the associated contract storage. + fn clear_forward(&self, ptr: &mut KeyPtr); +} + +/// Types that implement this trait can clear their state at an associated +/// contract storage cell. +/// +/// # Note +/// +/// This tries to compactly clear the entire entity's fields at a single +/// contract storage cell. +pub trait ClearAt { + /// Clears `self` packed at the contract storage cell determined by `at`. + fn clear_at(&self, at: Key) { + env::clear_contract_storage(at) + } +} + +macro_rules! impl_clear_for_primitive { + ( $($ty:ty),* $(,)? ) => { + $( + impl ClearForward for $ty { + fn clear_forward(&self, ptr: &mut KeyPtr) { + <$ty as ClearAt>::clear_at(self, ptr.next_for::<$ty>()) + } + } + + impl ClearAt for $ty {} + )* + }; +} +impl_clear_for_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + +macro_rules! impl_clear_for_array { + ( $($len:literal),* $(,)? ) => { + $( + impl ClearForward for [T; $len] + where + T: ClearForward, + { + fn clear_forward(&self, ptr: &mut KeyPtr) { + for elem in self.iter() { + ::clear_forward(elem, ptr) + } + } + } + + impl ClearAt for [T; $len] {} + )* + } +} +forward_supported_array_lens!(impl_clear_for_array); + +macro_rules! impl_clear_tuple { + ( $($frag:ident),* $(,)? ) => { + impl<$($frag),*> ClearForward for ($($frag),* ,) + where + $( + $frag: ClearForward, + )* + { + fn clear_forward(&self, ptr: &mut KeyPtr) { + #[allow(non_snake_case)] + let ($($frag),*,) = self; + $( + <$frag as ClearForward>::clear_forward($frag, ptr); + )* + } + } + + impl<$($frag),*> ClearAt for ($($frag),* ,) {} + } +} +impl_clear_tuple!(A); +impl_clear_tuple!(A, B); +impl_clear_tuple!(A, B, C); +impl_clear_tuple!(A, B, C, D); +impl_clear_tuple!(A, B, C, D, E); +impl_clear_tuple!(A, B, C, D, E, F); +impl_clear_tuple!(A, B, C, D, E, F, G); +impl_clear_tuple!(A, B, C, D, E, F, G, H); +impl_clear_tuple!(A, B, C, D, E, F, G, H, I); +impl_clear_tuple!(A, B, C, D, E, F, G, H, I, J); + +impl ClearForward for () { + fn clear_forward(&self, _ptr: &mut KeyPtr) {} +} + +impl ClearAt for () { + fn clear_at(&self, _at: Key) { /* literally do nothing here */ } +} + +impl ClearForward for PhantomData { + fn clear_forward(&self, _ptr: &mut KeyPtr) {} +} + +impl ClearAt for PhantomData { + fn clear_at(&self, _at: Key) { /* literally do nothing here */ } +} + +impl ClearForward for Option +where + T: ClearForward + StorageSize, +{ + fn clear_forward(&self, ptr: &mut KeyPtr) { + match self { + Some(val) => ::clear_forward(val, ptr), + None => (), + } + } +} + +impl ClearAt for Option +where + T: ClearAt, +{ + fn clear_at(&self, at: Key) { + match self { + Some(val) => ::clear_at(val, at), + None => env::clear_contract_storage(at), + } + } +} + +impl ClearForward for Result +where + T: ClearForward, + E: ClearForward, +{ + fn clear_forward(&self, ptr: &mut KeyPtr) { + match self { + Ok(val) => { + ClearForward::clear_forward(&0u8, ptr); + ::clear_forward(val, ptr); + } + Err(err) => { + ClearForward::clear_forward(&1u8, ptr); + ::clear_forward(err, ptr); + } + } + } +} + +impl ClearAt for Result {} + +impl ClearForward for ink_prelude::string::String { + fn clear_forward(&self, ptr: &mut KeyPtr) { + ::clear_at(self, ptr.next_for::()) + } +} + +impl ClearAt for ink_prelude::string::String {} + +impl ClearForward for ink_prelude::boxed::Box +where + T: ClearForward, +{ + fn clear_forward(&self, ptr: &mut KeyPtr) { + ::clear_forward(&*self, ptr) + } +} + +impl ClearAt for ink_prelude::boxed::Box +where + T: ClearAt, +{ + fn clear_at(&self, at: Key) { + ::clear_at(&*self, at) + } +} + +const _: () = { + use ink_prelude::collections::{ + BTreeSet, + BinaryHeap, + LinkedList, + VecDeque, + }; + #[cfg(not(feature = "std"))] + use ink_prelude::vec::Vec; + + macro_rules! impl_clear_at_for_collection { + ( $($collection:ident),* $(,)? ) => { + $( + impl ClearAt for $collection {} + )* + }; + } + impl_clear_at_for_collection!(Vec, BTreeSet, BinaryHeap, LinkedList, VecDeque,); +}; + +impl ClearAt for ink_prelude::collections::BTreeMap {} diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index a8a19c72100..a8c213be31d 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -40,6 +40,7 @@ impl_array_len_less_equals_32_for!( 24, 25, 26, 27, 28, 29, 30, 31, 32, ); +mod clear; mod pull; mod push; mod storage_size; @@ -47,6 +48,10 @@ mod storage_size; use ink_primitives::Key; pub use self::{ + clear::{ + ClearAt, + ClearForward, + }, pull::{ PullAt, PullForward, From 84077c4b10ebf7640e528eeaedd3174bd56fc35f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 27 Feb 2020 21:42:04 +0100 Subject: [PATCH 051/142] [core] add ClearForward impls for lazy entities --- core/src/storage/lazy/lazy_cell.rs | 15 +++++++++++++++ core/src/storage/lazy/mod.rs | 10 ++++++++++ core/src/storage/mod.rs | 2 ++ 3 files changed, 27 insertions(+) diff --git a/core/src/storage/lazy/lazy_cell.rs b/core/src/storage/lazy/lazy_cell.rs index 608a501a0c2..a4e9b2df10a 100644 --- a/core/src/storage/lazy/lazy_cell.rs +++ b/core/src/storage/lazy/lazy_cell.rs @@ -16,6 +16,7 @@ use super::super::{ KeyPtr, PullForward, PushForward, + ClearForward, StorageSize, }; use core::cell::UnsafeCell; @@ -113,6 +114,20 @@ where } } +impl ClearForward for LazyCell +where + T: ClearForward, +{ + fn clear_forward(&self, ptr: &mut KeyPtr) { + // We skip clear contract storage if we are still in unloaded form. + if let LazyCellEntry::Occupied(occupied) = self.kind() { + if let Some(value) = &occupied.value { + ::clear_forward(value, ptr) + } + } + } +} + impl LazyCell { /// Creates an already populated lazy storage cell. /// diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index 8c3e9ac230e..2ec40804083 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -23,6 +23,7 @@ use super::{ KeyPtr, PullForward, PushForward, + ClearForward, StorageSize, }; use ink_primitives::Key; @@ -66,6 +67,15 @@ where } } +impl ClearForward for Lazy +where + T: ClearForward, +{ + fn clear_forward(&self, ptr: &mut KeyPtr) { + as ClearForward>::clear_forward(&self.cell, ptr) + } +} + impl Lazy { /// Creates an eagerly populated lazy storage value. #[must_use] diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 32dd1876b1c..1fdf5ea6cb8 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -123,6 +123,8 @@ pub use self::{ PullForward, PushAt, PushForward, + ClearAt, + ClearForward, StorageSize, }, }; From 86273fe226b2d04cbca495fbfe6fec41b7c74fe0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 27 Feb 2020 21:42:32 +0100 Subject: [PATCH 052/142] [core] impl storage traits for ink_primitive::Key --- core/src/storage/traits/clear.rs | 2 +- core/src/storage/traits/pull.rs | 2 +- core/src/storage/traits/push.rs | 2 +- core/src/storage/traits/storage_size.rs | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/storage/traits/clear.rs b/core/src/storage/traits/clear.rs index 8a55fb84519..c93a05c4ec4 100644 --- a/core/src/storage/traits/clear.rs +++ b/core/src/storage/traits/clear.rs @@ -59,7 +59,7 @@ macro_rules! impl_clear_for_primitive { )* }; } -impl_clear_for_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); +impl_clear_for_primitive!(Key, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); macro_rules! impl_clear_for_array { ( $($len:literal),* $(,)? ) => { diff --git a/core/src/storage/traits/pull.rs b/core/src/storage/traits/pull.rs index 193152972de..812a662571e 100644 --- a/core/src/storage/traits/pull.rs +++ b/core/src/storage/traits/pull.rs @@ -74,7 +74,7 @@ macro_rules! impl_pull_for_primitive { )* }; } -impl_pull_for_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); +impl_pull_for_primitive!(Key, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); macro_rules! impl_pull_for_array { ( $($len:literal),* $(,)? ) => { diff --git a/core/src/storage/traits/push.rs b/core/src/storage/traits/push.rs index 95974edf1f3..82b1fa64809 100644 --- a/core/src/storage/traits/push.rs +++ b/core/src/storage/traits/push.rs @@ -62,7 +62,7 @@ macro_rules! impl_push_for_primitive { )* }; } -impl_push_for_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); +impl_push_for_primitive!(Key, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); macro_rules! impl_push_for_array { ( $($len:literal),* $(,)? ) => { diff --git a/core/src/storage/traits/storage_size.rs b/core/src/storage/traits/storage_size.rs index f861f902b8f..89cc2861f1a 100644 --- a/core/src/storage/traits/storage_size.rs +++ b/core/src/storage/traits/storage_size.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +use ink_primitives::Key; + /// Implemented by types that can be stored on contract storage. /// /// Tells the amount of storage cells the type requires to be stored. @@ -30,7 +32,7 @@ macro_rules! impl_storage_size_for_primitive { )* }; } -impl_storage_size_for_primitive!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); +impl_storage_size_for_primitive!(Key, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); macro_rules! impl_storage_size_for_array { ( $($n:literal),* $(,)? ) => { From b00374dbdc55e088d7881de98d48554ef7a84a68 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 27 Feb 2020 21:42:43 +0100 Subject: [PATCH 053/142] [core] implement storage::Box --- core/src/storage/collections2/boxed/mod.rs | 93 +++++++++ core/src/storage/collections2/boxed/traits.rs | 196 ++++++++++++++++++ core/src/storage/collections2/mod.rs | 1 + core/src/storage/mod.rs | 1 + 4 files changed, 291 insertions(+) create mode 100644 core/src/storage/collections2/boxed/mod.rs create mode 100644 core/src/storage/collections2/boxed/traits.rs diff --git a/core/src/storage/collections2/boxed/mod.rs b/core/src/storage/collections2/boxed/mod.rs new file mode 100644 index 00000000000..bcd19dddfa1 --- /dev/null +++ b/core/src/storage/collections2/boxed/mod.rs @@ -0,0 +1,93 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod traits; + +use crate::{ + storage::{ + Lazy, + PullForward, + ClearForward, + StorageSize, + }, +}; +use ink_primitives::Key; + +/// Allocates a new storage key for the given `T` dynamically on the storage. +fn allocate_dynamically() -> Key +where + T: StorageSize, +{ + // TODO: Actual implementation is still missing! + Key([0x42; 32]) +} + +/// An indirection to some dynamically allocated storage entity. +pub struct Box +where + T: ClearForward, +{ + /// The storage area where the boxed storage entity is stored. + key: Key, + /// The cache for the boxed storage entity. + value: Lazy, +} + +impl Box +where + T: ClearForward + StorageSize, +{ + /// Creates a new boxed entity. + pub fn new(value: T) -> Self { + Self { + key: allocate_dynamically::(), + value: Lazy::new(value), + } + } +} + +impl Box +where + T: ClearForward + StorageSize + PullForward, +{ + /// Returns a shared reference to the boxed value. + /// + /// # Note + /// + /// This loads the value from the pointed to contract storage + /// if this did not happed before. + /// + /// # Panics + /// + /// If loading from contract storage failed. + #[must_use] + pub fn get(&self) -> &T { + self.value.get() + } + + /// Returns an exclusive reference to the boxed value. + /// + /// # Note + /// + /// This loads the value from the pointed to contract storage + /// if this did not happed before. + /// + /// # Panics + /// + /// If loading from contract storage failed. + #[must_use] + pub fn get_mut(&mut self) -> &mut T { + self.value.get_mut() + } +} diff --git a/core/src/storage/collections2/boxed/traits.rs b/core/src/storage/collections2/boxed/traits.rs new file mode 100644 index 00000000000..091147888f0 --- /dev/null +++ b/core/src/storage/collections2/boxed/traits.rs @@ -0,0 +1,196 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::Box as StorageBox; +use crate::{ + storage, + storage::{ + ClearForward, + KeyPtr, + PullForward, + PushForward, + StorageSize, + }, +}; +use ink_primitives::Key; + +impl StorageSize for StorageBox +where + T: ClearForward + StorageSize, +{ + /// A boxed entity always uses exactly 1 cell for the key. + /// + /// The indirectly stored storage entity is not considered because the + /// `StorageSize` is only concerned with inplace storage usage. + const SIZE: u64 = 1; +} + +impl PullForward for StorageBox +where + T: ClearForward, +{ + fn pull_forward(ptr: &mut KeyPtr) -> Self { + let key = ::pull_forward(ptr); + Self { + key, + value: storage::Lazy::lazy(key), + } + } +} + +impl PushForward for StorageBox +where + T: ClearForward, + storage::Lazy: PushForward, +{ + fn push_forward(&self, ptr: &mut KeyPtr) { + PushForward::push_forward(&self.key, ptr); + PushForward::push_forward(&self.value, &mut KeyPtr::from(self.key)); + } +} + +impl ClearForward for StorageBox +where + T: ClearForward, + storage::Lazy: ClearForward, +{ + fn clear_forward(&self, ptr: &mut KeyPtr) { + ClearForward::clear_forward(&self.key, ptr); + ClearForward::clear_forward(&self.value, &mut KeyPtr::from(self.key)); + } +} + +impl Drop for StorageBox +where + T: ClearForward, +{ + fn drop(&mut self) { + ClearForward::clear_forward(&self.value, &mut KeyPtr::from(self.key)); + } +} + +impl core::cmp::PartialEq for StorageBox +where + T: PartialEq + ClearForward + StorageSize + PullForward, +{ + fn eq(&self, other: &Self) -> bool { + PartialEq::eq(self.get(), other.get()) + } +} + +impl core::cmp::Eq for StorageBox where T: Eq + ClearForward + StorageSize + PullForward {} + +impl core::cmp::PartialOrd for StorageBox +where + T: PartialOrd + ClearForward + StorageSize + PullForward, +{ + fn partial_cmp(&self, other: &Self) -> Option { + PartialOrd::partial_cmp(self.get(), other.get()) + } + fn lt(&self, other: &Self) -> bool { + PartialOrd::lt(self.get(), other.get()) + } + fn le(&self, other: &Self) -> bool { + PartialOrd::le(self.get(), other.get()) + } + fn ge(&self, other: &Self) -> bool { + PartialOrd::ge(self.get(), other.get()) + } + fn gt(&self, other: &Self) -> bool { + PartialOrd::gt(self.get(), other.get()) + } +} + +impl core::cmp::Ord for StorageBox +where + T: core::cmp::Ord + ClearForward + StorageSize + PullForward, +{ + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + Ord::cmp(self.get(), other.get()) + } +} + +impl core::fmt::Display for StorageBox +where + T: core::fmt::Display + ClearForward + StorageSize + PullForward, +{ + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Display::fmt(self.get(), f) + } +} + +impl core::hash::Hash for StorageBox +where + T: core::hash::Hash + ClearForward + StorageSize + PullForward, +{ + fn hash(&self, state: &mut H) { + self.get().hash(state); + } +} + +impl core::convert::AsRef for StorageBox +where + T: StorageSize + ClearForward + PullForward, +{ + fn as_ref(&self) -> &T { + self.get() + } +} + +impl core::convert::AsMut for StorageBox +where + T: StorageSize + ClearForward + PullForward, +{ + fn as_mut(&mut self) -> &mut T { + self.get_mut() + } +} + +impl ink_prelude::borrow::Borrow for StorageBox +where + T: StorageSize + ClearForward + PullForward, +{ + fn borrow(&self) -> &T { + self.get() + } +} + +impl ink_prelude::borrow::BorrowMut for StorageBox +where + T: StorageSize + ClearForward + PullForward, +{ + fn borrow_mut(&mut self) -> &mut T { + self.get_mut() + } +} + +impl core::ops::Deref for StorageBox +where + T: StorageSize + ClearForward + PullForward, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl core::ops::DerefMut for StorageBox +where + T: StorageSize + ClearForward + PullForward, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.get_mut() + } +} diff --git a/core/src/storage/collections2/mod.rs b/core/src/storage/collections2/mod.rs index c0cf909715d..67356236671 100644 --- a/core/src/storage/collections2/mod.rs +++ b/core/src/storage/collections2/mod.rs @@ -12,4 +12,5 @@ // See the License for the specific language governing permissions and // limitations under the License. +pub mod boxed; pub mod vec; diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 1fdf5ea6cb8..008ae9ff42c 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -109,6 +109,7 @@ pub use self::{ }, collections2::{ vec::Vec as Vec2, + boxed::Box, }, flush::Flush, lazy::{ From a7ee2da402b7167f6a8c89f2f06bfba6432f2726 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 28 Feb 2020 15:37:29 +0100 Subject: [PATCH 054/142] [core] generalize LazyChunk to LazyMap and support LazyChunk and LazyMapping through it --- .../lazy/{lazy_chunk.rs => lazy_map.rs} | 219 ++++++++++++------ core/src/storage/lazy/mod.rs | 4 +- core/src/storage/mod.rs | 1 + 3 files changed, 153 insertions(+), 71 deletions(-) rename core/src/storage/lazy/{lazy_chunk.rs => lazy_map.rs} (71%) diff --git a/core/src/storage/lazy/lazy_chunk.rs b/core/src/storage/lazy/lazy_map.rs similarity index 71% rename from core/src/storage/lazy/lazy_chunk.rs rename to core/src/storage/lazy/lazy_map.rs index 7508d390e40..3b9c86b9490 100644 --- a/core/src/storage/lazy/lazy_chunk.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -18,17 +18,72 @@ use super::super::{ PushForward, StorageSize, }; -use core::cell::UnsafeCell; -use ink_primitives::Key; - +use core::{ + cell::{Cell, UnsafeCell}, + cmp::{ + Eq, + Ord, + }, +}; use ink_prelude::{ boxed::Box, collections::BTreeMap, }; +use ink_primitives::Key; /// The index type used in the lazy storage chunk. pub type Index = u32; +/// Types implementing this trait can use the `LazyMap` in order to +/// convert themselves into an actual storage Key. +/// +/// # Note +/// +/// By default implemented by `u32` as index for [`ink_core::storage::LazyChunk`] +/// and by `Key` itself as identify function in order to eventually support +/// Solidity-like storage mappings. +pub trait KeyMapping { + /// Converts `self` into a storage key using the lazy map parameter. + fn to_storage_key(&self, offset: &Key) -> Key; +} + +impl KeyMapping for Index +where + Value: StorageSize, +{ + fn to_storage_key(&self, offset: &Key) -> Key { + *offset + (*self as u64 * ::SIZE) + } +} + +impl KeyMapping for Key { + fn to_storage_key(&self, _offset: &Key) -> Key { + // TODO: Actually implement this correctly similar to how Solidity + // handles these cases. + *self + } +} + +/// A chunk of contiguously stored storage entities indexed by integers. +/// +/// # Note +/// +/// - Loads each values within the chunk lazily. +/// - This is a low-level storage primitive used by some high-level +/// storage primitives in order to manage the contract storage for a whole +/// chunk of storage cells. +pub type LazyChunk = LazyMap; + +/// A Solidity-like mapping of storage entities indexed by key hashes. +/// +/// # Note +/// +/// - Loads each values within the chunk lazily. +/// - This is a low-level storage primitive used by some high-level +/// storage primitives in order to manage the contract storage similar +/// as to how Solidity mappings distribute their storage entries. +pub type LazyMapping = LazyMap; + /// A lazy storage chunk that spans over a whole chunk of storage cells. /// /// # Note @@ -38,8 +93,8 @@ pub type Index = u32; /// chunk of storage cells. /// /// A chunk of storage cells is a contiguous range of 2^32 storage cells. -#[derive(Debug, Default)] -pub struct LazyChunk { +#[derive(Debug)] +pub struct LazyMap { /// The offset key for the chunk of cells. /// /// If the lazy chunk has been initialized during contract initialization @@ -51,7 +106,19 @@ pub struct LazyChunk { /// The subset of currently cached entries of the lazy storage chunk. /// /// An entry is cached as soon as it is loaded or written. - cached_entries: UnsafeCell>, + cached_entries: UnsafeCell>, +} + +impl Default for LazyMap +where + K: Ord, +{ + fn default() -> Self { + Self { + key: None, + cached_entries: UnsafeCell::new(EntryMap::default()), + } + } } /// The map for the contract storage entries. @@ -60,8 +127,8 @@ pub struct LazyChunk { /// /// We keep the whole entry in a `Box` in order to prevent pointer /// invalidation upon updating the cache through `&self` methods as in -/// [`LazyChunk::get`]. -pub type EntryMap = BTreeMap>>; +/// [`LazyMap::get`]. +pub type EntryMap = BTreeMap>>; /// An entry within the lazy chunk #[derive(Debug)] @@ -70,7 +137,7 @@ pub struct Entry { value: Option, /// This is `true` if the `value` has been mutated and is potentially /// out-of-sync with the contract storage. - mutated: core::cell::Cell, + mutated: Cell, } impl PushForward for Entry @@ -135,7 +202,10 @@ impl Entry { } } -impl LazyChunk { +impl LazyMap +where + K: Ord, +{ /// Creates a new empty lazy chunk that cannot be mutated. pub fn new() -> Self { Self { @@ -144,25 +214,64 @@ impl LazyChunk { } } - /// Returns a shared reference to the entries. - fn entries(&self) -> &EntryMap { - // SAFETY: We return a shared reference while `entries` is a method - // with a `&self` receiver so we don't break lifetime or borrow - // rules in isolation. + /// Returns a shared reference to the underlying entries. + fn entries(&self) -> &EntryMap { + // SAFETY: It is safe to return a `&` reference from a `&self` receiver. unsafe { &*self.cached_entries.get() } } + + /// Returns an exclusive reference to the underlying entries. + fn entries_mut(&mut self) -> &mut EntryMap { + // SAFETY: It is safe to return a `&mut` reference from a `&mut self` receiver. + unsafe { &mut *self.cached_entries.get() } + } + + /// Puts the new value at the given index. + /// + /// # Note + /// + /// - Use [`LazyMap::put`]`(None)` in order to remove an element. + /// - Prefer this method over [`LazyMap::put_get`] if you are not interested + /// in the old value of the same cell index. + /// + /// # Panics + /// + /// - If the lazy chunk is in an invalid state that forbids interaction. + /// - If the decoding of the old element at the given index failed. + pub fn put(&mut self, index: K, new_value: Option) { + self.entries_mut().insert( + index, + Box::new(Entry { + value: new_value, + mutated: Cell::new(true), + }), + ); + } } impl StorageSize for LazyChunk where T: StorageSize, { + /// A lazy chunk is contiguous and its size can be determined by the + /// total number of elements it could theoretically hold. const SIZE: u64 = ::SIZE * (core::u32::MAX as u64); } -impl PullForward for LazyChunk +impl StorageSize for LazyMapping where T: StorageSize, +{ + /// A lazy mapping is similar to a Solidity mapping that distributes its + /// stored entities across the entire contract storage so its inplace size + /// is actually just 1. + const SIZE: u64 = 1; +} + +impl PullForward for LazyMap +where + K: Ord, + Self: StorageSize, { fn pull_forward(ptr: &mut KeyPtr) -> Self { Self { @@ -172,26 +281,27 @@ where } } -impl PushForward for LazyChunk +impl PushForward for LazyMap where - T: PushForward + StorageSize, + Self: StorageSize, + K: KeyMapping + Ord, + V: PushForward + StorageSize, { fn push_forward(&self, ptr: &mut KeyPtr) { let key = ptr.next_for::(); - let elem_size = ::SIZE; assert_eq!(self.key, Some(key)); - for (&index, entry) in self.entries().iter().filter(|(_, entry)| entry.mutated()) - { - let offset: Key = key + (index as u64 * elem_size); + for (index, entry) in self.entries().iter().filter(|(_, entry)| entry.mutated()) { + let offset: Key = >::to_storage_key(index, &key); let mut ptr = KeyPtr::from(offset); PushForward::push_forward(&**entry, &mut ptr); } } } -impl LazyChunk +impl LazyMap where - T: StorageSize + PullForward, + K: KeyMapping + Ord + Eq, + V: StorageSize + PullForward, { /// Lazily loads the value at the given index. /// @@ -217,7 +327,7 @@ where /// a `*mut Entry` pointer that allows for exclusive access. This is safe /// within internal use only and should never be given outside of the lazy /// entity for public `&self` methods. - unsafe fn lazily_load(&self, index: Index) -> *mut Entry { + unsafe fn lazily_load(&self, index: K) -> *mut Entry { let key = match self.key { Some(key) => key, None => panic!("cannot load lazily in this state"), @@ -234,13 +344,13 @@ where #[allow(unused_unsafe)] let cached_entries = unsafe { &mut *self.cached_entries.get() }; use ink_prelude::collections::btree_map::Entry as BTreeMapEntry; + let off_key = >::to_storage_key(&index, &key); match cached_entries.entry(index) { BTreeMapEntry::Occupied(occupied) => &mut **occupied.into_mut(), BTreeMapEntry::Vacant(vacant) => { - let value = as PullForward>::pull_forward(&mut KeyPtr::from( - key + index, - )); - let mutated = core::cell::Cell::new(false); + let value = + as PullForward>::pull_forward(&mut KeyPtr::from(off_key)); + let mutated = Cell::new(false); &mut **vacant.insert(Box::new(Entry { value, mutated })) } } @@ -257,7 +367,7 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the lazy chunk is not in a state that allows lazy loading. - fn lazily_load_mut(&mut self, index: Index) -> &mut Entry { + fn lazily_load_mut(&mut self, index: K) -> &mut Entry { // SAFETY: // - Returning a `&mut Entry` is safe because entities inside the // cache are stored within a `Box` to not invalidate references into @@ -271,11 +381,11 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the decoding of the element at the given index failed. - pub fn get(&self, index: Index) -> Option<&T> { + pub fn get(&self, index: K) -> Option<&V> { // SAFETY: Dereferencing the `*mut T` pointer into a `&T` is safe // since this method's receiver is `&self` so we do not // leak non-shared references to the outside. - let entry: &Entry = unsafe { &*self.lazily_load(index) }; + let entry: &Entry = unsafe { &*self.lazily_load(index) }; entry.value() } @@ -285,7 +395,7 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the decoding of the element at the given index failed. - pub fn get_mut(&mut self, index: Index) -> Option<&mut T> { + pub fn get_mut(&mut self, index: K) -> Option<&mut V> { self.lazily_load_mut(index).value_mut() } @@ -299,57 +409,28 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the decoding of the element at the given index failed. - pub fn take(&mut self, index: Index) -> Option { + pub fn take(&mut self, index: K) -> Option { self.lazily_load_mut(index).take_value() } } -impl LazyChunk { - /// Returns an exclusive reference to the cached entry map. - pub fn entries_mut(&mut self) -> &mut EntryMap { - // SAFETY: It is safe to return a `&mut` reference from a `&mut` receiver method. - unsafe { &mut *self.cached_entries.get() } - } - - /// Puts the new value at the given index. - /// - /// # Note - /// - /// - Use [`LazyChunk::put`]`(None)` in order to remove an element. - /// - Prefer this method over [`LazyChunk::put_get`] if you are not interested - /// in the old value of the same cell index. - /// - /// # Panics - /// - /// - If the lazy chunk is in an invalid state that forbids interaction. - /// - If the decoding of the old element at the given index failed. - pub fn put(&mut self, index: Index, new_value: Option) { - self.entries_mut().insert( - index, - Box::new(Entry { - value: new_value, - mutated: core::cell::Cell::new(true), - }), - ); - } -} - -impl LazyChunk +impl LazyMap where - T: StorageSize + PullForward, + K: KeyMapping + Ord + Eq, + V: StorageSize + PullForward, { /// Puts the new value at the given index and returns the old value if any. /// /// # Note /// - /// - Use [`LazyChunk::put_get`]`(None)` in order to remove an element + /// - Use [`LazyMap::put_get`]`(None)` in order to remove an element /// and retrieve the old element back. /// /// # Panics /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the decoding of the old element at the given index failed. - pub fn put_get(&mut self, index: Index, new_value: Option) -> Option { + pub fn put_get(&mut self, index: K, new_value: Option) -> Option { self.lazily_load_mut(index).put(new_value) } @@ -361,7 +442,7 @@ where /// /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the decoding of one of the elements failed. - pub fn swap(&mut self, x: Index, y: Index) { + pub fn swap(&mut self, x: K, y: K) { if x == y { // Bail out early if both indices are the same. return diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index 2ec40804083..5c40e370049 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -13,11 +13,11 @@ // limitations under the License. mod lazy_cell; -mod lazy_chunk; +mod lazy_map; pub use self::{ lazy_cell::LazyCell, - lazy_chunk::LazyChunk, + lazy_map::{LazyChunk, LazyMapping}, }; use super::{ KeyPtr, diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 008ae9ff42c..7c9c28bf1fc 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -116,6 +116,7 @@ pub use self::{ Lazy, LazyCell, LazyChunk, + LazyMapping, }, pack::Pack, traits::{ From e1ad67108c04f795360aaccd23658ef1ccd90733 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 28 Feb 2020 17:11:30 +0100 Subject: [PATCH 055/142] [core] impl Default for storage::Vec2 --- core/src/storage/collections2/vec/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index fd745d3c02c..22dd5bfe992 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -46,6 +46,15 @@ pub struct Vec { elems: storage::LazyChunk, } +impl Default for Vec { + fn default() -> Self { + Self { + len: storage::Lazy::new(0), + elems: storage::LazyChunk::default(), + } + } +} + impl Vec { /// Creates a new empty storage vector. pub fn new() -> Self { From 7e3684f4ebc317906f749cee4e1a179ec24a5df0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 28 Feb 2020 17:12:05 +0100 Subject: [PATCH 056/142] [core] apply rustfmt --- core/src/storage/collections2/boxed/mod.rs | 12 +++++------- core/src/storage/collections2/boxed/traits.rs | 5 ++++- core/src/storage/collections2/vec/traits.rs | 6 ++---- core/src/storage/lazy/lazy_cell.rs | 2 +- core/src/storage/lazy/lazy_map.rs | 5 ++++- core/src/storage/lazy/mod.rs | 7 +++++-- core/src/storage/mod.rs | 6 +++--- core/src/storage/traits/clear.rs | 6 ++++-- 8 files changed, 28 insertions(+), 21 deletions(-) diff --git a/core/src/storage/collections2/boxed/mod.rs b/core/src/storage/collections2/boxed/mod.rs index bcd19dddfa1..d165637269a 100644 --- a/core/src/storage/collections2/boxed/mod.rs +++ b/core/src/storage/collections2/boxed/mod.rs @@ -14,13 +14,11 @@ mod traits; -use crate::{ - storage::{ - Lazy, - PullForward, - ClearForward, - StorageSize, - }, +use crate::storage::{ + ClearForward, + Lazy, + PullForward, + StorageSize, }; use ink_primitives::Key; diff --git a/core/src/storage/collections2/boxed/traits.rs b/core/src/storage/collections2/boxed/traits.rs index 091147888f0..9b222d84ab1 100644 --- a/core/src/storage/collections2/boxed/traits.rs +++ b/core/src/storage/collections2/boxed/traits.rs @@ -89,7 +89,10 @@ where } } -impl core::cmp::Eq for StorageBox where T: Eq + ClearForward + StorageSize + PullForward {} +impl core::cmp::Eq for StorageBox where + T: Eq + ClearForward + StorageSize + PullForward +{ +} impl core::cmp::PartialOrd for StorageBox where diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index 082488b8da6..67e44fd77b1 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -29,8 +29,7 @@ where type Output = T; fn index(&self, index: u32) -> &Self::Output { - self.get(index) - .expect("index out of bounds") + self.get(index).expect("index out of bounds") } } @@ -39,8 +38,7 @@ where T: StorageSize + PullForward, { fn index_mut(&mut self, index: u32) -> &mut Self::Output { - self.get_mut(index) - .expect("index out of bounds") + self.get_mut(index).expect("index out of bounds") } } diff --git a/core/src/storage/lazy/lazy_cell.rs b/core/src/storage/lazy/lazy_cell.rs index a4e9b2df10a..26ac74119c3 100644 --- a/core/src/storage/lazy/lazy_cell.rs +++ b/core/src/storage/lazy/lazy_cell.rs @@ -13,10 +13,10 @@ // limitations under the License. use super::super::{ + ClearForward, KeyPtr, PullForward, PushForward, - ClearForward, StorageSize, }; use core::cell::UnsafeCell; diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index 3b9c86b9490..9042526a960 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -19,7 +19,10 @@ use super::super::{ StorageSize, }; use core::{ - cell::{Cell, UnsafeCell}, + cell::{ + Cell, + UnsafeCell, + }, cmp::{ Eq, Ord, diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index 5c40e370049..5e95417b768 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -17,13 +17,16 @@ mod lazy_map; pub use self::{ lazy_cell::LazyCell, - lazy_map::{LazyChunk, LazyMapping}, + lazy_map::{ + LazyChunk, + LazyMapping, + }, }; use super::{ + ClearForward, KeyPtr, PullForward, PushForward, - ClearForward, StorageSize, }; use ink_primitives::Key; diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 7c9c28bf1fc..daf602a788b 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -108,8 +108,8 @@ pub use self::{ }, }, collections2::{ - vec::Vec as Vec2, boxed::Box, + vec::Vec as Vec2, }, flush::Flush, lazy::{ @@ -120,13 +120,13 @@ pub use self::{ }, pack::Pack, traits::{ + ClearAt, + ClearForward, KeyPtr, PullAt, PullForward, PushAt, PushForward, - ClearAt, - ClearForward, StorageSize, }, }; diff --git a/core/src/storage/traits/clear.rs b/core/src/storage/traits/clear.rs index c93a05c4ec4..6655724bb5a 100644 --- a/core/src/storage/traits/clear.rs +++ b/core/src/storage/traits/clear.rs @@ -117,7 +117,8 @@ impl ClearForward for () { } impl ClearAt for () { - fn clear_at(&self, _at: Key) { /* literally do nothing here */ } + fn clear_at(&self, _at: Key) { // literally do nothing here + } } impl ClearForward for PhantomData { @@ -125,7 +126,8 @@ impl ClearForward for PhantomData { } impl ClearAt for PhantomData { - fn clear_at(&self, _at: Key) { /* literally do nothing here */ } + fn clear_at(&self, _at: Key) { // literally do nothing here + } } impl ClearForward for Option From 09f8a260a1bfd5844cfa9e367c6f66c81eeb66e3 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 28 Feb 2020 17:58:45 +0100 Subject: [PATCH 057/142] [core] remove ArrayLenLessEquals32 check trait --- core/src/storage/traits/mod.rs | 16 ---------------- core/src/storage/traits/pull.rs | 3 +-- core/src/storage/traits/push.rs | 2 -- 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index a8c213be31d..ed927dfc686 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -20,26 +20,10 @@ macro_rules! forward_supported_array_lens { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 64, 96, 128, 256, 512, 1024, 2048, 4096, - 8192, 16384, } }; } -/// Implemented by array of sizes of up to 32. -pub trait ArrayLenLessEquals32 {} -macro_rules! impl_array_len_less_equals_32_for { - ( $($n:literal),* $(,)? ) => { - $( - impl ArrayLenLessEquals32 for [T; $n] {} - )* - } -} -impl_array_len_less_equals_32_for!( - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, -); - mod clear; mod pull; mod push; diff --git a/core/src/storage/traits/pull.rs b/core/src/storage/traits/pull.rs index 812a662571e..ab719aa47ff 100644 --- a/core/src/storage/traits/pull.rs +++ b/core/src/storage/traits/pull.rs @@ -13,7 +13,6 @@ // limitations under the License. use super::{ - ArrayLenLessEquals32, KeyPtr, StorageSize, }; @@ -81,7 +80,7 @@ macro_rules! impl_pull_for_array { $( impl PullForward for [T; $len] where - Self: IsArray + ArrayLenLessEquals32, + Self: IsArray, ::Item: PullForward, { fn pull_forward(ptr: &mut KeyPtr) -> Self { diff --git a/core/src/storage/traits/push.rs b/core/src/storage/traits/push.rs index 82b1fa64809..7eac5f1532f 100644 --- a/core/src/storage/traits/push.rs +++ b/core/src/storage/traits/push.rs @@ -13,7 +13,6 @@ // limitations under the License. use super::{ - ArrayLenLessEquals32, KeyPtr, StorageSize, }; @@ -69,7 +68,6 @@ macro_rules! impl_push_for_array { $( impl PushForward for [T; $len] where - Self: ArrayLenLessEquals32, T: PushForward, { fn push_forward(&self, ptr: &mut KeyPtr) { From c607cf8e9a207a256e531b264e4ff893382ee8dd Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 29 Feb 2020 16:39:42 +0100 Subject: [PATCH 058/142] [core] simplify import name --- core/src/storage/collections2/vec/traits.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index 67e44fd77b1..2a067a5a533 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::Vec as StorageVec; use crate::{ storage, storage::{ @@ -22,7 +23,7 @@ use crate::{ }, }; -impl core::ops::Index for storage::Vec2 +impl core::ops::Index for StorageVec where T: StorageSize + PullForward, { @@ -33,7 +34,7 @@ where } } -impl core::ops::IndexMut for storage::Vec2 +impl core::ops::IndexMut for StorageVec where T: StorageSize + PullForward, { @@ -42,7 +43,7 @@ where } } -impl StorageSize for storage::Vec2 +impl StorageSize for StorageVec where T: StorageSize, { @@ -50,7 +51,7 @@ where ::SIZE + as StorageSize>::SIZE; } -impl PullForward for storage::Vec2 +impl PullForward for StorageVec where T: StorageSize, { @@ -62,7 +63,7 @@ where } } -impl PushForward for storage::Vec2 +impl PushForward for StorageVec where storage::LazyChunk: PushForward, { From 1c1f36a1e13e98aa2f9e8a32353d08a2eeff9903 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 29 Feb 2020 16:40:00 +0100 Subject: [PATCH 059/142] [core] impl IntoIter for &storage::Vec2 --- core/src/storage/collections2/vec/traits.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index 2a067a5a533..1d031ca6c36 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -43,6 +43,18 @@ where } } +impl<'a, T: 'a> IntoIterator for &'a StorageVec +where + T: StorageSize + PullForward, +{ + type Item = &'a T; + type IntoIter = super::Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + impl StorageSize for StorageVec where T: StorageSize, From 48722fbc1111ec5980c3203faf9fc8da34b1f39f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 29 Feb 2020 16:40:17 +0100 Subject: [PATCH 060/142] [core] impl LazyMap::key --- core/src/storage/lazy/lazy_map.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index 9042526a960..5e97d638202 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -217,6 +217,11 @@ where } } + /// Returns the offset key of the lazy map if any. + pub fn key(&self) -> Option<&Key> { + self.key.as_ref() + } + /// Returns a shared reference to the underlying entries. fn entries(&self) -> &EntryMap { // SAFETY: It is safe to return a `&` reference from a `&self` receiver. From 98ec1e51beb8408661cdadc25b36d6c7bf639582 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 29 Feb 2020 16:40:29 +0100 Subject: [PATCH 061/142] [core] export LazyMap --- core/src/storage/lazy/mod.rs | 1 + core/src/storage/mod.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index 5e95417b768..ad3fac788ea 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -20,6 +20,7 @@ pub use self::{ lazy_map::{ LazyChunk, LazyMapping, + LazyMap, }, }; use super::{ diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index daf602a788b..9b2dc1d86d5 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -117,6 +117,7 @@ pub use self::{ LazyCell, LazyChunk, LazyMapping, + LazyMap, }, pack::Pack, traits::{ From 98b5d334f8128c819033b7d9fa6f8eb808b608c0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 29 Feb 2020 16:40:50 +0100 Subject: [PATCH 062/142] [core] impl ClearForward for storage::Vec2 --- core/src/storage/collections2/vec/traits.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index 1d031ca6c36..bebc91e0c57 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -16,6 +16,7 @@ use super::Vec as StorageVec; use crate::{ storage, storage::{ + ClearForward, KeyPtr, PullForward, PushForward, @@ -84,3 +85,20 @@ where PushForward::push_forward(&self.elems, ptr); } } + +impl ClearForward for StorageVec +where + T: StorageSize + ClearForward + PullForward, +{ + fn clear_forward(&self, ptr: &mut KeyPtr) { + ClearForward::clear_forward(&self.len(), ptr); + if let Some(key) = self.elems.key() { + for (index, elem) in self.iter().enumerate() { + ::clear_forward( + elem, + &mut KeyPtr::from(*key + index as u32), + ) + } + } + } +} From f9f3e57041cfa04d2dee43cd401f7bd48a276f5a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 29 Feb 2020 16:41:19 +0100 Subject: [PATCH 063/142] [core] apply rustfmt --- core/src/env/engine/on_chain/ext.rs | 1 + core/src/storage/lazy/mod.rs | 2 +- core/src/storage/mod.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/env/engine/on_chain/ext.rs b/core/src/env/engine/on_chain/ext.rs index c42cadbc741..4639556fbba 100644 --- a/core/src/env/engine/on_chain/ext.rs +++ b/core/src/env/engine/on_chain/ext.rs @@ -60,6 +60,7 @@ mod sys { value_ptr: u32, value_len: u32, ); + pub fn ext_get_storage(key_ptr: u32) -> u32; pub fn ext_get_runtime_storage(key_ptr: u32, key_len: u32) -> u32; diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index ad3fac788ea..56ce4d79a85 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -19,8 +19,8 @@ pub use self::{ lazy_cell::LazyCell, lazy_map::{ LazyChunk, - LazyMapping, LazyMap, + LazyMapping, }, }; use super::{ diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 9b2dc1d86d5..ad997dbef1f 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -116,8 +116,8 @@ pub use self::{ Lazy, LazyCell, LazyChunk, - LazyMapping, LazyMap, + LazyMapping, }, pack::Pack, traits::{ From 47dcc3a502be505d66a053a0f38310b2d426c6ef Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 3 Mar 2020 14:28:35 +0100 Subject: [PATCH 064/142] [core] add storage::LazyMap::key_at method --- core/src/storage/lazy/lazy_map.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index 5e97d638202..66e515f6a2f 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -205,6 +205,23 @@ impl Entry { } } +impl LazyMap +where + K: KeyMapping + Ord, +{ + /// Returns the storage key associated with the given index. + pub fn key_at(&self, at: &Q) -> Option + where + Q: core::borrow::Borrow, + K: Ord, + { + self.key() + .map(|key| { + >::to_storage_key(at.borrow(), &key) + }) + } +} + impl LazyMap where K: Ord, From 3adc97a1da37381512c40fc8e5738e8a8737a760 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 3 Mar 2020 14:29:03 +0100 Subject: [PATCH 065/142] [core] make use of storage::LazyMap::key_at in ClearForward impl --- core/src/storage/collections2/vec/traits.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index bebc91e0c57..c70ff1600a8 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -92,13 +92,18 @@ where { fn clear_forward(&self, ptr: &mut KeyPtr) { ClearForward::clear_forward(&self.len(), ptr); - if let Some(key) = self.elems.key() { - for (index, elem) in self.iter().enumerate() { - ::clear_forward( - elem, - &mut KeyPtr::from(*key + index as u32), - ) - } + if self.elems.key().is_none() { + return + } + for (index, elem) in self.iter().enumerate() { + ::clear_forward( + elem, + &mut KeyPtr::from( + self.elems + .key_at(&(index as u32)) + .expect("expected a key mapping since self.elems.key() is some"), + ), + ) } } } From 4e39fb956a1c135403106506aebebe091415d81e Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 4 Mar 2020 18:46:35 +0100 Subject: [PATCH 066/142] [core] add StorageFootprint and SaturatingStorage traits They shall replace the StorageSize trait and fix some problems with it for generic types with associated consts. Also SaturatingStorage is a marker trait that helps filter out certain storage type combinations that we currently cannot support. --- core/Cargo.toml | 1 + core/src/storage/traits/mod.rs | 24 ++++ core/src/storage/traits/storage_size.rs | 184 ++++++++++++++++++++++-- 3 files changed, 200 insertions(+), 9 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 6d1e8773ad8..e788a72e15c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -28,6 +28,7 @@ smallvec = { version = "1.0", default-features = false, features = ["union"] } cfg-if = "0.1" num-traits = { version = "0.2.1", default-features = false, features = ["i128"] } array-init = "0.1" +typenum = "1.11.2" # Only used in the off-chain environment. # diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index ed927dfc686..d16309cbfc2 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -24,6 +24,30 @@ macro_rules! forward_supported_array_lens { }; } +macro_rules! forward_supported_array_lens_ty { + ( $mac:ident ) => { + const _: () = { + #[rustfmt::skip] + use typenum::{ + Z0, + P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, + P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, + P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, + P31, P32, + }; + $mac! { + ( 0, Z0), ( 1, P1), ( 2, P2), ( 3, P3), ( 4, P4), + ( 5, P5), ( 6, P6), ( 7, P7), ( 8, P8), ( 9, P9), + (10, P10), (11, P11), (12, P12), (13, P13), (14, P14), + (15, P15), (16, P16), (17, P17), (18, P18), (19, P19), + (20, P20), (21, P21), (22, P22), (23, P23), (24, P24), + (25, P25), (26, P26), (27, P27), (28, P28), (29, P29), + (30, P30), (31, P31), (32, P32), + } + }; + }; +} + mod clear; mod pull; mod push; diff --git a/core/src/storage/traits/storage_size.rs b/core/src/storage/traits/storage_size.rs index 89cc2861f1a..7d00c2983e2 100644 --- a/core/src/storage/traits/storage_size.rs +++ b/core/src/storage/traits/storage_size.rs @@ -23,12 +23,58 @@ pub trait StorageSize { const SIZE: u64; } +use core::ops::{ + Add, + Mul, +}; +use typenum::{ + IsEqual, + Max, + Maximum, + Prod, + Sum, + B1 as True, + P1, + Z0, +}; + +/// Implemented by types that can be stored on contract storage. +/// +/// Tells the amount of storage cells the type requires to be stored. +pub trait StorageFootprint { + /// The number of storage cells required by `Self` to be stored + /// on the contract storage. + /// + /// # Note + /// + /// Using a type (`typenum`) here instead of an associated constant + /// solves some problems for implementations of generics because Rust's + /// handling of those associated constants is not mature while it can + /// easily handle the `typenum` types solving the same underlying problem + /// of representing a computable compile-time number. + /// + /// We should switch back to associated constants once the Rust compiler + /// is more mature at handling them in generics. + type Value; +} + +/// Types implementing this trait are guaranteed to always use the same amount +/// of storage cells as described by the [`StorageSize`] trait. +/// +/// It is a bug to implement this trait for a type that does not respect this +/// behaviour. +pub trait SaturatingStorage: StorageSize {} + macro_rules! impl_storage_size_for_primitive { ( $($ty:ty),* ) => { $( impl StorageSize for $ty { const SIZE: u64 = 1; } + impl StorageFootprint for $ty { + type Value = P1; + } + impl SaturatingStorage for $ty {} )* }; } @@ -43,11 +89,30 @@ macro_rules! impl_storage_size_for_array { { const SIZE: u64 = ::SIZE * $n; } + impl SaturatingStorage for [T; $n] + where + T: SaturatingStorage, + {} )* }; } forward_supported_array_lens!(impl_storage_size_for_array); +macro_rules! impl_storage_size_for_array2 { + ( $(($n:literal, $t:ty)),* $(,)? ) => { + $( + impl StorageFootprint for [T; $n] + where + T: StorageFootprint, + ::Value: Mul<$t>, + { + type Value = Prod<::Value, $t>; + } + )* + }; +} +forward_supported_array_lens_ty!(impl_storage_size_for_array2); + macro_rules! impl_storage_size_tuple { ( $($frag:ident),* $(,)? ) => { #[allow(unused_parens)] @@ -63,6 +128,12 @@ macro_rules! impl_storage_size_tuple { )* ; } + impl<$($frag),*> SaturatingStorage for ($($frag),* ,) + where + $( + $frag: SaturatingStorage, + )* + {} } } impl_storage_size_tuple!(A); @@ -76,13 +147,44 @@ impl_storage_size_tuple!(A, B, C, D, E, F, G, H); impl_storage_size_tuple!(A, B, C, D, E, F, G, H, I); impl_storage_size_tuple!(A, B, C, D, E, F, G, H, I, J); +macro_rules! impl_storage_size_tuple { + ( $($frag:ident),* ) => { + impl StorageFootprint for (T1 $(, $frag)* ,) + where + T1: StorageFootprint, + ($($frag ,)*): StorageFootprint, + ::Value: Add<<($($frag ,)*) as StorageFootprint>::Value>, + { + type Value = Sum<::Value, <($($frag ,)*) as StorageFootprint>::Value>; + } + } +} +impl_storage_size_tuple!(); +impl_storage_size_tuple!(T2); +impl_storage_size_tuple!(T2, T3); +impl_storage_size_tuple!(T2, T3, T4); +impl_storage_size_tuple!(T2, T3, T4, T5); +impl_storage_size_tuple!(T2, T3, T4, T5, T6); +impl_storage_size_tuple!(T2, T3, T4, T5, T6, T7); +impl_storage_size_tuple!(T2, T3, T4, T5, T6, T7, T8); +impl_storage_size_tuple!(T2, T3, T4, T5, T6, T7, T8, T9); +impl_storage_size_tuple!(T2, T3, T4, T5, T6, T7, T8, T9, T10); + impl StorageSize for () { const SIZE: u64 = 0; } +impl StorageFootprint for () { + type Value = Z0; +} +impl SaturatingStorage for () {} impl StorageSize for core::marker::PhantomData { const SIZE: u64 = 0; } +impl StorageFootprint for core::marker::PhantomData { + type Value = Z0; +} +impl SaturatingStorage for core::marker::PhantomData {} impl StorageSize for Option where @@ -90,6 +192,24 @@ where { const SIZE: u64 = ::SIZE; } +impl StorageFootprint for Option +where + T: StorageFootprint, +{ + type Value = ::Value; +} +impl SaturatingStorage for Option +where + T: SaturatingStorage, +{ + // Actually whether `SaturatingStorage` for `Option` should be + // implemented is an interesting question since it either takes up no + // storage cells in the current implementation or it takes the same amount + // of storage cells as `T`. + // But since the amount of storage cells taken can always be derived from + // the current state of the `Option` (`Some` or `None`) and compile-time + // determined by `T` it should be okay to implement. +} impl StorageSize for Result where @@ -105,6 +225,24 @@ where }; } +impl StorageFootprint for Result +where + T: StorageFootprint, + E: StorageFootprint, + ::Value: Max<::Value>, +{ + type Value = Maximum<::Value, ::Value>; +} + +impl SaturatingStorage for Result +where + T: StorageFootprint + SaturatingStorage, + E: StorageFootprint + SaturatingStorage, + ::Value: + IsEqual<::Value, Output = True>, +{ +} + impl StorageSize for ink_prelude::boxed::Box where T: StorageSize, @@ -112,30 +250,58 @@ where const SIZE: u64 = ::SIZE; } +impl StorageFootprint for ink_prelude::boxed::Box +where + T: StorageFootprint, +{ + type Value = ::Value; +} + +impl SaturatingStorage for Box where T: SaturatingStorage {} + impl StorageSize for ink_prelude::string::String { const SIZE: u64 = 1; } -impl StorageSize for ink_prelude::vec::Vec { - const SIZE: u64 = 1; +impl StorageFootprint for ink_prelude::string::String { + type Value = P1; } -impl StorageSize for ink_prelude::collections::BinaryHeap { +impl SaturatingStorage for String {} + +impl StorageSize for ink_prelude::vec::Vec { const SIZE: u64 = 1; } -impl StorageSize for ink_prelude::collections::BTreeSet { - const SIZE: u64 = 1; +impl StorageFootprint for ink_prelude::vec::Vec { + type Value = P1; } +impl SaturatingStorage for ink_prelude::vec::Vec {} + impl StorageSize for ink_prelude::collections::BTreeMap { const SIZE: u64 = 1; } -impl StorageSize for ink_prelude::collections::VecDeque { - const SIZE: u64 = 1; +impl StorageFootprint for ink_prelude::collections::BTreeMap { + type Value = P1; } -impl StorageSize for ink_prelude::collections::LinkedList { - const SIZE: u64 = 1; +impl SaturatingStorage for ink_prelude::collections::BTreeMap {} + +macro_rules! impl_storage_size_for_collection { + ( $($name:ident),* $(,)? ) => { + $( + impl StorageSize for ink_prelude::collections::$name { + const SIZE: u64 = 1; + } + + impl StorageFootprint for ink_prelude::collections::$name { + type Value = P1; + } + + impl SaturatingStorage for ink_prelude::collections::$name {} + )* + }; } +impl_storage_size_for_collection!(BinaryHeap, BTreeSet, VecDeque, LinkedList,); From b6d38e1afbe88bb96b0107adca7bceed6787097d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 5 Mar 2020 13:13:16 +0100 Subject: [PATCH 067/142] [core] rename storage_size.rs -> footprint.rs --- core/src/storage/traits/{storage_size.rs => footprint.rs} | 0 core/src/storage/traits/mod.rs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename core/src/storage/traits/{storage_size.rs => footprint.rs} (100%) diff --git a/core/src/storage/traits/storage_size.rs b/core/src/storage/traits/footprint.rs similarity index 100% rename from core/src/storage/traits/storage_size.rs rename to core/src/storage/traits/footprint.rs diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index d16309cbfc2..9405d993a4f 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -51,7 +51,7 @@ macro_rules! forward_supported_array_lens_ty { mod clear; mod pull; mod push; -mod storage_size; +mod footprint; use ink_primitives::Key; From ce5409332b25ab32e4af49f28747d97b753ae9d6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 5 Mar 2020 13:13:59 +0100 Subject: [PATCH 068/142] [core] fix core::traits re-exports --- core/src/storage/traits/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index 9405d993a4f..757f1ad9da4 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -68,7 +68,11 @@ pub use self::{ PushAt, PushForward, }, - storage_size::StorageSize, + footprint::{ + StorageSize, + StorageFootprint, + SaturatingStorage, + }, }; /// A key pointer. From 15213eb25552b0e91e6159289a7f2c5d080e667b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 5 Mar 2020 15:43:45 +0100 Subject: [PATCH 069/142] [core] implement StorageFootprint for lazy and the new collection types --- core/src/storage/collections2/boxed/traits.rs | 24 ++++++++++++++++++- core/src/storage/collections2/vec/traits.rs | 13 ++++++++++ core/src/storage/lazy/lazy_cell.rs | 8 +++++++ core/src/storage/lazy/lazy_map.rs | 10 ++++++++ core/src/storage/mod.rs | 2 ++ 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/core/src/storage/collections2/boxed/traits.rs b/core/src/storage/collections2/boxed/traits.rs index 9b222d84ab1..267ca8288c4 100644 --- a/core/src/storage/collections2/boxed/traits.rs +++ b/core/src/storage/collections2/boxed/traits.rs @@ -21,13 +21,15 @@ use crate::{ PullForward, PushForward, StorageSize, + StorageFootprint, + SaturatingStorage, }, }; use ink_primitives::Key; impl StorageSize for StorageBox where - T: ClearForward + StorageSize, + T: ClearForward, { /// A boxed entity always uses exactly 1 cell for the key. /// @@ -36,6 +38,26 @@ where const SIZE: u64 = 1; } +impl StorageFootprint for StorageBox +where + T: ClearForward, +{ + /// A boxed entity always uses exactly 1 cell for its storage. + /// + /// The indirectly stored storage entity is not considered because the + /// `StorageSize` is only concerned with inplace storage usage. + type Value = typenum::P1; +} + +impl SaturatingStorage for StorageBox +where + T: ClearForward, +{ + // A boxed entity always uses exactly 1 cell for its storage. + // + // Therefore the associated storage region is always saturated. +} + impl PullForward for StorageBox where T: ClearForward, diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index c70ff1600a8..44fd41e71b2 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -21,8 +21,11 @@ use crate::{ PullForward, PushForward, StorageSize, + StorageFootprint, }, }; +use typenum::Add1; +use core::ops::Add; impl core::ops::Index for StorageVec where @@ -64,6 +67,16 @@ where ::SIZE + as StorageSize>::SIZE; } +impl StorageFootprint for StorageVec +where + T: StorageFootprint, + storage::LazyChunk: StorageFootprint, + as StorageFootprint>::Value: Add, +{ + type Value = + Add1< as StorageFootprint>::Value>; +} + impl PullForward for StorageVec where T: StorageSize, diff --git a/core/src/storage/lazy/lazy_cell.rs b/core/src/storage/lazy/lazy_cell.rs index 26ac74119c3..418224422a7 100644 --- a/core/src/storage/lazy/lazy_cell.rs +++ b/core/src/storage/lazy/lazy_cell.rs @@ -18,6 +18,7 @@ use super::super::{ PullForward, PushForward, StorageSize, + StorageFootprint, }; use core::cell::UnsafeCell; use ink_primitives::Key; @@ -91,6 +92,13 @@ where const SIZE: u64 = ::SIZE; } +impl StorageFootprint for LazyCell +where + T: StorageFootprint, +{ + type Value = ::Value; +} + impl PullForward for LazyCell where T: StorageSize, diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index 66e515f6a2f..4159dc3a393 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -17,6 +17,7 @@ use super::super::{ PullForward, PushForward, StorageSize, + StorageFootprint, }; use core::{ cell::{ @@ -33,6 +34,7 @@ use ink_prelude::{ collections::BTreeMap, }; use ink_primitives::Key; +use typenum::Prod; /// The index type used in the lazy storage chunk. pub type Index = u32; @@ -283,6 +285,14 @@ where const SIZE: u64 = ::SIZE * (core::u32::MAX as u64); } +impl StorageFootprint for LazyChunk +where + T: StorageFootprint, + ::Value: core::ops::Mul, +{ + type Value = Prod<::Value, typenum::P4294967296>; +} + impl StorageSize for LazyMapping where T: StorageSize, diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index ad997dbef1f..85fd15dce9f 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -129,6 +129,8 @@ pub use self::{ PushAt, PushForward, StorageSize, + StorageFootprint, + SaturatingStorage, }, }; From 3ecb1324877bdb5639f6971afeaa43ad882be954 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 6 Mar 2020 09:50:03 +0100 Subject: [PATCH 070/142] [core] fix trait bounds for IndexMut impl of storage::Vec --- core/src/storage/collections2/vec/traits.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index 44fd41e71b2..f0615e4e3db 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -22,6 +22,7 @@ use crate::{ PushForward, StorageSize, StorageFootprint, + SaturatingStorage, }, }; use typenum::Add1; @@ -40,7 +41,7 @@ where impl core::ops::IndexMut for StorageVec where - T: StorageSize + PullForward, + T: SaturatingStorage + StorageFootprint + StorageSize + PullForward, { fn index_mut(&mut self, index: u32) -> &mut Self::Output { self.get_mut(index).expect("index out of bounds") From 1489cdf40102363483c1fe3aca86f4838a149fec Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 6 Mar 2020 09:50:26 +0100 Subject: [PATCH 071/142] [core] add initial unit test for storage::Vec --- core/src/storage/collections2/vec/mod.rs | 3 +++ core/src/storage/collections2/vec/tests.rs | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 core/src/storage/collections2/vec/tests.rs diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 22dd5bfe992..1f13f13df77 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -15,6 +15,9 @@ mod iter; mod traits; +#[cfg(test)] +mod tests; + pub use self::iter::Iter; use crate::{ storage, diff --git a/core/src/storage/collections2/vec/tests.rs b/core/src/storage/collections2/vec/tests.rs new file mode 100644 index 00000000000..f7e089800fe --- /dev/null +++ b/core/src/storage/collections2/vec/tests.rs @@ -0,0 +1,13 @@ +use super::Vec as StorageVec; + +#[test] +fn new_vec_works() { + let vec = >::new(); + assert!(vec.is_empty()); + assert_eq!(vec.len(), 0); + assert!(vec.iter().next().is_none()); + let default = as Default>::default(); + assert!(default.is_empty()); + assert_eq!(default.len(), 0); + assert!(default.iter().next().is_none()); +} From de11554ba94480bc25d4b5050661a36dd23ec0a6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 6 Mar 2020 09:50:54 +0100 Subject: [PATCH 072/142] [core] refactor and add storage::Vec::{first, first_mut, last, last_mut} --- core/src/storage/collections2/vec/mod.rs | 75 ++++++++++++++++-------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 1f13f13df77..ac288bd0722 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -24,6 +24,8 @@ use crate::{ storage::{ PullForward, StorageSize, + StorageFootprint, + SaturatingStorage, }, }; @@ -82,29 +84,6 @@ impl Vec where T: StorageSize + PullForward, { - /// Appends an element to the back of the vector. - pub fn push(&mut self, value: T) { - assert!( - self.len() < core::u32::MAX, - "cannot push more elements into the storage vector" - ); - let last_index = self.len(); - *self.len += 1; - self.elems.put(last_index, Some(value)); - } - - /// Pops the last element from the vector and returns it. - // - /// Returns `None` if the vector is empty. - pub fn pop(&mut self) -> Option { - if self.is_empty() { - return None - } - let last_index = self.len() - 1; - *self.len = last_index; - self.elems.take(last_index) - } - /// Returns an iterator over the references of all elements stored in the vector. /// /// # Note @@ -124,6 +103,17 @@ where None } + /// Returns a shared reference to the first element if any. + pub fn first(&self) -> Option<&T> { + self.get(0) + } + + /// Returns a shared reference to the last element if any. + pub fn last(&self) -> Option<&T> { + let last_index = self.len() - 1; + self.get(last_index) + } + /// Returns a shared reference to the indexed element. /// /// Returns `None` if `index` is out of bounds. @@ -131,6 +121,45 @@ where self.within_bounds(index) .and_then(|index| self.elems.get(index)) } +} + +impl Vec +where + T: StorageFootprint + SaturatingStorage + StorageSize + PullForward, +{ + /// Appends an element to the back of the vector. + pub fn push(&mut self, value: T) { + assert!( + self.len() < core::u32::MAX, + "cannot push more elements into the storage vector" + ); + let last_index = self.len(); + *self.len += 1; + self.elems.put(last_index, Some(value)); + } + + /// Pops the last element from the vector and returns it. + // + /// Returns `None` if the vector is empty. + pub fn pop(&mut self) -> Option { + if self.is_empty() { + return None + } + let last_index = self.len() - 1; + *self.len = last_index; + self.elems.take(last_index) + } + + /// Returns an exclusive reference to the first element if any. + pub fn first_mut(&mut self) -> Option<&mut T> { + self.get_mut(0) + } + + /// Returns an exclusive reference to the last element if any. + pub fn last_mut(&mut self) -> Option<&mut T> { + let last_index = self.len() - 1; + self.get_mut(last_index) + } /// Returns an exclusive reference to the indexed element. /// From cb20d9a5f218c5bec4cf1631425af66823d3ff9c Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 6 Mar 2020 10:38:17 +0100 Subject: [PATCH 073/142] [core] add LazyMap::remove --- core/src/storage/lazy/lazy_map.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index 4159dc3a393..a2bb6a80068 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -447,6 +447,24 @@ where pub fn take(&mut self, index: K) -> Option { self.lazily_load_mut(index).take_value() } + + /// Removes the element at the given index if any. + pub fn remove(&mut self, index: K) { + use ink_prelude::collections::btree_map::Entry as BTreeMapEntry; + match self.entries_mut().entry(index) { + BTreeMapEntry::Vacant(vacant) => { + vacant.insert(Box::new(Entry { + value: None, + mutated: Cell::new(true), + })); + } + BTreeMapEntry::Occupied(occupied) => { + let entry = occupied.into_mut(); + entry.value = None; + entry.mutated = Cell::new(true); + } + } + } } impl LazyMap From b4ea55928b7c71e035108743e58dc19852ae8d69 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 6 Mar 2020 10:38:31 +0100 Subject: [PATCH 074/142] [core] add storage::Vec2::pop_drop --- core/src/storage/collections2/vec/mod.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index ac288bd0722..8854f4a80d4 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -150,6 +150,22 @@ where self.elems.take(last_index) } + /// Pops the last element from the vector and immediately drops it. + /// + /// Does nothing if the vector is empty. + /// + /// # Note + /// + /// This operation is a bit more efficient than [`Self::pop`] for some use cases. + pub fn pop_drop(&mut self) { + if self.is_empty() { + return + } + let last_index = self.len() - 1; + *self.len = last_index; + self.elems.remove(last_index); + } + /// Returns an exclusive reference to the first element if any. pub fn first_mut(&mut self) -> Option<&mut T> { self.get_mut(0) From aac1444381f7cd92e19fe313663854679200bc82 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 6 Mar 2020 10:40:45 +0100 Subject: [PATCH 075/142] [core] add note to LazyMap::remove docs --- core/src/storage/lazy/lazy_map.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index a2bb6a80068..e1c62eb6897 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -449,6 +449,12 @@ where } /// Removes the element at the given index if any. + /// + /// # Note + /// + /// This method should be preferred over [`Self::take`] in cases where the + /// caller is not interested in the taken (removed) value since it is more + /// efficient for some use cases. pub fn remove(&mut self, index: K) { use ink_prelude::collections::btree_map::Entry as BTreeMapEntry; match self.entries_mut().entry(index) { From 88ccce805372d9fdb2d5e3dd5839e8b8de7ca67b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 6 Mar 2020 15:36:37 +0100 Subject: [PATCH 076/142] [core] improve doc comment of LazyMap::new --- core/src/storage/lazy/lazy_map.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index e1c62eb6897..c8515476ee2 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -228,7 +228,12 @@ impl LazyMap where K: Ord, { - /// Creates a new empty lazy chunk that cannot be mutated. + /// Creates a new empty lazy map. + /// + /// # Note + /// + /// A lazy map created this way cannot be used to load from the contract storage. + /// All operations that directly or indirectly load from storage will panic. pub fn new() -> Self { Self { key: None, From fcba40cf15d3767fb02a2f76d9fba20cf11dbb0f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 11 Mar 2020 10:05:29 +0100 Subject: [PATCH 077/142] [core] add KeyPtr::next_for2 that makes use of StorageFootprint --- core/src/storage/traits/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index 757f1ad9da4..7b77f463ce2 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -106,4 +106,16 @@ impl KeyPtr { self.key += ::SIZE; copy } + + /// Advances the key by the given amount derive by `T` and its `StorageSize` + /// and returns the next `Key` for usage by the storage element. + pub fn next_for2(&mut self) -> Key + where + T: StorageFootprint, + ::Value: typenum::Integer, + { + let copy = self.key; + self.key += <::Value as typenum::Integer>::to_i64() as u64; + copy + } } From 1c7a6dc555caf5c99b2914220cd84766cc800825 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 11 Mar 2020 10:06:15 +0100 Subject: [PATCH 078/142] [core] add Entry for lazy abstractions --- core/src/storage/lazy/entry.rs | 131 +++++++++++++++++++++++++++++++++ core/src/storage/lazy/mod.rs | 2 + 2 files changed, 133 insertions(+) create mode 100644 core/src/storage/lazy/entry.rs diff --git a/core/src/storage/lazy/entry.rs b/core/src/storage/lazy/entry.rs new file mode 100644 index 00000000000..421479bbc37 --- /dev/null +++ b/core/src/storage/lazy/entry.rs @@ -0,0 +1,131 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::storage::{ + KeyPtr, + PushForward, + StorageSize, +}; +use core::cell::Cell; + +/// The entry of a single cached value of a lazy storage data structure. +#[derive(Debug, Clone)] +pub struct Entry { + /// The value or `None` if the value has been removed. + value: Option, + /// This is `true` if the `value` is dirty and needs to be synchronized + /// with the underlying contract storage. + state: Cell, +} + +/// The state of the entry. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum EntryState { + /// The entry's value must be synchronized with the contract storage. + Mutated, + /// The entry's value preserved the value from the contract storage. + Preserved, +} + +impl PushForward for Entry +where + T: PushForward + StorageSize, +{ + fn push_forward(&self, ptr: &mut KeyPtr) { + // Reset the state because we just synced. + self.state.set(EntryState::Preserved); + // Since `self.value` is of type `Option` this will eventually + // clear the underlying storage entry if `self.value` is `None`. + self.value.push_forward(ptr); + } +} + +impl Entry { + /// Creates a new entry with the value and state. + pub fn new(value: Option, state: EntryState) -> Self { + Self { + value, + state: Cell::new(state), + } + } + + /// Returns `true` if the cached value of the entry has potentially been mutated. + pub fn mutated(&self) -> bool { + self.state.get() == EntryState::Mutated + } + + /// Returns `true` if the cached value of the entry has potentially been mutated. + pub fn is_mutated(&self) -> bool { + self.state.get() == EntryState::Mutated + } + + /// Returns a shared reference to the value of the entry. + pub fn value(&self) -> Option<&T> { + self.value.as_ref() + } + + /// Returns an exclusive reference to the value of the entry. + /// + /// # Note + /// + /// This changes the `mutate` state of the entry if the entry was occupied + /// since the caller could potentially change the returned value. + pub fn value_mut(&mut self) -> Option<&mut T> { + self.state.set( + if self.value.is_some() { + EntryState::Mutated + } else { + EntryState::Preserved + }, + ); + self.value.as_mut() + } + + /// Takes the value from the entry and returns it. + /// + /// # Note + /// + /// This changes the `mutate` state of the entry if the entry was occupied. + pub fn take_value(&mut self) -> Option { + self.state.set( + if self.value.is_some() { + EntryState::Mutated + } else { + EntryState::Preserved + }, + ); + self.value.take() + } + + /// Converts the entry into its value. + pub fn into_value(self) -> Option { + self.value + } + + /// Puts the new value into the entry and returns the old value. + /// + /// # Note + /// + /// This changes the `mutate` state of the entry to `true` as long as at + /// least one of `old_value` and `new_value` is `Some`. + pub fn put(&mut self, new_value: Option) -> Option { + match new_value { + Some(new_value) => { + self.state.set(EntryState::Mutated); + self.value.replace(new_value) + } + None => self.take_value(), + } + } +} diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index 56ce4d79a85..ec2c0ef89aa 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod entry; mod lazy_cell; mod lazy_map; +use self::entry::{Entry, EntryState}; pub use self::{ lazy_cell::LazyCell, lazy_map::{ From ab21767da7e5844d19bcd5fd4fa3d040780686c8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 11 Mar 2020 10:06:29 +0100 Subject: [PATCH 079/142] [core] initial (unfinished) implementation of LazyArray --- core/src/storage/lazy/lazy_array.rs | 289 ++++++++++++++++++++++++++++ core/src/storage/lazy/mod.rs | 2 + core/src/storage/mod.rs | 1 + 3 files changed, 292 insertions(+) create mode 100644 core/src/storage/lazy/lazy_array.rs diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs new file mode 100644 index 00000000000..72c94059632 --- /dev/null +++ b/core/src/storage/lazy/lazy_array.rs @@ -0,0 +1,289 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{ + Entry, + EntryState, +}; +use crate::storage::{ + KeyPtr, + PullForward, + PushForward, + SaturatingStorage, + StorageFootprint, +}; +use core::{ + cell::UnsafeCell, + mem, + ops::Mul, +}; +use ink_primitives::Key; +use typenum::{ + Integer, + Prod, + P32, +}; + +/// The index type used in the lazy storage chunk. +pub type Index = u32; + +/// The number of cached elements a lazy array holds. +const SIZE: usize = 32; + +/// A lazy storage array that spans over 32 storage cells. +/// +/// # Note +/// +/// Computes operations on the underlying 32 storage cells in a lazy fashion. +/// Due to the size constraints the `LazyArray` is generally more efficient +/// than the [`super::LazyMap`] for most use cases with limited elements. +/// +/// This is mainly used as low-level storage primitives by other high-level +/// storage primitives in order to manage the contract storage for a whole +/// chunk of storage cells. +#[derive(Debug)] +pub struct LazyArray { + /// The offset key for the 32 cells. + /// + /// If the lazy chunk has been initialized during contract initialization + /// the key will be `None` since there won't be a storage region associated + /// to the lazy chunk which prevents it from lazily loading elements. This, + /// however, is only checked at contract runtime. We might incorporate + /// compile-time checks for this particular use case later on. + key: Option, + /// The subset of currently cached entries of the lazy storage chunk. + /// + /// An entry is cached as soon as it is loaded or written. + cached_entries: UnsafeCell>, +} + +/// The underlying array cache for the [`LazyArray`]. +#[derive(Debug)] +pub struct EntryArray { + /// The cache entries of the entry array. + entries: [Option>; SIZE], +} + +impl EntryArray { + pub fn new() -> Self { + Self { + entries: Default::default(), + } + } +} + +impl Default for EntryArray { + fn default() -> Self { + Self::new() + } +} + +impl EntryArray { + /// Puts the the new value into the indexed slot and + /// returns the old value if any. + fn put(&mut self, at: Index, new_value: Option) -> Option { + mem::replace( + &mut self.entries[at as usize], + Some(Entry::new(new_value, EntryState::Mutated)), + ) + .map(Entry::into_value) + .flatten() + } + + /// Returns a shared reference to the value at the given index if any. + fn get(&self, at: Index) -> Option<&T> { + self.entries[at as usize] + .as_ref() + .map(Entry::value) + .flatten() + } + + /// Returns a shared reference to the value at the given index if any. + fn get_mut(&mut self, at: Index) -> Option<&mut T> { + self.entries[at as usize] + .as_mut() + .map(Entry::value_mut) + .flatten() + } +} + +impl LazyArray { + /// Creates a new empty lazy array. + /// + /// # Note + /// + /// A lazy array created this way cannot be used to load from the contract storage. + /// All operations that directly or indirectly load from storage will panic. + pub fn new() -> Self { + Self { + key: None, + cached_entries: UnsafeCell::new(Default::default()), + } + } + + /// Returns the offset key of the lazy array if any. + pub fn key(&self) -> Option<&Key> { + self.key.as_ref() + } + + /// Returns a shared reference to the underlying cached entries. + /// + /// # Safety + /// + /// This operation is safe since it returns a shared reference from + /// a `&self` which is viable in safe Rust. + fn cached_entries(&self) -> &EntryArray { + unsafe { &*self.cached_entries.get() } + } + + /// Returns an exclusive reference to the underlying cached entries. + /// + /// # Safety + /// + /// This operation is safe since it returns an exclusive reference from + /// a `&mut self` which is viable in safe Rust. + fn cached_entries_mut(&mut self) -> &mut EntryArray { + unsafe { &mut *self.cached_entries.get() } + } + + /// Puts a new value into the given indexed slot. + /// + /// # Note + /// + /// Use [`Self::put_get`]`(None)` to remove an element. + pub fn put(&mut self, at: Index, new_value: Option) { + self.cached_entries_mut().put(at, new_value); + } +} + +impl StorageFootprint for LazyArray +where + T: StorageFootprint, + ::Value: Mul, +{ + type Value = Prod<::Value, P32>; +} + +impl PushForward for LazyArray +where + Self: StorageFootprint, + ::Value: Integer, + T: StorageFootprint + SaturatingStorage + PushForward, + ::Value: Integer, +{ + fn push_forward(&self, ptr: &mut KeyPtr) { + let offset_key = ptr.next_for2::(); + for (index, entry) in self.cached_entries().entries.iter().enumerate() { + if let Some(entry) = entry { + if !entry.is_mutated() { + continue + } + let footprint = + <::Value as Integer>::to_i64() as u64; + let key = offset_key + (index as u64 * footprint); + match entry.value() { + Some(value) => { + // Update associated storage entries. + ::push_forward(value, &mut KeyPtr::from(key)) + } + None => { + // Clean-up associated storage entries. + if footprint > 32 { + // Bail out if footprint is too big. + // + // TODO: + // - Use compile-time solution to prevent situations like these. + // This should be simple now since we are using `typenum` instead + // of associated constants. + return + } + use crate::env; + for i in 0..footprint as u32 { + env::clear_contract_storage(key + i); + } + } + } + } + } + } +} + +impl LazyArray +where + T: StorageFootprint, + ::Value: Integer, +{ + /// Returns the offset key for the given index if not out of bounds. + pub fn key_at(&self, at: Index) -> Option { + if at as usize >= SIZE { + return None + } + self.key.map(|key| { + key + ((at as u64) + * <::Value as Integer>::to_i64() as u64) + }) + } +} + +impl LazyArray +where + T: StorageFootprint + PullForward, +{ + /// Returns a shared reference to the element at the given index if any. + /// + /// # Note + /// + /// This operation eventually loads from contract storage. + pub fn get(&self, at: Index) -> Option<&T> { + todo!() + } + + /// Returns an exclusive reference to the element at the given index if any. + /// + /// # Note + /// + /// This operation eventually loads from contract storage. + pub fn get_mut(&mut self, at: Index) -> Option<&mut T> { + todo!() + } + + /// Removes the element at the given index and returns it if any. + /// + /// # Note + /// + /// This operation eventually loads from contract storage. + pub fn take(&mut self, at: Index) -> Option { + todo!() + } + + /// Puts the new value into the indexed slot and returns the old value if any. + /// + /// # Note + /// + /// - This operation eventually loads from contract storage. + /// - Prefer [`Self::put`] if you are not interested in the old value. + /// - Use [`Self::put_get`]`(None)` to remove an element. + pub fn put_get(&mut self, at: Index, new_value: Option) -> Option { + todo!() + } + + /// Swaps the values at indices x and y. + /// + /// # Note + /// + /// This operation eventually loads from contract storage. + pub fn swap(&mut self, a: Index, b: Index) { + todo!() + } +} diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index ec2c0ef89aa..71e702f946e 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -13,11 +13,13 @@ // limitations under the License. mod entry; +mod lazy_array; mod lazy_cell; mod lazy_map; use self::entry::{Entry, EntryState}; pub use self::{ + lazy_array::LazyArray, lazy_cell::LazyCell, lazy_map::{ LazyChunk, diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 85fd15dce9f..f69349b6ab8 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -118,6 +118,7 @@ pub use self::{ LazyChunk, LazyMap, LazyMapping, + LazyArray, }, pack::Pack, traits::{ From 6df087c38c56a8a5c63dc72a455055165de0404d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 12 Mar 2020 11:35:47 +0100 Subject: [PATCH 080/142] [core] fix and improve some doc comments --- core/src/storage/collections2/vec/mod.rs | 2 +- core/src/storage/lazy/lazy_array.rs | 9 +++++---- core/src/storage/lazy/lazy_map.rs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 8854f4a80d4..a160ad011b1 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -156,7 +156,7 @@ where /// /// # Note /// - /// This operation is a bit more efficient than [`Self::pop`] for some use cases. + /// This operation is a bit more efficient than [`Vec::pop`] for some use cases. pub fn pop_drop(&mut self) { if self.is_empty() { return diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs index 72c94059632..f0e9e36fe3f 100644 --- a/core/src/storage/lazy/lazy_array.rs +++ b/core/src/storage/lazy/lazy_array.rs @@ -47,7 +47,7 @@ const SIZE: usize = 32; /// /// Computes operations on the underlying 32 storage cells in a lazy fashion. /// Due to the size constraints the `LazyArray` is generally more efficient -/// than the [`super::LazyMap`] for most use cases with limited elements. +/// than the [`LazyMap`](`super::LazyMap`) for most use cases with limited elements. /// /// This is mainly used as low-level storage primitives by other high-level /// storage primitives in order to manage the contract storage for a whole @@ -76,6 +76,7 @@ pub struct EntryArray { } impl EntryArray { + /// Creates a new entry array cache. pub fn new() -> Self { Self { entries: Default::default(), @@ -161,7 +162,7 @@ impl LazyArray { /// /// # Note /// - /// Use [`Self::put_get`]`(None)` to remove an element. + /// Use [`LazyArray::put_get`]`(None)` to remove an element. pub fn put(&mut self, at: Index, new_value: Option) { self.cached_entries_mut().put(at, new_value); } @@ -272,8 +273,8 @@ where /// # Note /// /// - This operation eventually loads from contract storage. - /// - Prefer [`Self::put`] if you are not interested in the old value. - /// - Use [`Self::put_get`]`(None)` to remove an element. + /// - Prefer [`LazyArray::put`] if you are not interested in the old value. + /// - Use [`LazyArray::put_get`]`(None)` to remove an element. pub fn put_get(&mut self, at: Index, new_value: Option) -> Option { todo!() } diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index c8515476ee2..ea49cade762 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -457,7 +457,7 @@ where /// /// # Note /// - /// This method should be preferred over [`Self::take`] in cases where the + /// This method should be preferred over [`LazyMap::take`] in cases where the /// caller is not interested in the taken (removed) value since it is more /// efficient for some use cases. pub fn remove(&mut self, index: K) { From 5fee924cdd317c6c07475cf730f69691a2e7cf66 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 11:25:20 +0100 Subject: [PATCH 081/142] [core] refactor LazyMap to use NonNull instead of *mut --- core/src/storage/lazy/lazy_map.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index ea49cade762..47edcac7902 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -16,8 +16,8 @@ use super::super::{ KeyPtr, PullForward, PushForward, - StorageSize, StorageFootprint, + StorageSize, }; use core::{ cell::{ @@ -28,6 +28,7 @@ use core::{ Eq, Ord, }, + ptr::NonNull, }; use ink_prelude::{ boxed::Box, @@ -218,9 +219,7 @@ where K: Ord, { self.key() - .map(|key| { - >::to_storage_key(at.borrow(), &key) - }) + .map(|key| >::to_storage_key(at.borrow(), &key)) } } @@ -367,7 +366,7 @@ where /// a `*mut Entry` pointer that allows for exclusive access. This is safe /// within internal use only and should never be given outside of the lazy /// entity for public `&self` methods. - unsafe fn lazily_load(&self, index: K) -> *mut Entry { + unsafe fn lazily_load(&self, index: K) -> NonNull> { let key = match self.key { Some(key) => key, None => panic!("cannot load lazily in this state"), @@ -386,12 +385,14 @@ where use ink_prelude::collections::btree_map::Entry as BTreeMapEntry; let off_key = >::to_storage_key(&index, &key); match cached_entries.entry(index) { - BTreeMapEntry::Occupied(occupied) => &mut **occupied.into_mut(), + BTreeMapEntry::Occupied(occupied) => { + NonNull::from(&mut **occupied.into_mut()) + } BTreeMapEntry::Vacant(vacant) => { let value = as PullForward>::pull_forward(&mut KeyPtr::from(off_key)); let mutated = Cell::new(false); - &mut **vacant.insert(Box::new(Entry { value, mutated })) + NonNull::from(&mut **vacant.insert(Box::new(Entry { value, mutated }))) } } } @@ -412,7 +413,7 @@ where // - Returning a `&mut Entry` is safe because entities inside the // cache are stored within a `Box` to not invalidate references into // them upon operating on the outer cache. - unsafe { &mut *self.lazily_load(index) } + unsafe { self.lazily_load(index).as_mut() } } /// Returns a shared reference to the element at the given index if any. @@ -425,7 +426,7 @@ where // SAFETY: Dereferencing the `*mut T` pointer into a `&T` is safe // since this method's receiver is `&self` so we do not // leak non-shared references to the outside. - let entry: &Entry = unsafe { &*self.lazily_load(index) }; + let entry: &Entry = unsafe { self.lazily_load(index).as_ref() }; entry.value() } @@ -517,7 +518,7 @@ where // guarantees to return a pointer to a pinned entity // so that the returned references do not conflict with // each other. - unsafe { (&mut *self.lazily_load(x), &mut *self.lazily_load(y)) }; + unsafe { (self.lazily_load(x).as_mut(), self.lazily_load(y).as_mut()) }; if loaded_x.value.is_none() && loaded_y.value.is_none() { // Bail out since nothing has to be swapped if both values are `None`. return From 7522a8096f91b4b1c4f7c0d9e15d3d2fa6aac90f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 11:32:43 +0100 Subject: [PATCH 082/142] [core] fix some simple lifetime issues from last refactoring --- core/src/storage/lazy/lazy_map.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index 47edcac7902..db83ee5f109 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -413,7 +413,7 @@ where // - Returning a `&mut Entry` is safe because entities inside the // cache are stored within a `Box` to not invalidate references into // them upon operating on the outer cache. - unsafe { self.lazily_load(index).as_mut() } + unsafe { &mut *self.lazily_load(index).as_ptr() } } /// Returns a shared reference to the element at the given index if any. @@ -426,8 +426,7 @@ where // SAFETY: Dereferencing the `*mut T` pointer into a `&T` is safe // since this method's receiver is `&self` so we do not // leak non-shared references to the outside. - let entry: &Entry = unsafe { self.lazily_load(index).as_ref() }; - entry.value() + unsafe { &*self.lazily_load(index).as_ptr() }.value() } /// Returns an exclusive reference to the element at the given index if any. @@ -518,7 +517,10 @@ where // guarantees to return a pointer to a pinned entity // so that the returned references do not conflict with // each other. - unsafe { (self.lazily_load(x).as_mut(), self.lazily_load(y).as_mut()) }; + unsafe { ( + &mut *self.lazily_load(x).as_ptr(), + &mut *self.lazily_load(y).as_ptr(), + ) }; if loaded_x.value.is_none() && loaded_y.value.is_none() { // Bail out since nothing has to be swapped if both values are `None`. return From 4f2a05e1c5f98e6add382acdfb250b256df30b74 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 13:07:24 +0100 Subject: [PATCH 083/142] [core] remove lazy::Entry::mutated --- core/src/storage/lazy/entry.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/storage/lazy/entry.rs b/core/src/storage/lazy/entry.rs index 421479bbc37..cf8665acfe2 100644 --- a/core/src/storage/lazy/entry.rs +++ b/core/src/storage/lazy/entry.rs @@ -60,9 +60,6 @@ impl Entry { } } - /// Returns `true` if the cached value of the entry has potentially been mutated. - pub fn mutated(&self) -> bool { - self.state.get() == EntryState::Mutated } /// Returns `true` if the cached value of the entry has potentially been mutated. From 70cc10882d97a5ce9f88b54b9cbe71d43a66afa4 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 13:07:40 +0100 Subject: [PATCH 084/142] [core] add Entry::set_state --- core/src/storage/lazy/entry.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/storage/lazy/entry.rs b/core/src/storage/lazy/entry.rs index cf8665acfe2..d8357974eb9 100644 --- a/core/src/storage/lazy/entry.rs +++ b/core/src/storage/lazy/entry.rs @@ -60,6 +60,9 @@ impl Entry { } } + /// Sets the entry state to the new state. + pub fn set_state(&mut self, new_state: EntryState) { + self.state.set(new_state); } /// Returns `true` if the cached value of the entry has potentially been mutated. From f295c1f7b58c1b060288b00e1eb0956207036d38 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 13:08:04 +0100 Subject: [PATCH 085/142] [core] make LazyMap lazy loading work if fetched from cache --- core/src/storage/lazy/lazy_map.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index db83ee5f109..2cccd908723 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -339,7 +339,7 @@ where impl LazyMap where - K: KeyMapping + Ord + Eq, + K: KeyMapping + Ord + Eq + Clone, V: StorageSize + PullForward, { /// Lazily loads the value at the given index. @@ -367,10 +367,6 @@ where /// within internal use only and should never be given outside of the lazy /// entity for public `&self` methods. unsafe fn lazily_load(&self, index: K) -> NonNull> { - let key = match self.key { - Some(key) => key, - None => panic!("cannot load lazily in this state"), - }; // SAFETY: We have put the whole `cached_entries` mapping into an // `UnsafeCell` because of this caching functionality. The // trick here is that due to using `Box` internally @@ -383,12 +379,14 @@ where #[allow(unused_unsafe)] let cached_entries = unsafe { &mut *self.cached_entries.get() }; use ink_prelude::collections::btree_map::Entry as BTreeMapEntry; - let off_key = >::to_storage_key(&index, &key); + let index_copy = index.clone(); match cached_entries.entry(index) { BTreeMapEntry::Occupied(occupied) => { NonNull::from(&mut **occupied.into_mut()) } BTreeMapEntry::Vacant(vacant) => { + let key = self.key.expect("cannot load lazily in this state"); + let off_key = >::to_storage_key(&index_copy, &key); let value = as PullForward>::pull_forward(&mut KeyPtr::from(off_key)); let mutated = Cell::new(false); @@ -480,7 +478,7 @@ where impl LazyMap where - K: KeyMapping + Ord + Eq, + K: KeyMapping + Ord + Eq + Clone, V: StorageSize + PullForward, { /// Puts the new value at the given index and returns the old value if any. From a278b304975da2e924589cb92e411e2f1468eba3 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 13:08:20 +0100 Subject: [PATCH 086/142] [core] implement missing pieces from LazyArray --- core/src/storage/lazy/lazy_array.rs | 138 +++++++++++++++++++++++----- 1 file changed, 116 insertions(+), 22 deletions(-) diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs index f0e9e36fe3f..72a5d988442 100644 --- a/core/src/storage/lazy/lazy_array.rs +++ b/core/src/storage/lazy/lazy_array.rs @@ -22,11 +22,13 @@ use crate::storage::{ PushForward, SaturatingStorage, StorageFootprint, + StorageSize, }; use core::{ cell::UnsafeCell, mem, ops::Mul, + ptr::NonNull, }; use ink_primitives::Key; use typenum::{ @@ -38,8 +40,8 @@ use typenum::{ /// The index type used in the lazy storage chunk. pub type Index = u32; -/// The number of cached elements a lazy array holds. -const SIZE: usize = 32; +/// The capacity of a lazy array. +const CAPACITY: usize = 32; /// A lazy storage array that spans over 32 storage cells. /// @@ -72,7 +74,7 @@ pub struct LazyArray { #[derive(Debug)] pub struct EntryArray { /// The cache entries of the entry array. - entries: [Option>; SIZE], + entries: [Option>; CAPACITY], } impl EntryArray { @@ -102,20 +104,19 @@ impl EntryArray { .flatten() } - /// Returns a shared reference to the value at the given index if any. - fn get(&self, at: Index) -> Option<&T> { - self.entries[at as usize] - .as_ref() - .map(Entry::value) - .flatten() + /// Inserts a new entry into the cache and returns an exclusive reference to it. + fn insert_entry(&mut self, at: Index, entry: Entry) -> &mut Entry { + *&mut self.entries[at as usize] = Some(entry); + let entry: Option<&mut Entry> = (&mut self.entries[at as usize]).into(); + entry.expect("just inserted the entry") } - /// Returns a shared reference to the value at the given index if any. - fn get_mut(&mut self, at: Index) -> Option<&mut T> { - self.entries[at as usize] - .as_mut() - .map(Entry::value_mut) - .flatten() + /// Returns an exclusive reference to the entry at the given index if any. + fn get_entry_mut(&mut self, at: Index) -> Option<&mut Entry> { + if at as usize >= CAPACITY { + return None + } + self.entries[at as usize].as_mut() } } @@ -133,6 +134,11 @@ impl LazyArray { } } + /// Returns the constant capacity of the lazy array. + pub const fn capacity() -> u32 { + CAPACITY as u32 + } + /// Returns the offset key of the lazy array if any. pub fn key(&self) -> Option<&Key> { self.key.as_ref() @@ -227,7 +233,7 @@ where { /// Returns the offset key for the given index if not out of bounds. pub fn key_at(&self, at: Index) -> Option { - if at as usize >= SIZE { + if at as usize >= CAPACITY { return None } self.key.map(|key| { @@ -239,15 +245,65 @@ where impl LazyArray where - T: StorageFootprint + PullForward, + T: StorageFootprint + StorageSize + PullForward, + ::Value: Integer, { + /// Loads the entry at the given index. + /// + /// Tries to load the entry from cache and falls back to lazily load the + /// entry from the contract storage. + /// + /// # Panics + /// + /// - If the lazy array is in a state that forbids lazy loading. + /// - If the given index is out of bounds. + fn load_through_cache(&self, at: Index) -> NonNull> { + assert!((at as usize) < CAPACITY, "index is out of bounds"); + let cached_entries = unsafe { &mut *self.cached_entries.get() }; + match cached_entries.get_entry_mut(at) { + Some(entry) => { + // Load value from cache. + NonNull::from(entry) + } + None => { + // Load value from storage and put into cache. + // Then load value from cache. + let key = self.key_at(at).expect("cannot load lazily in this state"); + let value = + as PullForward>::pull_forward(&mut KeyPtr::from(key)); + let entry = Entry::new(value, EntryState::Preserved); + NonNull::from(cached_entries.insert_entry(at, entry)) + } + } + } + + /// Loads the entry at the given index. + /// + /// Tries to load the entry from cache and falls back to lazily load the + /// entry from the contract storage. + /// + /// # Panics + /// + /// - If the lazy array is in a state that forbids lazy loading. + /// - If the given index is out of bounds. + fn load_through_cache_mut(&mut self, index: Index) -> &mut Entry { + // SAFETY: + // Returning a `&mut Entry` from within a `&mut self` function + // won't allow creating aliasing between exclusive references. + unsafe { &mut *self.load_through_cache(index).as_ptr() } + } + /// Returns a shared reference to the element at the given index if any. /// /// # Note /// /// This operation eventually loads from contract storage. + /// + /// # Panics + /// + /// If the given index is out of bounds. pub fn get(&self, at: Index) -> Option<&T> { - todo!() + unsafe { &*self.load_through_cache(at).as_ptr() }.value() } /// Returns an exclusive reference to the element at the given index if any. @@ -255,8 +311,12 @@ where /// # Note /// /// This operation eventually loads from contract storage. + /// + /// # Panics + /// + /// If the given index is out of bounds. pub fn get_mut(&mut self, at: Index) -> Option<&mut T> { - todo!() + self.load_through_cache_mut(at).value_mut() } /// Removes the element at the given index and returns it if any. @@ -264,8 +324,12 @@ where /// # Note /// /// This operation eventually loads from contract storage. + /// + /// # Panics + /// + /// If the given index is out of bounds. pub fn take(&mut self, at: Index) -> Option { - todo!() + self.load_through_cache_mut(at).take_value() } /// Puts the new value into the indexed slot and returns the old value if any. @@ -275,8 +339,12 @@ where /// - This operation eventually loads from contract storage. /// - Prefer [`LazyArray::put`] if you are not interested in the old value. /// - Use [`LazyArray::put_get`]`(None)` to remove an element. + /// + /// # Panics + /// + /// If the given index is out of bounds. pub fn put_get(&mut self, at: Index, new_value: Option) -> Option { - todo!() + self.load_through_cache_mut(at).put(new_value) } /// Swaps the values at indices x and y. @@ -284,7 +352,33 @@ where /// # Note /// /// This operation eventually loads from contract storage. + /// + /// # Panics + /// + /// If any of the given indices is out of bounds. pub fn swap(&mut self, a: Index, b: Index) { - todo!() + assert!((a as usize) < CAPACITY, "a is out of bounds"); + assert!((b as usize) < CAPACITY, "b is out of bounds"); + if a == b { + // Bail out if both indices are equal. + return + } + let (loaded_a, loaded_b) = + // SAFETY: The loaded `x` and `y` entries are distinct from each + // other guaranteed by the previous checks so they cannot + // alias. + unsafe { ( + &mut *self.load_through_cache(a).as_ptr(), + &mut *self.load_through_cache(b).as_ptr(), + ) }; + if loaded_a.value().is_none() && loaded_b.value().is_none() { + // Bail out since nothing has to be swapped if both values are `None`. + return + } + // At this point at least one of the values is `Some` so we have to + // perform the swap and set both entry states to mutated. + loaded_a.set_state(EntryState::Mutated); + loaded_b.set_state(EntryState::Mutated); + core::mem::swap(&mut loaded_a.value_mut(), &mut loaded_b.value_mut()); } } From 467bc2ca01db2ee4f2596eeb8b533d02365031f0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 14:19:40 +0100 Subject: [PATCH 087/142] [core] replace direct usage of CAPACITY with Self::capacity() --- core/src/storage/lazy/lazy_array.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs index 72a5d988442..94543564f51 100644 --- a/core/src/storage/lazy/lazy_array.rs +++ b/core/src/storage/lazy/lazy_array.rs @@ -233,7 +233,7 @@ where { /// Returns the offset key for the given index if not out of bounds. pub fn key_at(&self, at: Index) -> Option { - if at as usize >= CAPACITY { + if at >= Self::capacity() { return None } self.key.map(|key| { @@ -258,7 +258,7 @@ where /// - If the lazy array is in a state that forbids lazy loading. /// - If the given index is out of bounds. fn load_through_cache(&self, at: Index) -> NonNull> { - assert!((at as usize) < CAPACITY, "index is out of bounds"); + assert!(at < Self::capacity(), "index is out of bounds"); let cached_entries = unsafe { &mut *self.cached_entries.get() }; match cached_entries.get_entry_mut(at) { Some(entry) => { @@ -357,8 +357,8 @@ where /// /// If any of the given indices is out of bounds. pub fn swap(&mut self, a: Index, b: Index) { - assert!((a as usize) < CAPACITY, "a is out of bounds"); - assert!((b as usize) < CAPACITY, "b is out of bounds"); + assert!(a < Self::capacity(), "a is out of bounds"); + assert!(b < Self::capacity(), "b is out of bounds"); if a == b { // Bail out if both indices are equal. return From eea79de0e4692fee721aeeed2f13c70f5e184c6e Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 17:18:48 +0100 Subject: [PATCH 088/142] [core] apply rustfmt --- core/src/storage/lazy/mod.rs | 5 ++++- core/src/storage/mod.rs | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index 71e702f946e..b9f469661ef 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -17,7 +17,10 @@ mod lazy_array; mod lazy_cell; mod lazy_map; -use self::entry::{Entry, EntryState}; +use self::entry::{ + Entry, + EntryState, +}; pub use self::{ lazy_array::LazyArray, lazy_cell::LazyCell, diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index f69349b6ab8..9a9bf09da5c 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -114,11 +114,11 @@ pub use self::{ flush::Flush, lazy::{ Lazy, + LazyArray, LazyCell, LazyChunk, LazyMap, LazyMapping, - LazyArray, }, pack::Pack, traits::{ @@ -129,9 +129,9 @@ pub use self::{ PullForward, PushAt, PushForward, - StorageSize, - StorageFootprint, SaturatingStorage, + StorageFootprint, + StorageSize, }, }; From 2d8cafff9b92eb38771a7fd8fa46b7eca407c8cc Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 17:27:57 +0100 Subject: [PATCH 089/142] [core] create LazyArray low-level abstraction with generic N --- core/Cargo.toml | 3 +- core/src/storage/lazy/lazy_array.rs | 111 +++++++++++++++++++++------- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index e788a72e15c..d8a0d518c66 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -28,7 +28,8 @@ smallvec = { version = "1.0", default-features = false, features = ["union"] } cfg-if = "0.1" num-traits = { version = "0.2.1", default-features = false, features = ["i128"] } array-init = "0.1" -typenum = "1.11.2" +typenum = "1.11" +generic-array = "0.13" # Only used in the off-chain environment. # diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs index 94543564f51..b7cdacc1d16 100644 --- a/core/src/storage/lazy/lazy_array.rs +++ b/core/src/storage/lazy/lazy_array.rs @@ -30,24 +30,33 @@ use core::{ ops::Mul, ptr::NonNull, }; +use generic_array::{ + ArrayLength, + GenericArray, +}; use ink_primitives::Key; use typenum::{ Integer, Prod, - P32, }; /// The index type used in the lazy storage chunk. pub type Index = u32; -/// The capacity of a lazy array. -const CAPACITY: usize = 32; +/// Utility trait for helping with lazy array construction. +pub trait EntryArrayLength: ArrayLength>> + Integer {} +impl EntryArrayLength for T +where + T: ArrayLength>> + Integer, +{} -/// A lazy storage array that spans over 32 storage cells. +/// A lazy storage array that spans over N storage cells. +/// +/// Storage data structure to emulate storage arrays: `[T; N]`. /// /// # Note /// -/// Computes operations on the underlying 32 storage cells in a lazy fashion. +/// Computes operations on the underlying N storage cells in a lazy fashion. /// Due to the size constraints the `LazyArray` is generally more efficient /// than the [`LazyMap`](`super::LazyMap`) for most use cases with limited elements. /// @@ -55,8 +64,11 @@ const CAPACITY: usize = 32; /// storage primitives in order to manage the contract storage for a whole /// chunk of storage cells. #[derive(Debug)] -pub struct LazyArray { - /// The offset key for the 32 cells. +pub struct LazyArray +where + N: EntryArrayLength, +{ + /// The offset key for the N cells. /// /// If the lazy chunk has been initialized during contract initialization /// the key will be `None` since there won't be a storage region associated @@ -67,17 +79,31 @@ pub struct LazyArray { /// The subset of currently cached entries of the lazy storage chunk. /// /// An entry is cached as soon as it is loaded or written. - cached_entries: UnsafeCell>, + cached_entries: UnsafeCell>, +} + +/// Returns the capacity for an array with the given array length. +fn array_capacity() -> u32 +where + N: EntryArrayLength, +{ + ::I32 as u32 } /// The underlying array cache for the [`LazyArray`]. #[derive(Debug)] -pub struct EntryArray { +pub struct EntryArray +where + N: EntryArrayLength, +{ /// The cache entries of the entry array. - entries: [Option>; CAPACITY], + entries: GenericArray>, N>, } -impl EntryArray { +impl EntryArray +where + N: EntryArrayLength, +{ /// Creates a new entry array cache. pub fn new() -> Self { Self { @@ -86,18 +112,30 @@ impl EntryArray { } } -impl Default for EntryArray { +impl Default for EntryArray +where + N: EntryArrayLength, +{ fn default() -> Self { Self::new() } } -impl EntryArray { +impl EntryArray +where + N: EntryArrayLength, +{ + /// Returns the constant capacity of the lazy array. + #[inline] + pub fn capacity() -> u32 { + array_capacity::() + } + /// Puts the the new value into the indexed slot and /// returns the old value if any. fn put(&mut self, at: Index, new_value: Option) -> Option { mem::replace( - &mut self.entries[at as usize], + &mut self.entries.as_mut_slice()[at as usize], Some(Entry::new(new_value, EntryState::Mutated)), ) .map(Entry::into_value) @@ -113,14 +151,17 @@ impl EntryArray { /// Returns an exclusive reference to the entry at the given index if any. fn get_entry_mut(&mut self, at: Index) -> Option<&mut Entry> { - if at as usize >= CAPACITY { + if at >= Self::capacity() { return None } self.entries[at as usize].as_mut() } } -impl LazyArray { +impl LazyArray +where + N: EntryArrayLength, +{ /// Creates a new empty lazy array. /// /// # Note @@ -135,8 +176,9 @@ impl LazyArray { } /// Returns the constant capacity of the lazy array. - pub const fn capacity() -> u32 { - CAPACITY as u32 + #[inline] + pub fn capacity() -> u32 { + array_capacity::() } /// Returns the offset key of the lazy array if any. @@ -150,7 +192,7 @@ impl LazyArray { /// /// This operation is safe since it returns a shared reference from /// a `&self` which is viable in safe Rust. - fn cached_entries(&self) -> &EntryArray { + fn cached_entries(&self) -> &EntryArray { unsafe { &*self.cached_entries.get() } } @@ -160,7 +202,7 @@ impl LazyArray { /// /// This operation is safe since it returns an exclusive reference from /// a `&mut self` which is viable in safe Rust. - fn cached_entries_mut(&mut self) -> &mut EntryArray { + fn cached_entries_mut(&mut self) -> &mut EntryArray { unsafe { &mut *self.cached_entries.get() } } @@ -174,20 +216,35 @@ impl LazyArray { } } -impl StorageFootprint for LazyArray +impl StorageFootprint for LazyArray where T: StorageFootprint, - ::Value: Mul, + N: EntryArrayLength, + ::Value: Mul, { - type Value = Prod<::Value, P32>; + type Value = Prod<::Value, N>; +} + +impl PullForward for LazyArray +where + Self: StorageFootprint + StorageSize, + N: EntryArrayLength, +{ + fn pull_forward(ptr: &mut KeyPtr) -> Self { + Self { + key: Some(ptr.next_for::()), + cached_entries: UnsafeCell::new(EntryArray::new()), + } + } } -impl PushForward for LazyArray +impl PushForward for LazyArray where Self: StorageFootprint, ::Value: Integer, T: StorageFootprint + SaturatingStorage + PushForward, ::Value: Integer, + N: EntryArrayLength, { fn push_forward(&self, ptr: &mut KeyPtr) { let offset_key = ptr.next_for2::(); @@ -226,10 +283,11 @@ where } } -impl LazyArray +impl LazyArray where T: StorageFootprint, ::Value: Integer, + N: EntryArrayLength, { /// Returns the offset key for the given index if not out of bounds. pub fn key_at(&self, at: Index) -> Option { @@ -243,10 +301,11 @@ where } } -impl LazyArray +impl LazyArray where T: StorageFootprint + StorageSize + PullForward, ::Value: Integer, + N: EntryArrayLength, { /// Loads the entry at the given index. /// From b36bb784ffa21869e081e8c54169b5b060fda0a9 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 21:07:48 +0100 Subject: [PATCH 090/142] [core] re-use storage::Vec2::new in Default impl --- core/src/storage/collections2/vec/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index a160ad011b1..9f9072f3e1d 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -53,10 +53,7 @@ pub struct Vec { impl Default for Vec { fn default() -> Self { - Self { - len: storage::Lazy::new(0), - elems: storage::LazyChunk::default(), - } + Self::new() } } From 0b4d191c3dffae2af40c1e6ed90b6f3a4e831558 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 21:08:16 +0100 Subject: [PATCH 091/142] [core] add LazyArrayLength helper trait --- core/src/storage/lazy/lazy_array.rs | 42 ++++++++++++++++------------- core/src/storage/lazy/mod.rs | 5 +++- core/src/storage/mod.rs | 1 + 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs index b7cdacc1d16..f996c02faef 100644 --- a/core/src/storage/lazy/lazy_array.rs +++ b/core/src/storage/lazy/lazy_array.rs @@ -44,11 +44,8 @@ use typenum::{ pub type Index = u32; /// Utility trait for helping with lazy array construction. -pub trait EntryArrayLength: ArrayLength>> + Integer {} -impl EntryArrayLength for T -where - T: ArrayLength>> + Integer, -{} +pub trait LazyArrayLength: ArrayLength>> + Integer {} +impl LazyArrayLength for T where T: ArrayLength>> + Integer {} /// A lazy storage array that spans over N storage cells. /// @@ -66,7 +63,7 @@ where #[derive(Debug)] pub struct LazyArray where - N: EntryArrayLength, + N: LazyArrayLength, { /// The offset key for the N cells. /// @@ -85,7 +82,7 @@ where /// Returns the capacity for an array with the given array length. fn array_capacity() -> u32 where - N: EntryArrayLength, + N: LazyArrayLength, { ::I32 as u32 } @@ -94,7 +91,7 @@ where #[derive(Debug)] pub struct EntryArray where - N: EntryArrayLength, + N: LazyArrayLength, { /// The cache entries of the entry array. entries: GenericArray>, N>, @@ -102,7 +99,7 @@ where impl EntryArray where - N: EntryArrayLength, + N: LazyArrayLength, { /// Creates a new entry array cache. pub fn new() -> Self { @@ -114,7 +111,7 @@ where impl Default for EntryArray where - N: EntryArrayLength, + N: LazyArrayLength, { fn default() -> Self { Self::new() @@ -123,7 +120,7 @@ where impl EntryArray where - N: EntryArrayLength, + N: LazyArrayLength, { /// Returns the constant capacity of the lazy array. #[inline] @@ -158,9 +155,18 @@ where } } +impl Default for LazyArray +where + N: LazyArrayLength, +{ + fn default() -> Self { + Self::new() + } +} + impl LazyArray where - N: EntryArrayLength, + N: LazyArrayLength, { /// Creates a new empty lazy array. /// @@ -219,7 +225,7 @@ where impl StorageFootprint for LazyArray where T: StorageFootprint, - N: EntryArrayLength, + N: LazyArrayLength, ::Value: Mul, { type Value = Prod<::Value, N>; @@ -228,7 +234,7 @@ where impl PullForward for LazyArray where Self: StorageFootprint + StorageSize, - N: EntryArrayLength, + N: LazyArrayLength, { fn pull_forward(ptr: &mut KeyPtr) -> Self { Self { @@ -240,11 +246,11 @@ where impl PushForward for LazyArray where + T: StorageFootprint + SaturatingStorage + PushForward, + N: LazyArrayLength, Self: StorageFootprint, ::Value: Integer, - T: StorageFootprint + SaturatingStorage + PushForward, ::Value: Integer, - N: EntryArrayLength, { fn push_forward(&self, ptr: &mut KeyPtr) { let offset_key = ptr.next_for2::(); @@ -287,7 +293,7 @@ impl LazyArray where T: StorageFootprint, ::Value: Integer, - N: EntryArrayLength, + N: LazyArrayLength, { /// Returns the offset key for the given index if not out of bounds. pub fn key_at(&self, at: Index) -> Option { @@ -305,7 +311,7 @@ impl LazyArray where T: StorageFootprint + StorageSize + PullForward, ::Value: Integer, - N: EntryArrayLength, + N: LazyArrayLength, { /// Loads the entry at the given index. /// diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index b9f469661ef..25798394f6c 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -22,7 +22,10 @@ use self::entry::{ EntryState, }; pub use self::{ - lazy_array::LazyArray, + lazy_array::{ + LazyArray, + LazyArrayLength, + }, lazy_cell::LazyCell, lazy_map::{ LazyChunk, diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 9a9bf09da5c..0aaf4456a92 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -115,6 +115,7 @@ pub use self::{ lazy::{ Lazy, LazyArray, + LazyArrayLength, LazyCell, LazyChunk, LazyMap, From fcf9fee4999e4aee49ea39a63d148452e2ca84f0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 13 Mar 2020 21:12:28 +0100 Subject: [PATCH 092/142] [core] add storage::SmallVec based on storage::LazyArray --- core/src/storage/collections2/mod.rs | 1 + .../src/storage/collections2/smallvec/iter.rs | 101 ++++++++ core/src/storage/collections2/smallvec/mod.rs | 243 ++++++++++++++++++ .../storage/collections2/smallvec/traits.rs | 108 ++++++++ core/src/storage/mod.rs | 1 + 5 files changed, 454 insertions(+) create mode 100644 core/src/storage/collections2/smallvec/iter.rs create mode 100644 core/src/storage/collections2/smallvec/mod.rs create mode 100644 core/src/storage/collections2/smallvec/traits.rs diff --git a/core/src/storage/collections2/mod.rs b/core/src/storage/collections2/mod.rs index 67356236671..a89fec6d906 100644 --- a/core/src/storage/collections2/mod.rs +++ b/core/src/storage/collections2/mod.rs @@ -14,3 +14,4 @@ pub mod boxed; pub mod vec; +pub mod smallvec; diff --git a/core/src/storage/collections2/smallvec/iter.rs b/core/src/storage/collections2/smallvec/iter.rs new file mode 100644 index 00000000000..000738c13d8 --- /dev/null +++ b/core/src/storage/collections2/smallvec/iter.rs @@ -0,0 +1,101 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::SmallVec; +use crate::storage::{ + LazyArrayLength, + PullForward, + StorageFootprint, + StorageSize, +}; + +/// An iterator over the values of a storage `SmallVec`. +#[derive(Debug)] +pub struct Iter<'a, T, N> +where + N: LazyArrayLength, +{ + /// The storage vector to iterate over. + vec: &'a SmallVec, + /// The current begin of the iteration. + begin: u32, + /// The current end of the iteration. + end: u32, +} + +impl<'a, T, N> Iter<'a, T, N> +where + N: LazyArrayLength, +{ + /// Creates a new iterator for the given storage vector. + pub(crate) fn new(vec: &'a SmallVec) -> Self { + Self { + vec, + begin: 0, + end: vec.len(), + } + } +} + +impl<'a, T, N> Iterator for Iter<'a, T, N> +where + T: StorageFootprint + PullForward, + T: StorageSize, + ::Value: typenum::marker_traits::Integer, + N: LazyArrayLength, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + let cur = self.begin; + self.begin += 1; + self.vec.get(cur) + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = (self.end - self.begin) as usize; + (remaining, Some(remaining)) + } +} + +impl<'a, T, N> ExactSizeIterator for Iter<'a, T, N> +where + T: StorageFootprint + PullForward, + T: StorageSize, + ::Value: typenum::marker_traits::Integer, + N: LazyArrayLength, +{ +} + +impl<'a, T, N> DoubleEndedIterator for Iter<'a, T, N> +where + T: StorageFootprint + PullForward, + T: StorageSize, + ::Value: typenum::marker_traits::Integer, + N: LazyArrayLength, +{ + fn next_back(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + debug_assert_ne!(self.end, 0); + self.end -= 1; + self.vec.get(self.end) + } +} diff --git a/core/src/storage/collections2/smallvec/mod.rs b/core/src/storage/collections2/smallvec/mod.rs new file mode 100644 index 00000000000..284c2138388 --- /dev/null +++ b/core/src/storage/collections2/smallvec/mod.rs @@ -0,0 +1,243 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod iter; +mod traits; + +pub use self::iter::Iter; +use crate::{ + storage, + storage::{ + PullForward, + StorageFootprint, + StorageSize, + SaturatingStorage, + LazyArrayLength, + LazyArray, + }, +}; + +/// The used index type. +type Index = u32; + +/// A contiguous growable array type. +/// +/// # Note +/// +/// - The `storage::SmallVec` has a very similar API compared to a `storage::Vec`. +/// The major difference between both data structures is that the `SmallVec` +/// can only contain up to a fixed amount of elements given by `N` whereas the +/// `Vec` can contain up to 2^32 elements which is the maximum for 32-bit Wasm +/// targets. +/// - The performance characteristics may be different from Rust's +/// `Vec` due to the internal differences. +/// - Allows to store up to N elements. +#[derive(Debug)] +pub struct SmallVec +where + N: LazyArrayLength, +{ + /// The current length of the small vector. + len: storage::Lazy, + /// The entries of the small vector. + elems: LazyArray, +} + +impl Default for SmallVec +where + N: LazyArrayLength, +{ + fn default() -> Self { + Self::new() + } +} + +impl SmallVec +where + N: LazyArrayLength, +{ + /// Creates a new empty vector. + pub fn new() -> Self { + Self { + len: storage::Lazy::new(0), + elems: Default::default(), + } + } + + /// Returns the capacity of the small vector. + pub fn capacity() -> u32 { + >::capacity() + } + + /// Returns the number of elements in the vector, also referred to as its 'length'. + pub fn len(&self) -> u32 { + *self.len.get() + } + + /// Returns `true` if the vector contains no elements. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl SmallVec +where + T: StorageFootprint + PullForward, + T: StorageSize, + ::Value: typenum::marker_traits::Integer, + N: LazyArrayLength, +{ + /// Returns an iterator over the references of all elements stored in the vector. + /// + /// # Note + /// + /// - It is **not** recommended to iterate over all elements of a storage vector. + /// - Try to avoid this if possible or iterate only over a minimal subset of + /// all elements using e.g. `Iterator::take(n)`. + pub fn iter(&self) -> Iter { + Iter::new(self) + } + + /// Returns the index if it is witihn bounds or `None` otherwise. + fn within_bounds(&self, index: Index) -> Option { + if index < self.len() { + return Some(index) + } + None + } + + /// Returns a shared reference to the first element if any. + pub fn first(&self) -> Option<&T> { + self.get(0) + } + + /// Returns a shared reference to the last element if any. + pub fn last(&self) -> Option<&T> { + let last_index = self.len() - 1; + self.get(last_index) + } + + /// Returns a shared reference to the indexed element. + /// + /// Returns `None` if `index` is out of bounds. + pub fn get(&self, index: u32) -> Option<&T> { + self.within_bounds(index) + .and_then(|index| self.elems.get(index)) + } +} + +impl SmallVec +where + T: StorageFootprint + SaturatingStorage + PullForward, + T: StorageSize, + ::Value: typenum::marker_traits::Integer, + N: LazyArrayLength, +{ + /// Appends an element to the back of the vector. + pub fn push(&mut self, value: T) { + assert!( + self.len() < Self::capacity(), + "cannot push more elements into the vector" + ); + let last_index = self.len(); + *self.len += 1; + self.elems.put(last_index, Some(value)); + } + + /// Pops the last element from the vector and returns it. + // + /// Returns `None` if the vector is empty. + pub fn pop(&mut self) -> Option { + if self.is_empty() { + return None + } + let last_index = self.len() - 1; + *self.len = last_index; + self.elems.take(last_index) + } + + /// Pops the last element from the vector and immediately drops it. + /// + /// Does nothing if the vector is empty. + /// + /// # Note + /// + /// This operation is a bit more efficient than [`SmallVec::pop`] for some use cases. + pub fn pop_drop(&mut self) { + if self.is_empty() { + return + } + let last_index = self.len() - 1; + *self.len = last_index; + self.elems.put(last_index, None); + } + + /// Returns an exclusive reference to the first element if any. + pub fn first_mut(&mut self) -> Option<&mut T> { + self.get_mut(0) + } + + /// Returns an exclusive reference to the last element if any. + pub fn last_mut(&mut self) -> Option<&mut T> { + let last_index = self.len() - 1; + self.get_mut(last_index) + } + + /// Returns an exclusive reference to the indexed element. + /// + /// Returns `None` if `index` is out of bounds. + pub fn get_mut(&mut self, index: u32) -> Option<&mut T> { + self.within_bounds(index) + .and_then(move |index| self.elems.get_mut(index)) + } + + /// Replaces the element at the given index and returns the old value. + /// + /// Returns `None` if `n` is out of bounds. + pub fn replace(&mut self, index: u32, f: F) -> Option + where + F: FnOnce() -> T, + { + self.within_bounds(index).map(|index| { + self.elems + .put_get(index, Some(f())) + .expect("expected an actual element since access is within bounds") + }) + } + + /// Swaps the elements at the given indices. + /// + /// # Panics + /// + /// If one or both indices are out of bounds. + pub fn swap(&mut self, a: u32, b: u32) { + self.elems.swap(a, b) + } + + /// Removes the indexed element from the vector and returns it. + /// + /// The last element of the vector is put into the indexed slot. + /// Returns `None` and does not mutate the vector if the index is out of bounds. + /// + /// # Note + /// + /// This operation does not preserve ordering but is constant time. + pub fn swap_remove(&mut self, n: u32) -> Option { + if self.is_empty() { + return None + } + self.elems.swap(n, self.len() - 1); + self.pop() + } +} diff --git a/core/src/storage/collections2/smallvec/traits.rs b/core/src/storage/collections2/smallvec/traits.rs new file mode 100644 index 00000000000..5168d3701fe --- /dev/null +++ b/core/src/storage/collections2/smallvec/traits.rs @@ -0,0 +1,108 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{ + Iter, + SmallVec, +}; +use crate::storage::{ + KeyPtr, + LazyArray, + LazyArrayLength, + PullForward, + PushForward, + SaturatingStorage, + StorageFootprint, + StorageSize, +}; +use core::ops::Add; +use typenum::Add1; + +impl core::ops::Index for SmallVec +where + T: StorageFootprint + PullForward, + T: StorageSize, + ::Value: typenum::marker_traits::Integer, + N: LazyArrayLength, +{ + type Output = T; + + fn index(&self, index: u32) -> &Self::Output { + self.get(index).expect("index out of bounds") + } +} + +impl core::ops::IndexMut for SmallVec +where + T: StorageFootprint + SaturatingStorage + PullForward, + T: StorageSize, + ::Value: typenum::marker_traits::Integer, + N: LazyArrayLength, +{ + fn index_mut(&mut self, index: u32) -> &mut Self::Output { + self.get_mut(index).expect("index out of bounds") + } +} + +impl<'a, T: 'a, N> IntoIterator for &'a SmallVec +where + T: StorageFootprint + PullForward, + T: StorageSize, + ::Value: typenum::marker_traits::Integer, + N: LazyArrayLength, +{ + type Item = &'a T; + type IntoIter = Iter<'a, T, N>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl StorageFootprint for SmallVec +where + T: StorageFootprint + PullForward, + T: StorageSize, + ::Value: typenum::marker_traits::Integer, + N: LazyArrayLength, + LazyArray: StorageFootprint, + as StorageFootprint>::Value: Add, +{ + type Value = Add1< as StorageFootprint>::Value>; +} + +impl PullForward for SmallVec +where + T: StorageSize, + N: LazyArrayLength, + LazyArray: PullForward, +{ + fn pull_forward(ptr: &mut KeyPtr) -> Self { + Self { + len: PullForward::pull_forward(ptr), + elems: PullForward::pull_forward(ptr), + } + } +} + +impl PushForward for SmallVec +where + LazyArray: PushForward, + N: LazyArrayLength, +{ + fn push_forward(&self, ptr: &mut KeyPtr) { + PushForward::push_forward(&self.len(), ptr); + PushForward::push_forward(&self.elems, ptr); + } +} diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 0aaf4456a92..021dcd87f19 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -110,6 +110,7 @@ pub use self::{ collections2::{ boxed::Box, vec::Vec as Vec2, + smallvec::SmallVec, }, flush::Flush, lazy::{ From b9b86e48e4e91dfbe6763518926d1e3b27e1d585 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 14 Mar 2020 13:21:30 +0100 Subject: [PATCH 093/142] [core] finalized refactoring StorageSize -> StorageFootprint semantics --- core/Cargo.toml | 2 +- core/src/lib.rs | 1 + core/src/storage/collections2/boxed/mod.rs | 8 +- core/src/storage/collections2/boxed/traits.rs | 38 ++-- core/src/storage/collections2/mod.rs | 2 +- .../src/storage/collections2/smallvec/iter.rs | 7 - core/src/storage/collections2/smallvec/mod.rs | 11 +- .../storage/collections2/smallvec/traits.rs | 21 +-- core/src/storage/collections2/vec/iter.rs | 8 +- core/src/storage/collections2/vec/mod.rs | 7 +- core/src/storage/collections2/vec/traits.rs | 34 ++-- core/src/storage/lazy/entry.rs | 4 +- core/src/storage/lazy/lazy_array.rs | 13 +- core/src/storage/lazy/lazy_cell.rs | 14 +- core/src/storage/lazy/lazy_map.rs | 52 +++--- core/src/storage/lazy/mod.rs | 37 ++-- core/src/storage/mod.rs | 6 +- core/src/storage/pack.rs | 13 +- core/src/storage/traits/clear.rs | 8 +- core/src/storage/traits/footprint.rs | 174 +++++------------- core/src/storage/traits/mod.rs | 25 +-- core/src/storage/traits/pull.rs | 10 +- core/src/storage/traits/push.rs | 17 +- 23 files changed, 201 insertions(+), 311 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index d8a0d518c66..20965410ca4 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -28,7 +28,7 @@ smallvec = { version = "1.0", default-features = false, features = ["union"] } cfg-if = "0.1" num-traits = { version = "0.2.1", default-features = false, features = ["i128"] } array-init = "0.1" -typenum = "1.11" +typenum = { version = "1.11", features = ["i128"] } generic-array = "0.13" # Only used in the off-chain environment. diff --git a/core/src/lib.rs b/core/src/lib.rs index d9408e2d6b4..2176d692c82 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -21,6 +21,7 @@ //! emulator for simple off-chain testing. #![cfg_attr(not(feature = "std"), no_std)] +#![feature(const_fn)] #![deny( bad_style, const_err, diff --git a/core/src/storage/collections2/boxed/mod.rs b/core/src/storage/collections2/boxed/mod.rs index d165637269a..b5e85c9e23e 100644 --- a/core/src/storage/collections2/boxed/mod.rs +++ b/core/src/storage/collections2/boxed/mod.rs @@ -18,14 +18,14 @@ use crate::storage::{ ClearForward, Lazy, PullForward, - StorageSize, + StorageFootprint, }; use ink_primitives::Key; /// Allocates a new storage key for the given `T` dynamically on the storage. fn allocate_dynamically() -> Key where - T: StorageSize, + T: StorageFootprint, { // TODO: Actual implementation is still missing! Key([0x42; 32]) @@ -44,7 +44,7 @@ where impl Box where - T: ClearForward + StorageSize, + T: ClearForward + StorageFootprint, { /// Creates a new boxed entity. pub fn new(value: T) -> Self { @@ -57,7 +57,7 @@ where impl Box where - T: ClearForward + StorageSize + PullForward, + T: ClearForward + StorageFootprint + PullForward, { /// Returns a shared reference to the boxed value. /// diff --git a/core/src/storage/collections2/boxed/traits.rs b/core/src/storage/collections2/boxed/traits.rs index 267ca8288c4..7e6e8daff5f 100644 --- a/core/src/storage/collections2/boxed/traits.rs +++ b/core/src/storage/collections2/boxed/traits.rs @@ -20,24 +20,12 @@ use crate::{ KeyPtr, PullForward, PushForward, - StorageSize, - StorageFootprint, SaturatingStorage, + StorageFootprint, }, }; use ink_primitives::Key; -impl StorageSize for StorageBox -where - T: ClearForward, -{ - /// A boxed entity always uses exactly 1 cell for the key. - /// - /// The indirectly stored storage entity is not considered because the - /// `StorageSize` is only concerned with inplace storage usage. - const SIZE: u64 = 1; -} - impl StorageFootprint for StorageBox where T: ClearForward, @@ -104,7 +92,7 @@ where impl core::cmp::PartialEq for StorageBox where - T: PartialEq + ClearForward + StorageSize + PullForward, + T: PartialEq + ClearForward + StorageFootprint + PullForward, { fn eq(&self, other: &Self) -> bool { PartialEq::eq(self.get(), other.get()) @@ -112,13 +100,13 @@ where } impl core::cmp::Eq for StorageBox where - T: Eq + ClearForward + StorageSize + PullForward + T: Eq + ClearForward + StorageFootprint + PullForward { } impl core::cmp::PartialOrd for StorageBox where - T: PartialOrd + ClearForward + StorageSize + PullForward, + T: PartialOrd + ClearForward + StorageFootprint + PullForward, { fn partial_cmp(&self, other: &Self) -> Option { PartialOrd::partial_cmp(self.get(), other.get()) @@ -139,7 +127,7 @@ where impl core::cmp::Ord for StorageBox where - T: core::cmp::Ord + ClearForward + StorageSize + PullForward, + T: core::cmp::Ord + ClearForward + StorageFootprint + PullForward, { fn cmp(&self, other: &Self) -> core::cmp::Ordering { Ord::cmp(self.get(), other.get()) @@ -148,7 +136,7 @@ where impl core::fmt::Display for StorageBox where - T: core::fmt::Display + ClearForward + StorageSize + PullForward, + T: core::fmt::Display + ClearForward + StorageFootprint + PullForward, { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { core::fmt::Display::fmt(self.get(), f) @@ -157,7 +145,7 @@ where impl core::hash::Hash for StorageBox where - T: core::hash::Hash + ClearForward + StorageSize + PullForward, + T: core::hash::Hash + ClearForward + StorageFootprint + PullForward, { fn hash(&self, state: &mut H) { self.get().hash(state); @@ -166,7 +154,7 @@ where impl core::convert::AsRef for StorageBox where - T: StorageSize + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward, { fn as_ref(&self) -> &T { self.get() @@ -175,7 +163,7 @@ where impl core::convert::AsMut for StorageBox where - T: StorageSize + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward, { fn as_mut(&mut self) -> &mut T { self.get_mut() @@ -184,7 +172,7 @@ where impl ink_prelude::borrow::Borrow for StorageBox where - T: StorageSize + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward, { fn borrow(&self) -> &T { self.get() @@ -193,7 +181,7 @@ where impl ink_prelude::borrow::BorrowMut for StorageBox where - T: StorageSize + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward, { fn borrow_mut(&mut self) -> &mut T { self.get_mut() @@ -202,7 +190,7 @@ where impl core::ops::Deref for StorageBox where - T: StorageSize + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward, { type Target = T; @@ -213,7 +201,7 @@ where impl core::ops::DerefMut for StorageBox where - T: StorageSize + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward, { fn deref_mut(&mut self) -> &mut Self::Target { self.get_mut() diff --git a/core/src/storage/collections2/mod.rs b/core/src/storage/collections2/mod.rs index a89fec6d906..a983ef88e75 100644 --- a/core/src/storage/collections2/mod.rs +++ b/core/src/storage/collections2/mod.rs @@ -13,5 +13,5 @@ // limitations under the License. pub mod boxed; -pub mod vec; pub mod smallvec; +pub mod vec; diff --git a/core/src/storage/collections2/smallvec/iter.rs b/core/src/storage/collections2/smallvec/iter.rs index 000738c13d8..8a1d4718187 100644 --- a/core/src/storage/collections2/smallvec/iter.rs +++ b/core/src/storage/collections2/smallvec/iter.rs @@ -17,7 +17,6 @@ use crate::storage::{ LazyArrayLength, PullForward, StorageFootprint, - StorageSize, }; /// An iterator over the values of a storage `SmallVec`. @@ -51,8 +50,6 @@ where impl<'a, T, N> Iterator for Iter<'a, T, N> where T: StorageFootprint + PullForward, - T: StorageSize, - ::Value: typenum::marker_traits::Integer, N: LazyArrayLength, { type Item = &'a T; @@ -76,8 +73,6 @@ where impl<'a, T, N> ExactSizeIterator for Iter<'a, T, N> where T: StorageFootprint + PullForward, - T: StorageSize, - ::Value: typenum::marker_traits::Integer, N: LazyArrayLength, { } @@ -85,8 +80,6 @@ where impl<'a, T, N> DoubleEndedIterator for Iter<'a, T, N> where T: StorageFootprint + PullForward, - T: StorageSize, - ::Value: typenum::marker_traits::Integer, N: LazyArrayLength, { fn next_back(&mut self) -> Option { diff --git a/core/src/storage/collections2/smallvec/mod.rs b/core/src/storage/collections2/smallvec/mod.rs index 284c2138388..e1f4d832196 100644 --- a/core/src/storage/collections2/smallvec/mod.rs +++ b/core/src/storage/collections2/smallvec/mod.rs @@ -19,12 +19,11 @@ pub use self::iter::Iter; use crate::{ storage, storage::{ + LazyArray, + LazyArrayLength, PullForward, - StorageFootprint, - StorageSize, SaturatingStorage, - LazyArrayLength, - LazyArray, + StorageFootprint, }, }; @@ -94,8 +93,6 @@ where impl SmallVec where T: StorageFootprint + PullForward, - T: StorageSize, - ::Value: typenum::marker_traits::Integer, N: LazyArrayLength, { /// Returns an iterator over the references of all elements stored in the vector. @@ -140,8 +137,6 @@ where impl SmallVec where T: StorageFootprint + SaturatingStorage + PullForward, - T: StorageSize, - ::Value: typenum::marker_traits::Integer, N: LazyArrayLength, { /// Appends an element to the back of the vector. diff --git a/core/src/storage/collections2/smallvec/traits.rs b/core/src/storage/collections2/smallvec/traits.rs index 5168d3701fe..34b5cc1cb36 100644 --- a/core/src/storage/collections2/smallvec/traits.rs +++ b/core/src/storage/collections2/smallvec/traits.rs @@ -24,16 +24,17 @@ use crate::storage::{ PushForward, SaturatingStorage, StorageFootprint, - StorageSize, + StorageFootprintOf, }; use core::ops::Add; -use typenum::Add1; +use typenum::{ + Add1, + Integer, +}; impl core::ops::Index for SmallVec where T: StorageFootprint + PullForward, - T: StorageSize, - ::Value: typenum::marker_traits::Integer, N: LazyArrayLength, { type Output = T; @@ -46,8 +47,6 @@ where impl core::ops::IndexMut for SmallVec where T: StorageFootprint + SaturatingStorage + PullForward, - T: StorageSize, - ::Value: typenum::marker_traits::Integer, N: LazyArrayLength, { fn index_mut(&mut self, index: u32) -> &mut Self::Output { @@ -58,8 +57,6 @@ where impl<'a, T: 'a, N> IntoIterator for &'a SmallVec where T: StorageFootprint + PullForward, - T: StorageSize, - ::Value: typenum::marker_traits::Integer, N: LazyArrayLength, { type Item = &'a T; @@ -73,18 +70,16 @@ where impl StorageFootprint for SmallVec where T: StorageFootprint + PullForward, - T: StorageSize, - ::Value: typenum::marker_traits::Integer, N: LazyArrayLength, LazyArray: StorageFootprint, - as StorageFootprint>::Value: Add, + StorageFootprintOf>: Add, + Add1>>: Integer, { - type Value = Add1< as StorageFootprint>::Value>; + type Value = Add1>>; } impl PullForward for SmallVec where - T: StorageSize, N: LazyArrayLength, LazyArray: PullForward, { diff --git a/core/src/storage/collections2/vec/iter.rs b/core/src/storage/collections2/vec/iter.rs index 7d0e70525b6..729d6ab6a57 100644 --- a/core/src/storage/collections2/vec/iter.rs +++ b/core/src/storage/collections2/vec/iter.rs @@ -16,7 +16,7 @@ use crate::{ storage, storage::{ PullForward, - StorageSize, + StorageFootprint, }, }; @@ -44,7 +44,7 @@ impl<'a, T> Iter<'a, T> { impl<'a, T> Iterator for Iter<'a, T> where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { type Item = &'a T; @@ -64,11 +64,11 @@ where } } -impl<'a, T> ExactSizeIterator for Iter<'a, T> where T: StorageSize + PullForward {} +impl<'a, T> ExactSizeIterator for Iter<'a, T> where T: StorageFootprint + PullForward {} impl<'a, T> DoubleEndedIterator for Iter<'a, T> where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { fn next_back(&mut self) -> Option { debug_assert!(self.begin <= self.end); diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 9f9072f3e1d..1c284de1b62 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -23,9 +23,8 @@ use crate::{ storage, storage::{ PullForward, - StorageSize, - StorageFootprint, SaturatingStorage, + StorageFootprint, }, }; @@ -79,7 +78,7 @@ impl Vec { impl Vec where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { /// Returns an iterator over the references of all elements stored in the vector. /// @@ -122,7 +121,7 @@ where impl Vec where - T: StorageFootprint + SaturatingStorage + StorageSize + PullForward, + T: StorageFootprint + SaturatingStorage + PullForward, { /// Appends an element to the back of the vector. pub fn push(&mut self, value: T) { diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index f0615e4e3db..b71fb57e8e7 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -20,17 +20,20 @@ use crate::{ KeyPtr, PullForward, PushForward, - StorageSize, - StorageFootprint, SaturatingStorage, + StorageFootprint, + StorageFootprintOf, }, }; -use typenum::Add1; use core::ops::Add; +use typenum::{ + Add1, + Integer, +}; impl core::ops::Index for StorageVec where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { type Output = T; @@ -41,7 +44,7 @@ where impl core::ops::IndexMut for StorageVec where - T: SaturatingStorage + StorageFootprint + StorageSize + PullForward, + T: SaturatingStorage + StorageFootprint + PullForward, { fn index_mut(&mut self, index: u32) -> &mut Self::Output { self.get_mut(index).expect("index out of bounds") @@ -50,7 +53,7 @@ where impl<'a, T: 'a> IntoIterator for &'a StorageVec where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { type Item = &'a T; type IntoIter = super::Iter<'a, T>; @@ -60,27 +63,20 @@ where } } -impl StorageSize for StorageVec -where - T: StorageSize, -{ - const SIZE: u64 = - ::SIZE + as StorageSize>::SIZE; -} - impl StorageFootprint for StorageVec where T: StorageFootprint, storage::LazyChunk: StorageFootprint, - as StorageFootprint>::Value: Add, + StorageFootprintOf>: Add, + Add1>>: Integer, { - type Value = - Add1< as StorageFootprint>::Value>; + type Value = Add1>>; } impl PullForward for StorageVec where - T: StorageSize, + T: StorageFootprint, + storage::LazyChunk: PullForward, { fn pull_forward(ptr: &mut KeyPtr) -> Self { Self { @@ -102,7 +98,7 @@ where impl ClearForward for StorageVec where - T: StorageSize + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward, { fn clear_forward(&self, ptr: &mut KeyPtr) { ClearForward::clear_forward(&self.len(), ptr); diff --git a/core/src/storage/lazy/entry.rs b/core/src/storage/lazy/entry.rs index d8357974eb9..4152def4de4 100644 --- a/core/src/storage/lazy/entry.rs +++ b/core/src/storage/lazy/entry.rs @@ -15,7 +15,7 @@ use crate::storage::{ KeyPtr, PushForward, - StorageSize, + StorageFootprint, }; use core::cell::Cell; @@ -40,7 +40,7 @@ pub enum EntryState { impl PushForward for Entry where - T: PushForward + StorageSize, + T: PushForward + StorageFootprint, { fn push_forward(&self, ptr: &mut KeyPtr) { // Reset the state because we just synced. diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs index f996c02faef..b677ce8d528 100644 --- a/core/src/storage/lazy/lazy_array.rs +++ b/core/src/storage/lazy/lazy_array.rs @@ -22,7 +22,7 @@ use crate::storage::{ PushForward, SaturatingStorage, StorageFootprint, - StorageSize, + StorageFootprintOf, }; use core::{ cell::UnsafeCell, @@ -226,19 +226,20 @@ impl StorageFootprint for LazyArray where T: StorageFootprint, N: LazyArrayLength, - ::Value: Mul, + StorageFootprintOf: Mul, + Prod, N>: Integer, { - type Value = Prod<::Value, N>; + type Value = Prod, N>; } impl PullForward for LazyArray where - Self: StorageFootprint + StorageSize, + Self: StorageFootprint, N: LazyArrayLength, { fn pull_forward(ptr: &mut KeyPtr) -> Self { Self { - key: Some(ptr.next_for::()), + key: Some(ptr.next_for2::()), cached_entries: UnsafeCell::new(EntryArray::new()), } } @@ -309,7 +310,7 @@ where impl LazyArray where - T: StorageFootprint + StorageSize + PullForward, + T: StorageFootprint + PullForward, ::Value: Integer, N: LazyArrayLength, { diff --git a/core/src/storage/lazy/lazy_cell.rs b/core/src/storage/lazy/lazy_cell.rs index 418224422a7..518a6097438 100644 --- a/core/src/storage/lazy/lazy_cell.rs +++ b/core/src/storage/lazy/lazy_cell.rs @@ -17,7 +17,6 @@ use super::super::{ KeyPtr, PullForward, PushForward, - StorageSize, StorageFootprint, }; use core::cell::UnsafeCell; @@ -85,13 +84,6 @@ pub struct LazyCell { kind: UnsafeCell>, } -impl StorageSize for LazyCell -where - T: StorageSize, -{ - const SIZE: u64 = ::SIZE; -} - impl StorageFootprint for LazyCell where T: StorageFootprint, @@ -101,10 +93,10 @@ where impl PullForward for LazyCell where - T: StorageSize, + T: StorageFootprint, { fn pull_forward(ptr: &mut KeyPtr) -> Self { - Self::lazy(ptr.next_for::()) + Self::lazy(ptr.next_for2::()) } } @@ -189,7 +181,7 @@ impl LazyCell { impl LazyCell where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { /// Loads the value lazily from contract storage. /// diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index 2cccd908723..d8a56feeb97 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -17,7 +17,7 @@ use super::super::{ PullForward, PushForward, StorageFootprint, - StorageSize, + StorageFootprintOf, }; use core::{ cell::{ @@ -35,7 +35,10 @@ use ink_prelude::{ collections::BTreeMap, }; use ink_primitives::Key; -use typenum::Prod; +use typenum::{ + Integer, + Prod, +}; /// The index type used in the lazy storage chunk. pub type Index = u32; @@ -55,10 +58,12 @@ pub trait KeyMapping { impl KeyMapping for Index where - Value: StorageSize, + Value: StorageFootprint, + ::Value: Integer, { fn to_storage_key(&self, offset: &Key) -> Key { - *offset + (*self as u64 * ::SIZE) + *offset + + (*self as u64 * <::Value as Integer>::I64 as u64) } } @@ -148,7 +153,7 @@ pub struct Entry { impl PushForward for Entry where - T: PushForward + StorageSize, + T: PushForward + StorageFootprint, { fn push_forward(&self, ptr: &mut KeyPtr) { // Reset the mutated entry flag because we just synced. @@ -280,41 +285,38 @@ where } } -impl StorageSize for LazyChunk -where - T: StorageSize, -{ - /// A lazy chunk is contiguous and its size can be determined by the - /// total number of elements it could theoretically hold. - const SIZE: u64 = ::SIZE * (core::u32::MAX as u64); -} +use core::ops::Mul; +use typenum::P4294967296; impl StorageFootprint for LazyChunk where T: StorageFootprint, - ::Value: core::ops::Mul, + StorageFootprintOf: Mul, + Prod, P4294967296>: Integer, { - type Value = Prod<::Value, typenum::P4294967296>; + /// A lazy chunk is contiguous and its size can be determined by the + /// total number of elements it could theoretically hold. + type Value = Prod, P4294967296>; } -impl StorageSize for LazyMapping +impl StorageFootprint for LazyMapping where - T: StorageSize, + T: StorageFootprint, { /// A lazy mapping is similar to a Solidity mapping that distributes its /// stored entities across the entire contract storage so its inplace size /// is actually just 1. - const SIZE: u64 = 1; + type Value = typenum::P1; } impl PullForward for LazyMap where K: Ord, - Self: StorageSize, + Self: StorageFootprint, { fn pull_forward(ptr: &mut KeyPtr) -> Self { Self { - key: Some(ptr.next_for::()), + key: Some(ptr.next_for2::()), cached_entries: UnsafeCell::new(BTreeMap::new()), } } @@ -322,12 +324,12 @@ where impl PushForward for LazyMap where - Self: StorageSize, + Self: StorageFootprint, K: KeyMapping + Ord, - V: PushForward + StorageSize, + V: PushForward + StorageFootprint, { fn push_forward(&self, ptr: &mut KeyPtr) { - let key = ptr.next_for::(); + let key = ptr.next_for2::(); assert_eq!(self.key, Some(key)); for (index, entry) in self.entries().iter().filter(|(_, entry)| entry.mutated()) { let offset: Key = >::to_storage_key(index, &key); @@ -340,7 +342,7 @@ where impl LazyMap where K: KeyMapping + Ord + Eq + Clone, - V: StorageSize + PullForward, + V: StorageFootprint + PullForward, { /// Lazily loads the value at the given index. /// @@ -479,7 +481,7 @@ where impl LazyMap where K: KeyMapping + Ord + Eq + Clone, - V: StorageSize + PullForward, + V: StorageFootprint + PullForward, { /// Puts the new value at the given index and returns the old value if any. /// diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index 25798394f6c..90c685829de 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -38,7 +38,8 @@ use super::{ KeyPtr, PullForward, PushForward, - StorageSize, + StorageFootprint, + StorageFootprintOf, }; use ink_primitives::Key; @@ -54,16 +55,16 @@ pub struct Lazy { cell: LazyCell, } -impl StorageSize for Lazy +impl StorageFootprint for Lazy where - T: StorageSize, + T: StorageFootprint, { - const SIZE: u64 = as StorageSize>::SIZE; + type Value = StorageFootprintOf; } impl PullForward for Lazy where - T: StorageSize, + T: StorageFootprint, { fn pull_forward(ptr: &mut KeyPtr) -> Self { Self { @@ -110,7 +111,7 @@ impl Lazy { impl Lazy where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { /// Returns a shared reference to the lazily loaded value. /// @@ -158,18 +159,18 @@ where impl core::cmp::PartialEq for Lazy where - T: PartialEq + StorageSize + PullForward, + T: PartialEq + StorageFootprint + PullForward, { fn eq(&self, other: &Self) -> bool { PartialEq::eq(self.get(), other.get()) } } -impl core::cmp::Eq for Lazy where T: Eq + StorageSize + PullForward {} +impl core::cmp::Eq for Lazy where T: Eq + StorageFootprint + PullForward {} impl core::cmp::PartialOrd for Lazy where - T: PartialOrd + StorageSize + PullForward, + T: PartialOrd + StorageFootprint + PullForward, { fn partial_cmp(&self, other: &Self) -> Option { PartialOrd::partial_cmp(self.get(), other.get()) @@ -190,7 +191,7 @@ where impl core::cmp::Ord for Lazy where - T: core::cmp::Ord + StorageSize + PullForward, + T: core::cmp::Ord + StorageFootprint + PullForward, { fn cmp(&self, other: &Self) -> core::cmp::Ordering { Ord::cmp(self.get(), other.get()) @@ -199,7 +200,7 @@ where impl core::fmt::Display for Lazy where - T: core::fmt::Display + StorageSize + PullForward, + T: core::fmt::Display + StorageFootprint + PullForward, { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { core::fmt::Display::fmt(self.get(), f) @@ -208,7 +209,7 @@ where impl core::hash::Hash for Lazy where - T: core::hash::Hash + StorageSize + PullForward, + T: core::hash::Hash + StorageFootprint + PullForward, { fn hash(&self, state: &mut H) { self.get().hash(state); @@ -217,7 +218,7 @@ where impl core::convert::AsRef for Lazy where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { fn as_ref(&self) -> &T { self.get() @@ -226,7 +227,7 @@ where impl core::convert::AsMut for Lazy where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { fn as_mut(&mut self) -> &mut T { self.get_mut() @@ -235,7 +236,7 @@ where impl ink_prelude::borrow::Borrow for Lazy where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { fn borrow(&self) -> &T { self.get() @@ -244,7 +245,7 @@ where impl ink_prelude::borrow::BorrowMut for Lazy where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { fn borrow_mut(&mut self) -> &mut T { self.get_mut() @@ -253,7 +254,7 @@ where impl core::ops::Deref for Lazy where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { type Target = T; @@ -264,7 +265,7 @@ where impl core::ops::DerefMut for Lazy where - T: StorageSize + PullForward, + T: StorageFootprint + PullForward, { fn deref_mut(&mut self) -> &mut Self::Target { self.get_mut() diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 021dcd87f19..a5af6f3087f 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -109,8 +109,8 @@ pub use self::{ }, collections2::{ boxed::Box, - vec::Vec as Vec2, smallvec::SmallVec, + vec::Vec as Vec2, }, flush::Flush, lazy::{ @@ -124,6 +124,8 @@ pub use self::{ }, pack::Pack, traits::{ + storage_footprint_u128, + storage_footprint_u64, ClearAt, ClearForward, KeyPtr, @@ -133,7 +135,7 @@ pub use self::{ PushForward, SaturatingStorage, StorageFootprint, - StorageSize, + StorageFootprintOf, }, }; diff --git a/core/src/storage/pack.rs b/core/src/storage/pack.rs index 30f931ddaae..da5bbefa670 100644 --- a/core/src/storage/pack.rs +++ b/core/src/storage/pack.rs @@ -18,7 +18,8 @@ use crate::storage::{ PullForward, PushAt, PushForward, - StorageSize, + SaturatingStorage, + StorageFootprint, }; use ink_primitives::Key; @@ -76,16 +77,18 @@ impl Pack { } } -impl StorageSize for Pack { - const SIZE: u64 = 1; +impl StorageFootprint for Pack { + type Value = typenum::P1; } +impl SaturatingStorage for Pack {} + impl PullForward for Pack where T: PullAt, { fn pull_forward(ptr: &mut KeyPtr) -> Self { - ::pull_at(ptr.next_for::()) + ::pull_at(ptr.next_for2::()) } } @@ -105,7 +108,7 @@ where T: PushAt, { fn push_forward(&self, ptr: &mut KeyPtr) { - ::push_at(self, ptr.next_for::()) + ::push_at(self, ptr.next_for2::()) } } diff --git a/core/src/storage/traits/clear.rs b/core/src/storage/traits/clear.rs index 6655724bb5a..2e4ef4a8e44 100644 --- a/core/src/storage/traits/clear.rs +++ b/core/src/storage/traits/clear.rs @@ -14,7 +14,7 @@ use super::{ KeyPtr, - StorageSize, + StorageFootprint, }; use crate::env; use core::marker::PhantomData; @@ -51,7 +51,7 @@ macro_rules! impl_clear_for_primitive { $( impl ClearForward for $ty { fn clear_forward(&self, ptr: &mut KeyPtr) { - <$ty as ClearAt>::clear_at(self, ptr.next_for::<$ty>()) + <$ty as ClearAt>::clear_at(self, ptr.next_for2::<$ty>()) } } @@ -132,7 +132,7 @@ impl ClearAt for PhantomData { impl ClearForward for Option where - T: ClearForward + StorageSize, + T: ClearForward + StorageFootprint, { fn clear_forward(&self, ptr: &mut KeyPtr) { match self { @@ -177,7 +177,7 @@ impl ClearAt for Result {} impl ClearForward for ink_prelude::string::String { fn clear_forward(&self, ptr: &mut KeyPtr) { - ::clear_at(self, ptr.next_for::()) + ::clear_at(self, ptr.next_for2::()) } } diff --git a/core/src/storage/traits/footprint.rs b/core/src/storage/traits/footprint.rs index 7d00c2983e2..8219ee5a5b5 100644 --- a/core/src/storage/traits/footprint.rs +++ b/core/src/storage/traits/footprint.rs @@ -14,20 +14,12 @@ use ink_primitives::Key; -/// Implemented by types that can be stored on contract storage. -/// -/// Tells the amount of storage cells the type requires to be stored. -pub trait StorageSize { - /// The number of storage cells required by `Self` to be stored - /// on the contract storage. - const SIZE: u64; -} - use core::ops::{ Add, Mul, }; use typenum::{ + Integer, IsEqual, Max, Maximum, @@ -55,107 +47,92 @@ pub trait StorageFootprint { /// /// We should switch back to associated constants once the Rust compiler /// is more mature at handling them in generics. - type Value; + type Value: Integer; +} + +/// Helper type alias for better readability. +pub type StorageFootprintOf = ::Value; + +/// Returns the `u64` representation of the storage footprint of `T`. +pub const fn storage_footprint_u64() -> u64 +where + T: StorageFootprint, +{ + as Integer>::I64 as u64 +} + +/// Returns the `u128` representation of the storage footprint of `T`. +pub const fn storage_footprint_u128() -> u128 +where + T: StorageFootprint, +{ + as Integer>::I128 as u128 } /// Types implementing this trait are guaranteed to always use the same amount -/// of storage cells as described by the [`StorageSize`] trait. +/// of storage cells as described by the [`StorageFootprint`] trait. /// /// It is a bug to implement this trait for a type that does not respect this /// behaviour. -pub trait SaturatingStorage: StorageSize {} +pub trait SaturatingStorage: StorageFootprint {} + +/// Helper trait that should be implemented for types instead of implementing +/// [`SaturatingStorage`] trait directly since it decouples the trait bounds +/// of its super trait [`StorageFootprint`]. +pub trait SaturatingStorageMarker {} +impl SaturatingStorage for T where T: StorageFootprint + SaturatingStorageMarker {} macro_rules! impl_storage_size_for_primitive { ( $($ty:ty),* ) => { $( - impl StorageSize for $ty { - const SIZE: u64 = 1; - } impl StorageFootprint for $ty { type Value = P1; } - impl SaturatingStorage for $ty {} + impl SaturatingStorageMarker for $ty {} )* }; } impl_storage_size_for_primitive!(Key, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); -macro_rules! impl_storage_size_for_array { - ( $($n:literal),* $(,)? ) => { - $( - impl StorageSize for [T; $n] - where - T: StorageSize, - { - const SIZE: u64 = ::SIZE * $n; - } - impl SaturatingStorage for [T; $n] - where - T: SaturatingStorage, - {} - )* - }; -} -forward_supported_array_lens!(impl_storage_size_for_array); - macro_rules! impl_storage_size_for_array2 { ( $(($n:literal, $t:ty)),* $(,)? ) => { $( impl StorageFootprint for [T; $n] where T: StorageFootprint, - ::Value: Mul<$t>, + StorageFootprintOf: Mul<$t>, + Prod, $t>: Integer, { - type Value = Prod<::Value, $t>; + type Value = Prod, $t>; } + impl SaturatingStorageMarker for [T; $n] + where + T: SaturatingStorage, + {} )* }; } forward_supported_array_lens_ty!(impl_storage_size_for_array2); macro_rules! impl_storage_size_tuple { - ( $($frag:ident),* $(,)? ) => { - #[allow(unused_parens)] - impl<$($frag),*> StorageSize for ($($frag),* ,) - where - $( - $frag: StorageSize, - )* - { - const SIZE: u64 = 0 - $( - + <$frag as StorageSize>::SIZE - )* - ; - } - impl<$($frag),*> SaturatingStorage for ($($frag),* ,) + ( $($frag:ident),* ) => { + impl SaturatingStorageMarker for (T1 $(, $frag)* ,) where + T1: SaturatingStorage, $( $frag: SaturatingStorage, )* {} - } -} -impl_storage_size_tuple!(A); -impl_storage_size_tuple!(A, B); -impl_storage_size_tuple!(A, B, C); -impl_storage_size_tuple!(A, B, C, D); -impl_storage_size_tuple!(A, B, C, D, E); -impl_storage_size_tuple!(A, B, C, D, E, F); -impl_storage_size_tuple!(A, B, C, D, E, F, G); -impl_storage_size_tuple!(A, B, C, D, E, F, G, H); -impl_storage_size_tuple!(A, B, C, D, E, F, G, H, I); -impl_storage_size_tuple!(A, B, C, D, E, F, G, H, I, J); -macro_rules! impl_storage_size_tuple { - ( $($frag:ident),* ) => { impl StorageFootprint for (T1 $(, $frag)* ,) where T1: StorageFootprint, ($($frag ,)*): StorageFootprint, - ::Value: Add<<($($frag ,)*) as StorageFootprint>::Value>, + StorageFootprintOf: Add, // Not sure why we need this trait bound for T1 ... + StorageFootprintOf: Add>, + Sum, StorageFootprintOf<($($frag ,)*)>>: Integer, { - type Value = Sum<::Value, <($($frag ,)*) as StorageFootprint>::Value>; + type Value = Sum, StorageFootprintOf<($($frag ,)*)>>; } } } @@ -170,28 +147,16 @@ impl_storage_size_tuple!(T2, T3, T4, T5, T6, T7, T8); impl_storage_size_tuple!(T2, T3, T4, T5, T6, T7, T8, T9); impl_storage_size_tuple!(T2, T3, T4, T5, T6, T7, T8, T9, T10); -impl StorageSize for () { - const SIZE: u64 = 0; -} impl StorageFootprint for () { type Value = Z0; } impl SaturatingStorage for () {} -impl StorageSize for core::marker::PhantomData { - const SIZE: u64 = 0; -} impl StorageFootprint for core::marker::PhantomData { type Value = Z0; } impl SaturatingStorage for core::marker::PhantomData {} -impl StorageSize for Option -where - T: StorageSize, -{ - const SIZE: u64 = ::SIZE; -} impl StorageFootprint for Option where T: StorageFootprint, @@ -211,57 +176,32 @@ where // determined by `T` it should be okay to implement. } -impl StorageSize for Result -where - T: StorageSize, - E: StorageSize, -{ - const SIZE: u64 = { - // The following returns the maximum value from the storage - // sizes of type `T` and `E` in a way that enables it to be used - // at compile-time. - [::SIZE, ::SIZE] - [(::SIZE < ::SIZE) as usize] - }; -} - impl StorageFootprint for Result where T: StorageFootprint, E: StorageFootprint, - ::Value: Max<::Value>, + StorageFootprintOf: Max>, + Maximum, StorageFootprintOf>: Integer, { - type Value = Maximum<::Value, ::Value>; + type Value = Maximum, StorageFootprintOf>; } -impl SaturatingStorage for Result +impl SaturatingStorageMarker for Result where T: StorageFootprint + SaturatingStorage, E: StorageFootprint + SaturatingStorage, - ::Value: - IsEqual<::Value, Output = True>, -{ -} - -impl StorageSize for ink_prelude::boxed::Box -where - T: StorageSize, + StorageFootprintOf: IsEqual, Output = True>, { - const SIZE: u64 = ::SIZE; } impl StorageFootprint for ink_prelude::boxed::Box where T: StorageFootprint, { - type Value = ::Value; + type Value = StorageFootprintOf; } -impl SaturatingStorage for Box where T: SaturatingStorage {} - -impl StorageSize for ink_prelude::string::String { - const SIZE: u64 = 1; -} +impl SaturatingStorageMarker for Box where T: SaturatingStorage {} impl StorageFootprint for ink_prelude::string::String { type Value = P1; @@ -269,20 +209,12 @@ impl StorageFootprint for ink_prelude::string::String { impl SaturatingStorage for String {} -impl StorageSize for ink_prelude::vec::Vec { - const SIZE: u64 = 1; -} - impl StorageFootprint for ink_prelude::vec::Vec { type Value = P1; } impl SaturatingStorage for ink_prelude::vec::Vec {} -impl StorageSize for ink_prelude::collections::BTreeMap { - const SIZE: u64 = 1; -} - impl StorageFootprint for ink_prelude::collections::BTreeMap { type Value = P1; } @@ -292,10 +224,6 @@ impl SaturatingStorage for ink_prelude::collections::BTreeMap {} macro_rules! impl_storage_size_for_collection { ( $($name:ident),* $(,)? ) => { $( - impl StorageSize for ink_prelude::collections::$name { - const SIZE: u64 = 1; - } - impl StorageFootprint for ink_prelude::collections::$name { type Value = P1; } diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index 7b77f463ce2..d13672d763a 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -49,9 +49,9 @@ macro_rules! forward_supported_array_lens_ty { } mod clear; +mod footprint; mod pull; mod push; -mod footprint; use ink_primitives::Key; @@ -60,6 +60,13 @@ pub use self::{ ClearAt, ClearForward, }, + footprint::{ + storage_footprint_u128, + storage_footprint_u64, + SaturatingStorage, + StorageFootprint, + StorageFootprintOf, + }, pull::{ PullAt, PullForward, @@ -68,11 +75,6 @@ pub use self::{ PushAt, PushForward, }, - footprint::{ - StorageSize, - StorageFootprint, - SaturatingStorage, - }, }; /// A key pointer. @@ -96,17 +98,6 @@ impl KeyPtr { self.key } - /// Advances the key by the given amount derive by `T` and its `StorageSize` - /// and returns the next `Key` for usage by the storage element. - pub fn next_for(&mut self) -> Key - where - T: StorageSize, - { - let copy = self.key; - self.key += ::SIZE; - copy - } - /// Advances the key by the given amount derive by `T` and its `StorageSize` /// and returns the next `Key` for usage by the storage element. pub fn next_for2(&mut self) -> Key diff --git a/core/src/storage/traits/pull.rs b/core/src/storage/traits/pull.rs index ab719aa47ff..32e0c677e2f 100644 --- a/core/src/storage/traits/pull.rs +++ b/core/src/storage/traits/pull.rs @@ -14,7 +14,7 @@ use super::{ KeyPtr, - StorageSize, + StorageFootprint, }; use crate::env; use array_init::{ @@ -62,7 +62,7 @@ macro_rules! impl_pull_for_primitive { $( impl PullForward for $ty { fn pull_forward(ptr: &mut KeyPtr) -> Self { - ::pull_at(ptr.next_for::()) + ::pull_at(ptr.next_for2::()) } } impl PullAt for $ty { @@ -161,7 +161,7 @@ impl PullAt for PhantomData { impl PullForward for Option where - T: PullForward + StorageSize, + T: PullForward + StorageFootprint, { fn pull_forward(ptr: &mut KeyPtr) -> Self { // We decode as `()` because at this point we are not interested @@ -189,7 +189,7 @@ where E: PullForward, { fn pull_forward(ptr: &mut KeyPtr) -> Self { - match pull_single_cell::(ptr.next_for::()) { + match pull_single_cell::(ptr.next_for2::()) { 0 => Ok(::pull_forward(ptr)), 1 => Err(::pull_forward(ptr)), _ => unreachable!("found invalid Result discriminator"), @@ -208,7 +208,7 @@ where impl PullForward for ink_prelude::string::String { fn pull_forward(ptr: &mut KeyPtr) -> Self { - ::pull_at(ptr.next_for::()) + ::pull_at(ptr.next_for2::()) } } diff --git a/core/src/storage/traits/push.rs b/core/src/storage/traits/push.rs index 7eac5f1532f..f3b3907151d 100644 --- a/core/src/storage/traits/push.rs +++ b/core/src/storage/traits/push.rs @@ -14,7 +14,7 @@ use super::{ KeyPtr, - StorageSize, + StorageFootprint, }; use crate::env; use core::marker::PhantomData; @@ -49,7 +49,7 @@ macro_rules! impl_push_for_primitive { $( impl PushForward for $ty { fn push_forward(&self, ptr: &mut KeyPtr) { - <$ty as PushAt>::push_at(self, ptr.next_for::<$ty>()) + <$ty as PushAt>::push_at(self, ptr.next_for2::<$ty>()) } } @@ -148,9 +148,12 @@ impl PushAt for PhantomData { fn push_at(&self, _at: Key) {} } +use typenum::Integer; + impl PushForward for Option where - T: PushForward + StorageSize, + T: PushForward + StorageFootprint, + ::Value: Integer, { /// We implement `PushForward` for `Option` in an optimized fashion /// leaving behind a cleared contract storage cell area in case of `None`. @@ -163,11 +166,11 @@ where Some(val) => ::push_forward(val, ptr), None => { // We still need to advance the key pointer. - let pos0 = ptr.next_for::(); + let pos0 = ptr.next_for2::(); // Bail out early if `StorageSize` is too big and the method // is used even though we have tried to prevent this at compile // time. - if ::SIZE > 32 { + if ::Value::I64 as u64 > 32 { return } // # ToDo @@ -175,7 +178,7 @@ where // Create a trait bound onto something like // `ClearForward` and `ClearAt` that have a sole purpose of // clearing the underlying storage of a storage entity. - for n in 0..::SIZE { + for n in 0..<::Value as Integer>::I64 as u64 { env::clear_contract_storage(pos0 + n); } } @@ -237,7 +240,7 @@ where impl PushForward for ink_prelude::string::String { fn push_forward(&self, ptr: &mut KeyPtr) { - ::push_at(self, ptr.next_for::()) + ::push_at(self, ptr.next_for2::()) } } From f5d9bc594df145e47dee55bf61de5daa9ee1b045 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sat, 14 Mar 2020 13:23:25 +0100 Subject: [PATCH 094/142] [core] add rustfmt::skip to macro_rules --- core/src/storage/traits/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index d13672d763a..a8d1ff9d257 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -24,6 +24,7 @@ macro_rules! forward_supported_array_lens { }; } +#[rustfmt::skip] macro_rules! forward_supported_array_lens_ty { ( $mac:ident ) => { const _: () = { From 1b224f40421d17a3358fde304ed6cb34854df2e6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 00:44:41 +0100 Subject: [PATCH 095/142] [core] add FromIterator for storage::{SmallVec, Vec} --- core/src/storage/collections2/smallvec/mod.rs | 8 ++++++- .../storage/collections2/smallvec/traits.rs | 22 ++++++++++++++++++- core/src/storage/collections2/vec/mod.rs | 7 +++++- core/src/storage/collections2/vec/traits.rs | 21 +++++++++++++++++- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/core/src/storage/collections2/smallvec/mod.rs b/core/src/storage/collections2/smallvec/mod.rs index e1f4d832196..784df4419cd 100644 --- a/core/src/storage/collections2/smallvec/mod.rs +++ b/core/src/storage/collections2/smallvec/mod.rs @@ -136,7 +136,7 @@ where impl SmallVec where - T: StorageFootprint + SaturatingStorage + PullForward, + T: StorageFootprint + SaturatingStorage, N: LazyArrayLength, { /// Appends an element to the back of the vector. @@ -149,7 +149,13 @@ where *self.len += 1; self.elems.put(last_index, Some(value)); } +} +impl SmallVec +where + T: StorageFootprint + SaturatingStorage + PullForward, + N: LazyArrayLength, +{ /// Pops the last element from the vector and returns it. // /// Returns `None` if the vector is empty. diff --git a/core/src/storage/collections2/smallvec/traits.rs b/core/src/storage/collections2/smallvec/traits.rs index 34b5cc1cb36..262fb2aa69b 100644 --- a/core/src/storage/collections2/smallvec/traits.rs +++ b/core/src/storage/collections2/smallvec/traits.rs @@ -26,7 +26,10 @@ use crate::storage::{ StorageFootprint, StorageFootprintOf, }; -use core::ops::Add; +use core::{ + iter::FromIterator, + ops::Add, +}; use typenum::{ Add1, Integer, @@ -67,6 +70,23 @@ where } } +impl FromIterator for SmallVec +where + T: StorageFootprint + SaturatingStorage, + N: LazyArrayLength, +{ + fn from_iter(iter: I) -> Self + where + I: IntoIterator, + { + let mut vec = SmallVec::new(); + for item in iter { + vec.push(item) + } + vec + } +} + impl StorageFootprint for SmallVec where T: StorageFootprint + PullForward, diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 1c284de1b62..9e41b4cd4fd 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -121,7 +121,7 @@ where impl Vec where - T: StorageFootprint + SaturatingStorage + PullForward, + T: StorageFootprint + SaturatingStorage, { /// Appends an element to the back of the vector. pub fn push(&mut self, value: T) { @@ -133,7 +133,12 @@ where *self.len += 1; self.elems.put(last_index, Some(value)); } +} +impl Vec +where + T: StorageFootprint + SaturatingStorage + PullForward, +{ /// Pops the last element from the vector and returns it. // /// Returns `None` if the vector is empty. diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index b71fb57e8e7..8cf270a17c2 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -25,7 +25,10 @@ use crate::{ StorageFootprintOf, }, }; -use core::ops::Add; +use core::{ + iter::FromIterator, + ops::Add, +}; use typenum::{ Add1, Integer, @@ -63,6 +66,22 @@ where } } +impl FromIterator for StorageVec +where + T: StorageFootprint + SaturatingStorage, +{ + fn from_iter(iter: I) -> Self + where + I: IntoIterator, + { + let mut vec = StorageVec::new(); + for item in iter { + vec.push(item) + } + vec + } +} + impl StorageFootprint for StorageVec where T: StorageFootprint, From 0c3002acc427208a83886430c606f100d7ea0819 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 09:52:38 +0100 Subject: [PATCH 096/142] [core] add Extend impl to storage::{SmallVec, Vec} --- .../storage/collections2/smallvec/traits.rs | 21 +++++++++++++++---- core/src/storage/collections2/vec/traits.rs | 20 ++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/core/src/storage/collections2/smallvec/traits.rs b/core/src/storage/collections2/smallvec/traits.rs index 262fb2aa69b..bc6cc69473b 100644 --- a/core/src/storage/collections2/smallvec/traits.rs +++ b/core/src/storage/collections2/smallvec/traits.rs @@ -27,7 +27,7 @@ use crate::storage::{ StorageFootprintOf, }; use core::{ - iter::FromIterator, + iter::{FromIterator, Extend}, ops::Add, }; use typenum::{ @@ -70,6 +70,21 @@ where } } +impl Extend for SmallVec +where + T: StorageFootprint + SaturatingStorage, + N: LazyArrayLength, +{ + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for item in iter { + self.push(item) + } + } +} + impl FromIterator for SmallVec where T: StorageFootprint + SaturatingStorage, @@ -80,9 +95,7 @@ where I: IntoIterator, { let mut vec = SmallVec::new(); - for item in iter { - vec.push(item) - } + vec.extend(iter); vec } } diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index 8cf270a17c2..ad07a372da2 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -26,7 +26,7 @@ use crate::{ }, }; use core::{ - iter::FromIterator, + iter::{FromIterator, Extend}, ops::Add, }; use typenum::{ @@ -66,6 +66,20 @@ where } } +impl Extend for StorageVec +where + T: StorageFootprint + SaturatingStorage, +{ + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for item in iter { + self.push(item) + } + } +} + impl FromIterator for StorageVec where T: StorageFootprint + SaturatingStorage, @@ -75,9 +89,7 @@ where I: IntoIterator, { let mut vec = StorageVec::new(); - for item in iter { - vec.push(item) - } + vec.extend(iter); vec } } From 5725b4329eea77bda6fdac3044e571cc85322557 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 10:13:13 +0100 Subject: [PATCH 097/142] [core] use unsigned typenum integers instead of signed --- core/src/storage/collections2/boxed/traits.rs | 2 +- .../storage/collections2/smallvec/traits.rs | 4 +-- core/src/storage/collections2/vec/traits.rs | 4 +-- core/src/storage/lazy/lazy_array.rs | 24 ++++++------- core/src/storage/lazy/lazy_map.rs | 11 +++--- core/src/storage/traits/footprint.rs | 34 +++++++++---------- 6 files changed, 38 insertions(+), 41 deletions(-) diff --git a/core/src/storage/collections2/boxed/traits.rs b/core/src/storage/collections2/boxed/traits.rs index 7e6e8daff5f..5ae04339963 100644 --- a/core/src/storage/collections2/boxed/traits.rs +++ b/core/src/storage/collections2/boxed/traits.rs @@ -34,7 +34,7 @@ where /// /// The indirectly stored storage entity is not considered because the /// `StorageSize` is only concerned with inplace storage usage. - type Value = typenum::P1; + type Value = typenum::U1; } impl SaturatingStorage for StorageBox diff --git a/core/src/storage/collections2/smallvec/traits.rs b/core/src/storage/collections2/smallvec/traits.rs index bc6cc69473b..99e00e368ff 100644 --- a/core/src/storage/collections2/smallvec/traits.rs +++ b/core/src/storage/collections2/smallvec/traits.rs @@ -32,7 +32,7 @@ use core::{ }; use typenum::{ Add1, - Integer, + Unsigned, }; impl core::ops::Index for SmallVec @@ -106,7 +106,7 @@ where N: LazyArrayLength, LazyArray: StorageFootprint, StorageFootprintOf>: Add, - Add1>>: Integer, + Add1>>: Unsigned, { type Value = Add1>>; } diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index ad07a372da2..44513956e2d 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -31,7 +31,7 @@ use core::{ }; use typenum::{ Add1, - Integer, + Unsigned, }; impl core::ops::Index for StorageVec @@ -99,7 +99,7 @@ where T: StorageFootprint, storage::LazyChunk: StorageFootprint, StorageFootprintOf>: Add, - Add1>>: Integer, + Add1>>: Unsigned, { type Value = Add1>>; } diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs index b677ce8d528..070abce1544 100644 --- a/core/src/storage/lazy/lazy_array.rs +++ b/core/src/storage/lazy/lazy_array.rs @@ -36,16 +36,16 @@ use generic_array::{ }; use ink_primitives::Key; use typenum::{ - Integer, Prod, + Unsigned, }; /// The index type used in the lazy storage chunk. pub type Index = u32; /// Utility trait for helping with lazy array construction. -pub trait LazyArrayLength: ArrayLength>> + Integer {} -impl LazyArrayLength for T where T: ArrayLength>> + Integer {} +pub trait LazyArrayLength: ArrayLength>> + Unsigned {} +impl LazyArrayLength for T where T: ArrayLength>> + Unsigned {} /// A lazy storage array that spans over N storage cells. /// @@ -84,7 +84,7 @@ fn array_capacity() -> u32 where N: LazyArrayLength, { - ::I32 as u32 + ::U32 } /// The underlying array cache for the [`LazyArray`]. @@ -227,7 +227,7 @@ where T: StorageFootprint, N: LazyArrayLength, StorageFootprintOf: Mul, - Prod, N>: Integer, + Prod, N>: Unsigned, { type Value = Prod, N>; } @@ -250,8 +250,8 @@ where T: StorageFootprint + SaturatingStorage + PushForward, N: LazyArrayLength, Self: StorageFootprint, - ::Value: Integer, - ::Value: Integer, + ::Value: Unsigned, + ::Value: Unsigned, { fn push_forward(&self, ptr: &mut KeyPtr) { let offset_key = ptr.next_for2::(); @@ -260,8 +260,7 @@ where if !entry.is_mutated() { continue } - let footprint = - <::Value as Integer>::to_i64() as u64; + let footprint = <::Value as Unsigned>::to_u64(); let key = offset_key + (index as u64 * footprint); match entry.value() { Some(value) => { @@ -293,7 +292,7 @@ where impl LazyArray where T: StorageFootprint, - ::Value: Integer, + ::Value: Unsigned, N: LazyArrayLength, { /// Returns the offset key for the given index if not out of bounds. @@ -302,8 +301,7 @@ where return None } self.key.map(|key| { - key + ((at as u64) - * <::Value as Integer>::to_i64() as u64) + key + ((at as u64) * <::Value as Unsigned>::to_u64()) }) } } @@ -311,7 +309,7 @@ where impl LazyArray where T: StorageFootprint + PullForward, - ::Value: Integer, + ::Value: Unsigned, N: LazyArrayLength, { /// Loads the entry at the given index. diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index d8a56feeb97..255a6d1ea79 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -36,8 +36,8 @@ use ink_prelude::{ }; use ink_primitives::Key; use typenum::{ - Integer, Prod, + Unsigned, }; /// The index type used in the lazy storage chunk. @@ -59,11 +59,10 @@ pub trait KeyMapping { impl KeyMapping for Index where Value: StorageFootprint, - ::Value: Integer, + ::Value: Unsigned, { fn to_storage_key(&self, offset: &Key) -> Key { - *offset - + (*self as u64 * <::Value as Integer>::I64 as u64) + *offset + (*self as u64 * <::Value as Unsigned>::U64) } } @@ -292,7 +291,7 @@ impl StorageFootprint for LazyChunk where T: StorageFootprint, StorageFootprintOf: Mul, - Prod, P4294967296>: Integer, + Prod, P4294967296>: Unsigned, { /// A lazy chunk is contiguous and its size can be determined by the /// total number of elements it could theoretically hold. @@ -306,7 +305,7 @@ where /// A lazy mapping is similar to a Solidity mapping that distributes its /// stored entities across the entire contract storage so its inplace size /// is actually just 1. - type Value = typenum::P1; + type Value = typenum::U1; } impl PullForward for LazyMap diff --git a/core/src/storage/traits/footprint.rs b/core/src/storage/traits/footprint.rs index 8219ee5a5b5..5f7a29b6598 100644 --- a/core/src/storage/traits/footprint.rs +++ b/core/src/storage/traits/footprint.rs @@ -19,15 +19,15 @@ use core::ops::{ Mul, }; use typenum::{ - Integer, IsEqual, Max, Maximum, Prod, Sum, + Unsigned, B1 as True, - P1, - Z0, + U0, + U1, }; /// Implemented by types that can be stored on contract storage. @@ -47,7 +47,7 @@ pub trait StorageFootprint { /// /// We should switch back to associated constants once the Rust compiler /// is more mature at handling them in generics. - type Value: Integer; + type Value: Unsigned; } /// Helper type alias for better readability. @@ -58,7 +58,7 @@ pub const fn storage_footprint_u64() -> u64 where T: StorageFootprint, { - as Integer>::I64 as u64 + as Unsigned>::U64 } /// Returns the `u128` representation of the storage footprint of `T`. @@ -66,7 +66,7 @@ pub const fn storage_footprint_u128() -> u128 where T: StorageFootprint, { - as Integer>::I128 as u128 + as Unsigned>::U128 } /// Types implementing this trait are guaranteed to always use the same amount @@ -86,7 +86,7 @@ macro_rules! impl_storage_size_for_primitive { ( $($ty:ty),* ) => { $( impl StorageFootprint for $ty { - type Value = P1; + type Value = U1; } impl SaturatingStorageMarker for $ty {} )* @@ -101,7 +101,7 @@ macro_rules! impl_storage_size_for_array2 { where T: StorageFootprint, StorageFootprintOf: Mul<$t>, - Prod, $t>: Integer, + Prod, $t>: Unsigned, { type Value = Prod, $t>; } @@ -128,9 +128,9 @@ macro_rules! impl_storage_size_tuple { where T1: StorageFootprint, ($($frag ,)*): StorageFootprint, - StorageFootprintOf: Add, // Not sure why we need this trait bound for T1 ... + StorageFootprintOf: Add, // Not sure why we need this trait bound for T1 ... StorageFootprintOf: Add>, - Sum, StorageFootprintOf<($($frag ,)*)>>: Integer, + Sum, StorageFootprintOf<($($frag ,)*)>>: Unsigned, { type Value = Sum, StorageFootprintOf<($($frag ,)*)>>; } @@ -148,12 +148,12 @@ impl_storage_size_tuple!(T2, T3, T4, T5, T6, T7, T8, T9); impl_storage_size_tuple!(T2, T3, T4, T5, T6, T7, T8, T9, T10); impl StorageFootprint for () { - type Value = Z0; + type Value = U0; } impl SaturatingStorage for () {} impl StorageFootprint for core::marker::PhantomData { - type Value = Z0; + type Value = U0; } impl SaturatingStorage for core::marker::PhantomData {} @@ -181,7 +181,7 @@ where T: StorageFootprint, E: StorageFootprint, StorageFootprintOf: Max>, - Maximum, StorageFootprintOf>: Integer, + Maximum, StorageFootprintOf>: Unsigned, { type Value = Maximum, StorageFootprintOf>; } @@ -204,19 +204,19 @@ where impl SaturatingStorageMarker for Box where T: SaturatingStorage {} impl StorageFootprint for ink_prelude::string::String { - type Value = P1; + type Value = U1; } impl SaturatingStorage for String {} impl StorageFootprint for ink_prelude::vec::Vec { - type Value = P1; + type Value = U1; } impl SaturatingStorage for ink_prelude::vec::Vec {} impl StorageFootprint for ink_prelude::collections::BTreeMap { - type Value = P1; + type Value = U1; } impl SaturatingStorage for ink_prelude::collections::BTreeMap {} @@ -225,7 +225,7 @@ macro_rules! impl_storage_size_for_collection { ( $($name:ident),* $(,)? ) => { $( impl StorageFootprint for ink_prelude::collections::$name { - type Value = P1; + type Value = U1; } impl SaturatingStorage for ink_prelude::collections::$name {} From d48263b27cb1626a66c7a6c46fcfcddbbe02297f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 10:13:46 +0100 Subject: [PATCH 098/142] [core] apply rustfmt, add missing license header --- core/src/storage/collections2/smallvec/traits.rs | 5 ++++- core/src/storage/collections2/vec/tests.rs | 14 ++++++++++++++ core/src/storage/collections2/vec/traits.rs | 5 ++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/core/src/storage/collections2/smallvec/traits.rs b/core/src/storage/collections2/smallvec/traits.rs index 99e00e368ff..e6c319c6b62 100644 --- a/core/src/storage/collections2/smallvec/traits.rs +++ b/core/src/storage/collections2/smallvec/traits.rs @@ -27,7 +27,10 @@ use crate::storage::{ StorageFootprintOf, }; use core::{ - iter::{FromIterator, Extend}, + iter::{ + Extend, + FromIterator, + }, ops::Add, }; use typenum::{ diff --git a/core/src/storage/collections2/vec/tests.rs b/core/src/storage/collections2/vec/tests.rs index f7e089800fe..72f9291e194 100644 --- a/core/src/storage/collections2/vec/tests.rs +++ b/core/src/storage/collections2/vec/tests.rs @@ -1,3 +1,17 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use super::Vec as StorageVec; #[test] diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index 44513956e2d..6366d0f30a3 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -26,7 +26,10 @@ use crate::{ }, }; use core::{ - iter::{FromIterator, Extend}, + iter::{ + Extend, + FromIterator, + }, ops::Add, }; use typenum::{ From 104678492e82948aeeb52103ab8a7f927517d363 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 10:26:33 +0100 Subject: [PATCH 099/142] [core] fix remaining issues with using unsigned typenum integers --- core/src/storage/pack.rs | 2 +- core/src/storage/traits/mod.rs | 4 ++-- core/src/storage/traits/push.rs | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/storage/pack.rs b/core/src/storage/pack.rs index da5bbefa670..bfa1e654e0c 100644 --- a/core/src/storage/pack.rs +++ b/core/src/storage/pack.rs @@ -78,7 +78,7 @@ impl Pack { } impl StorageFootprint for Pack { - type Value = typenum::P1; + type Value = typenum::U1; } impl SaturatingStorage for Pack {} diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index a8d1ff9d257..a3ef4bb2349 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -104,10 +104,10 @@ impl KeyPtr { pub fn next_for2(&mut self) -> Key where T: StorageFootprint, - ::Value: typenum::Integer, + ::Value: typenum::Unsigned, { let copy = self.key; - self.key += <::Value as typenum::Integer>::to_i64() as u64; + self.key += <::Value as typenum::Unsigned>::U128; copy } } diff --git a/core/src/storage/traits/push.rs b/core/src/storage/traits/push.rs index f3b3907151d..669122a7a07 100644 --- a/core/src/storage/traits/push.rs +++ b/core/src/storage/traits/push.rs @@ -148,12 +148,12 @@ impl PushAt for PhantomData { fn push_at(&self, _at: Key) {} } -use typenum::Integer; +use typenum::Unsigned; impl PushForward for Option where T: PushForward + StorageFootprint, - ::Value: Integer, + ::Value: Unsigned, { /// We implement `PushForward` for `Option` in an optimized fashion /// leaving behind a cleared contract storage cell area in case of `None`. @@ -170,7 +170,7 @@ where // Bail out early if `StorageSize` is too big and the method // is used even though we have tried to prevent this at compile // time. - if ::Value::I64 as u64 > 32 { + if ::Value::U64 > 32 { return } // # ToDo @@ -178,7 +178,7 @@ where // Create a trait bound onto something like // `ClearForward` and `ClearAt` that have a sole purpose of // clearing the underlying storage of a storage entity. - for n in 0..<::Value as Integer>::I64 as u64 { + for n in 0..<::Value as Unsigned>::U64 { env::clear_contract_storage(pos0 + n); } } From 2693c2a0afd498f067975357343a374bdc751ca8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 10:26:51 +0100 Subject: [PATCH 100/142] [core] fix LazyArrayLength auto impls --- core/src/storage/lazy/lazy_array.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs index 070abce1544..f2f47c5856a 100644 --- a/core/src/storage/lazy/lazy_array.rs +++ b/core/src/storage/lazy/lazy_array.rs @@ -38,6 +38,10 @@ use ink_primitives::Key; use typenum::{ Prod, Unsigned, + UTerm, + UInt, + B0, + B1, }; /// The index type used in the lazy storage chunk. @@ -45,7 +49,9 @@ pub type Index = u32; /// Utility trait for helping with lazy array construction. pub trait LazyArrayLength: ArrayLength>> + Unsigned {} -impl LazyArrayLength for T where T: ArrayLength>> + Unsigned {} +impl LazyArrayLength for UTerm {} +impl>>> LazyArrayLength for UInt {} +impl>>> LazyArrayLength for UInt {} /// A lazy storage array that spans over N storage cells. /// From 5a25feda7631ba299f7d69346d5b35125cc8b75a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 10:27:25 +0100 Subject: [PATCH 101/142] [core] add initial tests for storage::SmallVec --- core/src/storage/collections2/smallvec/mod.rs | 3 ++ .../storage/collections2/smallvec/tests.rs | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 core/src/storage/collections2/smallvec/tests.rs diff --git a/core/src/storage/collections2/smallvec/mod.rs b/core/src/storage/collections2/smallvec/mod.rs index 784df4419cd..29661c69982 100644 --- a/core/src/storage/collections2/smallvec/mod.rs +++ b/core/src/storage/collections2/smallvec/mod.rs @@ -15,6 +15,9 @@ mod iter; mod traits; +#[cfg(test)] +mod tests; + pub use self::iter::Iter; use crate::{ storage, diff --git a/core/src/storage/collections2/smallvec/tests.rs b/core/src/storage/collections2/smallvec/tests.rs new file mode 100644 index 00000000000..607babd8505 --- /dev/null +++ b/core/src/storage/collections2/smallvec/tests.rs @@ -0,0 +1,28 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::SmallVec; +use generic_array::typenum::*; + +#[test] +fn new_vec_works() { + let vec = >::new(); + assert!(vec.is_empty()); + assert_eq!(vec.len(), 0); + assert!(vec.iter().next().is_none()); + let default = as Default>::default(); + assert!(default.is_empty()); + assert_eq!(default.len(), 0); + assert!(default.iter().next().is_none()); +} From 3387453750821170a2458b2f7d298dbe0f89cd67 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 14:00:54 +0100 Subject: [PATCH 102/142] [core] add PartialEq and Eq impls for storage::{SmallVec, Vec} --- .../storage/collections2/smallvec/traits.rs | 20 +++++++++++++++++++ core/src/storage/collections2/vec/traits.rs | 18 +++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/core/src/storage/collections2/smallvec/traits.rs b/core/src/storage/collections2/smallvec/traits.rs index e6c319c6b62..2d5672f016f 100644 --- a/core/src/storage/collections2/smallvec/traits.rs +++ b/core/src/storage/collections2/smallvec/traits.rs @@ -137,3 +137,23 @@ where PushForward::push_forward(&self.elems, ptr); } } + +impl core::cmp::PartialEq for SmallVec +where + T: PartialEq + StorageFootprint + PullForward, + N: LazyArrayLength, +{ + fn eq(&self, other: &Self) -> bool { + if self.len() != other.len() { + return false + } + self.iter().zip(other.iter()).all(|(lhs, rhs)| lhs == rhs) + } +} + +impl core::cmp::Eq for SmallVec +where + T: Eq + StorageFootprint + PullForward, + N: LazyArrayLength, +{ +} diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index 6366d0f30a3..192ec08144c 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -151,3 +151,21 @@ where } } } + +impl core::cmp::PartialEq for StorageVec +where + T: PartialEq + StorageFootprint + PullForward, +{ + fn eq(&self, other: &Self) -> bool { + if self.len() != other.len() { + return false + } + self.iter().zip(other.iter()).all(|(lhs, rhs)| lhs == rhs) + } +} + +impl core::cmp::Eq for StorageVec +where + T: Eq + StorageFootprint + PullForward, +{ +} From 73ae7db53d6790b19b104fe79a72a6a4ffdaade4 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 14:03:27 +0100 Subject: [PATCH 103/142] [core] fix clippy warning --- core/src/storage/lazy/lazy_array.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs index f2f47c5856a..b39f4c72832 100644 --- a/core/src/storage/lazy/lazy_array.rs +++ b/core/src/storage/lazy/lazy_array.rs @@ -147,7 +147,7 @@ where /// Inserts a new entry into the cache and returns an exclusive reference to it. fn insert_entry(&mut self, at: Index, entry: Entry) -> &mut Entry { - *&mut self.entries[at as usize] = Some(entry); + self.entries[at as usize] = Some(entry); let entry: Option<&mut Entry> = (&mut self.entries[at as usize]).into(); entry.expect("just inserted the entry") } From 7a9424de78643489c3ea9d4cde8d3ccedd19942a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 16:12:15 +0100 Subject: [PATCH 104/142] [core] add panic to storage::Vec::swap if both indices are out of bounds --- core/src/storage/collections2/vec/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 9e41b4cd4fd..3aad15db461 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -206,6 +206,7 @@ where /// /// If one or both indices are out of bounds. pub fn swap(&mut self, a: u32, b: u32) { + assert!(a < self.len() && b < self.len(), "indices are out of bounds"); self.elems.swap(a, b) } From db112a1d0e4e62c1a2d777223a9789dd6b14d769 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 16:12:49 +0100 Subject: [PATCH 105/142] [core] make storage::Vec::pop_drop return Option<()> --- core/src/storage/collections2/vec/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 3aad15db461..4e39b43584d 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -153,18 +153,20 @@ where /// Pops the last element from the vector and immediately drops it. /// - /// Does nothing if the vector is empty. + /// Returns `Some(())` if an element has been removed and `None` otherwise. /// /// # Note /// - /// This operation is a bit more efficient than [`Vec::pop`] for some use cases. - pub fn pop_drop(&mut self) { + /// This operation is a bit more efficient than [`Vec::pop`] + /// since it avoids reading from contract storage in some use cases. + pub fn pop_drop(&mut self) -> Option<()> { if self.is_empty() { - return + return None } let last_index = self.len() - 1; *self.len = last_index; self.elems.remove(last_index); + Some(()) } /// Returns an exclusive reference to the first element if any. From d722e3dbc8961cb68135a99155581f14b91731af Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 16:13:03 +0100 Subject: [PATCH 106/142] [core] improve storage::Vec::replace impl --- core/src/storage/collections2/vec/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 4e39b43584d..9ec65b4f63b 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -195,10 +195,8 @@ where where F: FnOnce() -> T, { - self.within_bounds(index).map(|index| { - self.elems - .put_get(index, Some(f())) - .expect("expected an actual element since access is within bounds") + self.get_mut(index).map(|value| { + core::mem::replace(value, f()) }) } From 8c3c85feba3b11e3a8e75eccd5ad2203661ff0c5 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 16:13:17 +0100 Subject: [PATCH 107/142] [test] add unit tests for storage::Vec --- core/src/storage/collections2/vec/tests.rs | 191 +++++++++++++++++++++ 1 file changed, 191 insertions(+) diff --git a/core/src/storage/collections2/vec/tests.rs b/core/src/storage/collections2/vec/tests.rs index 72f9291e194..cc2b9274b9a 100644 --- a/core/src/storage/collections2/vec/tests.rs +++ b/core/src/storage/collections2/vec/tests.rs @@ -16,12 +16,203 @@ use super::Vec as StorageVec; #[test] fn new_vec_works() { + // `StorageVec::new` let vec = >::new(); assert!(vec.is_empty()); assert_eq!(vec.len(), 0); + assert_eq!(vec.get(0), None); assert!(vec.iter().next().is_none()); + // `StorageVec::default` let default = as Default>::default(); assert!(default.is_empty()); assert_eq!(default.len(), 0); + assert_eq!(vec.get(0), None); assert!(default.iter().next().is_none()); + // `StorageVec::new` and `StorageVec::default` should be equal. + assert_eq!(vec, default); +} + +#[test] +fn from_iterator_works() { + let some_primes = [1, 2, 3, 5, 7, 11, 13]; + assert_eq!(some_primes.iter().copied().collect::>(), { + let mut vec = StorageVec::new(); + for prime in &some_primes { + vec.push(*prime) + } + vec + }); +} + +#[test] +fn from_empty_iterator_works() { + assert_eq!( + [].iter().copied().collect::>(), + StorageVec::new(), + ); +} + +#[test] +fn push_pop_first_last_works() { + /// Asserts conditions are met for the given storage vector. + fn assert_vec(vec: &StorageVec, len: u32, first: F, last: L) + where + F: Into>, + L: Into>, + { + assert_eq!(vec.is_empty(), len == 0); + assert_eq!(vec.len(), len); + assert_eq!(vec.first().copied(), first.into()); + assert_eq!(vec.last().copied(), last.into()); + } + + let mut vec = StorageVec::new(); + assert_vec(&vec, 0, None, None); + + // Sequence of `push` + vec.push(b'a'); + assert_vec(&vec, 1, b'a', b'a'); + vec.push(b'b'); + assert_vec(&vec, 2, b'a', b'b'); + vec.push(b'c'); + assert_vec(&vec, 3, b'a', b'c'); + vec.push(b'd'); + assert_vec(&vec, 4, b'a', b'd'); + + // Sequence of `pop` + assert_eq!(vec.pop(), Some(b'd')); + assert_vec(&vec, 3, b'a', b'c'); + assert_eq!(vec.pop(), Some(b'c')); + assert_vec(&vec, 2, b'a', b'b'); + assert_eq!(vec.pop(), Some(b'b')); + assert_vec(&vec, 1, b'a', b'a'); + assert_eq!(vec.pop(), Some(b'a')); + assert_vec(&vec, 0, None, None); + + // Pop from empty vector. + assert_eq!(vec.pop(), None); + assert_vec(&vec, 0, None, None); +} + +#[test] +fn pop_drop_works() { + let elems = [b'a', b'b', b'c', b'd']; + let mut vec = vec_from_slice(&elems); + assert_eq!(vec.pop_drop(), Some(())); + assert_eq_slice(&vec, &elems[0..3]); + assert_eq!(vec.pop_drop(), Some(())); + assert_eq_slice(&vec, &elems[0..2]); + assert_eq!(vec.pop_drop(), Some(())); + assert_eq_slice(&vec, &elems[0..1]); + assert_eq!(vec.pop_drop(), Some(())); + assert_eq_slice(&vec, &[]); + assert_eq!(vec.pop_drop(), None); + assert_eq_slice(&vec, &[]); +} + +#[test] +fn get_works() { + let elems = [b'a', b'b', b'c', b'd']; + let mut vec = vec_from_slice(&elems); + for (n, mut expected) in elems.iter().copied().enumerate() { + let n = n as u32; + assert_eq!(vec.get(n), Some(&expected)); + assert_eq!(vec.get_mut(n), Some(&mut expected)); + } + let len = vec.len(); + assert_eq!(vec.get(len), None); + assert_eq!(vec.get_mut(len), None); +} + +#[test] +fn iter_next_works() { + let elems = [b'a', b'b', b'c', b'd']; + let vec = vec_from_slice(&elems); + let mut iter = vec.iter(); + assert_eq!(iter.next(), Some(&b'a')); + assert_eq!(iter.next(), Some(&b'b')); + assert_eq!(iter.next(), Some(&b'c')); + assert_eq!(iter.next(), Some(&b'd')); + assert_eq!(iter.next(), None); +} + +#[test] +fn iter_next_back_works() { + let elems = [b'a', b'b', b'c', b'd']; + let vec = vec_from_slice(&elems); + let mut iter = vec.iter().rev(); + assert_eq!(iter.next(), Some(&b'd')); + assert_eq!(iter.next(), Some(&b'c')); + assert_eq!(iter.next(), Some(&b'b')); + assert_eq!(iter.next(), Some(&b'a')); + assert_eq!(iter.next(), None); +} + +/// Asserts that the the given ordered storage vector elements are equal to the +/// ordered elements of the given slice. +fn assert_eq_slice(vec: &StorageVec, slice: &[u8]) { + assert_eq!(vec.len() as usize, slice.len()); + assert!(vec.iter().zip(slice.iter()).all(|(lhs, rhs)| *lhs == *rhs)) +} + +/// Creates a storage vector from the given slice. +fn vec_from_slice(slice: &[u8]) -> StorageVec { + slice.iter().copied().collect::>() +} + +#[test] +fn swap_works() { + let elems = [b'a', b'b', b'c', b'd']; + let mut vec = vec_from_slice(&elems); + + // Swap at same position is a no-op. + for index in 0..elems.len() as u32 { + vec.swap(index, index); + assert_eq_slice(&vec, &elems); + } + + // Swap first and second + vec.swap(0, 1); + assert_eq_slice(&vec, &[b'b', b'a', b'c', b'd']); + // Swap third and last + vec.swap(2, 3); + assert_eq_slice(&vec, &[b'b', b'a', b'd', b'c']); + // Swap first and last + vec.swap(0, 3); + assert_eq_slice(&vec, &[b'c', b'a', b'd', b'b']); +} + +#[test] +#[should_panic] +fn swap_one_invalid_index() { + let mut vec = vec_from_slice(&[b'a', b'b', b'c', b'd']); + vec.swap(0, vec.len()); +} + +#[test] +#[should_panic] +fn swap_both_invalid_indices() { + let mut vec = vec_from_slice(&[b'a', b'b', b'c', b'd']); + vec.swap(vec.len(), vec.len()); +} + +#[test] +fn swap_remove_works() { + let mut vec = vec_from_slice(&[b'a', b'b', b'c', b'd']); + + // Swap remove first element. + assert_eq!(vec.swap_remove(0), Some(b'a')); + assert_eq_slice(&vec, &[b'd', b'b', b'c']); + // Swap remove middle element. + assert_eq!(vec.swap_remove(1), Some(b'b')); + assert_eq_slice(&vec, &[b'd', b'c']); + // Swap remove last element. + assert_eq!(vec.swap_remove(1), Some(b'c')); + assert_eq_slice(&vec, &[b'd']); + // Swap remove only element. + assert_eq!(vec.swap_remove(0), Some(b'd')); + assert_eq_slice(&vec, &[]); + // Swap remove from empty vector. + assert_eq!(vec.swap_remove(0), None); + assert_eq_slice(&vec, &[]); } From 33abd5a5e9495a7dced99bb63c2dc4597f8955a7 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 16:13:41 +0100 Subject: [PATCH 108/142] [core] apply rustfmt --- core/src/storage/collections2/vec/mod.rs | 10 ++++++---- core/src/storage/collections2/vec/traits.rs | 6 +----- core/src/storage/lazy/lazy_array.rs | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 9ec65b4f63b..268be87bfce 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -195,9 +195,8 @@ where where F: FnOnce() -> T, { - self.get_mut(index).map(|value| { - core::mem::replace(value, f()) - }) + self.get_mut(index) + .map(|value| core::mem::replace(value, f())) } /// Swaps the elements at the given indices. @@ -206,7 +205,10 @@ where /// /// If one or both indices are out of bounds. pub fn swap(&mut self, a: u32, b: u32) { - assert!(a < self.len() && b < self.len(), "indices are out of bounds"); + assert!( + a < self.len() && b < self.len(), + "indices are out of bounds" + ); self.elems.swap(a, b) } diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index 192ec08144c..d0dbfaef9dc 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -164,8 +164,4 @@ where } } -impl core::cmp::Eq for StorageVec -where - T: Eq + StorageFootprint + PullForward, -{ -} +impl core::cmp::Eq for StorageVec where T: Eq + StorageFootprint + PullForward {} diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs index b39f4c72832..44364c57715 100644 --- a/core/src/storage/lazy/lazy_array.rs +++ b/core/src/storage/lazy/lazy_array.rs @@ -37,9 +37,9 @@ use generic_array::{ use ink_primitives::Key; use typenum::{ Prod, - Unsigned, - UTerm, UInt, + UTerm, + Unsigned, B0, B1, }; From bd0c80d0064325cb6bda19d31c56067ddc0ab80a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 16:15:51 +0100 Subject: [PATCH 109/142] [core] remove storage::{SmallVec, Vec}::replace methods --- core/src/storage/collections2/smallvec/mod.rs | 14 -------------- core/src/storage/collections2/vec/mod.rs | 11 ----------- 2 files changed, 25 deletions(-) diff --git a/core/src/storage/collections2/smallvec/mod.rs b/core/src/storage/collections2/smallvec/mod.rs index 29661c69982..d065e0f4041 100644 --- a/core/src/storage/collections2/smallvec/mod.rs +++ b/core/src/storage/collections2/smallvec/mod.rs @@ -206,20 +206,6 @@ where .and_then(move |index| self.elems.get_mut(index)) } - /// Replaces the element at the given index and returns the old value. - /// - /// Returns `None` if `n` is out of bounds. - pub fn replace(&mut self, index: u32, f: F) -> Option - where - F: FnOnce() -> T, - { - self.within_bounds(index).map(|index| { - self.elems - .put_get(index, Some(f())) - .expect("expected an actual element since access is within bounds") - }) - } - /// Swaps the elements at the given indices. /// /// # Panics diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 268be87bfce..e506d5b43d1 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -188,17 +188,6 @@ where .and_then(move |index| self.elems.get_mut(index)) } - /// Replaces the element at the given index and returns the old value. - /// - /// Returns `None` if `n` is out of bounds. - pub fn replace(&mut self, index: u32, f: F) -> Option - where - F: FnOnce() -> T, - { - self.get_mut(index) - .map(|value| core::mem::replace(value, f())) - } - /// Swaps the elements at the given indices. /// /// # Panics From 6f681eee8ace907072c1f7b4ca675801db158133 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 16:29:43 +0100 Subject: [PATCH 110/142] [core] implement storage::Vec::swap_remove_drop + unit tests --- core/src/storage/collections2/vec/mod.rs | 22 ++++++++++++++++++++++ core/src/storage/collections2/vec/tests.rs | 21 +++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index e506d5b43d1..fa96cb8ae3f 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -216,4 +216,26 @@ where self.elems.swap(n, self.len() - 1); self.pop() } + + /// Removes the indexed element from the vector. + /// + /// The last element of the vector is put into the indexed slot. + /// Returns `Some(())` if an element has been removed and `None` otherwise. + /// + /// # Note + /// + /// This operation should be preferred over [`Vec::swap_remove`] if there is + /// no need to return the removed element since it avoids a contract storage + /// read for some use cases. + pub fn swap_remove_drop(&mut self, n: u32) -> Option<()> { + if self.is_empty() { + return None + } + self.elems.remove(n); + let last_index = self.len() - 1; + let last = self.elems.take(last_index); + self.elems.put(n, last); + *self.len = last_index; + Some(()) + } } diff --git a/core/src/storage/collections2/vec/tests.rs b/core/src/storage/collections2/vec/tests.rs index cc2b9274b9a..e966f35d20f 100644 --- a/core/src/storage/collections2/vec/tests.rs +++ b/core/src/storage/collections2/vec/tests.rs @@ -216,3 +216,24 @@ fn swap_remove_works() { assert_eq!(vec.swap_remove(0), None); assert_eq_slice(&vec, &[]); } + +#[test] +fn swap_remove_drop_works() { + let mut vec = vec_from_slice(&[b'a', b'b', b'c', b'd']); + + // Swap remove first element. + assert_eq!(vec.swap_remove_drop(0), Some(())); + assert_eq_slice(&vec, &[b'd', b'b', b'c']); + // Swap remove middle element. + assert_eq!(vec.swap_remove_drop(1), Some(())); + assert_eq_slice(&vec, &[b'd', b'c']); + // Swap remove last element. + assert_eq!(vec.swap_remove_drop(1), Some(())); + assert_eq_slice(&vec, &[b'd']); + // Swap remove only element. + assert_eq!(vec.swap_remove_drop(0), Some(())); + assert_eq_slice(&vec, &[]); + // Swap remove from empty vector. + assert_eq!(vec.swap_remove_drop(0), None); + assert_eq_slice(&vec, &[]); +} From 0302a623d085bdaa6e870a4d7ccb9efba71708d6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 16:34:26 +0100 Subject: [PATCH 111/142] [core] remove storage::LazyMap::remove It is superseeded by storage::LazyMap::put(n, None) --- core/src/storage/collections2/vec/mod.rs | 4 ++-- core/src/storage/lazy/lazy_map.rs | 24 ------------------------ 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index fa96cb8ae3f..09a2ce2686e 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -165,7 +165,7 @@ where } let last_index = self.len() - 1; *self.len = last_index; - self.elems.remove(last_index); + self.elems.put(last_index, None); Some(()) } @@ -231,7 +231,7 @@ where if self.is_empty() { return None } - self.elems.remove(n); + self.elems.put(n, None); let last_index = self.len() - 1; let last = self.elems.take(last_index); self.elems.put(n, last); diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index 255a6d1ea79..adc4fd935c4 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -451,30 +451,6 @@ where pub fn take(&mut self, index: K) -> Option { self.lazily_load_mut(index).take_value() } - - /// Removes the element at the given index if any. - /// - /// # Note - /// - /// This method should be preferred over [`LazyMap::take`] in cases where the - /// caller is not interested in the taken (removed) value since it is more - /// efficient for some use cases. - pub fn remove(&mut self, index: K) { - use ink_prelude::collections::btree_map::Entry as BTreeMapEntry; - match self.entries_mut().entry(index) { - BTreeMapEntry::Vacant(vacant) => { - vacant.insert(Box::new(Entry { - value: None, - mutated: Cell::new(true), - })); - } - BTreeMapEntry::Occupied(occupied) => { - let entry = occupied.into_mut(); - entry.value = None; - entry.mutated = Cell::new(true); - } - } - } } impl LazyMap From 1a00b547527824afa74cf5ee60b65d53189ef231 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 16:34:39 +0100 Subject: [PATCH 112/142] [core] add storage::SmallVec::swap_remove_drop --- core/src/storage/collections2/smallvec/mod.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/core/src/storage/collections2/smallvec/mod.rs b/core/src/storage/collections2/smallvec/mod.rs index d065e0f4041..7183c38d681 100644 --- a/core/src/storage/collections2/smallvec/mod.rs +++ b/core/src/storage/collections2/smallvec/mod.rs @@ -230,4 +230,26 @@ where self.elems.swap(n, self.len() - 1); self.pop() } + + /// Removes the indexed element from the vector. + /// + /// The last element of the vector is put into the indexed slot. + /// Returns `Some(())` if an element has been removed and `None` otherwise. + /// + /// # Note + /// + /// This operation should be preferred over [`Vec::swap_remove`] if there is + /// no need to return the removed element since it avoids a contract storage + /// read for some use cases. + pub fn swap_remove_drop(&mut self, n: u32) -> Option<()> { + if self.is_empty() { + return None + } + self.elems.put(n, None); + let last_index = self.len() - 1; + let last = self.elems.take(last_index); + self.elems.put(n, last); + *self.len = last_index; + Some(()) + } } From 2a043d0628396baf631ad2d96bed4ca6faf64ef0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 20:54:13 +0100 Subject: [PATCH 113/142] [core] add storage::Vec::iter_mut + unit tests --- core/src/storage/collections2/vec/iter.rs | 86 +++++++++++++++++++++- core/src/storage/collections2/vec/mod.rs | 24 ++++-- core/src/storage/collections2/vec/tests.rs | 24 ++++++ 3 files changed, 128 insertions(+), 6 deletions(-) diff --git a/core/src/storage/collections2/vec/iter.rs b/core/src/storage/collections2/vec/iter.rs index 729d6ab6a57..e27be950369 100644 --- a/core/src/storage/collections2/vec/iter.rs +++ b/core/src/storage/collections2/vec/iter.rs @@ -16,11 +16,12 @@ use crate::{ storage, storage::{ PullForward, + SaturatingStorage, StorageFootprint, }, }; -/// An iterator over the values of a storage `Vec`. +/// An iterator over shared references to the elements of a storage vector. #[derive(Debug)] pub struct Iter<'a, T> { /// The storage vector to iterate over. @@ -80,3 +81,86 @@ where self.vec.get(self.end) } } + +/// An iterator over exclusive references to the elements of a storage vector. +#[derive(Debug)] +pub struct IterMut<'a, T> { + /// The storage vector to iterate over. + vec: &'a mut storage::Vec2, + /// The current begin of the iteration. + begin: u32, + /// The current end of the iteration. + end: u32, +} + +impl<'a, T> IterMut<'a, T> { + /// Creates a new iterator for the given storage vector. + pub(crate) fn new(vec: &'a mut storage::Vec2) -> Self { + let len = vec.len(); + Self { + vec, + begin: 0, + end: len, + } + } +} + +impl<'a, T> IterMut<'a, T> +where + T: StorageFootprint + SaturatingStorage + PullForward, +{ + fn get_mut(&mut self, at: u32) -> Option<&'a mut T> { + self.vec.get_mut(at).map(|value| { + // SAFETY: We extend the lifetime of the reference here. + // + // This is safe because the iterator yields an exclusive + // reference to every element in the iterated vector + // just once and also there can be only one such iterator + // for the same vector at the same time which is + // guaranteed by the constructor of the iterator. + unsafe { core::mem::transmute::<&mut T, &'a mut T>(value) } + }) + } +} + +impl<'a, T> Iterator for IterMut<'a, T> +where + T: StorageFootprint + SaturatingStorage + PullForward, +{ + type Item = &'a mut T; + + fn next(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + let cur = self.begin; + self.begin += 1; + self.get_mut(cur) + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = (self.end - self.begin) as usize; + (remaining, Some(remaining)) + } +} + +impl<'a, T> ExactSizeIterator for IterMut<'a, T> where + T: StorageFootprint + SaturatingStorage + PullForward +{ +} + +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> +where + T: StorageFootprint + SaturatingStorage + PullForward, +{ + fn next_back(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + debug_assert_ne!(self.end, 0); + self.end -= 1; + self.get_mut(self.end) + } +} diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage/collections2/vec/mod.rs index 09a2ce2686e..47224ed93b8 100644 --- a/core/src/storage/collections2/vec/mod.rs +++ b/core/src/storage/collections2/vec/mod.rs @@ -18,7 +18,10 @@ mod traits; #[cfg(test)] mod tests; -pub use self::iter::Iter; +pub use self::iter::{ + Iter, + IterMut, +}; use crate::{ storage, storage::{ @@ -80,17 +83,28 @@ impl Vec where T: StorageFootprint + PullForward, { - /// Returns an iterator over the references of all elements stored in the vector. + /// Returns an iterator yielding shared references to all elements of the vector. /// /// # Note /// - /// - It is **not** recommended to iterate over all elements of a storage vector. - /// - Try to avoid this if possible or iterate only over a minimal subset of - /// all elements using e.g. `Iterator::take(n)`. + /// Avoid unbounded iteration over big storage vectors. + /// Prefer using methods like `Iterator::take` in order to limit the number + /// of yielded elements. pub fn iter(&self) -> Iter { Iter::new(self) } + /// Returns an iterator yielding exclusive references to all elements of the vector. + /// + /// # Note + /// + /// Avoid unbounded iteration over big storage vectors. + /// Prefer using methods like `Iterator::take` in order to limit the number + /// of yielded elements. + pub fn iter_mut(&mut self) -> IterMut { + IterMut::new(self) + } + /// Returns the index if it is witihn bounds or `None` otherwise. fn within_bounds(&self, index: u32) -> Option { if index < self.len() { diff --git a/core/src/storage/collections2/vec/tests.rs b/core/src/storage/collections2/vec/tests.rs index e966f35d20f..d5cd386d46f 100644 --- a/core/src/storage/collections2/vec/tests.rs +++ b/core/src/storage/collections2/vec/tests.rs @@ -136,6 +136,18 @@ fn iter_next_works() { assert_eq!(iter.next(), None); } +#[test] +fn iter_mut_next_works() { + let elems = [b'a', b'b', b'c', b'd']; + let mut vec = vec_from_slice(&elems); + let mut iter = vec.iter_mut(); + assert_eq!(iter.next(), Some(&mut b'a')); + assert_eq!(iter.next(), Some(&mut b'b')); + assert_eq!(iter.next(), Some(&mut b'c')); + assert_eq!(iter.next(), Some(&mut b'd')); + assert_eq!(iter.next(), None); +} + #[test] fn iter_next_back_works() { let elems = [b'a', b'b', b'c', b'd']; @@ -148,6 +160,18 @@ fn iter_next_back_works() { assert_eq!(iter.next(), None); } +#[test] +fn iter_mut_next_back_works() { + let elems = [b'a', b'b', b'c', b'd']; + let mut vec = vec_from_slice(&elems); + let mut iter = vec.iter_mut().rev(); + assert_eq!(iter.next(), Some(&mut b'd')); + assert_eq!(iter.next(), Some(&mut b'c')); + assert_eq!(iter.next(), Some(&mut b'b')); + assert_eq!(iter.next(), Some(&mut b'a')); + assert_eq!(iter.next(), None); +} + /// Asserts that the the given ordered storage vector elements are equal to the /// ordered elements of the given slice. fn assert_eq_slice(vec: &StorageVec, slice: &[u8]) { From ffbb3e67073a0ac7469840b0175d54db5ae90f3c Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 15 Mar 2020 20:56:16 +0100 Subject: [PATCH 114/142] [core] add IntoIterator for &mut storage::Vec --- core/src/storage/collections2/vec/traits.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index d0dbfaef9dc..b52a19fd998 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -12,7 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::Vec as StorageVec; +use super::{ + Vec as StorageVec, + Iter, + IterMut, +}; use crate::{ storage, storage::{ @@ -62,13 +66,25 @@ where T: StorageFootprint + PullForward, { type Item = &'a T; - type IntoIter = super::Iter<'a, T>; + type IntoIter = Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { self.iter() } } +impl<'a, T: 'a> IntoIterator for &'a mut StorageVec +where + T: StorageFootprint + SaturatingStorage + PullForward, +{ + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + impl Extend for StorageVec where T: StorageFootprint + SaturatingStorage, From d9c408a85092796c7c047db0580acf6e5da9daec Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 11:05:01 +0100 Subject: [PATCH 115/142] [core] add unsafe extend_lifetime utility function --- core/src/storage/collections2/mod.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/src/storage/collections2/mod.rs b/core/src/storage/collections2/mod.rs index a983ef88e75..ac548b67405 100644 --- a/core/src/storage/collections2/mod.rs +++ b/core/src/storage/collections2/mod.rs @@ -15,3 +15,23 @@ pub mod boxed; pub mod smallvec; pub mod vec; + +/// Extends the lifetime 'a to the outliving lifetime 'b for the given reference. +/// +/// # Note +/// +/// This interface is a bit more constraint than a simple +/// [transmut](`core::mem::transmute`) and therefore preferred +/// for extending lifetimes only. +/// +/// # Safety +/// +/// This function is `unsafe` because lifetimes can be extended beyond the +/// lifetimes of the objects they are referencing and thus potentially create +/// dangling references if not used carefully. +pub(crate) unsafe fn extend_lifetime<'a, 'b: 'a, T>(reference: &'a mut T) -> &'b mut T { + #[allow(unused_unsafe)] + unsafe { + core::mem::transmute::<&'a mut T, &'b mut T>(reference) + } +} From e7cb35cd8e5de4a7e4a2ba24327772d7faec1552 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 11:05:32 +0100 Subject: [PATCH 116/142] [core] make use of new extend_lifetime utility function --- core/src/storage/collections2/vec/iter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/storage/collections2/vec/iter.rs b/core/src/storage/collections2/vec/iter.rs index e27be950369..19f6efab20b 100644 --- a/core/src/storage/collections2/vec/iter.rs +++ b/core/src/storage/collections2/vec/iter.rs @@ -109,7 +109,7 @@ impl<'a, T> IterMut<'a, T> where T: StorageFootprint + SaturatingStorage + PullForward, { - fn get_mut(&mut self, at: u32) -> Option<&'a mut T> { + fn get_mut<'b>(&'b mut self, at: u32) -> Option<&'a mut T> { self.vec.get_mut(at).map(|value| { // SAFETY: We extend the lifetime of the reference here. // @@ -118,7 +118,7 @@ where // just once and also there can be only one such iterator // for the same vector at the same time which is // guaranteed by the constructor of the iterator. - unsafe { core::mem::transmute::<&mut T, &'a mut T>(value) } + unsafe { extend_lifetime::<'b, 'a, T>(value) } }) } } From 8f28d33af8edd6500bf112005800834b30f36cf9 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 11:05:45 +0100 Subject: [PATCH 117/142] [core] add missing import --- core/src/storage/collections2/vec/iter.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/storage/collections2/vec/iter.rs b/core/src/storage/collections2/vec/iter.rs index 19f6efab20b..8f18cf0f7ab 100644 --- a/core/src/storage/collections2/vec/iter.rs +++ b/core/src/storage/collections2/vec/iter.rs @@ -15,6 +15,7 @@ use crate::{ storage, storage::{ + collections2::extend_lifetime, PullForward, SaturatingStorage, StorageFootprint, From 1fb54c7237236ed0b24eb02798c7a916541b15fe Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 11:06:02 +0100 Subject: [PATCH 118/142] [core] derive Clone and Copy for storage::vec::Iter --- core/src/storage/collections2/vec/iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/storage/collections2/vec/iter.rs b/core/src/storage/collections2/vec/iter.rs index 8f18cf0f7ab..71b3023d8f6 100644 --- a/core/src/storage/collections2/vec/iter.rs +++ b/core/src/storage/collections2/vec/iter.rs @@ -23,7 +23,7 @@ use crate::{ }; /// An iterator over shared references to the elements of a storage vector. -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct Iter<'a, T> { /// The storage vector to iterate over. vec: &'a storage::Vec2, From ca42204a3cb1e65277153c266842da7c50557438 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 11:06:30 +0100 Subject: [PATCH 119/142] [core] add storage::SmallVec::iter_mut --- .../src/storage/collections2/smallvec/iter.rs | 100 +++++++++++++++++- core/src/storage/collections2/smallvec/mod.rs | 24 ++++- 2 files changed, 117 insertions(+), 7 deletions(-) diff --git a/core/src/storage/collections2/smallvec/iter.rs b/core/src/storage/collections2/smallvec/iter.rs index 8a1d4718187..32381ff28da 100644 --- a/core/src/storage/collections2/smallvec/iter.rs +++ b/core/src/storage/collections2/smallvec/iter.rs @@ -14,13 +14,15 @@ use super::SmallVec; use crate::storage::{ + collections2::extend_lifetime, LazyArrayLength, PullForward, + SaturatingStorage, StorageFootprint, }; -/// An iterator over the values of a storage `SmallVec`. -#[derive(Debug)] +/// An iterator over shared references to the elements of a small storage vector. +#[derive(Debug, Clone, Copy)] pub struct Iter<'a, T, N> where N: LazyArrayLength, @@ -92,3 +94,97 @@ where self.vec.get(self.end) } } + +/// An iterator over exclusive references to the elements of a small storage vector. +#[derive(Debug)] +pub struct IterMut<'a, T, N> +where + N: LazyArrayLength, +{ + /// The storage vector to iterate over. + vec: &'a mut SmallVec, + /// The current begin of the iteration. + begin: u32, + /// The current end of the iteration. + end: u32, +} + +impl<'a, T, N> IterMut<'a, T, N> +where + N: LazyArrayLength, +{ + /// Creates a new iterator for the given storage vector. + pub(crate) fn new(vec: &'a mut SmallVec) -> Self { + let len = vec.len(); + Self { + vec, + begin: 0, + end: len, + } + } +} + +impl<'a, T, N> IterMut<'a, T, N> +where + T: StorageFootprint + SaturatingStorage + PullForward, + N: LazyArrayLength, +{ + fn get_mut<'b>(&'b mut self, at: u32) -> Option<&'a mut T> { + self.vec.get_mut(at).map(|value| { + // SAFETY: We extend the lifetime of the reference here. + // + // This is safe because the iterator yields an exclusive + // reference to every element in the iterated vector + // just once and also there can be only one such iterator + // for the same vector at the same time which is + // guaranteed by the constructor of the iterator. + unsafe { extend_lifetime::<'b, 'a, T>(value) } + }) + } +} + +impl<'a, T, N> Iterator for IterMut<'a, T, N> +where + T: StorageFootprint + SaturatingStorage + PullForward, + N: LazyArrayLength, +{ + type Item = &'a mut T; + + fn next(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + let cur = self.begin; + self.begin += 1; + self.get_mut(cur) + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = (self.end - self.begin) as usize; + (remaining, Some(remaining)) + } +} + +impl<'a, T, N> ExactSizeIterator for IterMut<'a, T, N> +where + T: StorageFootprint + SaturatingStorage + PullForward, + N: LazyArrayLength, +{ +} + +impl<'a, T, N> DoubleEndedIterator for IterMut<'a, T, N> +where + T: StorageFootprint + SaturatingStorage + PullForward, + N: LazyArrayLength, +{ + fn next_back(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + debug_assert_ne!(self.end, 0); + self.end -= 1; + self.get_mut(self.end) + } +} diff --git a/core/src/storage/collections2/smallvec/mod.rs b/core/src/storage/collections2/smallvec/mod.rs index 7183c38d681..acd0d49c08e 100644 --- a/core/src/storage/collections2/smallvec/mod.rs +++ b/core/src/storage/collections2/smallvec/mod.rs @@ -18,7 +18,10 @@ mod traits; #[cfg(test)] mod tests; -pub use self::iter::Iter; +pub use self::iter::{ + Iter, + IterMut, +}; use crate::{ storage, storage::{ @@ -98,17 +101,28 @@ where T: StorageFootprint + PullForward, N: LazyArrayLength, { - /// Returns an iterator over the references of all elements stored in the vector. + /// Returns an iterator yielding shared references to all elements. /// /// # Note /// - /// - It is **not** recommended to iterate over all elements of a storage vector. - /// - Try to avoid this if possible or iterate only over a minimal subset of - /// all elements using e.g. `Iterator::take(n)`. + /// - Avoid unbounded iteration over big storage vectors. + /// - Prefer using methods like `Iterator::take` in order to limit the number + /// of yielded elements. pub fn iter(&self) -> Iter { Iter::new(self) } + /// Returns an iterator yielding exclusive references to all elements. + /// + /// # Note + /// + /// - Avoid unbounded iteration over big storage vectors. + /// - Prefer using methods like `Iterator::take` in order to limit the number + /// of yielded elements. + pub fn iter_mut(&mut self) -> IterMut { + IterMut::new(self) + } + /// Returns the index if it is witihn bounds or `None` otherwise. fn within_bounds(&self, index: Index) -> Option { if index < self.len() { From 20ee83dab9fc10a73d891a06d84ec94ba1c2ad6b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 11:10:55 +0100 Subject: [PATCH 120/142] [core] rename KeyPtr::next_for2 -> next_for --- core/src/storage/lazy/lazy_array.rs | 4 ++-- core/src/storage/lazy/lazy_cell.rs | 4 ++-- core/src/storage/lazy/lazy_map.rs | 4 ++-- core/src/storage/pack.rs | 4 ++-- core/src/storage/traits/clear.rs | 2 +- core/src/storage/traits/mod.rs | 2 +- core/src/storage/traits/pull.rs | 4 ++-- core/src/storage/traits/push.rs | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage/lazy/lazy_array.rs index 44364c57715..8d1f9144756 100644 --- a/core/src/storage/lazy/lazy_array.rs +++ b/core/src/storage/lazy/lazy_array.rs @@ -245,7 +245,7 @@ where { fn pull_forward(ptr: &mut KeyPtr) -> Self { Self { - key: Some(ptr.next_for2::()), + key: Some(ptr.next_for::()), cached_entries: UnsafeCell::new(EntryArray::new()), } } @@ -260,7 +260,7 @@ where ::Value: Unsigned, { fn push_forward(&self, ptr: &mut KeyPtr) { - let offset_key = ptr.next_for2::(); + let offset_key = ptr.next_for::(); for (index, entry) in self.cached_entries().entries.iter().enumerate() { if let Some(entry) = entry { if !entry.is_mutated() { diff --git a/core/src/storage/lazy/lazy_cell.rs b/core/src/storage/lazy/lazy_cell.rs index 518a6097438..e7409ce3dfc 100644 --- a/core/src/storage/lazy/lazy_cell.rs +++ b/core/src/storage/lazy/lazy_cell.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::super::{ +use crate::storage::{ ClearForward, KeyPtr, PullForward, @@ -96,7 +96,7 @@ where T: StorageFootprint, { fn pull_forward(ptr: &mut KeyPtr) -> Self { - Self::lazy(ptr.next_for2::()) + Self::lazy(ptr.next_for::()) } } diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index adc4fd935c4..c26b1e65cae 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -315,7 +315,7 @@ where { fn pull_forward(ptr: &mut KeyPtr) -> Self { Self { - key: Some(ptr.next_for2::()), + key: Some(ptr.next_for::()), cached_entries: UnsafeCell::new(BTreeMap::new()), } } @@ -328,7 +328,7 @@ where V: PushForward + StorageFootprint, { fn push_forward(&self, ptr: &mut KeyPtr) { - let key = ptr.next_for2::(); + let key = ptr.next_for::(); assert_eq!(self.key, Some(key)); for (index, entry) in self.entries().iter().filter(|(_, entry)| entry.mutated()) { let offset: Key = >::to_storage_key(index, &key); diff --git a/core/src/storage/pack.rs b/core/src/storage/pack.rs index bfa1e654e0c..13d5afe77f5 100644 --- a/core/src/storage/pack.rs +++ b/core/src/storage/pack.rs @@ -88,7 +88,7 @@ where T: PullAt, { fn pull_forward(ptr: &mut KeyPtr) -> Self { - ::pull_at(ptr.next_for2::()) + ::pull_at(ptr.next_for::()) } } @@ -108,7 +108,7 @@ where T: PushAt, { fn push_forward(&self, ptr: &mut KeyPtr) { - ::push_at(self, ptr.next_for2::()) + ::push_at(self, ptr.next_for::()) } } diff --git a/core/src/storage/traits/clear.rs b/core/src/storage/traits/clear.rs index 2e4ef4a8e44..c9715ebc243 100644 --- a/core/src/storage/traits/clear.rs +++ b/core/src/storage/traits/clear.rs @@ -177,7 +177,7 @@ impl ClearAt for Result {} impl ClearForward for ink_prelude::string::String { fn clear_forward(&self, ptr: &mut KeyPtr) { - ::clear_at(self, ptr.next_for2::()) + ::clear_at(self, ptr.next_for::()) } } diff --git a/core/src/storage/traits/mod.rs b/core/src/storage/traits/mod.rs index a3ef4bb2349..d369402ea58 100644 --- a/core/src/storage/traits/mod.rs +++ b/core/src/storage/traits/mod.rs @@ -101,7 +101,7 @@ impl KeyPtr { /// Advances the key by the given amount derive by `T` and its `StorageSize` /// and returns the next `Key` for usage by the storage element. - pub fn next_for2(&mut self) -> Key + pub fn next_for(&mut self) -> Key where T: StorageFootprint, ::Value: typenum::Unsigned, diff --git a/core/src/storage/traits/pull.rs b/core/src/storage/traits/pull.rs index 32e0c677e2f..b1d6f1e31c3 100644 --- a/core/src/storage/traits/pull.rs +++ b/core/src/storage/traits/pull.rs @@ -189,7 +189,7 @@ where E: PullForward, { fn pull_forward(ptr: &mut KeyPtr) -> Self { - match pull_single_cell::(ptr.next_for2::()) { + match pull_single_cell::(ptr.next_for::()) { 0 => Ok(::pull_forward(ptr)), 1 => Err(::pull_forward(ptr)), _ => unreachable!("found invalid Result discriminator"), @@ -208,7 +208,7 @@ where impl PullForward for ink_prelude::string::String { fn pull_forward(ptr: &mut KeyPtr) -> Self { - ::pull_at(ptr.next_for2::()) + ::pull_at(ptr.next_for::()) } } diff --git a/core/src/storage/traits/push.rs b/core/src/storage/traits/push.rs index 669122a7a07..ef36c7f1ef2 100644 --- a/core/src/storage/traits/push.rs +++ b/core/src/storage/traits/push.rs @@ -166,7 +166,7 @@ where Some(val) => ::push_forward(val, ptr), None => { // We still need to advance the key pointer. - let pos0 = ptr.next_for2::(); + let pos0 = ptr.next_for::(); // Bail out early if `StorageSize` is too big and the method // is used even though we have tried to prevent this at compile // time. @@ -240,7 +240,7 @@ where impl PushForward for ink_prelude::string::String { fn push_forward(&self, ptr: &mut KeyPtr) { - ::push_at(self, ptr.next_for2::()) + ::push_at(self, ptr.next_for::()) } } From cfdc61ad812b622ade9cfc38c1fa1839fa26ab8e Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 11:11:56 +0100 Subject: [PATCH 121/142] [core] do missing renamings in macros --- core/src/storage/traits/clear.rs | 2 +- core/src/storage/traits/pull.rs | 2 +- core/src/storage/traits/push.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/storage/traits/clear.rs b/core/src/storage/traits/clear.rs index c9715ebc243..ae9a8f26db2 100644 --- a/core/src/storage/traits/clear.rs +++ b/core/src/storage/traits/clear.rs @@ -51,7 +51,7 @@ macro_rules! impl_clear_for_primitive { $( impl ClearForward for $ty { fn clear_forward(&self, ptr: &mut KeyPtr) { - <$ty as ClearAt>::clear_at(self, ptr.next_for2::<$ty>()) + <$ty as ClearAt>::clear_at(self, ptr.next_for::<$ty>()) } } diff --git a/core/src/storage/traits/pull.rs b/core/src/storage/traits/pull.rs index b1d6f1e31c3..0aa8c3d610d 100644 --- a/core/src/storage/traits/pull.rs +++ b/core/src/storage/traits/pull.rs @@ -62,7 +62,7 @@ macro_rules! impl_pull_for_primitive { $( impl PullForward for $ty { fn pull_forward(ptr: &mut KeyPtr) -> Self { - ::pull_at(ptr.next_for2::()) + ::pull_at(ptr.next_for::()) } } impl PullAt for $ty { diff --git a/core/src/storage/traits/push.rs b/core/src/storage/traits/push.rs index ef36c7f1ef2..3d1da26a3c3 100644 --- a/core/src/storage/traits/push.rs +++ b/core/src/storage/traits/push.rs @@ -49,7 +49,7 @@ macro_rules! impl_push_for_primitive { $( impl PushForward for $ty { fn push_forward(&self, ptr: &mut KeyPtr) { - <$ty as PushAt>::push_at(self, ptr.next_for2::<$ty>()) + <$ty as PushAt>::push_at(self, ptr.next_for::<$ty>()) } } From 7b646111d8243a49b1a2d106fb10ad1892bd15a7 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 11:47:41 +0100 Subject: [PATCH 122/142] [core] refactor LazyCell --- core/src/storage/collections2/boxed/mod.rs | 7 +- core/src/storage/collections2/boxed/traits.rs | 44 +++-- core/src/storage/lazy/lazy_cell.rs | 176 ++++++++++-------- core/src/storage/lazy/mod.rs | 5 +- 4 files changed, 135 insertions(+), 97 deletions(-) diff --git a/core/src/storage/collections2/boxed/mod.rs b/core/src/storage/collections2/boxed/mod.rs index b5e85c9e23e..ae21224c4ca 100644 --- a/core/src/storage/collections2/boxed/mod.rs +++ b/core/src/storage/collections2/boxed/mod.rs @@ -18,6 +18,7 @@ use crate::storage::{ ClearForward, Lazy, PullForward, + SaturatingStorage, StorageFootprint, }; use ink_primitives::Key; @@ -34,7 +35,7 @@ where /// An indirection to some dynamically allocated storage entity. pub struct Box where - T: ClearForward, + T: ClearForward + SaturatingStorage, { /// The storage area where the boxed storage entity is stored. key: Key, @@ -44,7 +45,7 @@ where impl Box where - T: ClearForward + StorageFootprint, + T: ClearForward + StorageFootprint + SaturatingStorage, { /// Creates a new boxed entity. pub fn new(value: T) -> Self { @@ -57,7 +58,7 @@ where impl Box where - T: ClearForward + StorageFootprint + PullForward, + T: ClearForward + StorageFootprint + PullForward + SaturatingStorage, { /// Returns a shared reference to the boxed value. /// diff --git a/core/src/storage/collections2/boxed/traits.rs b/core/src/storage/collections2/boxed/traits.rs index 5ae04339963..490cf06de31 100644 --- a/core/src/storage/collections2/boxed/traits.rs +++ b/core/src/storage/collections2/boxed/traits.rs @@ -28,7 +28,7 @@ use ink_primitives::Key; impl StorageFootprint for StorageBox where - T: ClearForward, + T: ClearForward + SaturatingStorage, { /// A boxed entity always uses exactly 1 cell for its storage. /// @@ -39,7 +39,7 @@ where impl SaturatingStorage for StorageBox where - T: ClearForward, + T: ClearForward + SaturatingStorage, { // A boxed entity always uses exactly 1 cell for its storage. // @@ -48,7 +48,7 @@ where impl PullForward for StorageBox where - T: ClearForward, + T: ClearForward + SaturatingStorage, { fn pull_forward(ptr: &mut KeyPtr) -> Self { let key = ::pull_forward(ptr); @@ -61,7 +61,7 @@ where impl PushForward for StorageBox where - T: ClearForward, + T: ClearForward + SaturatingStorage, storage::Lazy: PushForward, { fn push_forward(&self, ptr: &mut KeyPtr) { @@ -72,7 +72,7 @@ where impl ClearForward for StorageBox where - T: ClearForward, + T: ClearForward + SaturatingStorage, storage::Lazy: ClearForward, { fn clear_forward(&self, ptr: &mut KeyPtr) { @@ -83,7 +83,7 @@ where impl Drop for StorageBox where - T: ClearForward, + T: ClearForward + SaturatingStorage, { fn drop(&mut self) { ClearForward::clear_forward(&self.value, &mut KeyPtr::from(self.key)); @@ -92,7 +92,7 @@ where impl core::cmp::PartialEq for StorageBox where - T: PartialEq + ClearForward + StorageFootprint + PullForward, + T: PartialEq + ClearForward + StorageFootprint + PullForward + SaturatingStorage, { fn eq(&self, other: &Self) -> bool { PartialEq::eq(self.get(), other.get()) @@ -100,13 +100,13 @@ where } impl core::cmp::Eq for StorageBox where - T: Eq + ClearForward + StorageFootprint + PullForward + T: Eq + ClearForward + StorageFootprint + PullForward + SaturatingStorage { } impl core::cmp::PartialOrd for StorageBox where - T: PartialOrd + ClearForward + StorageFootprint + PullForward, + T: PartialOrd + ClearForward + StorageFootprint + PullForward + SaturatingStorage, { fn partial_cmp(&self, other: &Self) -> Option { PartialOrd::partial_cmp(self.get(), other.get()) @@ -127,7 +127,7 @@ where impl core::cmp::Ord for StorageBox where - T: core::cmp::Ord + ClearForward + StorageFootprint + PullForward, + T: core::cmp::Ord + ClearForward + StorageFootprint + PullForward + SaturatingStorage, { fn cmp(&self, other: &Self) -> core::cmp::Ordering { Ord::cmp(self.get(), other.get()) @@ -136,7 +136,11 @@ where impl core::fmt::Display for StorageBox where - T: core::fmt::Display + ClearForward + StorageFootprint + PullForward, + T: core::fmt::Display + + ClearForward + + StorageFootprint + + PullForward + + SaturatingStorage, { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { core::fmt::Display::fmt(self.get(), f) @@ -145,7 +149,11 @@ where impl core::hash::Hash for StorageBox where - T: core::hash::Hash + ClearForward + StorageFootprint + PullForward, + T: core::hash::Hash + + ClearForward + + StorageFootprint + + PullForward + + SaturatingStorage, { fn hash(&self, state: &mut H) { self.get().hash(state); @@ -154,7 +162,7 @@ where impl core::convert::AsRef for StorageBox where - T: StorageFootprint + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward + SaturatingStorage, { fn as_ref(&self) -> &T { self.get() @@ -163,7 +171,7 @@ where impl core::convert::AsMut for StorageBox where - T: StorageFootprint + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward + SaturatingStorage, { fn as_mut(&mut self) -> &mut T { self.get_mut() @@ -172,7 +180,7 @@ where impl ink_prelude::borrow::Borrow for StorageBox where - T: StorageFootprint + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward + SaturatingStorage, { fn borrow(&self) -> &T { self.get() @@ -181,7 +189,7 @@ where impl ink_prelude::borrow::BorrowMut for StorageBox where - T: StorageFootprint + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward + SaturatingStorage, { fn borrow_mut(&mut self) -> &mut T { self.get_mut() @@ -190,7 +198,7 @@ where impl core::ops::Deref for StorageBox where - T: StorageFootprint + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward + SaturatingStorage, { type Target = T; @@ -201,7 +209,7 @@ where impl core::ops::DerefMut for StorageBox where - T: StorageFootprint + ClearForward + PullForward, + T: StorageFootprint + ClearForward + PullForward + SaturatingStorage, { fn deref_mut(&mut self) -> &mut Self::Target { self.get_mut() diff --git a/core/src/storage/lazy/lazy_cell.rs b/core/src/storage/lazy/lazy_cell.rs index e7409ce3dfc..237c808c193 100644 --- a/core/src/storage/lazy/lazy_cell.rs +++ b/core/src/storage/lazy/lazy_cell.rs @@ -12,14 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::{ + Entry, + EntryState, +}; use crate::storage::{ ClearForward, KeyPtr, PullForward, PushForward, + SaturatingStorage, StorageFootprint, }; -use core::cell::UnsafeCell; +use core::{ + cell::UnsafeCell, + ptr::NonNull, +}; use ink_primitives::Key; /// The lazy storage entry can be in either of two states. @@ -33,7 +41,7 @@ pub enum LazyCellEntry { /// A true lazy storage entity that loads its contract storage value upon first use. Vacant(VacantEntry), /// An already loaded eager lazy storage entity. - Occupied(OccupiedEntry), + Occupied(Entry), } /// The lazy storage entity is in a lazy state. @@ -50,20 +58,6 @@ impl VacantEntry { } } -/// An already loaded or otherwise occupied eager lazy storage entity. -#[derive(Debug)] -pub struct OccupiedEntry { - /// The loaded value. - pub value: Option, -} - -impl OccupiedEntry { - /// Creates a new eager lazy storage entity with the given value. - pub fn new(value: Option) -> Self { - Self { value } - } -} - /// A lazy storage entity. /// /// This loads its value from storage upon first use. @@ -102,13 +96,30 @@ where impl PushForward for LazyCell where - T: PushForward, + T: PushForward + SaturatingStorage, { fn push_forward(&self, ptr: &mut KeyPtr) { // We skip pushing to contract storage if we are still in unloaded form. if let LazyCellEntry::Occupied(occupied) = self.kind() { - if let Some(value) = &occupied.value { - ::push_forward(value, ptr) + if !occupied.is_mutated() { + // Don't sync with storage if the value has not been mutated. + return + } + match occupied.value() { + Some(value) => ::push_forward(value, ptr), + None => { + // TODO: Find better and more general clean-up strategy with + // the help of the proposed subtrie API. + let footprint = crate::storage::storage_footprint_u64::(); + if footprint >= 32 { + panic!("cannot clean up more than 32 cells at once") + } + let key = ptr.next_for::(); + // Clean up storage associated with the value. + for n in 0..footprint { + crate::env::clear_contract_storage(key + n) + } + } } } } @@ -116,15 +127,27 @@ where impl ClearForward for LazyCell where - T: ClearForward, + T: ClearForward + SaturatingStorage, { - fn clear_forward(&self, ptr: &mut KeyPtr) { - // We skip clear contract storage if we are still in unloaded form. - if let LazyCellEntry::Occupied(occupied) = self.kind() { - if let Some(value) = &occupied.value { - ::clear_forward(value, ptr) - } - } + fn clear_forward(&self, _ptr: &mut KeyPtr) { + // Not implemented because at this point we are unsure whether + // the whole clean-up traits are useful at all. + todo!() + } +} + +impl From for LazyCell { + fn from(value: T) -> Self { + Self::new(Some(value)) + } +} + +impl Default for LazyCell +where + T: Default, +{ + fn default() -> Self { + Self::new(Some(Default::default())) } } @@ -141,8 +164,9 @@ impl LazyCell { I: Into>, { Self { - kind: UnsafeCell::new(LazyCellEntry::Occupied(OccupiedEntry::new( + kind: UnsafeCell::new(LazyCellEntry::Occupied(Entry::new( value.into(), + EntryState::Mutated, ))), } } @@ -168,44 +192,71 @@ impl LazyCell { // Rust rules. unsafe { &*self.kind.get() } } - - /// Returns an exclusive reference to the inner lazy kind. - #[must_use] - fn kind_mut(&mut self) -> &mut LazyCellEntry { - // SAFETY: We just return an exclusive reference while the method receiver - // is an exclusive reference (&mut self) itself. So we respect normal - // Rust rules. - unsafe { &mut *self.kind.get() } - } } impl LazyCell where T: StorageFootprint + PullForward, { - /// Loads the value lazily from contract storage. + /// Loads the storage entry. /// - /// # Note - /// - /// - After a successful call there will be an occupied value in the entry. - /// - Does nothing if value has already been loaded. + /// Tries to load the entry from cache and falls back to lazily load the + /// entry from the contract storage. /// /// # Panics /// - /// If a value has been loaded that failed to decode into `T`. - fn load_value_lazily(&self) { + /// Upon lazy loading if the lazy cell is in a state that forbids lazy loading. + unsafe fn load_through_cache(&self) -> NonNull> { // SAFETY: This is critical because we mutably access the entry. // However, we mutate the entry only if it is vacant. // If the entry is occupied by a value we return early. // This way we do not invalidate pointers to this value. + #[allow(unused_unsafe)] let kind = unsafe { &mut *self.kind.get() }; - if let LazyCellEntry::Vacant(vacant) = kind { - let value = - as PullForward>::pull_forward(&mut KeyPtr::from(vacant.key)); - *kind = LazyCellEntry::Occupied(OccupiedEntry::new(value)); + match kind { + LazyCellEntry::Vacant(vacant) => { + // Load the value from contract storage lazily. + let mut key_ptr = KeyPtr::from(vacant.key); + let value = as PullForward>::pull_forward(&mut key_ptr); + let entry = Entry::new(value, EntryState::Mutated); + *kind = LazyCellEntry::Occupied(entry); + match kind { + LazyCellEntry::Vacant(_) => { + unreachable!("we just occupied the entry") + } + LazyCellEntry::Occupied(entry) => NonNull::from(entry), + } + } + LazyCellEntry::Occupied(entry) => NonNull::from(entry), } } + /// Returns a shared reference to the entry. + fn get_entry(&self) -> &Entry { + // SAFETY: We load the entry either from cache of from contract storage. + // + // This is safe because we are just returning a shared reference + // from within a `&self` method. This also cannot change the + // loaded value and thus cannot change the `mutate` flag of the + // entry. Aliases using this method are safe since ink! is + // single-threaded. + unsafe { &*self.load_through_cache().as_ptr() } + } + + /// Returns an exclusive reference to the entry. + fn get_entry_mut(&mut self) -> &mut Entry { + // SAFETY: We load the entry either from cache of from contract storage. + // + // This is safe because we are just returning a shared reference + // from within a `&self` method. This also cannot change the + // loaded value and thus cannot change the `mutate` flag of the + // entry. Aliases using this method are safe since ink! is + // single-threaded. + let entry = unsafe { &mut *self.load_through_cache().as_ptr() }; + entry.set_state(EntryState::Mutated); + entry + } + /// Returns a shared reference to the value. /// /// # Note @@ -217,14 +268,10 @@ where /// If decoding the loaded value to `T` failed. #[must_use] pub fn get(&self) -> Option<&T> { - self.load_value_lazily(); - match self.kind() { - LazyCellEntry::Vacant(_) => unreachable!("assumed occupied value here"), - LazyCellEntry::Occupied(occupied) => occupied.value.as_ref(), - } + self.get_entry().value() } - /// Returns an exclusive reference to the value. + /// Returns a shared reference to the value. /// /// # Note /// @@ -235,25 +282,6 @@ where /// If decoding the loaded value to `T` failed. #[must_use] pub fn get_mut(&mut self) -> Option<&mut T> { - self.load_value_lazily(); - match self.kind_mut() { - LazyCellEntry::Vacant(_) => unreachable!("assumed occupied value here"), - LazyCellEntry::Occupied(occupied) => occupied.value.as_mut(), - } - } -} - -impl From for LazyCell { - fn from(value: T) -> Self { - Self::new(Some(value)) - } -} - -impl Default for LazyCell -where - T: Default, -{ - fn default() -> Self { - Self::new(Some(Default::default())) + self.get_entry_mut().value_mut() } } diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage/lazy/mod.rs index 90c685829de..0c68cd6bfe0 100644 --- a/core/src/storage/lazy/mod.rs +++ b/core/src/storage/lazy/mod.rs @@ -38,6 +38,7 @@ use super::{ KeyPtr, PullForward, PushForward, + SaturatingStorage, StorageFootprint, StorageFootprintOf, }; @@ -75,7 +76,7 @@ where impl PushForward for Lazy where - T: PushForward, + T: PushForward + SaturatingStorage, { fn push_forward(&self, ptr: &mut KeyPtr) { as PushForward>::push_forward(&self.cell, ptr) @@ -84,7 +85,7 @@ where impl ClearForward for Lazy where - T: ClearForward, + T: ClearForward + SaturatingStorage, { fn clear_forward(&self, ptr: &mut KeyPtr) { as ClearForward>::clear_forward(&self.cell, ptr) From c36574d0a3e5177443cbff00f13e0fb114f97142 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 14:57:55 +0100 Subject: [PATCH 123/142] [core] minor refactoring --- core/src/storage/lazy/lazy_map.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage/lazy/lazy_map.rs index c26b1e65cae..8d998f9af0b 100644 --- a/core/src/storage/lazy/lazy_map.rs +++ b/core/src/storage/lazy/lazy_map.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::super::{ +use crate::storage::{ KeyPtr, PullForward, PushForward, @@ -28,6 +28,7 @@ use core::{ Eq, Ord, }, + ops::Mul, ptr::NonNull, }; use ink_prelude::{ @@ -38,6 +39,7 @@ use ink_primitives::Key; use typenum::{ Prod, Unsigned, + P4294967296, }; /// The index type used in the lazy storage chunk. @@ -284,9 +286,6 @@ where } } -use core::ops::Mul; -use typenum::P4294967296; - impl StorageFootprint for LazyChunk where T: StorageFootprint, From 4039332471852a6c0a81a5f3d7d19304015d859b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 15:00:55 +0100 Subject: [PATCH 124/142] [core] fix imports in no_std --- core/src/storage/traits/footprint.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/storage/traits/footprint.rs b/core/src/storage/traits/footprint.rs index 5f7a29b6598..b265fa41152 100644 --- a/core/src/storage/traits/footprint.rs +++ b/core/src/storage/traits/footprint.rs @@ -201,13 +201,13 @@ where type Value = StorageFootprintOf; } -impl SaturatingStorageMarker for Box where T: SaturatingStorage {} +impl SaturatingStorageMarker for ink_prelude::boxed::Box where T: SaturatingStorage {} impl StorageFootprint for ink_prelude::string::String { type Value = U1; } -impl SaturatingStorage for String {} +impl SaturatingStorage for ink_prelude::string::String {} impl StorageFootprint for ink_prelude::vec::Vec { type Value = U1; From b40db412b4cf2ae36a229ccbe1b3ba49e6fa43c9 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 15:05:45 +0100 Subject: [PATCH 125/142] [core] apply rustfmt --- core/src/storage/collections2/vec/traits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage/collections2/vec/traits.rs index b52a19fd998..33a96c0cec0 100644 --- a/core/src/storage/collections2/vec/traits.rs +++ b/core/src/storage/collections2/vec/traits.rs @@ -13,9 +13,9 @@ // limitations under the License. use super::{ - Vec as StorageVec, Iter, IterMut, + Vec as StorageVec, }; use crate::{ storage, From 30ef3f3399af3fe1899c615964b0359aa9534297 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 15:34:59 +0100 Subject: [PATCH 126/142] [core] move new storage abstractions to storage2 --- core/src/storage/mod.rs | 62 +++++++++---------- .../collections}/boxed/mod.rs | 0 .../collections}/boxed/traits.rs | 0 .../collections}/mod.rs | 0 .../collections}/smallvec/iter.rs | 0 .../collections}/smallvec/mod.rs | 0 .../collections}/smallvec/tests.rs | 0 .../collections}/smallvec/traits.rs | 0 .../collections}/vec/iter.rs | 0 .../collections}/vec/mod.rs | 0 .../collections}/vec/tests.rs | 0 .../collections}/vec/traits.rs | 0 core/src/{storage => storage2}/lazy/entry.rs | 0 .../{storage => storage2}/lazy/lazy_array.rs | 0 .../{storage => storage2}/lazy/lazy_cell.rs | 0 .../{storage => storage2}/lazy/lazy_map.rs | 0 core/src/{storage => storage2}/lazy/mod.rs | 0 core/src/{storage => storage2}/pack.rs | 0 .../src/{storage => storage2}/traits/clear.rs | 0 .../{storage => storage2}/traits/footprint.rs | 0 core/src/{storage => storage2}/traits/mod.rs | 0 core/src/{storage => storage2}/traits/pull.rs | 0 core/src/{storage => storage2}/traits/push.rs | 0 23 files changed, 29 insertions(+), 33 deletions(-) rename core/src/{storage/collections2 => storage2/collections}/boxed/mod.rs (100%) rename core/src/{storage/collections2 => storage2/collections}/boxed/traits.rs (100%) rename core/src/{storage/collections2 => storage2/collections}/mod.rs (100%) rename core/src/{storage/collections2 => storage2/collections}/smallvec/iter.rs (100%) rename core/src/{storage/collections2 => storage2/collections}/smallvec/mod.rs (100%) rename core/src/{storage/collections2 => storage2/collections}/smallvec/tests.rs (100%) rename core/src/{storage/collections2 => storage2/collections}/smallvec/traits.rs (100%) rename core/src/{storage/collections2 => storage2/collections}/vec/iter.rs (100%) rename core/src/{storage/collections2 => storage2/collections}/vec/mod.rs (100%) rename core/src/{storage/collections2 => storage2/collections}/vec/tests.rs (100%) rename core/src/{storage/collections2 => storage2/collections}/vec/traits.rs (100%) rename core/src/{storage => storage2}/lazy/entry.rs (100%) rename core/src/{storage => storage2}/lazy/lazy_array.rs (100%) rename core/src/{storage => storage2}/lazy/lazy_cell.rs (100%) rename core/src/{storage => storage2}/lazy/lazy_map.rs (100%) rename core/src/{storage => storage2}/lazy/mod.rs (100%) rename core/src/{storage => storage2}/pack.rs (100%) rename core/src/{storage => storage2}/traits/clear.rs (100%) rename core/src/{storage => storage2}/traits/footprint.rs (100%) rename core/src/{storage => storage2}/traits/mod.rs (100%) rename core/src/{storage => storage2}/traits/pull.rs (100%) rename core/src/{storage => storage2}/traits/push.rs (100%) diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index a5af6f3087f..1e8ca28826f 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -73,11 +73,7 @@ pub mod alloc; pub mod cell; pub mod chunk; mod collections; -mod collections2; mod flush; -mod lazy; -mod pack; -mod traits; mod value; pub use self::{ @@ -107,36 +103,36 @@ pub use self::{ Vec, }, }, - collections2::{ - boxed::Box, - smallvec::SmallVec, - vec::Vec as Vec2, - }, flush::Flush, - lazy::{ - Lazy, - LazyArray, - LazyArrayLength, - LazyCell, - LazyChunk, - LazyMap, - LazyMapping, - }, - pack::Pack, - traits::{ - storage_footprint_u128, - storage_footprint_u64, - ClearAt, - ClearForward, - KeyPtr, - PullAt, - PullForward, - PushAt, - PushForward, - SaturatingStorage, - StorageFootprint, - StorageFootprintOf, - }, + // collections2::{ + // boxed::Box, + // smallvec::SmallVec, + // vec::Vec as Vec2, + // }, + // lazy::{ + // Lazy, + // LazyArray, + // LazyArrayLength, + // LazyCell, + // LazyChunk, + // LazyMap, + // LazyMapping, + // }, + // pack::Pack, + // traits::{ + // storage_footprint_u128, + // storage_footprint_u64, + // ClearAt, + // ClearForward, + // KeyPtr, + // PullAt, + // PullForward, + // PushAt, + // PushForward, + // SaturatingStorage, + // StorageFootprint, + // StorageFootprintOf, + // }, }; #[doc(inline)] diff --git a/core/src/storage/collections2/boxed/mod.rs b/core/src/storage2/collections/boxed/mod.rs similarity index 100% rename from core/src/storage/collections2/boxed/mod.rs rename to core/src/storage2/collections/boxed/mod.rs diff --git a/core/src/storage/collections2/boxed/traits.rs b/core/src/storage2/collections/boxed/traits.rs similarity index 100% rename from core/src/storage/collections2/boxed/traits.rs rename to core/src/storage2/collections/boxed/traits.rs diff --git a/core/src/storage/collections2/mod.rs b/core/src/storage2/collections/mod.rs similarity index 100% rename from core/src/storage/collections2/mod.rs rename to core/src/storage2/collections/mod.rs diff --git a/core/src/storage/collections2/smallvec/iter.rs b/core/src/storage2/collections/smallvec/iter.rs similarity index 100% rename from core/src/storage/collections2/smallvec/iter.rs rename to core/src/storage2/collections/smallvec/iter.rs diff --git a/core/src/storage/collections2/smallvec/mod.rs b/core/src/storage2/collections/smallvec/mod.rs similarity index 100% rename from core/src/storage/collections2/smallvec/mod.rs rename to core/src/storage2/collections/smallvec/mod.rs diff --git a/core/src/storage/collections2/smallvec/tests.rs b/core/src/storage2/collections/smallvec/tests.rs similarity index 100% rename from core/src/storage/collections2/smallvec/tests.rs rename to core/src/storage2/collections/smallvec/tests.rs diff --git a/core/src/storage/collections2/smallvec/traits.rs b/core/src/storage2/collections/smallvec/traits.rs similarity index 100% rename from core/src/storage/collections2/smallvec/traits.rs rename to core/src/storage2/collections/smallvec/traits.rs diff --git a/core/src/storage/collections2/vec/iter.rs b/core/src/storage2/collections/vec/iter.rs similarity index 100% rename from core/src/storage/collections2/vec/iter.rs rename to core/src/storage2/collections/vec/iter.rs diff --git a/core/src/storage/collections2/vec/mod.rs b/core/src/storage2/collections/vec/mod.rs similarity index 100% rename from core/src/storage/collections2/vec/mod.rs rename to core/src/storage2/collections/vec/mod.rs diff --git a/core/src/storage/collections2/vec/tests.rs b/core/src/storage2/collections/vec/tests.rs similarity index 100% rename from core/src/storage/collections2/vec/tests.rs rename to core/src/storage2/collections/vec/tests.rs diff --git a/core/src/storage/collections2/vec/traits.rs b/core/src/storage2/collections/vec/traits.rs similarity index 100% rename from core/src/storage/collections2/vec/traits.rs rename to core/src/storage2/collections/vec/traits.rs diff --git a/core/src/storage/lazy/entry.rs b/core/src/storage2/lazy/entry.rs similarity index 100% rename from core/src/storage/lazy/entry.rs rename to core/src/storage2/lazy/entry.rs diff --git a/core/src/storage/lazy/lazy_array.rs b/core/src/storage2/lazy/lazy_array.rs similarity index 100% rename from core/src/storage/lazy/lazy_array.rs rename to core/src/storage2/lazy/lazy_array.rs diff --git a/core/src/storage/lazy/lazy_cell.rs b/core/src/storage2/lazy/lazy_cell.rs similarity index 100% rename from core/src/storage/lazy/lazy_cell.rs rename to core/src/storage2/lazy/lazy_cell.rs diff --git a/core/src/storage/lazy/lazy_map.rs b/core/src/storage2/lazy/lazy_map.rs similarity index 100% rename from core/src/storage/lazy/lazy_map.rs rename to core/src/storage2/lazy/lazy_map.rs diff --git a/core/src/storage/lazy/mod.rs b/core/src/storage2/lazy/mod.rs similarity index 100% rename from core/src/storage/lazy/mod.rs rename to core/src/storage2/lazy/mod.rs diff --git a/core/src/storage/pack.rs b/core/src/storage2/pack.rs similarity index 100% rename from core/src/storage/pack.rs rename to core/src/storage2/pack.rs diff --git a/core/src/storage/traits/clear.rs b/core/src/storage2/traits/clear.rs similarity index 100% rename from core/src/storage/traits/clear.rs rename to core/src/storage2/traits/clear.rs diff --git a/core/src/storage/traits/footprint.rs b/core/src/storage2/traits/footprint.rs similarity index 100% rename from core/src/storage/traits/footprint.rs rename to core/src/storage2/traits/footprint.rs diff --git a/core/src/storage/traits/mod.rs b/core/src/storage2/traits/mod.rs similarity index 100% rename from core/src/storage/traits/mod.rs rename to core/src/storage2/traits/mod.rs diff --git a/core/src/storage/traits/pull.rs b/core/src/storage2/traits/pull.rs similarity index 100% rename from core/src/storage/traits/pull.rs rename to core/src/storage2/traits/pull.rs diff --git a/core/src/storage/traits/push.rs b/core/src/storage2/traits/push.rs similarity index 100% rename from core/src/storage/traits/push.rs rename to core/src/storage2/traits/push.rs From e2567d7125fa7eddbf0eb8b53ba2dc272ec891f6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 15:42:48 +0100 Subject: [PATCH 127/142] [core] re-enable storage2 module --- core/src/lib.rs | 1 + core/src/storage/mod.rs | 29 ----------- core/src/storage2/collections/boxed/mod.rs | 2 +- core/src/storage2/collections/boxed/traits.rs | 4 +- .../src/storage2/collections/smallvec/iter.rs | 4 +- core/src/storage2/collections/smallvec/mod.rs | 4 +- .../storage2/collections/smallvec/traits.rs | 2 +- core/src/storage2/collections/vec/iter.rs | 6 +-- core/src/storage2/collections/vec/mod.rs | 4 +- core/src/storage2/collections/vec/traits.rs | 4 +- core/src/storage2/lazy/entry.rs | 2 +- core/src/storage2/lazy/lazy_array.rs | 2 +- core/src/storage2/lazy/lazy_cell.rs | 5 +- core/src/storage2/lazy/lazy_map.rs | 2 +- core/src/storage2/mod.rs | 51 +++++++++++++++++++ core/src/storage2/pack.rs | 2 +- 16 files changed, 74 insertions(+), 50 deletions(-) create mode 100644 core/src/storage2/mod.rs diff --git a/core/src/lib.rs b/core/src/lib.rs index 2176d692c82..0e0106cc466 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -49,6 +49,7 @@ extern crate ink_alloc; pub mod env; pub mod storage; +pub mod storage2; // Needed for derive macros of `core/derive` sub crate. pub(crate) use crate as ink_core; diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 1e8ca28826f..efc02831246 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -104,35 +104,6 @@ pub use self::{ }, }, flush::Flush, - // collections2::{ - // boxed::Box, - // smallvec::SmallVec, - // vec::Vec as Vec2, - // }, - // lazy::{ - // Lazy, - // LazyArray, - // LazyArrayLength, - // LazyCell, - // LazyChunk, - // LazyMap, - // LazyMapping, - // }, - // pack::Pack, - // traits::{ - // storage_footprint_u128, - // storage_footprint_u64, - // ClearAt, - // ClearForward, - // KeyPtr, - // PullAt, - // PullForward, - // PushAt, - // PushForward, - // SaturatingStorage, - // StorageFootprint, - // StorageFootprintOf, - // }, }; #[doc(inline)] diff --git a/core/src/storage2/collections/boxed/mod.rs b/core/src/storage2/collections/boxed/mod.rs index ae21224c4ca..b5839dab653 100644 --- a/core/src/storage2/collections/boxed/mod.rs +++ b/core/src/storage2/collections/boxed/mod.rs @@ -14,7 +14,7 @@ mod traits; -use crate::storage::{ +use crate::storage2::{ ClearForward, Lazy, PullForward, diff --git a/core/src/storage2/collections/boxed/traits.rs b/core/src/storage2/collections/boxed/traits.rs index 490cf06de31..83dd6394eae 100644 --- a/core/src/storage2/collections/boxed/traits.rs +++ b/core/src/storage2/collections/boxed/traits.rs @@ -14,8 +14,8 @@ use super::Box as StorageBox; use crate::{ - storage, - storage::{ + storage2 as storage, + storage2::{ ClearForward, KeyPtr, PullForward, diff --git a/core/src/storage2/collections/smallvec/iter.rs b/core/src/storage2/collections/smallvec/iter.rs index 32381ff28da..90f95da630d 100644 --- a/core/src/storage2/collections/smallvec/iter.rs +++ b/core/src/storage2/collections/smallvec/iter.rs @@ -13,8 +13,8 @@ // limitations under the License. use super::SmallVec; -use crate::storage::{ - collections2::extend_lifetime, +use crate::storage2::{ + collections::extend_lifetime, LazyArrayLength, PullForward, SaturatingStorage, diff --git a/core/src/storage2/collections/smallvec/mod.rs b/core/src/storage2/collections/smallvec/mod.rs index acd0d49c08e..69883356c8a 100644 --- a/core/src/storage2/collections/smallvec/mod.rs +++ b/core/src/storage2/collections/smallvec/mod.rs @@ -23,8 +23,8 @@ pub use self::iter::{ IterMut, }; use crate::{ - storage, - storage::{ + storage2 as storage, + storage2::{ LazyArray, LazyArrayLength, PullForward, diff --git a/core/src/storage2/collections/smallvec/traits.rs b/core/src/storage2/collections/smallvec/traits.rs index 2d5672f016f..8ffe64def8d 100644 --- a/core/src/storage2/collections/smallvec/traits.rs +++ b/core/src/storage2/collections/smallvec/traits.rs @@ -16,7 +16,7 @@ use super::{ Iter, SmallVec, }; -use crate::storage::{ +use crate::storage2::{ KeyPtr, LazyArray, LazyArrayLength, diff --git a/core/src/storage2/collections/vec/iter.rs b/core/src/storage2/collections/vec/iter.rs index 71b3023d8f6..978bfea40bb 100644 --- a/core/src/storage2/collections/vec/iter.rs +++ b/core/src/storage2/collections/vec/iter.rs @@ -13,9 +13,9 @@ // limitations under the License. use crate::{ - storage, - storage::{ - collections2::extend_lifetime, + storage2 as storage, + storage2::{ + collections::extend_lifetime, PullForward, SaturatingStorage, StorageFootprint, diff --git a/core/src/storage2/collections/vec/mod.rs b/core/src/storage2/collections/vec/mod.rs index 47224ed93b8..de0eccfa2f7 100644 --- a/core/src/storage2/collections/vec/mod.rs +++ b/core/src/storage2/collections/vec/mod.rs @@ -23,8 +23,8 @@ pub use self::iter::{ IterMut, }; use crate::{ - storage, - storage::{ + storage2 as storage, + storage2::{ PullForward, SaturatingStorage, StorageFootprint, diff --git a/core/src/storage2/collections/vec/traits.rs b/core/src/storage2/collections/vec/traits.rs index 33a96c0cec0..afd27e37645 100644 --- a/core/src/storage2/collections/vec/traits.rs +++ b/core/src/storage2/collections/vec/traits.rs @@ -18,8 +18,8 @@ use super::{ Vec as StorageVec, }; use crate::{ - storage, - storage::{ + storage2 as storage, + storage2::{ ClearForward, KeyPtr, PullForward, diff --git a/core/src/storage2/lazy/entry.rs b/core/src/storage2/lazy/entry.rs index 4152def4de4..509e4291cef 100644 --- a/core/src/storage2/lazy/entry.rs +++ b/core/src/storage2/lazy/entry.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::storage::{ +use crate::storage2::{ KeyPtr, PushForward, StorageFootprint, diff --git a/core/src/storage2/lazy/lazy_array.rs b/core/src/storage2/lazy/lazy_array.rs index 8d1f9144756..67d4a9d1927 100644 --- a/core/src/storage2/lazy/lazy_array.rs +++ b/core/src/storage2/lazy/lazy_array.rs @@ -16,7 +16,7 @@ use super::{ Entry, EntryState, }; -use crate::storage::{ +use crate::storage2::{ KeyPtr, PullForward, PushForward, diff --git a/core/src/storage2/lazy/lazy_cell.rs b/core/src/storage2/lazy/lazy_cell.rs index 237c808c193..ab17e5075f0 100644 --- a/core/src/storage2/lazy/lazy_cell.rs +++ b/core/src/storage2/lazy/lazy_cell.rs @@ -16,7 +16,8 @@ use super::{ Entry, EntryState, }; -use crate::storage::{ +use crate::storage2::{ + storage_footprint_u64, ClearForward, KeyPtr, PullForward, @@ -110,7 +111,7 @@ where None => { // TODO: Find better and more general clean-up strategy with // the help of the proposed subtrie API. - let footprint = crate::storage::storage_footprint_u64::(); + let footprint = storage_footprint_u64::(); if footprint >= 32 { panic!("cannot clean up more than 32 cells at once") } diff --git a/core/src/storage2/lazy/lazy_map.rs b/core/src/storage2/lazy/lazy_map.rs index 8d998f9af0b..c22428ce813 100644 --- a/core/src/storage2/lazy/lazy_map.rs +++ b/core/src/storage2/lazy/lazy_map.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::storage::{ +use crate::storage2::{ KeyPtr, PullForward, PushForward, diff --git a/core/src/storage2/mod.rs b/core/src/storage2/mod.rs new file mode 100644 index 00000000000..d569e58a359 --- /dev/null +++ b/core/src/storage2/mod.rs @@ -0,0 +1,51 @@ +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub mod collections; +pub mod lazy; +mod traits; +mod pack; + +#[doc(inline)] +pub use self::{ + collections::{ + boxed::Box, + smallvec::SmallVec, + vec::Vec as Vec2, + }, + lazy::{ + Lazy, + LazyArray, + LazyArrayLength, + LazyCell, + LazyChunk, + LazyMap, + LazyMapping, + }, + pack::Pack, + traits::{ + storage_footprint_u128, + storage_footprint_u64, + ClearAt, + ClearForward, + KeyPtr, + PullAt, + PullForward, + PushAt, + PushForward, + SaturatingStorage, + StorageFootprint, + StorageFootprintOf, + }, +}; diff --git a/core/src/storage2/pack.rs b/core/src/storage2/pack.rs index 13d5afe77f5..0b9227b5520 100644 --- a/core/src/storage2/pack.rs +++ b/core/src/storage2/pack.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::storage::{ +use crate::storage2::{ KeyPtr, PullAt, PullForward, From 2bdb5742198352bb628f43fd79e01b8484fbec54 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 15:44:39 +0100 Subject: [PATCH 128/142] [core] rename storage2::Vec2 -> Vec --- core/src/storage2/collections/vec/iter.rs | 8 ++++---- core/src/storage2/mod.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/storage2/collections/vec/iter.rs b/core/src/storage2/collections/vec/iter.rs index 978bfea40bb..97c8d7b0762 100644 --- a/core/src/storage2/collections/vec/iter.rs +++ b/core/src/storage2/collections/vec/iter.rs @@ -26,7 +26,7 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub struct Iter<'a, T> { /// The storage vector to iterate over. - vec: &'a storage::Vec2, + vec: &'a storage::Vec, /// The current begin of the iteration. begin: u32, /// The current end of the iteration. @@ -35,7 +35,7 @@ pub struct Iter<'a, T> { impl<'a, T> Iter<'a, T> { /// Creates a new iterator for the given storage vector. - pub(crate) fn new(vec: &'a storage::Vec2) -> Self { + pub(crate) fn new(vec: &'a storage::Vec) -> Self { Self { vec, begin: 0, @@ -87,7 +87,7 @@ where #[derive(Debug)] pub struct IterMut<'a, T> { /// The storage vector to iterate over. - vec: &'a mut storage::Vec2, + vec: &'a mut storage::Vec, /// The current begin of the iteration. begin: u32, /// The current end of the iteration. @@ -96,7 +96,7 @@ pub struct IterMut<'a, T> { impl<'a, T> IterMut<'a, T> { /// Creates a new iterator for the given storage vector. - pub(crate) fn new(vec: &'a mut storage::Vec2) -> Self { + pub(crate) fn new(vec: &'a mut storage::Vec) -> Self { let len = vec.len(); Self { vec, diff --git a/core/src/storage2/mod.rs b/core/src/storage2/mod.rs index d569e58a359..dd148c5ba87 100644 --- a/core/src/storage2/mod.rs +++ b/core/src/storage2/mod.rs @@ -22,7 +22,7 @@ pub use self::{ collections::{ boxed::Box, smallvec::SmallVec, - vec::Vec as Vec2, + vec::Vec, }, lazy::{ Lazy, From 244933e212675a47009ef8ff704baddb70d194ef Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 15:44:53 +0100 Subject: [PATCH 129/142] [core] add brief doc to storage2 --- core/src/storage2/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/storage2/mod.rs b/core/src/storage2/mod.rs index dd148c5ba87..8212ddae329 100644 --- a/core/src/storage2/mod.rs +++ b/core/src/storage2/mod.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Core abstractions for storage manipulation. (revision 2) + pub mod collections; pub mod lazy; mod traits; From 7bb5007596b8b89b50c4914e15e35cb3bdd1ca4a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Mon, 16 Mar 2020 19:17:17 +0100 Subject: [PATCH 130/142] [core] re-use lazy::Entry from storage::LazyMap --- core/src/storage2/lazy/entry.rs | 17 +++++ core/src/storage2/lazy/lazy_map.rs | 109 ++++++----------------------- 2 files changed, 37 insertions(+), 89 deletions(-) diff --git a/core/src/storage2/lazy/entry.rs b/core/src/storage2/lazy/entry.rs index 509e4291cef..42480022875 100644 --- a/core/src/storage2/lazy/entry.rs +++ b/core/src/storage2/lazy/entry.rs @@ -92,6 +92,23 @@ impl Entry { self.value.as_mut() } + /// Returns an exclusive reference to the entry value. + /// + /// # Note + /// + /// This changes the `mutate` state of the entry if the entry was occupied + /// since the caller could potentially change the returned value. + pub fn value_as_mut_ref(&mut self) -> &mut Option { + self.state.set( + if self.value.is_some() { + EntryState::Mutated + } else { + EntryState::Preserved + }, + ); + &mut self.value + } + /// Takes the value from the entry and returns it. /// /// # Note diff --git a/core/src/storage2/lazy/lazy_map.rs b/core/src/storage2/lazy/lazy_map.rs index c22428ce813..7e4bf50b634 100644 --- a/core/src/storage2/lazy/lazy_map.rs +++ b/core/src/storage2/lazy/lazy_map.rs @@ -20,10 +20,7 @@ use crate::storage2::{ StorageFootprintOf, }; use core::{ - cell::{ - Cell, - UnsafeCell, - }, + cell::UnsafeCell, cmp::{ Eq, Ord, @@ -142,77 +139,10 @@ where /// [`LazyMap::get`]. pub type EntryMap = BTreeMap>>; -/// An entry within the lazy chunk -#[derive(Debug)] -pub struct Entry { - /// The current value of the entry. - value: Option, - /// This is `true` if the `value` has been mutated and is potentially - /// out-of-sync with the contract storage. - mutated: Cell, -} - -impl PushForward for Entry -where - T: PushForward + StorageFootprint, -{ - fn push_forward(&self, ptr: &mut KeyPtr) { - // Reset the mutated entry flag because we just synced. - self.mutated.set(false); - // Since `self.value` is of type `Option` this will eventually - // clear the underlying storage entry if `self.value` is `None`. - self.value.push_forward(ptr); - } -} - -impl Entry { - /// Returns `true` if the cached value of the entry has potentially been mutated. - pub fn mutated(&self) -> bool { - self.mutated.get() - } - - /// Returns a shared reference to the value of the entry. - pub fn value(&self) -> Option<&T> { - self.value.as_ref() - } - - /// Returns an exclusive reference to the value of the entry. - /// - /// # Note - /// - /// This changes the `mutate` state of the entry if the entry was occupied - /// since the caller could potentially change the returned value. - pub fn value_mut(&mut self) -> Option<&mut T> { - self.mutated.set(self.value.is_some()); - self.value.as_mut() - } - - /// Takes the value from the entry and returns it. - /// - /// # Note - /// - /// This changes the `mutate` state of the entry if the entry was occupied. - pub fn take_value(&mut self) -> Option { - self.mutated.set(self.value.is_some()); - self.value.take() - } - - /// Puts the new value into the entry and returns the old value. - /// - /// # Note - /// - /// This changes the `mutate` state of the entry to `true` as long as at - /// least one of `old_value` and `new_value` is `Some`. - pub fn put(&mut self, new_value: Option) -> Option { - match new_value { - Some(new_value) => { - self.mutated.set(true); - self.value.replace(new_value) - } - None => self.take_value(), - } - } -} +use super::{ + Entry, + EntryState, +}; impl LazyMap where @@ -276,13 +206,8 @@ where /// - If the lazy chunk is in an invalid state that forbids interaction. /// - If the decoding of the old element at the given index failed. pub fn put(&mut self, index: K, new_value: Option) { - self.entries_mut().insert( - index, - Box::new(Entry { - value: new_value, - mutated: Cell::new(true), - }), - ); + self.entries_mut() + .insert(index, Box::new(Entry::new(new_value, EntryState::Mutated))); } } @@ -329,7 +254,11 @@ where fn push_forward(&self, ptr: &mut KeyPtr) { let key = ptr.next_for::(); assert_eq!(self.key, Some(key)); - for (index, entry) in self.entries().iter().filter(|(_, entry)| entry.mutated()) { + for (index, entry) in self + .entries() + .iter() + .filter(|(_, entry)| entry.is_mutated()) + { let offset: Key = >::to_storage_key(index, &key); let mut ptr = KeyPtr::from(offset); PushForward::push_forward(&**entry, &mut ptr); @@ -389,8 +318,10 @@ where let off_key = >::to_storage_key(&index_copy, &key); let value = as PullForward>::pull_forward(&mut KeyPtr::from(off_key)); - let mutated = Cell::new(false); - NonNull::from(&mut **vacant.insert(Box::new(Entry { value, mutated }))) + NonNull::from( + &mut **vacant + .insert(Box::new(Entry::new(value, EntryState::Preserved))), + ) } } } @@ -495,14 +426,14 @@ where &mut *self.lazily_load(x).as_ptr(), &mut *self.lazily_load(y).as_ptr(), ) }; - if loaded_x.value.is_none() && loaded_y.value.is_none() { + if loaded_x.value().is_none() && loaded_y.value().is_none() { // Bail out since nothing has to be swapped if both values are `None`. return } // Set the `mutate` flag since at this point at least one of the loaded // values is guaranteed to be `Some`. - loaded_x.mutated.set(true); - loaded_y.mutated.set(true); - core::mem::swap(&mut loaded_x.value, &mut loaded_y.value); + loaded_x.set_state(EntryState::Mutated); + loaded_y.set_state(EntryState::Mutated); + core::mem::swap(loaded_x.value_as_mut_ref(), loaded_y.value_as_mut_ref()); } } From 07c35e8cbd578bc211624e803746f9c53047feb9 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 20 Mar 2020 19:31:10 +0100 Subject: [PATCH 131/142] [core] add inline annotations to some clean up trait impls --- core/src/storage2/traits/clear.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/storage2/traits/clear.rs b/core/src/storage2/traits/clear.rs index ae9a8f26db2..f1745564a50 100644 --- a/core/src/storage2/traits/clear.rs +++ b/core/src/storage2/traits/clear.rs @@ -41,6 +41,7 @@ pub trait ClearForward { /// contract storage cell. pub trait ClearAt { /// Clears `self` packed at the contract storage cell determined by `at`. + #[inline] fn clear_at(&self, at: Key) { env::clear_contract_storage(at) } @@ -50,6 +51,7 @@ macro_rules! impl_clear_for_primitive { ( $($ty:ty),* $(,)? ) => { $( impl ClearForward for $ty { + #[inline] fn clear_forward(&self, ptr: &mut KeyPtr) { <$ty as ClearAt>::clear_at(self, ptr.next_for::<$ty>()) } From a57cb014139954070efcd1081cb3876916bb6873 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 25 Mar 2020 13:16:52 +0100 Subject: [PATCH 132/142] [core] add Pack::{into_inner, as_inner, as_inner_mut} inherent methods --- core/src/storage2/pack.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/storage2/pack.rs b/core/src/storage2/pack.rs index 0b9227b5520..0e43557f582 100644 --- a/core/src/storage2/pack.rs +++ b/core/src/storage2/pack.rs @@ -66,6 +66,21 @@ impl Pack { Self { inner: value } } + /// Returns the packed value. + pub fn into_inner(pack: Self) -> T { + pack.inner + } + + /// Returns a shared reference to the packed value. + pub fn as_inner(pack: &Pack) -> &T { + pack.get() + } + + /// Returns an exclusive reference to the packed value. + pub fn as_inner_mut(pack: &mut Pack) -> &mut T { + pack.get_mut() + } + /// Returns a shared reference to the packed value. fn get(&self) -> &T { &self.inner From 75b23acea9d0a657e90bd5cd3e296f994bc49f9e Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 25 Mar 2020 13:17:09 +0100 Subject: [PATCH 133/142] re-export push_single_cell utility function --- core/src/storage2/mod.rs | 1 + core/src/storage2/traits/mod.rs | 1 + core/src/storage2/traits/pull.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/storage2/mod.rs b/core/src/storage2/mod.rs index 8212ddae329..5c1a98097cb 100644 --- a/core/src/storage2/mod.rs +++ b/core/src/storage2/mod.rs @@ -49,5 +49,6 @@ pub use self::{ SaturatingStorage, StorageFootprint, StorageFootprintOf, + pull_single_cell, }, }; diff --git a/core/src/storage2/traits/mod.rs b/core/src/storage2/traits/mod.rs index d369402ea58..abe2c73c317 100644 --- a/core/src/storage2/traits/mod.rs +++ b/core/src/storage2/traits/mod.rs @@ -71,6 +71,7 @@ pub use self::{ pull::{ PullAt, PullForward, + pull_single_cell, }, push::{ PushAt, diff --git a/core/src/storage2/traits/pull.rs b/core/src/storage2/traits/pull.rs index 0aa8c3d610d..3cde0069a31 100644 --- a/core/src/storage2/traits/pull.rs +++ b/core/src/storage2/traits/pull.rs @@ -48,7 +48,7 @@ pub trait PullAt { fn pull_at(at: Key) -> Self; } -fn pull_single_cell(at: Key) -> T +pub fn pull_single_cell(at: Key) -> T where T: scale::Decode, { From c865d5c83b43c397e7be8eaa7a033644b0fb96c1 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 25 Mar 2020 13:17:26 +0100 Subject: [PATCH 134/142] [core] add initial skeleton of storage2::Stash --- core/src/storage2/collections/mod.rs | 1 + core/src/storage2/collections/stash/mod.rs | 213 +++++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 core/src/storage2/collections/stash/mod.rs diff --git a/core/src/storage2/collections/mod.rs b/core/src/storage2/collections/mod.rs index ac548b67405..f1652cc5cff 100644 --- a/core/src/storage2/collections/mod.rs +++ b/core/src/storage2/collections/mod.rs @@ -15,6 +15,7 @@ pub mod boxed; pub mod smallvec; pub mod vec; +pub mod stash; /// Extends the lifetime 'a to the outliving lifetime 'b for the given reference. /// diff --git a/core/src/storage2/collections/stash/mod.rs b/core/src/storage2/collections/stash/mod.rs new file mode 100644 index 00000000000..005637c3dd9 --- /dev/null +++ b/core/src/storage2/collections/stash/mod.rs @@ -0,0 +1,213 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::storage2::{ + LazyChunk, + Pack, + PullAt, + PullForward, + PushForward, + StorageFootprint, +}; +use ink_primitives::Key; + +type Index = u32; + +pub struct Stash { + /// The combined and commonly used header data. + header: Pack
, + /// The storage entries of the stash. + entries: LazyChunk>>, +} + +pub struct Header { + /// The latest vacant index. + next_vacant: Index, + /// The number of items stored in the stash. + /// + /// # Note + /// + /// We cannot simply use the underlying length of the vector + /// since it would include vacant slots as well. + len: u32, + /// The maximum length the stash ever had. + max_len: u32, +} + +#[derive(Debug, scale::Encode, scale::Decode)] +pub enum Entry { + Vacant(Index), + Occupied(T), +} + +impl Entry { + /// Returns `true` if the entry is occupied. + pub fn is_occupied(&self) -> bool { + if let Entry::Occupied(_) = self { + true + } else { + false + } + } + + /// Returns `true` if the entry is vacant. + pub fn is_vacant(&self) -> bool { + !self.is_occupied() + } +} + +impl PullAt for Entry +where + T: scale::Decode, +{ + fn pull_at(at: Key) -> Self { + crate::storage2::pull_single_cell(at) + } +} + +impl Stash { + /// Creates a new empty stash. + pub fn new() -> Self { + Self { + header: Pack::new(Header { + next_vacant: 0, + len: 0, + max_len: 0, + }), + entries: LazyChunk::new(), + } + } + + /// Returns the number of elements stored in the stash. + pub fn len(&self) -> u32 { + self.header.len + } + + /// Returns `true` if the stash contains no elements. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the maximum number of element stored in the + /// stash at the same time. + pub fn max_len(&self) -> u32 { + self.header.max_len + } + + /// Returns the next vacant index. + fn next_vacant(&self) -> u32 { + self.header.next_vacant + } + + /// Returns the underlying key to the cells. + /// + /// # Note + /// + /// This is a low-level utility getter and should + /// normally not be required by users. + pub fn entries_key(&self) -> Option<&Key> { + self.entries.key() + } +} + +impl Stash +where + T: scale::Decode + StorageFootprint + PullForward, +{ + /// Returns a shared reference to the element at the given index. + pub fn get(&self, at: Index) -> Option<&T> { + self.entries.get(at).and_then(|entry| { + match Pack::as_inner(entry) { + Entry::Occupied(val) => Some(val), + Entry::Vacant(_) => None, + } + }) + } + + /// Returns an exclusive reference to the element at the given index. + pub fn get_mut(&mut self, at: Index) -> Option<&mut T> { + self.entries.get_mut(at).and_then(|entry| { + match Pack::as_inner_mut(entry) { + Entry::Occupied(val) => Some(val), + Entry::Vacant(_) => None, + } + }) + } +} + +impl Stash +where + T: scale::Codec + StorageFootprint + PullForward, +{ + /// Put the element into the stash at the next vacant position. + /// + /// Returns the stash index that the element was put into. + pub fn put(&mut self, new_value: T) -> Index { + let current_vacant = self.header.next_vacant; + debug_assert!(current_vacant <= self.len()); + let new_entry = Some(Pack::new(Entry::Occupied(new_value))); + if current_vacant == self.len() { + // Push the new element to the end if all entries are occupied. + self.entries.put(current_vacant, new_entry); + self.header.next_vacant = current_vacant + 1; + self.header.max_len += 1; + } else { + // Put the new element to the most recent vacant index if not all entries are occupied. + let old_entry = self + .entries + .put_get(current_vacant, new_entry) + .expect("a `next_vacant` index must point to an occupied cell"); + let next_vacant = match Pack::into_inner(old_entry) { + Entry::Vacant(next_vacant) => next_vacant, + Entry::Occupied(_) => { + unreachable!("a `next_vacant` index must point to a vacant entry") + } + }; + self.header.next_vacant = next_vacant; + } + self.header.len += 1; + current_vacant + } + + /// Takes the element stored at the given index if any. + pub fn take(&mut self, at: Index) -> Option { + // match self.entries.get(at) { + // None => return None, + // Some(entry) if Pack::as_inner(entry).is_vacant() => { + // return None + // // if let Entry::Vacant(_) = Pack::as_inner(entry) {} + // // match self + // // .entries + // // .put(at, Entry::Vacant(self.next_vacant())) + // // .expect("already asserted that the entry exists") + // // { + // // Entry::Occupied(val) => { + // // self.header.next_vacant = n; + // // debug_assert!(!self.is_empty()); + // // self.header.len -= 1; + // // Some(val) + // // } + // // Entry::Vacant(_) => { + // // unreachable!("already asserted that the entry is occupied") + // // } + // // } + // } + // _ => (), + // } + // self + // .entries + // .put_get(at, Some(Entry::Vacant(self.next_vacant()))) + todo!() + } +} From a394d7549e7628453e98bc95d64b005768db925c Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 25 Mar 2020 18:09:17 +0100 Subject: [PATCH 135/142] [core] implement storage2::Stash::take --- core/src/storage2/collections/stash/mod.rs | 50 ++++++++++------------ 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/core/src/storage2/collections/stash/mod.rs b/core/src/storage2/collections/stash/mod.rs index 005637c3dd9..788f9ecaa96 100644 --- a/core/src/storage2/collections/stash/mod.rs +++ b/core/src/storage2/collections/stash/mod.rs @@ -21,6 +21,7 @@ use crate::storage2::{ StorageFootprint, }; use ink_primitives::Key; +use core::mem::MaybeUninit; type Index = u32; @@ -182,32 +183,27 @@ where /// Takes the element stored at the given index if any. pub fn take(&mut self, at: Index) -> Option { - // match self.entries.get(at) { - // None => return None, - // Some(entry) if Pack::as_inner(entry).is_vacant() => { - // return None - // // if let Entry::Vacant(_) = Pack::as_inner(entry) {} - // // match self - // // .entries - // // .put(at, Entry::Vacant(self.next_vacant())) - // // .expect("already asserted that the entry exists") - // // { - // // Entry::Occupied(val) => { - // // self.header.next_vacant = n; - // // debug_assert!(!self.is_empty()); - // // self.header.len -= 1; - // // Some(val) - // // } - // // Entry::Vacant(_) => { - // // unreachable!("already asserted that the entry is occupied") - // // } - // // } - // } - // _ => (), - // } - // self - // .entries - // .put_get(at, Some(Entry::Vacant(self.next_vacant()))) - todo!() + let next_vacant_index = self.next_vacant(); + match self.entries.get_mut(at) { + None => None, + Some(packed) => { + let entry_mut = Pack::as_inner_mut(packed); + if entry_mut.is_vacant() { + // Bail out of the taken entry is already vacant. + return None + } + // At this point we know that the entry is occupied with a value. + let new_vacant_entry = Entry::Vacant(next_vacant_index); + let taken_entry = core::mem::replace(entry_mut, new_vacant_entry); + match taken_entry { + Entry::Occupied(value) => { + self.header.next_vacant = at; + self.header.len -= 1; + Some(value) + } + Entry::Vacant(_) => unreachable!("the entry must be occupied at this point"), + } + } + } } } From 045dde42d106864578e6ffd311463983da945877 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 26 Mar 2020 13:48:24 +0100 Subject: [PATCH 136/142] [core] implement iterators and standard traits for storage2::Stash --- core/src/storage2/collections/stash/impls.rs | 128 ++++++++++++++ core/src/storage2/collections/stash/iter.rs | 170 +++++++++++++++++++ core/src/storage2/collections/stash/mod.rs | 42 ++++- core/src/storage2/mod.rs | 1 + 4 files changed, 339 insertions(+), 2 deletions(-) create mode 100644 core/src/storage2/collections/stash/impls.rs create mode 100644 core/src/storage2/collections/stash/iter.rs diff --git a/core/src/storage2/collections/stash/impls.rs b/core/src/storage2/collections/stash/impls.rs new file mode 100644 index 00000000000..06f7d5cebd5 --- /dev/null +++ b/core/src/storage2/collections/stash/impls.rs @@ -0,0 +1,128 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{ + Iter, + IterMut, + Stash as StorageStash, +}; +use crate::{ + storage2 as storage, + storage2::{ + ClearForward, + KeyPtr, + PullForward, + PushForward, + SaturatingStorage, + StorageFootprint, + StorageFootprintOf, + }, +}; +use core::iter::{ + Extend, + FromIterator, +}; +// use typenum::{ +// Add1, +// Unsigned, +// }; + +impl core::ops::Index for StorageStash +where + T: StorageFootprint + PullForward + scale::Decode, +{ + type Output = T; + + fn index(&self, index: u32) -> &Self::Output { + self.get(index).expect("index out of bounds") + } +} + +impl core::ops::IndexMut for StorageStash +where + T: SaturatingStorage + StorageFootprint + PullForward + scale::Decode, +{ + fn index_mut(&mut self, index: u32) -> &mut Self::Output { + self.get_mut(index).expect("index out of bounds") + } +} + +impl<'a, T: 'a> IntoIterator for &'a StorageStash +where + T: StorageFootprint + PullForward + scale::Decode, +{ + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a, T: 'a> IntoIterator for &'a mut StorageStash +where + T: StorageFootprint + SaturatingStorage + PullForward + scale::Decode, +{ + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } +} + +impl Extend for StorageStash +where + T: StorageFootprint + SaturatingStorage + PullForward + scale::Codec, +{ + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for item in iter { + self.put(item); + } + } +} + +impl FromIterator for StorageStash +where + T: StorageFootprint + SaturatingStorage + PullForward + scale::Codec, +{ + fn from_iter(iter: I) -> Self + where + I: IntoIterator, + { + let mut vec = StorageStash::new(); + vec.extend(iter); + vec + } +} + +impl core::cmp::PartialEq for StorageStash +where + T: PartialEq + StorageFootprint + PullForward + scale::Decode, +{ + fn eq(&self, other: &Self) -> bool { + if self.len() != other.len() { + return false + } + self.iter().zip(other.iter()).all(|(lhs, rhs)| lhs == rhs) + } +} + +impl core::cmp::Eq for StorageStash where + T: Eq + StorageFootprint + PullForward + scale::Decode +{ +} diff --git a/core/src/storage2/collections/stash/iter.rs b/core/src/storage2/collections/stash/iter.rs new file mode 100644 index 00000000000..90b9c672586 --- /dev/null +++ b/core/src/storage2/collections/stash/iter.rs @@ -0,0 +1,170 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + storage2 as storage, + storage2::{ + collections::extend_lifetime, + PullForward, + SaturatingStorage, + StorageFootprint, + }, +}; + +/// An iterator over shared references to the elements of a storage stash. +#[derive(Debug, Clone, Copy)] +pub struct Iter<'a, T> { + /// The storage stash to iterate over. + stash: &'a storage::Stash, + /// The current begin of the iteration. + begin: u32, + /// The current end of the iteration. + end: u32, +} + +impl<'a, T> Iter<'a, T> { + /// Creates a new iterator for the given storage stash. + pub(crate) fn new(stash: &'a storage::Stash) -> Self { + Self { + stash, + begin: 0, + end: stash.len(), + } + } +} + +impl<'a, T> Iterator for Iter<'a, T> +where + T: StorageFootprint + PullForward + scale::Decode, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + let cur = self.begin; + self.begin += 1; + self.stash.get(cur) + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = (self.end - self.begin) as usize; + (remaining, Some(remaining)) + } +} + +impl<'a, T> ExactSizeIterator for Iter<'a, T> where + T: StorageFootprint + PullForward + scale::Decode +{ +} + +impl<'a, T> DoubleEndedIterator for Iter<'a, T> +where + T: StorageFootprint + PullForward + scale::Decode, +{ + fn next_back(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + debug_assert_ne!(self.end, 0); + self.end -= 1; + self.stash.get(self.end) + } +} + +/// An iterator over exclusive references to the elements of a storage stash. +#[derive(Debug)] +pub struct IterMut<'a, T> { + /// The storage stash to iterate over. + stash: &'a mut storage::Stash, + /// The current begin of the iteration. + begin: u32, + /// The current end of the iteration. + end: u32, +} + +impl<'a, T> IterMut<'a, T> { + /// Creates a new iterator for the given storage stash. + pub(crate) fn new(stash: &'a mut storage::Stash) -> Self { + let len = stash.len(); + Self { + stash, + begin: 0, + end: len, + } + } +} + +impl<'a, T> IterMut<'a, T> +where + T: StorageFootprint + SaturatingStorage + PullForward + scale::Decode, +{ + fn get_mut<'b>(&'b mut self, at: u32) -> Option<&'a mut T> { + self.stash.get_mut(at).map(|value| { + // SAFETY: We extend the lifetime of the reference here. + // + // This is safe because the iterator yields an exclusive + // reference to every element in the iterated vector + // just once and also there can be only one such iterator + // for the same vector at the same time which is + // guaranteed by the constructor of the iterator. + unsafe { extend_lifetime::<'b, 'a, T>(value) } + }) + } +} + +impl<'a, T> Iterator for IterMut<'a, T> +where + T: StorageFootprint + SaturatingStorage + PullForward + scale::Decode, +{ + type Item = &'a mut T; + + fn next(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + let cur = self.begin; + self.begin += 1; + self.get_mut(cur) + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = (self.end - self.begin) as usize; + (remaining, Some(remaining)) + } +} + +impl<'a, T> ExactSizeIterator for IterMut<'a, T> where + T: StorageFootprint + SaturatingStorage + PullForward + scale::Decode +{ +} + +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> +where + T: StorageFootprint + SaturatingStorage + PullForward + scale::Decode, +{ + fn next_back(&mut self) -> Option { + debug_assert!(self.begin <= self.end); + if self.begin == self.end { + return None + } + debug_assert_ne!(self.end, 0); + self.end -= 1; + self.get_mut(self.end) + } +} diff --git a/core/src/storage2/collections/stash/mod.rs b/core/src/storage2/collections/stash/mod.rs index 788f9ecaa96..7f049f7194d 100644 --- a/core/src/storage2/collections/stash/mod.rs +++ b/core/src/storage2/collections/stash/mod.rs @@ -12,6 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod iter; +mod impls; + +#[cfg(test)] +mod tests; + +pub use self::iter::{ + Iter, + IterMut, +}; use crate::storage2::{ LazyChunk, Pack, @@ -21,10 +31,11 @@ use crate::storage2::{ StorageFootprint, }; use ink_primitives::Key; -use core::mem::MaybeUninit; +/// An index into the stash. type Index = u32; +#[derive(Debug)] pub struct Stash { /// The combined and commonly used header data. header: Pack
, @@ -32,6 +43,7 @@ pub struct Stash { entries: LazyChunk>>, } +#[derive(Debug)] pub struct Header { /// The latest vacant index. next_vacant: Index, @@ -46,9 +58,12 @@ pub struct Header { max_len: u32, } +/// An entry within the stash. #[derive(Debug, scale::Encode, scale::Decode)] pub enum Entry { + /// A vacant entry that holds the index to the next vacant entry. Vacant(Index), + /// An occupied entry that hold the value. Occupied(T), } @@ -120,6 +135,28 @@ impl Stash { pub fn entries_key(&self) -> Option<&Key> { self.entries.key() } + + /// Returns an iterator yielding shared references to all elements of the stash. + /// + /// # Note + /// + /// Avoid unbounded iteration over big storage stashs. + /// Prefer using methods like `Iterator::take` in order to limit the number + /// of yielded elements. + pub fn iter(&self) -> Iter { + Iter::new(self) + } + + /// Returns an iterator yielding exclusive references to all elements of the stash. + /// + /// # Note + /// + /// Avoid unbounded iteration over big storage stashs. + /// Prefer using methods like `Iterator::take` in order to limit the number + /// of yielded elements. + pub fn iter_mut(&mut self) -> IterMut { + IterMut::new(self) + } } impl Stash @@ -201,7 +238,8 @@ where self.header.len -= 1; Some(value) } - Entry::Vacant(_) => unreachable!("the entry must be occupied at this point"), + Entry::Vacant(_) => { + unreachable!("the entry must be occupied at this point") } } } diff --git a/core/src/storage2/mod.rs b/core/src/storage2/mod.rs index 5c1a98097cb..c74b5cfc5b4 100644 --- a/core/src/storage2/mod.rs +++ b/core/src/storage2/mod.rs @@ -25,6 +25,7 @@ pub use self::{ boxed::Box, smallvec::SmallVec, vec::Vec, + stash::Stash, }, lazy::{ Lazy, From b590677ac95064a98b9d942192b9073bd44596f0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 27 Mar 2020 09:57:49 +0100 Subject: [PATCH 137/142] [core] implement storage traits for storage2::Stash --- core/src/storage2/collections/stash/impls.rs | 9 -- core/src/storage2/collections/stash/mod.rs | 15 +-- .../src/storage2/collections/stash/storage.rs | 115 ++++++++++++++++++ 3 files changed, 118 insertions(+), 21 deletions(-) create mode 100644 core/src/storage2/collections/stash/storage.rs diff --git a/core/src/storage2/collections/stash/impls.rs b/core/src/storage2/collections/stash/impls.rs index 06f7d5cebd5..aeebd2b89da 100644 --- a/core/src/storage2/collections/stash/impls.rs +++ b/core/src/storage2/collections/stash/impls.rs @@ -18,25 +18,16 @@ use super::{ Stash as StorageStash, }; use crate::{ - storage2 as storage, storage2::{ - ClearForward, - KeyPtr, PullForward, - PushForward, SaturatingStorage, StorageFootprint, - StorageFootprintOf, }, }; use core::iter::{ Extend, FromIterator, }; -// use typenum::{ -// Add1, -// Unsigned, -// }; impl core::ops::Index for StorageStash where diff --git a/core/src/storage2/collections/stash/mod.rs b/core/src/storage2/collections/stash/mod.rs index 7f049f7194d..1229a350a39 100644 --- a/core/src/storage2/collections/stash/mod.rs +++ b/core/src/storage2/collections/stash/mod.rs @@ -14,6 +14,7 @@ mod iter; mod impls; +mod storage; #[cfg(test)] mod tests; @@ -25,9 +26,7 @@ pub use self::iter::{ use crate::storage2::{ LazyChunk, Pack, - PullAt, PullForward, - PushForward, StorageFootprint, }; use ink_primitives::Key; @@ -43,7 +42,8 @@ pub struct Stash { entries: LazyChunk>>, } -#[derive(Debug)] +/// Stores general commonly required information about the storage stash. +#[derive(Debug, scale::Encode, scale::Decode)] pub struct Header { /// The latest vacant index. next_vacant: Index, @@ -83,15 +83,6 @@ impl Entry { } } -impl PullAt for Entry -where - T: scale::Decode, -{ - fn pull_at(at: Key) -> Self { - crate::storage2::pull_single_cell(at) - } -} - impl Stash { /// Creates a new empty stash. pub fn new() -> Self { diff --git a/core/src/storage2/collections/stash/storage.rs b/core/src/storage2/collections/stash/storage.rs new file mode 100644 index 00000000000..c5bad25ece0 --- /dev/null +++ b/core/src/storage2/collections/stash/storage.rs @@ -0,0 +1,115 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{ + Entry, + Header, + Stash as StorageStash, +}; +use crate::{ + storage2 as storage, + storage2::{ + ClearForward, + KeyPtr, + Pack, + PullAt, + PullForward, + PushAt, + PushForward, + StorageFootprint, + StorageFootprintOf, + }, +}; +use core::ops::Add; +use ink_primitives::Key; +use typenum::{ + Add1, + Unsigned, +}; + +impl StorageFootprint for StorageStash +where + T: StorageFootprint, + storage::LazyChunk: StorageFootprint, + StorageFootprintOf>: Add, + Add1>>: Unsigned, +{ + type Value = Add1>>; +} + +impl PullForward for StorageStash +where + Entry: StorageFootprint + scale::Decode, + storage::LazyChunk>>: PullForward, +{ + fn pull_forward(ptr: &mut KeyPtr) -> Self { + Self { + header: PullForward::pull_forward(ptr), + entries: PullForward::pull_forward(ptr), + } + } +} + +impl PullAt for Header { + fn pull_at(at: Key) -> Self { + crate::storage2::pull_single_cell(at) + } +} + +impl PushAt for Header { + fn push_at(&self, at: Key) { + crate::env::set_contract_storage::(at, self) + } +} + +impl PullAt for Entry +where + T: scale::Decode, +{ + fn pull_at(at: Key) -> Self { + crate::storage2::pull_single_cell(at) + } +} + +impl PushForward for StorageStash +where + storage::LazyChunk>>: PushForward, +{ + fn push_forward(&self, ptr: &mut KeyPtr) { + PushForward::push_forward(&self.header, ptr); + PushForward::push_forward(&self.entries, ptr); + } +} + +impl ClearForward for StorageStash +where + T: StorageFootprint + ClearForward + PullForward + scale::Decode, +{ + fn clear_forward(&self, ptr: &mut KeyPtr) { + ClearForward::clear_forward(&self.len(), ptr); + if self.entries.key().is_none() { + return + } + for (index, elem) in self.iter().enumerate() { + ::clear_forward( + elem, + &mut KeyPtr::from( + self.entries + .key_at(&(index as u32)) + .expect("expected a key mapping since self.elems.key() is some"), + ), + ) + } + } +} From 6a683f871b423b112fb70e5427d612a43dd07d70 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 27 Mar 2020 10:07:48 +0100 Subject: [PATCH 138/142] [core] split trait implementation file for storage::Vec --- core/src/storage2/collections/vec/mod.rs | 12 +-- core/src/storage2/collections/vec/storage.rs | 86 ++++++++++++++++++++ core/src/storage2/collections/vec/traits.rs | 83 ++----------------- 3 files changed, 100 insertions(+), 81 deletions(-) create mode 100644 core/src/storage2/collections/vec/storage.rs diff --git a/core/src/storage2/collections/vec/mod.rs b/core/src/storage2/collections/vec/mod.rs index de0eccfa2f7..8006241f84b 100644 --- a/core/src/storage2/collections/vec/mod.rs +++ b/core/src/storage2/collections/vec/mod.rs @@ -14,6 +14,7 @@ mod iter; mod traits; +mod storage; #[cfg(test)] mod tests; @@ -23,8 +24,9 @@ pub use self::iter::{ IterMut, }; use crate::{ - storage2 as storage, storage2::{ + Lazy, + LazyChunk, PullForward, SaturatingStorage, StorageFootprint, @@ -48,9 +50,9 @@ use crate::{ #[derive(Debug)] pub struct Vec { /// The length of the vector. - len: storage::Lazy, + len: Lazy, /// The synchronized cells to operate on the contract storage. - elems: storage::LazyChunk, + elems: LazyChunk, } impl Default for Vec { @@ -63,8 +65,8 @@ impl Vec { /// Creates a new empty storage vector. pub fn new() -> Self { Self { - len: storage::Lazy::new(0), - elems: storage::LazyChunk::new(), + len: Lazy::new(0), + elems: LazyChunk::new(), } } diff --git a/core/src/storage2/collections/vec/storage.rs b/core/src/storage2/collections/vec/storage.rs new file mode 100644 index 00000000000..c8b2423f899 --- /dev/null +++ b/core/src/storage2/collections/vec/storage.rs @@ -0,0 +1,86 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::Vec as StorageVec; +use crate::{ + storage2 as storage, + storage2::{ + ClearForward, + KeyPtr, + PullForward, + PushForward, + StorageFootprint, + StorageFootprintOf, + }, +}; +use core::ops::Add; +use typenum::{ + Add1, + Unsigned, +}; + +impl StorageFootprint for StorageVec +where + T: StorageFootprint, + storage::LazyChunk: StorageFootprint, + StorageFootprintOf>: Add, + Add1>>: Unsigned, +{ + type Value = Add1>>; +} + +impl PullForward for StorageVec +where + T: StorageFootprint, + storage::LazyChunk: PullForward, +{ + fn pull_forward(ptr: &mut KeyPtr) -> Self { + Self { + len: PullForward::pull_forward(ptr), + elems: PullForward::pull_forward(ptr), + } + } +} + +impl PushForward for StorageVec +where + storage::LazyChunk: PushForward, +{ + fn push_forward(&self, ptr: &mut KeyPtr) { + PushForward::push_forward(&self.len(), ptr); + PushForward::push_forward(&self.elems, ptr); + } +} + +impl ClearForward for StorageVec +where + T: StorageFootprint + ClearForward + PullForward, +{ + fn clear_forward(&self, ptr: &mut KeyPtr) { + ClearForward::clear_forward(&self.len(), ptr); + if self.elems.key().is_none() { + return + } + for (index, elem) in self.iter().enumerate() { + ::clear_forward( + elem, + &mut KeyPtr::from( + self.elems + .key_at(&(index as u32)) + .expect("expected a key mapping since self.elems.key() is some"), + ), + ) + } + } +} diff --git a/core/src/storage2/collections/vec/traits.rs b/core/src/storage2/collections/vec/traits.rs index afd27e37645..f2580edd39d 100644 --- a/core/src/storage2/collections/vec/traits.rs +++ b/core/src/storage2/collections/vec/traits.rs @@ -17,28 +17,14 @@ use super::{ IterMut, Vec as StorageVec, }; -use crate::{ - storage2 as storage, - storage2::{ - ClearForward, - KeyPtr, - PullForward, - PushForward, - SaturatingStorage, - StorageFootprint, - StorageFootprintOf, - }, +use crate::storage2::{ + PullForward, + SaturatingStorage, + StorageFootprint, }; -use core::{ - iter::{ - Extend, - FromIterator, - }, - ops::Add, -}; -use typenum::{ - Add1, - Unsigned, +use core::iter::{ + Extend, + FromIterator, }; impl core::ops::Index for StorageVec @@ -113,61 +99,6 @@ where } } -impl StorageFootprint for StorageVec -where - T: StorageFootprint, - storage::LazyChunk: StorageFootprint, - StorageFootprintOf>: Add, - Add1>>: Unsigned, -{ - type Value = Add1>>; -} - -impl PullForward for StorageVec -where - T: StorageFootprint, - storage::LazyChunk: PullForward, -{ - fn pull_forward(ptr: &mut KeyPtr) -> Self { - Self { - len: PullForward::pull_forward(ptr), - elems: PullForward::pull_forward(ptr), - } - } -} - -impl PushForward for StorageVec -where - storage::LazyChunk: PushForward, -{ - fn push_forward(&self, ptr: &mut KeyPtr) { - PushForward::push_forward(&self.len(), ptr); - PushForward::push_forward(&self.elems, ptr); - } -} - -impl ClearForward for StorageVec -where - T: StorageFootprint + ClearForward + PullForward, -{ - fn clear_forward(&self, ptr: &mut KeyPtr) { - ClearForward::clear_forward(&self.len(), ptr); - if self.elems.key().is_none() { - return - } - for (index, elem) in self.iter().enumerate() { - ::clear_forward( - elem, - &mut KeyPtr::from( - self.elems - .key_at(&(index as u32)) - .expect("expected a key mapping since self.elems.key() is some"), - ), - ) - } - } -} - impl core::cmp::PartialEq for StorageVec where T: PartialEq + StorageFootprint + PullForward, From 8af4486c53a41bee36968c2b1c22806a46754493 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 27 Mar 2020 10:12:39 +0100 Subject: [PATCH 139/142] [core] split trait impls for storage2::SmallVec --- .../smallvec/{traits.rs => impls.rs} | 52 +-------------- core/src/storage2/collections/smallvec/mod.rs | 9 +-- .../storage2/collections/smallvec/storage.rs | 64 +++++++++++++++++++ 3 files changed, 72 insertions(+), 53 deletions(-) rename core/src/storage2/collections/smallvec/{traits.rs => impls.rs} (70%) create mode 100644 core/src/storage2/collections/smallvec/storage.rs diff --git a/core/src/storage2/collections/smallvec/traits.rs b/core/src/storage2/collections/smallvec/impls.rs similarity index 70% rename from core/src/storage2/collections/smallvec/traits.rs rename to core/src/storage2/collections/smallvec/impls.rs index 8ffe64def8d..755f8aca16f 100644 --- a/core/src/storage2/collections/smallvec/traits.rs +++ b/core/src/storage2/collections/smallvec/impls.rs @@ -17,25 +17,14 @@ use super::{ SmallVec, }; use crate::storage2::{ - KeyPtr, - LazyArray, LazyArrayLength, PullForward, - PushForward, SaturatingStorage, StorageFootprint, - StorageFootprintOf, }; -use core::{ - iter::{ - Extend, - FromIterator, - }, - ops::Add, -}; -use typenum::{ - Add1, - Unsigned, +use core::iter::{ + Extend, + FromIterator, }; impl core::ops::Index for SmallVec @@ -103,41 +92,6 @@ where } } -impl StorageFootprint for SmallVec -where - T: StorageFootprint + PullForward, - N: LazyArrayLength, - LazyArray: StorageFootprint, - StorageFootprintOf>: Add, - Add1>>: Unsigned, -{ - type Value = Add1>>; -} - -impl PullForward for SmallVec -where - N: LazyArrayLength, - LazyArray: PullForward, -{ - fn pull_forward(ptr: &mut KeyPtr) -> Self { - Self { - len: PullForward::pull_forward(ptr), - elems: PullForward::pull_forward(ptr), - } - } -} - -impl PushForward for SmallVec -where - LazyArray: PushForward, - N: LazyArrayLength, -{ - fn push_forward(&self, ptr: &mut KeyPtr) { - PushForward::push_forward(&self.len(), ptr); - PushForward::push_forward(&self.elems, ptr); - } -} - impl core::cmp::PartialEq for SmallVec where T: PartialEq + StorageFootprint + PullForward, diff --git a/core/src/storage2/collections/smallvec/mod.rs b/core/src/storage2/collections/smallvec/mod.rs index 69883356c8a..6f8cd287b1c 100644 --- a/core/src/storage2/collections/smallvec/mod.rs +++ b/core/src/storage2/collections/smallvec/mod.rs @@ -13,7 +13,8 @@ // limitations under the License. mod iter; -mod traits; +mod storage; +mod impls; #[cfg(test)] mod tests; @@ -23,8 +24,8 @@ pub use self::iter::{ IterMut, }; use crate::{ - storage2 as storage, storage2::{ + Lazy, LazyArray, LazyArrayLength, PullForward, @@ -54,7 +55,7 @@ where N: LazyArrayLength, { /// The current length of the small vector. - len: storage::Lazy, + len: Lazy, /// The entries of the small vector. elems: LazyArray, } @@ -75,7 +76,7 @@ where /// Creates a new empty vector. pub fn new() -> Self { Self { - len: storage::Lazy::new(0), + len: Lazy::new(0), elems: Default::default(), } } diff --git a/core/src/storage2/collections/smallvec/storage.rs b/core/src/storage2/collections/smallvec/storage.rs new file mode 100644 index 00000000000..3bd1c135d82 --- /dev/null +++ b/core/src/storage2/collections/smallvec/storage.rs @@ -0,0 +1,64 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::SmallVec; +use crate::storage2::{ + KeyPtr, + LazyArray, + LazyArrayLength, + PullForward, + PushForward, + StorageFootprint, + StorageFootprintOf, +}; +use core::ops::Add; +use typenum::{ + Add1, + Unsigned, +}; + +impl StorageFootprint for SmallVec +where + T: StorageFootprint + PullForward, + N: LazyArrayLength, + LazyArray: StorageFootprint, + StorageFootprintOf>: Add, + Add1>>: Unsigned, +{ + type Value = Add1>>; +} + +impl PullForward for SmallVec +where + N: LazyArrayLength, + LazyArray: PullForward, +{ + fn pull_forward(ptr: &mut KeyPtr) -> Self { + Self { + len: PullForward::pull_forward(ptr), + elems: PullForward::pull_forward(ptr), + } + } +} + +impl PushForward for SmallVec +where + LazyArray: PushForward, + N: LazyArrayLength, +{ + fn push_forward(&self, ptr: &mut KeyPtr) { + PushForward::push_forward(&self.len(), ptr); + PushForward::push_forward(&self.elems, ptr); + } +} From 35f1ee9ac71cfabe01651655a1a1a2679dcc0546 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 27 Mar 2020 10:12:55 +0100 Subject: [PATCH 140/142] [core] add doc comments to new trait impl files --- core/src/storage2/collections/stash/impls.rs | 2 ++ core/src/storage2/collections/stash/storage.rs | 2 ++ core/src/storage2/collections/vec/storage.rs | 2 ++ core/src/storage2/collections/vec/traits.rs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/core/src/storage2/collections/stash/impls.rs b/core/src/storage2/collections/stash/impls.rs index aeebd2b89da..4e390850c69 100644 --- a/core/src/storage2/collections/stash/impls.rs +++ b/core/src/storage2/collections/stash/impls.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Implementation of generic traits that are useful for the storage stash. + use super::{ Iter, IterMut, diff --git a/core/src/storage2/collections/stash/storage.rs b/core/src/storage2/collections/stash/storage.rs index c5bad25ece0..8ab9f9463b0 100644 --- a/core/src/storage2/collections/stash/storage.rs +++ b/core/src/storage2/collections/stash/storage.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Implementation of ink! storage traits. + use super::{ Entry, Header, diff --git a/core/src/storage2/collections/vec/storage.rs b/core/src/storage2/collections/vec/storage.rs index c8b2423f899..d32d4f62079 100644 --- a/core/src/storage2/collections/vec/storage.rs +++ b/core/src/storage2/collections/vec/storage.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Implementation of ink! storage traits. + use super::Vec as StorageVec; use crate::{ storage2 as storage, diff --git a/core/src/storage2/collections/vec/traits.rs b/core/src/storage2/collections/vec/traits.rs index f2580edd39d..f644684db63 100644 --- a/core/src/storage2/collections/vec/traits.rs +++ b/core/src/storage2/collections/vec/traits.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Implementation of generic traits that are useful for the storage vector. + use super::{ Iter, IterMut, From 8a7a62b7d5ac151fcaa0661904d2786acda03893 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 27 Mar 2020 13:19:04 +0100 Subject: [PATCH 141/142] [core] rename storage2::Stash::max_len -> len_entries --- core/src/storage2/collections/stash/mod.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/core/src/storage2/collections/stash/mod.rs b/core/src/storage2/collections/stash/mod.rs index 1229a350a39..5703aab6885 100644 --- a/core/src/storage2/collections/stash/mod.rs +++ b/core/src/storage2/collections/stash/mod.rs @@ -54,8 +54,8 @@ pub struct Header { /// We cannot simply use the underlying length of the vector /// since it would include vacant slots as well. len: u32, - /// The maximum length the stash ever had. - max_len: u32, + /// The number of entries currently managed by the stash. + len_entries: u32, } /// An entry within the stash. @@ -90,7 +90,7 @@ impl Stash { header: Pack::new(Header { next_vacant: 0, len: 0, - max_len: 0, + len_entries: 0, }), entries: LazyChunk::new(), } @@ -106,10 +106,9 @@ impl Stash { self.len() == 0 } - /// Returns the maximum number of element stored in the - /// stash at the same time. - pub fn max_len(&self) -> u32 { - self.header.max_len + /// Returns the number of entries currently managed by the storage stash. + fn len_entries(&self) -> u32 { + self.header.len_entries } /// Returns the next vacant index. @@ -190,7 +189,7 @@ where // Push the new element to the end if all entries are occupied. self.entries.put(current_vacant, new_entry); self.header.next_vacant = current_vacant + 1; - self.header.max_len += 1; + self.header.len_entries += 1; } else { // Put the new element to the most recent vacant index if not all entries are occupied. let old_entry = self From ce51d79d927f2d0f75ff4d6a60cd7ffbeb7d5825 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 29 Mar 2020 11:16:41 +0200 Subject: [PATCH 142/142] [core] make storage2::Stash vacant entries doubly linked This has the advantage that the defragmentation can be programmed linearly and that the stash can more easily propagate refilling entries with low indices. --- core/src/storage2/collections/stash/mod.rs | 301 +++++++++++++++++---- 1 file changed, 255 insertions(+), 46 deletions(-) diff --git a/core/src/storage2/collections/stash/mod.rs b/core/src/storage2/collections/stash/mod.rs index 5703aab6885..c423583c602 100644 --- a/core/src/storage2/collections/stash/mod.rs +++ b/core/src/storage2/collections/stash/mod.rs @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod iter; mod impls; +mod iter; mod storage; #[cfg(test)] @@ -46,7 +46,12 @@ pub struct Stash { #[derive(Debug, scale::Encode, scale::Decode)] pub struct Header { /// The latest vacant index. - next_vacant: Index, + /// + /// - If all entries are occupied: + /// - Points to the entry at index `self.len`. + /// - If some entries are vacant: + /// - Points to the entry that has been vacated most recently. + last_vacant: Index, /// The number of items stored in the stash. /// /// # Note @@ -58,11 +63,23 @@ pub struct Header { len_entries: u32, } +/// A vacant entry with previous and next vacant indices. +#[derive(Debug, Copy, Clone, scale::Encode, scale::Decode)] +pub struct VacantEntry { + /// The next vacant index. + next: Index, + /// The previous vacant index. + prev: Index, +} + /// An entry within the stash. +/// +/// The vacant entries within a storage stash form a doubly linked list of +/// vacant entries that is used to quickly re-use their vacant storage. #[derive(Debug, scale::Encode, scale::Decode)] pub enum Entry { - /// A vacant entry that holds the index to the next vacant entry. - Vacant(Index), + /// A vacant entry that holds the index to the next and previous vacant entry. + Vacant(VacantEntry), /// An occupied entry that hold the value. Occupied(T), } @@ -81,6 +98,22 @@ impl Entry { pub fn is_vacant(&self) -> bool { !self.is_occupied() } + + /// Returns the vacant entry if the entry is vacant, otherwise returns `None`. + fn try_to_vacant(&self) -> Option { + match self { + Entry::Occupied(_) => None, + Entry::Vacant(vacant_entry) => Some(*vacant_entry), + } + } + + /// Returns the vacant entry if the entry is vacant, otherwise returns `None`. + fn try_to_vacant_mut(&mut self) -> Option<&mut VacantEntry> { + match self { + Entry::Occupied(_) => None, + Entry::Vacant(vacant_entry) => Some(vacant_entry), + } + } } impl Stash { @@ -88,7 +121,7 @@ impl Stash { pub fn new() -> Self { Self { header: Pack::new(Header { - next_vacant: 0, + last_vacant: 0, len: 0, len_entries: 0, }), @@ -111,11 +144,6 @@ impl Stash { self.header.len_entries } - /// Returns the next vacant index. - fn next_vacant(&self) -> u32 { - self.header.next_vacant - } - /// Returns the underlying key to the cells. /// /// # Note @@ -147,6 +175,20 @@ impl Stash { pub fn iter_mut(&mut self) -> IterMut { IterMut::new(self) } + + /// Returns `true` if the storage stash has vacant entries. + fn has_vacant_entries(&self) -> bool { + self.header.last_vacant != self.header.len_entries + } + + /// Returns the index of the last vacant entry if any. + fn last_vacant_index(&self) -> Option { + if self.has_vacant_entries() { + Some(self.header.last_vacant) + } else { + None + } + } } impl Stash @@ -158,7 +200,7 @@ where self.entries.get(at).and_then(|entry| { match Pack::as_inner(entry) { Entry::Occupied(val) => Some(val), - Entry::Vacant(_) => None, + Entry::Vacant { .. } => None, } }) } @@ -168,7 +210,7 @@ where self.entries.get_mut(at).and_then(|entry| { match Pack::as_inner_mut(entry) { Entry::Occupied(val) => Some(val), - Entry::Vacant(_) => None, + Entry::Vacant { .. } => None, } }) } @@ -178,60 +220,227 @@ impl Stash where T: scale::Codec + StorageFootprint + PullForward, { + /// Rebinds the `prev` and `next` bindings of the neighbours of the vacant entry. + /// + /// # Note + /// + /// The `removed_index` points to the index of the removed vacant entry. + fn remove_vacant_entry(&mut self, removed_index: Index, vacant_entry: VacantEntry) { + let prev_vacant = vacant_entry.prev; + let next_vacant = vacant_entry.next; + if prev_vacant == removed_index && next_vacant == removed_index { + // There is no other vacant entry left in the storage stash so + // there is nothing to update. Bail out early. + return + } + if prev_vacant == next_vacant { + // There is only one other vacant entry left. + // We can update the single vacant entry in a single look-up. + self.entries + .get_mut(prev_vacant) + .map(Pack::as_inner_mut) + .map(Entry::try_to_vacant_mut) + .expect("`prev` must point to an existing entry at this point") + .map(|entry| { + entry.prev = prev_vacant; + entry.next = prev_vacant; + }); + } else { + // There are multiple other vacant entries left. + self.entries + .get_mut(prev_vacant) + .map(Pack::as_inner_mut) + .map(Entry::try_to_vacant_mut) + .expect("`prev` must point to an existing entry at this point") + .map(|entry| { + entry.next = next_vacant; + }); + self.entries + .get_mut(next_vacant) + .map(Pack::as_inner_mut) + .map(Entry::try_to_vacant_mut) + .expect("`next` must point to an existing entry at this point") + .map(|entry| { + entry.prev = prev_vacant; + }); + } + // Bind the last vacant pointer to the vacant position with the lower index. + // This has the effect that lower indices are refilled more quickly. + self.header.last_vacant = core::cmp::min(prev_vacant, next_vacant); + } + /// Put the element into the stash at the next vacant position. /// /// Returns the stash index that the element was put into. pub fn put(&mut self, new_value: T) -> Index { - let current_vacant = self.header.next_vacant; - debug_assert!(current_vacant <= self.len()); let new_entry = Some(Pack::new(Entry::Occupied(new_value))); - if current_vacant == self.len() { - // Push the new element to the end if all entries are occupied. - self.entries.put(current_vacant, new_entry); - self.header.next_vacant = current_vacant + 1; - self.header.len_entries += 1; - } else { + let new_index = if let Some(index) = self.last_vacant_index() { // Put the new element to the most recent vacant index if not all entries are occupied. let old_entry = self .entries - .put_get(current_vacant, new_entry) + .put_get(index, new_entry) .expect("a `next_vacant` index must point to an occupied cell"); - let next_vacant = match Pack::into_inner(old_entry) { - Entry::Vacant(next_vacant) => next_vacant, + let vacant_entry = match Pack::into_inner(old_entry) { + Entry::Vacant(vacant_entry) => vacant_entry, Entry::Occupied(_) => { - unreachable!("a `next_vacant` index must point to a vacant entry") + unreachable!("next_vacant must point to a vacant entry") } }; - self.header.next_vacant = next_vacant; - } + self.remove_vacant_entry(index, vacant_entry); + index + } else { + // Push the new element to the end if all entries are occupied. + self.entries.put(self.header.len_entries, new_entry); + self.header.last_vacant += 1; + self.header.len_entries += 1; + self.header.len_entries + }; self.header.len += 1; - current_vacant + new_index } /// Takes the element stored at the given index if any. pub fn take(&mut self, at: Index) -> Option { - let next_vacant_index = self.next_vacant(); - match self.entries.get_mut(at) { - None => None, - Some(packed) => { - let entry_mut = Pack::as_inner_mut(packed); - if entry_mut.is_vacant() { - // Bail out of the taken entry is already vacant. - return None + // Cases: + // - There are vacant entries already. + // - There are no vacant entries before. + if at >= self.len() { + // Early return since `at` index is out of bounds. + return None + } + // Precompute prev and next vacant entires as we might need them later. + // Due to borrow checker constraints we cannot have this at a later stage. + let (prev, next) = if let Some(index) = self.last_vacant_index() { + self.entries + .get(index) + .expect("last_vacant must point to an existing entry") + .try_to_vacant() + .map(|vacant_entry| (vacant_entry.prev, vacant_entry.next)) + .expect("last_vacant must point to a vacant entry") + } else { + // Default prev and next to the given at index. + // So the resulting vacant index is pointing to itself. + (at, at) + }; + let entry_mut = + Pack::as_inner_mut(self.entries.get_mut(at).expect("index is within bounds")); + if entry_mut.is_vacant() { + // Early return if the taken entry is already vacant. + return None + } + // At this point we know that the entry is occupied with a value. + let new_vacant_entry = Entry::Vacant(VacantEntry { prev, next }); + let taken_entry = core::mem::replace(entry_mut, new_vacant_entry); + // Update links from and to neighbouring vacant entries. + if prev == next { + // Previous and next are the same so we can update the vacant + // neighbour with a single look-up. + self.entries + .get_mut(next) + .map(Pack::as_inner_mut) + .map(Entry::try_to_vacant_mut) + .expect("`next` must point to an existing entry at this point") + .map(|entry| { + entry.prev = at; + entry.next = at; + }); + } else { + // Previous and next vacant entries are different and thus need + // different look-ups to update them. + self.entries + .get_mut(prev) + .map(Pack::as_inner_mut) + .map(Entry::try_to_vacant_mut) + .expect("`prev` must point to an existing entry at this point") + .map(|entry| { + entry.next = at; + }); + self.entries + .get_mut(next) + .map(Pack::as_inner_mut) + .map(Entry::try_to_vacant_mut) + .expect("`next` must point to an existing entry at this point") + .map(|entry| { + entry.prev = at; + }); + } + // Take the value out of the taken occupied entry and return it. + match taken_entry { + Entry::Occupied(value) => { + use core::cmp::min; + self.header.last_vacant = min(at, min(prev, next)); + self.header.len -= 1; + Some(value) + } + Entry::Vacant { .. } => { + unreachable!("the taken entry is known to be occupied") + } + } + } + + /// Defragments the underlying storage to minimize footprint. + /// + /// This might invalidate indices stored outside of the stash. + /// + /// # Callback + /// + /// In order to keep those indices up-to-date the caller can provide + /// a callback function that is called for every moved entry + /// with a shared reference to the entries value and the old as well + /// as the new index. + /// + /// # Note + /// + /// - If `max_iterations` is `Some` concrete value it is used in order to + /// bound the number of iterations and won't try to defrag until the stash + /// is optimally compacted. + /// - Users are adviced to call this method using `Some` concrete + /// value to keep gas costs within certain bounds. + /// - The call to the given callback takes place before the reinsertion + /// of the shifted occupied entry. + pub fn defrag(&mut self, max_iterations: Option, mut callback: C) + where + C: FnMut(Index, Index, &T), + { + let len_entries = self.len_entries(); + for index in (0..len_entries) + .rev() + .take(max_iterations.unwrap_or(len_entries) as usize) + { + if !self.has_vacant_entries() { + // Bail out as soon as there are no more vacant entries left. + return + } + match Pack::into_inner( + self.entries.take(index).expect("index is within bounds"), + ) { + Entry::Vacant(vacant_entry) => { + // Remove the vacant entry and rebind its neighbours. + self.remove_vacant_entry(index, vacant_entry); } - // At this point we know that the entry is occupied with a value. - let new_vacant_entry = Entry::Vacant(next_vacant_index); - let taken_entry = core::mem::replace(entry_mut, new_vacant_entry); - match taken_entry { - Entry::Occupied(value) => { - self.header.next_vacant = at; - self.header.len -= 1; - Some(value) - } - Entry::Vacant(_) => { - unreachable!("the entry must be occupied at this point") + Entry::Occupied(value) => { + // Move the occupied entry into one of the remaining vacant + // entries. We do not re-use the `put` method to not update + // the length and other header information. + let vacant_index = self + .last_vacant_index() + .expect("it has been asserted that there are vacant entries"); + callback(index, vacant_index, &value); + let new_entry = Some(Pack::new(Entry::Occupied(value))); + let old_entry = self + .entries + .put_get(vacant_index, new_entry) + .expect("a `next_vacant` index must point to an occupied cell"); + let vacant_entry = match Pack::into_inner(old_entry) { + Entry::Vacant(vacant_entry) => vacant_entry, + Entry::Occupied(_) => { + unreachable!("next_vacant must point to a vacant entry") + } + }; + self.remove_vacant_entry(index, vacant_entry); } } + self.header.len_entries -= 1; } } }