From fafa185b3dc2471b9d9ef0e9ff576b0a35c57bf5 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 23 Sep 2022 16:51:24 +0200 Subject: [PATCH 01/17] Implement basic queue --- packages/storage-plus/src/lib.rs | 4 + packages/storage-plus/src/queue.rs | 306 +++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 packages/storage-plus/src/queue.rs diff --git a/packages/storage-plus/src/lib.rs b/packages/storage-plus/src/lib.rs index 03b2b52d1..67b2e9e0b 100644 --- a/packages/storage-plus/src/lib.rs +++ b/packages/storage-plus/src/lib.rs @@ -12,6 +12,7 @@ mod keys; mod map; mod path; mod prefix; +mod queue; mod snapshot; #[cfg(feature = "iterator")] @@ -35,6 +36,9 @@ pub use map::Map; pub use path::Path; #[cfg(feature = "iterator")] pub use prefix::{range_with_prefix, Prefix}; +pub use queue::Queue; +#[cfg(feature = "iterator")] +pub use queue::QueueIter; #[cfg(feature = "iterator")] pub use snapshot::{SnapshotItem, SnapshotMap, Strategy}; diff --git a/packages/storage-plus/src/queue.rs b/packages/storage-plus/src/queue.rs new file mode 100644 index 000000000..d55c0695a --- /dev/null +++ b/packages/storage-plus/src/queue.rs @@ -0,0 +1,306 @@ +use std::{convert::TryInto, marker::PhantomData}; + +use cosmwasm_std::{to_vec, StdError, StdResult, Storage}; +use serde::{de::DeserializeOwned, Serialize}; + +use crate::helpers::{may_deserialize, namespaces_with_key}; + +// metadata keys need to have different length than the position type (4 bytes) to prevent collisions +const TAIL_KEY: &[u8] = b"t"; +const HEAD_KEY: &[u8] = b"h"; + +/// A queue stores multiple items at the given key. It provides efficient FIFO access. +pub struct Queue<'a, T> { + // prefix of the queue items + namespace: &'a [u8], + // see https://doc.rust-lang.org/std/marker/struct.PhantomData.html#unused-type-parameters for why this is needed + item_type: PhantomData, +} + +impl<'a, T> Queue<'a, T> { + pub const fn new(prefix: &'a str) -> Self { + Self { + namespace: prefix.as_bytes(), + item_type: PhantomData, + } + } +} + +impl<'a, T: Serialize + DeserializeOwned> Queue<'a, T> { + /// Adds the given value to the end of the queue + pub fn push(&self, storage: &mut dyn Storage, value: &T) -> StdResult<()> { + // save value + let pos = self.tail(storage)?; + self.set_at(storage, pos, value)?; + // update tail + self.set_tail(storage, pos.wrapping_add(1)); + + Ok(()) + } + + /// Removes the first item of the queue and returns it + pub fn pop(&self, storage: &mut dyn Storage) -> StdResult> { + // get position + let pos = self.head(storage)?; + let value = self.get_at(storage, pos)?; + if value.is_some() { + self.remove_at(storage, pos); + // only update head if a value was popped + self.set_head(storage, pos.wrapping_add(1)); + } + Ok(value) + } + + /// Gets the length of the queue. + pub fn len(&self, storage: &dyn Storage) -> StdResult { + Ok(self.tail(storage)?.wrapping_sub(self.head(storage)?)) + } + + /// Returns `true` if the queue contains no elements. + pub fn is_empty(&self, storage: &dyn Storage) -> StdResult { + Ok(self.len(storage)? == 0) + } + + /// Gets the head position from storage. + /// + /// Points to the front of the queue (where elements are popped). + fn head(&self, storage: &dyn Storage) -> StdResult { + self.read_meta_key(storage, HEAD_KEY) + } + + /// Gets the tail position from storage. + /// + /// Points to the end of the queue (where elements are pushed). + fn tail(&self, storage: &dyn Storage) -> StdResult { + self.read_meta_key(storage, TAIL_KEY) + } + + fn set_head(&self, storage: &mut dyn Storage, value: u32) { + self.set_meta_key(storage, HEAD_KEY, value); + } + + fn set_tail(&self, storage: &mut dyn Storage, value: u32) { + self.set_meta_key(storage, TAIL_KEY, value); + } + + /// Helper method for `tail` and `head` methods to handle reading the value from storage + fn read_meta_key(&self, storage: &dyn Storage, key: &[u8]) -> StdResult { + let full_key = namespaces_with_key(&[self.namespace], key); + storage + .get(&full_key) + .map(|vec| { + Ok(u32::from_be_bytes( + vec.as_slice() + .try_into() + .map_err(|e| StdError::parse_err("u32", e))?, + )) + }) + .unwrap_or(Ok(0)) + } + + /// Helper method for `set_tail` and `set_head` methods to write to storage + #[inline] + fn set_meta_key(&self, storage: &mut dyn Storage, key: &[u8], value: u32) { + let full_key = namespaces_with_key(&[self.namespace], key); + storage.set(&full_key, &value.to_be_bytes()); + } + + /// Tries to get the value at the given position (without bounds checking) + /// Used internally when popping + fn get_at(&self, storage: &dyn Storage, pos: u32) -> StdResult> { + let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes()); + may_deserialize(&storage.get(&prefixed_key)) + } + + /// Removes the value at the given position + /// Used internally when popping + fn remove_at(&self, storage: &mut dyn Storage, pos: u32) { + let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes()); + storage.remove(&prefixed_key); + } + + /// Tries to set the value at the given position (without bounds checking) + /// Used internally when pushing + fn set_at(&self, storage: &mut dyn Storage, pos: u32, value: &T) -> StdResult<()> { + let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes()); + storage.set(&prefixed_key, &to_vec(value)?); + + Ok(()) + } +} + +#[cfg(feature = "iterator")] +impl<'a, T: Serialize + DeserializeOwned> Queue<'a, T> { + pub fn iter(&self, storage: &'a dyn Storage) -> StdResult> { + Ok(QueueIter { + queue: self, + storage, + start: self.head(storage)?, + end: self.tail(storage)?, + }) + } +} + +#[cfg(feature = "iterator")] +pub struct QueueIter<'a, T> +where + T: Serialize + DeserializeOwned, +{ + queue: &'a Queue<'a, T>, + storage: &'a dyn Storage, + start: u32, + end: u32, +} + +impl<'a, T> Iterator for QueueIter<'a, T> +where + T: Serialize + DeserializeOwned, +{ + type Item = StdResult; + + fn next(&mut self) -> Option { + if self.start == self.end { + return None; + } + + let item = self.queue.get_at(self.storage, self.start).transpose()?; + self.start = self.start.wrapping_add(1); + + Some(item) + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.end.wrapping_sub(self.start) as usize; + (len, Some(len)) + } + + /// The default implementation calls `next` repeatedly, which is very costly in our case. + /// It is used when skipping over items, so this allows cheap skipping. + /// + /// Once `advance_by` is stabilized, we can implement that instead (`nth` calls it internally). + fn nth(&mut self, n: usize) -> Option { + let start_lt_end = self.start < self.end; + self.start = self.start.wrapping_add(n as u32); + // make sure that we didn't skip past the end + if self.start > self.end && start_lt_end || self.start < self.end && !start_lt_end { + // start and end switched places, which means the iterator is empty now + self.start = self.end; + } + self.next() + } +} + +#[cfg(test)] +mod tests { + use crate::queue::Queue; + + use cosmwasm_std::testing::MockStorage; + use cosmwasm_std::StdResult; + + #[test] + fn push_and_pop() { + const PEOPLE: Queue = Queue::new("people"); + let mut store = MockStorage::new(); + + // push some entries + PEOPLE.push(&mut store, &"jack".to_owned()).unwrap(); + PEOPLE.push(&mut store, &"john".to_owned()).unwrap(); + PEOPLE.push(&mut store, &"joanne".to_owned()).unwrap(); + + // pop them, should be in correct order + assert_eq!("jack", PEOPLE.pop(&mut store).unwrap().unwrap()); + assert_eq!("john", PEOPLE.pop(&mut store).unwrap().unwrap()); + + // push again in-between + PEOPLE.push(&mut store, &"jason".to_owned()).unwrap(); + + // pop last person from first batch + assert_eq!("joanne", PEOPLE.pop(&mut store).unwrap().unwrap()); + + // pop the entry pushed in-between + assert_eq!("jason", PEOPLE.pop(&mut store).unwrap().unwrap()); + + // nothing after that + assert_eq!(None, PEOPLE.pop(&mut store).unwrap()); + } + + #[test] + fn length() { + let queue: Queue = Queue::new("test"); + let mut store = MockStorage::new(); + + assert_eq!(queue.len(&store).unwrap(), 0); + + // push some entries + queue.push(&mut store, &1234).unwrap(); + queue.push(&mut store, &2345).unwrap(); + queue.push(&mut store, &3456).unwrap(); + queue.push(&mut store, &4567).unwrap(); + assert_eq!(queue.len(&store).unwrap(), 4); + + // pop some + queue.pop(&mut store).unwrap(); + queue.pop(&mut store).unwrap(); + queue.pop(&mut store).unwrap(); + assert_eq!(queue.len(&store).unwrap(), 1); + + // pop the last one + queue.pop(&mut store).unwrap(); + assert_eq!(queue.len(&store).unwrap(), 0); + + // should stay 0 after that + queue.pop(&mut store).unwrap(); + assert_eq!( + queue.len(&store).unwrap(), + 0, + "popping from empty queue should keep length 0" + ); + } + + #[test] + fn iterator() { + let queue: Queue = Queue::new("test"); + let mut store = MockStorage::new(); + + // push some items + queue.push(&mut store, &1).unwrap(); + queue.push(&mut store, &2).unwrap(); + queue.push(&mut store, &3).unwrap(); + queue.push(&mut store, &4).unwrap(); + + let items: StdResult> = queue.iter(&mut store).unwrap().collect(); + assert_eq!(items.unwrap(), [1, 2, 3, 4]); + + let mut iter = queue.iter(&mut store).unwrap(); + assert_eq!(iter.nth(6), None); + assert_eq!(iter.start, iter.end, "iter should detect skipping too far"); + assert_eq!(iter.next(), None); + } + + #[test] + fn wrapping() { + let queue: Queue = Queue::new("test"); + let mut store = MockStorage::new(); + + // simulate queue that was pushed and popped `u32::MAX` times + queue.set_head(&mut store, u32::MAX); + queue.set_tail(&mut store, u32::MAX); + + // should be empty + assert_eq!(queue.pop(&mut store).unwrap(), None); + assert_eq!(queue.len(&store).unwrap(), 0); + + // pushing should still work + queue.push(&mut store, &1).unwrap(); + assert_eq!( + queue.len(&store).unwrap(), + 1, + "length should calculate correctly, even when wrapping" + ); + assert_eq!( + queue.pop(&mut store).unwrap(), + Some(1), + "popping should work, even when wrapping" + ); + } +} From b43413844dfca80ef4c7c7e6a5b1c1da8ad28af1 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 26 Sep 2022 11:50:36 +0200 Subject: [PATCH 02/17] Add double ended iterator for queue --- packages/storage-plus/src/queue.rs | 48 ++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/packages/storage-plus/src/queue.rs b/packages/storage-plus/src/queue.rs index d55c0695a..5cf3dd14a 100644 --- a/packages/storage-plus/src/queue.rs +++ b/packages/storage-plus/src/queue.rs @@ -174,22 +174,52 @@ where (len, Some(len)) } - /// The default implementation calls `next` repeatedly, which is very costly in our case. - /// It is used when skipping over items, so this allows cheap skipping. - /// - /// Once `advance_by` is stabilized, we can implement that instead (`nth` calls it internally). + // The default implementation calls `next` repeatedly, which is very costly in our case. + // It is used when skipping over items, so this allows cheap skipping. + // + // Once `advance_by` is stabilized, we can implement that instead (`nth` calls it internally). fn nth(&mut self, n: usize) -> Option { - let start_lt_end = self.start < self.end; - self.start = self.start.wrapping_add(n as u32); - // make sure that we didn't skip past the end - if self.start > self.end && start_lt_end || self.start < self.end && !start_lt_end { - // start and end switched places, which means the iterator is empty now + // make sure that we don't skip past the end + if self.end.wrapping_sub(self.start) < n as u32 { + // mark as empty self.start = self.end; + } else { + self.start = self.start.wrapping_add(n as u32); } self.next() } } +impl<'a, T> DoubleEndedIterator for QueueIter<'a, T> +where + T: Serialize + DeserializeOwned, +{ + fn next_back(&mut self) -> Option { + if self.start == self.end { + return None; + } + + let item = self + .queue + .get_at(self.storage, self.end.wrapping_sub(1)) // end points to position after last element + .transpose()?; + self.end = self.end.wrapping_sub(1); + + Some(item) + } + + // see [`QueueIter::nth`] + fn nth_back(&mut self, n: usize) -> Option { + // make sure that we don't skip past the start + if self.end.wrapping_sub(self.start) < n as u32 { + // mark as empty + self.end = self.start; + } else { + self.end = self.end.wrapping_sub(n as u32); + } + self.next_back() + } +} #[cfg(test)] mod tests { use crate::queue::Queue; From 8bc0e959983188b48384b32e170d56acf833b9da Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 26 Sep 2022 11:52:00 +0200 Subject: [PATCH 03/17] Add more queue tests --- packages/storage-plus/src/queue.rs | 71 ++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/packages/storage-plus/src/queue.rs b/packages/storage-plus/src/queue.rs index 5cf3dd14a..92fe5e4b0 100644 --- a/packages/storage-plus/src/queue.rs +++ b/packages/storage-plus/src/queue.rs @@ -38,7 +38,7 @@ impl<'a, T: Serialize + DeserializeOwned> Queue<'a, T> { Ok(()) } - /// Removes the first item of the queue and returns it + /// Removes the first element of the queue and returns it pub fn pop(&self, storage: &mut dyn Storage) -> StdResult> { // get position let pos = self.head(storage)?; @@ -63,22 +63,26 @@ impl<'a, T: Serialize + DeserializeOwned> Queue<'a, T> { /// Gets the head position from storage. /// - /// Points to the front of the queue (where elements are popped). + /// Unless the queue is empty, this points to the first element. + #[inline] fn head(&self, storage: &dyn Storage) -> StdResult { self.read_meta_key(storage, HEAD_KEY) } /// Gets the tail position from storage. /// - /// Points to the end of the queue (where elements are pushed). + /// This points to the first empty position after the last element. + #[inline] fn tail(&self, storage: &dyn Storage) -> StdResult { self.read_meta_key(storage, TAIL_KEY) } + #[inline] fn set_head(&self, storage: &mut dyn Storage, value: u32) { self.set_meta_key(storage, HEAD_KEY, value); } + #[inline] fn set_tail(&self, storage: &mut dyn Storage, value: u32) { self.set_meta_key(storage, TAIL_KEY, value); } @@ -260,6 +264,7 @@ mod tests { let mut store = MockStorage::new(); assert_eq!(queue.len(&store).unwrap(), 0); + assert_eq!(queue.is_empty(&store).unwrap(), true); // push some entries queue.push(&mut store, &1234).unwrap(); @@ -267,16 +272,19 @@ mod tests { queue.push(&mut store, &3456).unwrap(); queue.push(&mut store, &4567).unwrap(); assert_eq!(queue.len(&store).unwrap(), 4); + assert_eq!(queue.is_empty(&store).unwrap(), false); // pop some queue.pop(&mut store).unwrap(); queue.pop(&mut store).unwrap(); queue.pop(&mut store).unwrap(); assert_eq!(queue.len(&store).unwrap(), 1); + assert_eq!(queue.is_empty(&store).unwrap(), false); // pop the last one queue.pop(&mut store).unwrap(); assert_eq!(queue.len(&store).unwrap(), 0); + assert_eq!(queue.is_empty(&store).unwrap(), true); // should stay 0 after that queue.pop(&mut store).unwrap(); @@ -285,6 +293,7 @@ mod tests { 0, "popping from empty queue should keep length 0" ); + assert_eq!(queue.is_empty(&store).unwrap(), true); } #[test] @@ -301,10 +310,49 @@ mod tests { let items: StdResult> = queue.iter(&mut store).unwrap().collect(); assert_eq!(items.unwrap(), [1, 2, 3, 4]); + // nth should work correctly let mut iter = queue.iter(&mut store).unwrap(); assert_eq!(iter.nth(6), None); assert_eq!(iter.start, iter.end, "iter should detect skipping too far"); assert_eq!(iter.next(), None); + + let mut iter = queue.iter(&mut store).unwrap(); + assert_eq!(iter.nth(1).unwrap().unwrap(), 2); + assert_eq!(iter.next().unwrap().unwrap(), 3); + } + + #[test] + fn reverse_iterator() { + let queue: Queue = Queue::new("test"); + let mut store = MockStorage::new(); + + // push some items + queue.push(&mut store, &1).unwrap(); + queue.push(&mut store, &2).unwrap(); + queue.push(&mut store, &3).unwrap(); + queue.push(&mut store, &4).unwrap(); + + let items: StdResult> = queue.iter(&mut store).unwrap().rev().collect(); + assert_eq!(items.unwrap(), [4, 3, 2, 1]); + + // nth should work correctly + let mut iter = queue.iter(&mut store).unwrap(); + assert_eq!(iter.nth_back(6), None); + assert_eq!(iter.start, iter.end, "iter should detect skipping too far"); + assert_eq!(iter.next_back(), None); + + let mut iter = queue.iter(&mut store).unwrap().rev(); + assert_eq!(iter.nth(1).unwrap().unwrap(), 3); + assert_eq!(iter.next().unwrap().unwrap(), 2); + + // mixed + let mut iter = queue.iter(&mut store).unwrap(); + assert_eq!(iter.next().unwrap().unwrap(), 1); + assert_eq!(iter.next_back().unwrap().unwrap(), 4); + assert_eq!(iter.next_back().unwrap().unwrap(), 3); + assert_eq!(iter.next().unwrap().unwrap(), 2); + assert_eq!(iter.next(), None); + assert_eq!(iter.next_back(), None); } #[test] @@ -332,5 +380,22 @@ mod tests { Some(1), "popping should work, even when wrapping" ); + + queue.set_head(&mut store, u32::MAX); + queue.set_tail(&mut store, u32::MAX); + + queue.push(&mut store, &1).unwrap(); + queue.push(&mut store, &2).unwrap(); + queue.push(&mut store, &3).unwrap(); + queue.push(&mut store, &4).unwrap(); + queue.push(&mut store, &5).unwrap(); + + let mut iter = queue.iter(&store).unwrap(); + assert_eq!(iter.next().unwrap().unwrap(), 1); + assert_eq!(iter.next().unwrap().unwrap(), 2); + assert_eq!(iter.next_back().unwrap().unwrap(), 5); + assert_eq!(iter.nth(1).unwrap().unwrap(), 4); + assert_eq!(iter.nth(1), None); + assert_eq!(iter.start, iter.end); } } From f339278a242bc2722ad6270860a05ad7450f8da9 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 26 Sep 2022 12:24:50 +0200 Subject: [PATCH 04/17] Add deque functionality to queue --- .../storage-plus/src/{queue.rs => deque.rs} | 157 +++++++++++------- packages/storage-plus/src/lib.rs | 8 +- 2 files changed, 99 insertions(+), 66 deletions(-) rename packages/storage-plus/src/{queue.rs => deque.rs} (69%) diff --git a/packages/storage-plus/src/queue.rs b/packages/storage-plus/src/deque.rs similarity index 69% rename from packages/storage-plus/src/queue.rs rename to packages/storage-plus/src/deque.rs index 92fe5e4b0..3366fd335 100644 --- a/packages/storage-plus/src/queue.rs +++ b/packages/storage-plus/src/deque.rs @@ -10,14 +10,14 @@ const TAIL_KEY: &[u8] = b"t"; const HEAD_KEY: &[u8] = b"h"; /// A queue stores multiple items at the given key. It provides efficient FIFO access. -pub struct Queue<'a, T> { - // prefix of the queue items +pub struct Deque<'a, T> { + // prefix of the deque items namespace: &'a [u8], // see https://doc.rust-lang.org/std/marker/struct.PhantomData.html#unused-type-parameters for why this is needed item_type: PhantomData, } -impl<'a, T> Queue<'a, T> { +impl<'a, T> Deque<'a, T> { pub const fn new(prefix: &'a str) -> Self { Self { namespace: prefix.as_bytes(), @@ -26,9 +26,9 @@ impl<'a, T> Queue<'a, T> { } } -impl<'a, T: Serialize + DeserializeOwned> Queue<'a, T> { - /// Adds the given value to the end of the queue - pub fn push(&self, storage: &mut dyn Storage, value: &T) -> StdResult<()> { +impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { + /// Adds the given value to the end of the deque + pub fn push_back(&self, storage: &mut dyn Storage, value: &T) -> StdResult<()> { // save value let pos = self.tail(storage)?; self.set_at(storage, pos, value)?; @@ -38,8 +38,32 @@ impl<'a, T: Serialize + DeserializeOwned> Queue<'a, T> { Ok(()) } - /// Removes the first element of the queue and returns it - pub fn pop(&self, storage: &mut dyn Storage) -> StdResult> { + /// Adds the given value to the front of the deque + pub fn push_front(&self, storage: &mut dyn Storage, value: &T) -> StdResult<()> { + // need to subtract first, because head potentially points to existing element + let pos = self.head(storage)?.wrapping_sub(1); + self.set_at(storage, pos, value)?; + // update head + self.set_head(storage, pos); + + Ok(()) + } + + /// Removes the last element of the deque and returns it + pub fn pop_back(&self, storage: &mut dyn Storage) -> StdResult> { + // get position + let pos = self.tail(storage)?.wrapping_sub(1); + let value = self.get_at(storage, pos)?; + if value.is_some() { + self.remove_at(storage, pos); + // only update tail if a value was popped + self.set_tail(storage, pos); + } + Ok(value) + } + + /// Removes the first element of the deque and returns it + pub fn pop_front(&self, storage: &mut dyn Storage) -> StdResult> { // get position let pos = self.head(storage)?; let value = self.get_at(storage, pos)?; @@ -51,19 +75,19 @@ impl<'a, T: Serialize + DeserializeOwned> Queue<'a, T> { Ok(value) } - /// Gets the length of the queue. + /// Gets the length of the deque. pub fn len(&self, storage: &dyn Storage) -> StdResult { Ok(self.tail(storage)?.wrapping_sub(self.head(storage)?)) } - /// Returns `true` if the queue contains no elements. + /// Returns `true` if the deque contains no elements. pub fn is_empty(&self, storage: &dyn Storage) -> StdResult { Ok(self.len(storage)? == 0) } /// Gets the head position from storage. /// - /// Unless the queue is empty, this points to the first element. + /// Unless the deque is empty, this points to the first element. #[inline] fn head(&self, storage: &dyn Storage) -> StdResult { self.read_meta_key(storage, HEAD_KEY) @@ -134,10 +158,10 @@ impl<'a, T: Serialize + DeserializeOwned> Queue<'a, T> { } #[cfg(feature = "iterator")] -impl<'a, T: Serialize + DeserializeOwned> Queue<'a, T> { - pub fn iter(&self, storage: &'a dyn Storage) -> StdResult> { - Ok(QueueIter { - queue: self, +impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { + pub fn iter(&self, storage: &'a dyn Storage) -> StdResult> { + Ok(DequeIter { + deque: self, storage, start: self.head(storage)?, end: self.tail(storage)?, @@ -146,17 +170,17 @@ impl<'a, T: Serialize + DeserializeOwned> Queue<'a, T> { } #[cfg(feature = "iterator")] -pub struct QueueIter<'a, T> +pub struct DequeIter<'a, T> where T: Serialize + DeserializeOwned, { - queue: &'a Queue<'a, T>, + deque: &'a Deque<'a, T>, storage: &'a dyn Storage, start: u32, end: u32, } -impl<'a, T> Iterator for QueueIter<'a, T> +impl<'a, T> Iterator for DequeIter<'a, T> where T: Serialize + DeserializeOwned, { @@ -167,7 +191,7 @@ where return None; } - let item = self.queue.get_at(self.storage, self.start).transpose()?; + let item = self.deque.get_at(self.storage, self.start).transpose()?; self.start = self.start.wrapping_add(1); Some(item) @@ -194,7 +218,7 @@ where } } -impl<'a, T> DoubleEndedIterator for QueueIter<'a, T> +impl<'a, T> DoubleEndedIterator for DequeIter<'a, T> where T: Serialize + DeserializeOwned, { @@ -204,7 +228,7 @@ where } let item = self - .queue + .deque .get_at(self.storage, self.end.wrapping_sub(1)) // end points to position after last element .transpose()?; self.end = self.end.wrapping_sub(1); @@ -212,7 +236,7 @@ where Some(item) } - // see [`QueueIter::nth`] + // see [`DequeIter::nth`] fn nth_back(&mut self, n: usize) -> Option { // make sure that we don't skip past the start if self.end.wrapping_sub(self.start) < n as u32 { @@ -226,68 +250,77 @@ where } #[cfg(test)] mod tests { - use crate::queue::Queue; + use crate::deque::Deque; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::StdResult; #[test] fn push_and_pop() { - const PEOPLE: Queue = Queue::new("people"); + const PEOPLE: Deque = Deque::new("people"); let mut store = MockStorage::new(); // push some entries - PEOPLE.push(&mut store, &"jack".to_owned()).unwrap(); - PEOPLE.push(&mut store, &"john".to_owned()).unwrap(); - PEOPLE.push(&mut store, &"joanne".to_owned()).unwrap(); + PEOPLE.push_back(&mut store, &"jack".to_owned()).unwrap(); + PEOPLE.push_back(&mut store, &"john".to_owned()).unwrap(); + PEOPLE.push_back(&mut store, &"joanne".to_owned()).unwrap(); // pop them, should be in correct order - assert_eq!("jack", PEOPLE.pop(&mut store).unwrap().unwrap()); - assert_eq!("john", PEOPLE.pop(&mut store).unwrap().unwrap()); + assert_eq!("jack", PEOPLE.pop_front(&mut store).unwrap().unwrap()); + assert_eq!("john", PEOPLE.pop_front(&mut store).unwrap().unwrap()); // push again in-between - PEOPLE.push(&mut store, &"jason".to_owned()).unwrap(); + PEOPLE.push_back(&mut store, &"jason".to_owned()).unwrap(); // pop last person from first batch - assert_eq!("joanne", PEOPLE.pop(&mut store).unwrap().unwrap()); + assert_eq!("joanne", PEOPLE.pop_front(&mut store).unwrap().unwrap()); // pop the entry pushed in-between - assert_eq!("jason", PEOPLE.pop(&mut store).unwrap().unwrap()); + assert_eq!("jason", PEOPLE.pop_front(&mut store).unwrap().unwrap()); // nothing after that - assert_eq!(None, PEOPLE.pop(&mut store).unwrap()); + assert_eq!(None, PEOPLE.pop_front(&mut store).unwrap()); + + // now push to the front + PEOPLE.push_front(&mut store, &"pascal".to_owned()).unwrap(); + PEOPLE.push_front(&mut store, &"peter".to_owned()).unwrap(); + PEOPLE.push_front(&mut store, &"paul".to_owned()).unwrap(); + + assert_eq!("pascal", PEOPLE.pop_back(&mut store).unwrap().unwrap()); + assert_eq!("paul", PEOPLE.pop_front(&mut store).unwrap().unwrap()); + assert_eq!("peter", PEOPLE.pop_back(&mut store).unwrap().unwrap()); } #[test] fn length() { - let queue: Queue = Queue::new("test"); + let queue: Deque = Deque::new("test"); let mut store = MockStorage::new(); assert_eq!(queue.len(&store).unwrap(), 0); assert_eq!(queue.is_empty(&store).unwrap(), true); // push some entries - queue.push(&mut store, &1234).unwrap(); - queue.push(&mut store, &2345).unwrap(); - queue.push(&mut store, &3456).unwrap(); - queue.push(&mut store, &4567).unwrap(); + queue.push_back(&mut store, &1234).unwrap(); + queue.push_back(&mut store, &2345).unwrap(); + queue.push_back(&mut store, &3456).unwrap(); + queue.push_back(&mut store, &4567).unwrap(); assert_eq!(queue.len(&store).unwrap(), 4); assert_eq!(queue.is_empty(&store).unwrap(), false); // pop some - queue.pop(&mut store).unwrap(); - queue.pop(&mut store).unwrap(); - queue.pop(&mut store).unwrap(); + queue.pop_front(&mut store).unwrap(); + queue.pop_front(&mut store).unwrap(); + queue.pop_front(&mut store).unwrap(); assert_eq!(queue.len(&store).unwrap(), 1); assert_eq!(queue.is_empty(&store).unwrap(), false); // pop the last one - queue.pop(&mut store).unwrap(); + queue.pop_front(&mut store).unwrap(); assert_eq!(queue.len(&store).unwrap(), 0); assert_eq!(queue.is_empty(&store).unwrap(), true); // should stay 0 after that - queue.pop(&mut store).unwrap(); + queue.pop_front(&mut store).unwrap(); assert_eq!( queue.len(&store).unwrap(), 0, @@ -298,14 +331,14 @@ mod tests { #[test] fn iterator() { - let queue: Queue = Queue::new("test"); + let queue: Deque = Deque::new("test"); let mut store = MockStorage::new(); // push some items - queue.push(&mut store, &1).unwrap(); - queue.push(&mut store, &2).unwrap(); - queue.push(&mut store, &3).unwrap(); - queue.push(&mut store, &4).unwrap(); + queue.push_back(&mut store, &1).unwrap(); + queue.push_back(&mut store, &2).unwrap(); + queue.push_back(&mut store, &3).unwrap(); + queue.push_back(&mut store, &4).unwrap(); let items: StdResult> = queue.iter(&mut store).unwrap().collect(); assert_eq!(items.unwrap(), [1, 2, 3, 4]); @@ -323,14 +356,14 @@ mod tests { #[test] fn reverse_iterator() { - let queue: Queue = Queue::new("test"); + let queue: Deque = Deque::new("test"); let mut store = MockStorage::new(); // push some items - queue.push(&mut store, &1).unwrap(); - queue.push(&mut store, &2).unwrap(); - queue.push(&mut store, &3).unwrap(); - queue.push(&mut store, &4).unwrap(); + queue.push_back(&mut store, &1).unwrap(); + queue.push_back(&mut store, &2).unwrap(); + queue.push_back(&mut store, &3).unwrap(); + queue.push_back(&mut store, &4).unwrap(); let items: StdResult> = queue.iter(&mut store).unwrap().rev().collect(); assert_eq!(items.unwrap(), [4, 3, 2, 1]); @@ -357,7 +390,7 @@ mod tests { #[test] fn wrapping() { - let queue: Queue = Queue::new("test"); + let queue: Deque = Deque::new("test"); let mut store = MockStorage::new(); // simulate queue that was pushed and popped `u32::MAX` times @@ -365,18 +398,18 @@ mod tests { queue.set_tail(&mut store, u32::MAX); // should be empty - assert_eq!(queue.pop(&mut store).unwrap(), None); + assert_eq!(queue.pop_front(&mut store).unwrap(), None); assert_eq!(queue.len(&store).unwrap(), 0); // pushing should still work - queue.push(&mut store, &1).unwrap(); + queue.push_back(&mut store, &1).unwrap(); assert_eq!( queue.len(&store).unwrap(), 1, "length should calculate correctly, even when wrapping" ); assert_eq!( - queue.pop(&mut store).unwrap(), + queue.pop_front(&mut store).unwrap(), Some(1), "popping should work, even when wrapping" ); @@ -384,11 +417,11 @@ mod tests { queue.set_head(&mut store, u32::MAX); queue.set_tail(&mut store, u32::MAX); - queue.push(&mut store, &1).unwrap(); - queue.push(&mut store, &2).unwrap(); - queue.push(&mut store, &3).unwrap(); - queue.push(&mut store, &4).unwrap(); - queue.push(&mut store, &5).unwrap(); + queue.push_back(&mut store, &1).unwrap(); + queue.push_back(&mut store, &2).unwrap(); + queue.push_back(&mut store, &3).unwrap(); + queue.push_back(&mut store, &4).unwrap(); + queue.push_back(&mut store, &5).unwrap(); let mut iter = queue.iter(&store).unwrap(); assert_eq!(iter.next().unwrap().unwrap(), 1); diff --git a/packages/storage-plus/src/lib.rs b/packages/storage-plus/src/lib.rs index 67b2e9e0b..b9b65f806 100644 --- a/packages/storage-plus/src/lib.rs +++ b/packages/storage-plus/src/lib.rs @@ -1,5 +1,6 @@ mod bound; mod de; +mod deque; mod endian; mod helpers; mod indexed_map; @@ -12,12 +13,14 @@ mod keys; mod map; mod path; mod prefix; -mod queue; mod snapshot; #[cfg(feature = "iterator")] pub use bound::{Bound, Bounder, PrefixBound, RawBound}; pub use de::KeyDeserialize; +pub use deque::Deque; +#[cfg(feature = "iterator")] +pub use deque::DequeIter; pub use endian::Endian; #[cfg(feature = "iterator")] pub use indexed_map::{IndexList, IndexedMap}; @@ -36,9 +39,6 @@ pub use map::Map; pub use path::Path; #[cfg(feature = "iterator")] pub use prefix::{range_with_prefix, Prefix}; -pub use queue::Queue; -#[cfg(feature = "iterator")] -pub use queue::QueueIter; #[cfg(feature = "iterator")] pub use snapshot::{SnapshotItem, SnapshotMap, Strategy}; From 4c325b56fd5d4f814a55c2891e20fd041fb5c5d8 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 26 Sep 2022 12:26:59 +0200 Subject: [PATCH 05/17] Add missing feature checks --- packages/storage-plus/src/deque.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index 3366fd335..d0395eb95 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -180,6 +180,7 @@ where end: u32, } +#[cfg(feature = "iterator")] impl<'a, T> Iterator for DequeIter<'a, T> where T: Serialize + DeserializeOwned, @@ -218,6 +219,7 @@ where } } +#[cfg(feature = "iterator")] impl<'a, T> DoubleEndedIterator for DequeIter<'a, T> where T: Serialize + DeserializeOwned, From 9982a4670bd689d8eaa12b9d9ff60355ca4c82f5 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 26 Sep 2022 12:50:45 +0200 Subject: [PATCH 06/17] Add front and back methods to deque --- packages/storage-plus/src/deque.rs | 170 +++++++++++++++++------------ 1 file changed, 101 insertions(+), 69 deletions(-) diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index d0395eb95..756490f0e 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -9,7 +9,7 @@ use crate::helpers::{may_deserialize, namespaces_with_key}; const TAIL_KEY: &[u8] = b"t"; const HEAD_KEY: &[u8] = b"h"; -/// A queue stores multiple items at the given key. It provides efficient FIFO access. +/// A deque stores multiple items at the given key. It provides efficient FIFO and LIFO access. pub struct Deque<'a, T> { // prefix of the deque items namespace: &'a [u8], @@ -31,7 +31,7 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { pub fn push_back(&self, storage: &mut dyn Storage, value: &T) -> StdResult<()> { // save value let pos = self.tail(storage)?; - self.set_at(storage, pos, value)?; + self.set_at_unchecked(storage, pos, value)?; // update tail self.set_tail(storage, pos.wrapping_add(1)); @@ -42,7 +42,7 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { pub fn push_front(&self, storage: &mut dyn Storage, value: &T) -> StdResult<()> { // need to subtract first, because head potentially points to existing element let pos = self.head(storage)?.wrapping_sub(1); - self.set_at(storage, pos, value)?; + self.set_at_unchecked(storage, pos, value)?; // update head self.set_head(storage, pos); @@ -53,9 +53,9 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { pub fn pop_back(&self, storage: &mut dyn Storage) -> StdResult> { // get position let pos = self.tail(storage)?.wrapping_sub(1); - let value = self.get_at(storage, pos)?; + let value = self.get_at_unchecked(storage, pos)?; if value.is_some() { - self.remove_at(storage, pos); + self.remove_at_unchecked(storage, pos); // only update tail if a value was popped self.set_tail(storage, pos); } @@ -66,15 +66,27 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { pub fn pop_front(&self, storage: &mut dyn Storage) -> StdResult> { // get position let pos = self.head(storage)?; - let value = self.get_at(storage, pos)?; + let value = self.get_at_unchecked(storage, pos)?; if value.is_some() { - self.remove_at(storage, pos); + self.remove_at_unchecked(storage, pos); // only update head if a value was popped self.set_head(storage, pos.wrapping_add(1)); } Ok(value) } + /// Returns the first element of the deque without removing it + pub fn front(&self, storage: &dyn Storage) -> StdResult> { + let pos = self.head(storage)?; + self.get_at_unchecked(storage, pos) + } + + /// Returns the first element of the deque without removing it + pub fn back(&self, storage: &dyn Storage) -> StdResult> { + let pos = self.tail(storage)?.wrapping_sub(1); + self.get_at_unchecked(storage, pos) + } + /// Gets the length of the deque. pub fn len(&self, storage: &dyn Storage) -> StdResult { Ok(self.tail(storage)?.wrapping_sub(self.head(storage)?)) @@ -134,22 +146,22 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { } /// Tries to get the value at the given position (without bounds checking) - /// Used internally when popping - fn get_at(&self, storage: &dyn Storage, pos: u32) -> StdResult> { + /// Used internally + fn get_at_unchecked(&self, storage: &dyn Storage, pos: u32) -> StdResult> { let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes()); may_deserialize(&storage.get(&prefixed_key)) } /// Removes the value at the given position - /// Used internally when popping - fn remove_at(&self, storage: &mut dyn Storage, pos: u32) { + /// Used internally + fn remove_at_unchecked(&self, storage: &mut dyn Storage, pos: u32) { let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes()); storage.remove(&prefixed_key); } /// Tries to set the value at the given position (without bounds checking) /// Used internally when pushing - fn set_at(&self, storage: &mut dyn Storage, pos: u32, value: &T) -> StdResult<()> { + fn set_at_unchecked(&self, storage: &mut dyn Storage, pos: u32, value: &T) -> StdResult<()> { let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes()); storage.set(&prefixed_key, &to_vec(value)?); @@ -192,7 +204,10 @@ where return None; } - let item = self.deque.get_at(self.storage, self.start).transpose()?; + let item = self + .deque + .get_at_unchecked(self.storage, self.start) + .transpose()?; self.start = self.start.wrapping_add(1); Some(item) @@ -231,7 +246,7 @@ where let item = self .deque - .get_at(self.storage, self.end.wrapping_sub(1)) // end points to position after last element + .get_at_unchecked(self.storage, self.end.wrapping_sub(1)) // end points to position after last element .transpose()?; self.end = self.end.wrapping_sub(1); @@ -295,93 +310,93 @@ mod tests { #[test] fn length() { - let queue: Deque = Deque::new("test"); + let deque: Deque = Deque::new("test"); let mut store = MockStorage::new(); - assert_eq!(queue.len(&store).unwrap(), 0); - assert_eq!(queue.is_empty(&store).unwrap(), true); + assert_eq!(deque.len(&store).unwrap(), 0); + assert_eq!(deque.is_empty(&store).unwrap(), true); // push some entries - queue.push_back(&mut store, &1234).unwrap(); - queue.push_back(&mut store, &2345).unwrap(); - queue.push_back(&mut store, &3456).unwrap(); - queue.push_back(&mut store, &4567).unwrap(); - assert_eq!(queue.len(&store).unwrap(), 4); - assert_eq!(queue.is_empty(&store).unwrap(), false); + deque.push_front(&mut store, &1234).unwrap(); + deque.push_back(&mut store, &2345).unwrap(); + deque.push_front(&mut store, &3456).unwrap(); + deque.push_back(&mut store, &4567).unwrap(); + assert_eq!(deque.len(&store).unwrap(), 4); + assert_eq!(deque.is_empty(&store).unwrap(), false); // pop some - queue.pop_front(&mut store).unwrap(); - queue.pop_front(&mut store).unwrap(); - queue.pop_front(&mut store).unwrap(); - assert_eq!(queue.len(&store).unwrap(), 1); - assert_eq!(queue.is_empty(&store).unwrap(), false); + deque.pop_front(&mut store).unwrap(); + deque.pop_back(&mut store).unwrap(); + deque.pop_front(&mut store).unwrap(); + assert_eq!(deque.len(&store).unwrap(), 1); + assert_eq!(deque.is_empty(&store).unwrap(), false); // pop the last one - queue.pop_front(&mut store).unwrap(); - assert_eq!(queue.len(&store).unwrap(), 0); - assert_eq!(queue.is_empty(&store).unwrap(), true); + deque.pop_front(&mut store).unwrap(); + assert_eq!(deque.len(&store).unwrap(), 0); + assert_eq!(deque.is_empty(&store).unwrap(), true); // should stay 0 after that - queue.pop_front(&mut store).unwrap(); + assert_eq!(deque.pop_back(&mut store).unwrap(), None); assert_eq!( - queue.len(&store).unwrap(), + deque.len(&store).unwrap(), 0, - "popping from empty queue should keep length 0" + "popping from empty deque should keep length 0" ); - assert_eq!(queue.is_empty(&store).unwrap(), true); + assert_eq!(deque.is_empty(&store).unwrap(), true); } #[test] fn iterator() { - let queue: Deque = Deque::new("test"); + let deque: Deque = Deque::new("test"); let mut store = MockStorage::new(); // push some items - queue.push_back(&mut store, &1).unwrap(); - queue.push_back(&mut store, &2).unwrap(); - queue.push_back(&mut store, &3).unwrap(); - queue.push_back(&mut store, &4).unwrap(); + deque.push_back(&mut store, &1).unwrap(); + deque.push_back(&mut store, &2).unwrap(); + deque.push_back(&mut store, &3).unwrap(); + deque.push_back(&mut store, &4).unwrap(); - let items: StdResult> = queue.iter(&mut store).unwrap().collect(); + let items: StdResult> = deque.iter(&mut store).unwrap().collect(); assert_eq!(items.unwrap(), [1, 2, 3, 4]); // nth should work correctly - let mut iter = queue.iter(&mut store).unwrap(); + let mut iter = deque.iter(&mut store).unwrap(); assert_eq!(iter.nth(6), None); assert_eq!(iter.start, iter.end, "iter should detect skipping too far"); assert_eq!(iter.next(), None); - let mut iter = queue.iter(&mut store).unwrap(); + let mut iter = deque.iter(&mut store).unwrap(); assert_eq!(iter.nth(1).unwrap().unwrap(), 2); assert_eq!(iter.next().unwrap().unwrap(), 3); } #[test] fn reverse_iterator() { - let queue: Deque = Deque::new("test"); + let deque: Deque = Deque::new("test"); let mut store = MockStorage::new(); // push some items - queue.push_back(&mut store, &1).unwrap(); - queue.push_back(&mut store, &2).unwrap(); - queue.push_back(&mut store, &3).unwrap(); - queue.push_back(&mut store, &4).unwrap(); + deque.push_back(&mut store, &1).unwrap(); + deque.push_back(&mut store, &2).unwrap(); + deque.push_back(&mut store, &3).unwrap(); + deque.push_back(&mut store, &4).unwrap(); - let items: StdResult> = queue.iter(&mut store).unwrap().rev().collect(); + let items: StdResult> = deque.iter(&mut store).unwrap().rev().collect(); assert_eq!(items.unwrap(), [4, 3, 2, 1]); // nth should work correctly - let mut iter = queue.iter(&mut store).unwrap(); + let mut iter = deque.iter(&mut store).unwrap(); assert_eq!(iter.nth_back(6), None); assert_eq!(iter.start, iter.end, "iter should detect skipping too far"); assert_eq!(iter.next_back(), None); - let mut iter = queue.iter(&mut store).unwrap().rev(); + let mut iter = deque.iter(&mut store).unwrap().rev(); assert_eq!(iter.nth(1).unwrap().unwrap(), 3); assert_eq!(iter.next().unwrap().unwrap(), 2); // mixed - let mut iter = queue.iter(&mut store).unwrap(); + let mut iter = deque.iter(&mut store).unwrap(); assert_eq!(iter.next().unwrap().unwrap(), 1); assert_eq!(iter.next_back().unwrap().unwrap(), 4); assert_eq!(iter.next_back().unwrap().unwrap(), 3); @@ -392,40 +407,40 @@ mod tests { #[test] fn wrapping() { - let queue: Deque = Deque::new("test"); + let deque: Deque = Deque::new("test"); let mut store = MockStorage::new(); - // simulate queue that was pushed and popped `u32::MAX` times - queue.set_head(&mut store, u32::MAX); - queue.set_tail(&mut store, u32::MAX); + // simulate deque that was pushed and popped `u32::MAX` times + deque.set_head(&mut store, u32::MAX); + deque.set_tail(&mut store, u32::MAX); // should be empty - assert_eq!(queue.pop_front(&mut store).unwrap(), None); - assert_eq!(queue.len(&store).unwrap(), 0); + assert_eq!(deque.pop_front(&mut store).unwrap(), None); + assert_eq!(deque.len(&store).unwrap(), 0); // pushing should still work - queue.push_back(&mut store, &1).unwrap(); + deque.push_back(&mut store, &1).unwrap(); assert_eq!( - queue.len(&store).unwrap(), + deque.len(&store).unwrap(), 1, "length should calculate correctly, even when wrapping" ); assert_eq!( - queue.pop_front(&mut store).unwrap(), + deque.pop_front(&mut store).unwrap(), Some(1), "popping should work, even when wrapping" ); - queue.set_head(&mut store, u32::MAX); - queue.set_tail(&mut store, u32::MAX); + deque.set_head(&mut store, u32::MAX); + deque.set_tail(&mut store, u32::MAX); - queue.push_back(&mut store, &1).unwrap(); - queue.push_back(&mut store, &2).unwrap(); - queue.push_back(&mut store, &3).unwrap(); - queue.push_back(&mut store, &4).unwrap(); - queue.push_back(&mut store, &5).unwrap(); + deque.push_back(&mut store, &1).unwrap(); + deque.push_back(&mut store, &2).unwrap(); + deque.push_back(&mut store, &3).unwrap(); + deque.push_back(&mut store, &4).unwrap(); + deque.push_back(&mut store, &5).unwrap(); - let mut iter = queue.iter(&store).unwrap(); + let mut iter = deque.iter(&store).unwrap(); assert_eq!(iter.next().unwrap().unwrap(), 1); assert_eq!(iter.next().unwrap().unwrap(), 2); assert_eq!(iter.next_back().unwrap().unwrap(), 5); @@ -433,4 +448,21 @@ mod tests { assert_eq!(iter.nth(1), None); assert_eq!(iter.start, iter.end); } + + #[test] + fn front_back() { + let deque: Deque = Deque::new("test"); + let mut store = MockStorage::new(); + + assert_eq!(deque.back(&store).unwrap(), None); + deque.push_back(&mut store, &1).unwrap(); + assert_eq!(deque.back(&store).unwrap(), Some(1)); + assert_eq!(deque.front(&store).unwrap(), Some(1)); + deque.push_back(&mut store, &2).unwrap(); + assert_eq!(deque.back(&store).unwrap(), Some(2)); + assert_eq!(deque.front(&store).unwrap(), Some(1)); + deque.push_front(&mut store, &3).unwrap(); + assert_eq!(deque.back(&store).unwrap(), Some(2)); + assert_eq!(deque.front(&store).unwrap(), Some(3)); + } } From c2062cb7ebce26eb3278260543ea5a09aaf6e7e5 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 26 Sep 2022 14:07:20 +0200 Subject: [PATCH 07/17] Add deque to readme --- packages/storage-plus/README.md | 63 ++++++++++++++++++++++++++++++ packages/storage-plus/src/deque.rs | 59 +++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/packages/storage-plus/README.md b/packages/storage-plus/README.md index a47823688..f192e2578 100644 --- a/packages/storage-plus/README.md +++ b/packages/storage-plus/README.md @@ -635,3 +635,66 @@ In the particular case of `MultiIndex`, the primary key (`PK`) type parameter al the index key (the part that corresponds to the primary key, that is). So, to correctly use type-safe bounds over multi-indexes ranges, it is fundamental for this `PK` type to be correctly defined, so that it matches the primary key type, or its (typically owned) deserialization variant. + +## Deque + +The usage of a [`Deque`](./src/deque.rs) is pretty straight-forward. +Conceptually it works like a storage-backed `VecDeque`and can be used as a queue or stack. +It allows you to push and pop elements on both ends and also read the first or last element without mutating the deque. + +Example Usage: + +```rust +#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] +struct Data { + pub name: String, + pub age: i32, +} + +const DATA: Deque = Deque::new("data"); + +fn demo() -> StdResult<()> { + let mut store = MockStorage::new(); + + // read methods return Option, so None if the deque is empty + let empty = DATA.front(&store)?; + assert_eq!(None, empty); + + // some example entries + let p1 = Data { + name: "admin".to_string(), + age: 1234, + }; + let p2 = Data { + name: "user".to_string(), + age: 123, + }; + + // use it like a queue by pushing and popping at opposite ends + DATA.push_back(&mut store, &p1)?; + DATA.push_back(&mut store, &p2)?; + + let admin = DATA.pop_front(&mut store)?; + assert_eq!(admin.as_ref(), Some(&p1)); + let user = DATA.pop_front(&mut store)?; + assert_eq!(user.as_ref(), Some(&p2)); + + // or push and pop at the same end to use it as a stack + DATA.push_back(&mut store, &p1)?; + DATA.push_back(&mut store, &p2)?; + + let user = DATA.pop_back(&mut store)?; + assert_eq!(user.as_ref(), Some(&p2)); + let admin = DATA.pop_back(&mut store)?; + assert_eq!(admin.as_ref(), Some(&p1)); + + // you can also iterate over it + DATA.push_front(&mut store, &p1)?; + DATA.push_front(&mut store, &p2)?; + + let all: StdResult> = DATA.iter(&store)?.collect(); + assert_eq!(all?, [p2, p1]); + + Ok(()) +} +``` \ No newline at end of file diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index 756490f0e..78bcef8b2 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -145,7 +145,7 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { storage.set(&full_key, &value.to_be_bytes()); } - /// Tries to get the value at the given position (without bounds checking) + /// Tries to get the value at the given position /// Used internally fn get_at_unchecked(&self, storage: &dyn Storage, pos: u32) -> StdResult> { let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes()); @@ -159,7 +159,7 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { storage.remove(&prefixed_key); } - /// Tries to set the value at the given position (without bounds checking) + /// Tries to set the value at the given position /// Used internally when pushing fn set_at_unchecked(&self, storage: &mut dyn Storage, pos: u32, value: &T) -> StdResult<()> { let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes()); @@ -271,6 +271,7 @@ mod tests { use cosmwasm_std::testing::MockStorage; use cosmwasm_std::StdResult; + use serde::{Deserialize, Serialize}; #[test] fn push_and_pop() { @@ -465,4 +466,58 @@ mod tests { assert_eq!(deque.back(&store).unwrap(), Some(2)); assert_eq!(deque.front(&store).unwrap(), Some(3)); } + + #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] + struct Data { + pub name: String, + pub age: i32, + } + + const DATA: Deque = Deque::new("data"); + + #[test] + fn readme_works() -> StdResult<()> { + let mut store = MockStorage::new(); + + // read methods return Option, so None if the deque is empty + let empty = DATA.front(&store)?; + assert_eq!(None, empty); + + // some example entries + let p1 = Data { + name: "admin".to_string(), + age: 1234, + }; + let p2 = Data { + name: "user".to_string(), + age: 123, + }; + + // use it like a queue by pushing and popping at opposite ends + DATA.push_back(&mut store, &p1)?; + DATA.push_back(&mut store, &p2)?; + + let admin = DATA.pop_front(&mut store)?; + assert_eq!(admin.as_ref(), Some(&p1)); + let user = DATA.pop_front(&mut store)?; + assert_eq!(user.as_ref(), Some(&p2)); + + // or push and pop at the same end to use it as a stack + DATA.push_back(&mut store, &p1)?; + DATA.push_back(&mut store, &p2)?; + + let user = DATA.pop_back(&mut store)?; + assert_eq!(user.as_ref(), Some(&p2)); + let admin = DATA.pop_back(&mut store)?; + assert_eq!(admin.as_ref(), Some(&p1)); + + // you can also iterate over it + DATA.push_front(&mut store, &p1)?; + DATA.push_front(&mut store, &p2)?; + + let all: StdResult> = DATA.iter(&store)?.collect(); + assert_eq!(all?, [p2, p1]); + + Ok(()) + } } From f36e920f4a57bb6c4034ea91bba43c2f68bb36e4 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 26 Sep 2022 14:59:20 +0200 Subject: [PATCH 08/17] Fix tests --- packages/storage-plus/src/deque.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index 78bcef8b2..0c0f6f395 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -348,6 +348,7 @@ mod tests { } #[test] + #[cfg(feature = "iterator")] fn iterator() { let deque: Deque = Deque::new("test"); let mut store = MockStorage::new(); @@ -373,6 +374,7 @@ mod tests { } #[test] + #[cfg(feature = "iterator")] fn reverse_iterator() { let deque: Deque = Deque::new("test"); let mut store = MockStorage::new(); @@ -431,6 +433,13 @@ mod tests { Some(1), "popping should work, even when wrapping" ); + } + + #[test] + #[cfg(feature = "iterator")] + fn wrapping_iterator() { + let deque: Deque = Deque::new("test"); + let mut store = MockStorage::new(); deque.set_head(&mut store, u32::MAX); deque.set_tail(&mut store, u32::MAX); @@ -476,6 +485,7 @@ mod tests { const DATA: Deque = Deque::new("data"); #[test] + #[cfg(feature = "iterator")] fn readme_works() -> StdResult<()> { let mut store = MockStorage::new(); From 420cce8605a1050583efe281b9e6e6d86d890dfc Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 26 Sep 2022 15:06:08 +0200 Subject: [PATCH 09/17] Fix lints --- packages/storage-plus/src/deque.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index 0c0f6f395..58f5935a5 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -88,6 +88,7 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { } /// Gets the length of the deque. + #[allow(clippy::len_without_is_empty)] pub fn len(&self, storage: &dyn Storage) -> StdResult { Ok(self.tail(storage)?.wrapping_sub(self.head(storage)?)) } @@ -315,7 +316,7 @@ mod tests { let mut store = MockStorage::new(); assert_eq!(deque.len(&store).unwrap(), 0); - assert_eq!(deque.is_empty(&store).unwrap(), true); + assert!(deque.is_empty(&store).unwrap()); // push some entries deque.push_front(&mut store, &1234).unwrap(); @@ -323,19 +324,19 @@ mod tests { deque.push_front(&mut store, &3456).unwrap(); deque.push_back(&mut store, &4567).unwrap(); assert_eq!(deque.len(&store).unwrap(), 4); - assert_eq!(deque.is_empty(&store).unwrap(), false); + assert!(!deque.is_empty(&store).unwrap()); // pop some deque.pop_front(&mut store).unwrap(); deque.pop_back(&mut store).unwrap(); deque.pop_front(&mut store).unwrap(); assert_eq!(deque.len(&store).unwrap(), 1); - assert_eq!(deque.is_empty(&store).unwrap(), false); + assert!(!deque.is_empty(&store).unwrap()); // pop the last one deque.pop_front(&mut store).unwrap(); assert_eq!(deque.len(&store).unwrap(), 0); - assert_eq!(deque.is_empty(&store).unwrap(), true); + assert!(deque.is_empty(&store).unwrap()); // should stay 0 after that assert_eq!(deque.pop_back(&mut store).unwrap(), None); @@ -344,7 +345,7 @@ mod tests { 0, "popping from empty deque should keep length 0" ); - assert_eq!(deque.is_empty(&store).unwrap(), true); + assert!(deque.is_empty(&store).unwrap()); } #[test] @@ -359,16 +360,16 @@ mod tests { deque.push_back(&mut store, &3).unwrap(); deque.push_back(&mut store, &4).unwrap(); - let items: StdResult> = deque.iter(&mut store).unwrap().collect(); + let items: StdResult> = deque.iter(&store).unwrap().collect(); assert_eq!(items.unwrap(), [1, 2, 3, 4]); // nth should work correctly - let mut iter = deque.iter(&mut store).unwrap(); + let mut iter = deque.iter(&store).unwrap(); assert_eq!(iter.nth(6), None); assert_eq!(iter.start, iter.end, "iter should detect skipping too far"); assert_eq!(iter.next(), None); - let mut iter = deque.iter(&mut store).unwrap(); + let mut iter = deque.iter(&store).unwrap(); assert_eq!(iter.nth(1).unwrap().unwrap(), 2); assert_eq!(iter.next().unwrap().unwrap(), 3); } @@ -385,21 +386,21 @@ mod tests { deque.push_back(&mut store, &3).unwrap(); deque.push_back(&mut store, &4).unwrap(); - let items: StdResult> = deque.iter(&mut store).unwrap().rev().collect(); + let items: StdResult> = deque.iter(&store).unwrap().rev().collect(); assert_eq!(items.unwrap(), [4, 3, 2, 1]); // nth should work correctly - let mut iter = deque.iter(&mut store).unwrap(); + let mut iter = deque.iter(&store).unwrap(); assert_eq!(iter.nth_back(6), None); assert_eq!(iter.start, iter.end, "iter should detect skipping too far"); assert_eq!(iter.next_back(), None); - let mut iter = deque.iter(&mut store).unwrap().rev(); + let mut iter = deque.iter(&store).unwrap().rev(); assert_eq!(iter.nth(1).unwrap().unwrap(), 3); assert_eq!(iter.next().unwrap().unwrap(), 2); // mixed - let mut iter = deque.iter(&mut store).unwrap(); + let mut iter = deque.iter(&store).unwrap(); assert_eq!(iter.next().unwrap().unwrap(), 1); assert_eq!(iter.next_back().unwrap().unwrap(), 4); assert_eq!(iter.next_back().unwrap().unwrap(), 3); From 19bb5cd8615a9867fe0c4e14e4ff73470fece7f5 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 27 Sep 2022 09:37:53 +0200 Subject: [PATCH 10/17] Throw error instead of silently ending iterator --- packages/storage-plus/src/deque.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index 58f5935a5..72fe668f9 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, marker::PhantomData}; +use std::{any::type_name, convert::TryInto, marker::PhantomData}; use cosmwasm_std::{to_vec, StdError, StdResult, Storage}; use serde::{de::DeserializeOwned, Serialize}; @@ -208,7 +208,7 @@ where let item = self .deque .get_at_unchecked(self.storage, self.start) - .transpose()?; + .and_then(|item| item.ok_or_else(|| StdError::not_found(type_name::()))); self.start = self.start.wrapping_add(1); Some(item) @@ -248,7 +248,7 @@ where let item = self .deque .get_at_unchecked(self.storage, self.end.wrapping_sub(1)) // end points to position after last element - .transpose()?; + .and_then(|item| item.ok_or_else(|| StdError::not_found(type_name::()))); self.end = self.end.wrapping_sub(1); Some(item) From 7b8342d823cba319be4ec3a0dd9e1723dd599464 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 27 Sep 2022 10:02:33 +0200 Subject: [PATCH 11/17] Rename methods --- packages/storage-plus/src/deque.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index 72fe668f9..374df597c 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -31,7 +31,7 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { pub fn push_back(&self, storage: &mut dyn Storage, value: &T) -> StdResult<()> { // save value let pos = self.tail(storage)?; - self.set_at_unchecked(storage, pos, value)?; + self.set_unchecked(storage, pos, value)?; // update tail self.set_tail(storage, pos.wrapping_add(1)); @@ -42,7 +42,7 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { pub fn push_front(&self, storage: &mut dyn Storage, value: &T) -> StdResult<()> { // need to subtract first, because head potentially points to existing element let pos = self.head(storage)?.wrapping_sub(1); - self.set_at_unchecked(storage, pos, value)?; + self.set_unchecked(storage, pos, value)?; // update head self.set_head(storage, pos); @@ -53,9 +53,9 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { pub fn pop_back(&self, storage: &mut dyn Storage) -> StdResult> { // get position let pos = self.tail(storage)?.wrapping_sub(1); - let value = self.get_at_unchecked(storage, pos)?; + let value = self.get_unchecked(storage, pos)?; if value.is_some() { - self.remove_at_unchecked(storage, pos); + self.remove_unchecked(storage, pos); // only update tail if a value was popped self.set_tail(storage, pos); } @@ -66,9 +66,9 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { pub fn pop_front(&self, storage: &mut dyn Storage) -> StdResult> { // get position let pos = self.head(storage)?; - let value = self.get_at_unchecked(storage, pos)?; + let value = self.get_unchecked(storage, pos)?; if value.is_some() { - self.remove_at_unchecked(storage, pos); + self.remove_unchecked(storage, pos); // only update head if a value was popped self.set_head(storage, pos.wrapping_add(1)); } @@ -78,13 +78,13 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { /// Returns the first element of the deque without removing it pub fn front(&self, storage: &dyn Storage) -> StdResult> { let pos = self.head(storage)?; - self.get_at_unchecked(storage, pos) + self.get_unchecked(storage, pos) } /// Returns the first element of the deque without removing it pub fn back(&self, storage: &dyn Storage) -> StdResult> { let pos = self.tail(storage)?.wrapping_sub(1); - self.get_at_unchecked(storage, pos) + self.get_unchecked(storage, pos) } /// Gets the length of the deque. @@ -148,21 +148,21 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { /// Tries to get the value at the given position /// Used internally - fn get_at_unchecked(&self, storage: &dyn Storage, pos: u32) -> StdResult> { + fn get_unchecked(&self, storage: &dyn Storage, pos: u32) -> StdResult> { let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes()); may_deserialize(&storage.get(&prefixed_key)) } /// Removes the value at the given position /// Used internally - fn remove_at_unchecked(&self, storage: &mut dyn Storage, pos: u32) { + fn remove_unchecked(&self, storage: &mut dyn Storage, pos: u32) { let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes()); storage.remove(&prefixed_key); } /// Tries to set the value at the given position /// Used internally when pushing - fn set_at_unchecked(&self, storage: &mut dyn Storage, pos: u32, value: &T) -> StdResult<()> { + fn set_unchecked(&self, storage: &mut dyn Storage, pos: u32, value: &T) -> StdResult<()> { let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes()); storage.set(&prefixed_key, &to_vec(value)?); @@ -207,7 +207,7 @@ where let item = self .deque - .get_at_unchecked(self.storage, self.start) + .get_unchecked(self.storage, self.start) .and_then(|item| item.ok_or_else(|| StdError::not_found(type_name::()))); self.start = self.start.wrapping_add(1); @@ -247,7 +247,7 @@ where let item = self .deque - .get_at_unchecked(self.storage, self.end.wrapping_sub(1)) // end points to position after last element + .get_unchecked(self.storage, self.end.wrapping_sub(1)) // end points to position after last element .and_then(|item| item.ok_or_else(|| StdError::not_found(type_name::()))); self.end = self.end.wrapping_sub(1); From 82e843b74e71783bd501db96c9ea11b75caf653e Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 27 Sep 2022 10:12:53 +0200 Subject: [PATCH 12/17] Fix docs --- packages/storage-plus/README.md | 4 ++-- packages/storage-plus/src/deque.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/storage-plus/README.md b/packages/storage-plus/README.md index f192e2578..e92069d2c 100644 --- a/packages/storage-plus/README.md +++ b/packages/storage-plus/README.md @@ -639,7 +639,7 @@ to be correctly defined, so that it matches the primary key type, or its (typica ## Deque The usage of a [`Deque`](./src/deque.rs) is pretty straight-forward. -Conceptually it works like a storage-backed `VecDeque`and can be used as a queue or stack. +Conceptually it works like a storage-backed version of Rust std's `VecDeque` and can be used as a queue or stack. It allows you to push and pop elements on both ends and also read the first or last element without mutating the deque. Example Usage: @@ -656,7 +656,7 @@ const DATA: Deque = Deque::new("data"); fn demo() -> StdResult<()> { let mut store = MockStorage::new(); - // read methods return Option, so None if the deque is empty + // read methods return a wrapped Option, so None if the deque is empty let empty = DATA.front(&store)?; assert_eq!(None, empty); diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index 374df597c..42832f527 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -490,7 +490,7 @@ mod tests { fn readme_works() -> StdResult<()> { let mut store = MockStorage::new(); - // read methods return Option, so None if the deque is empty + // read methods return a wrapped Option, so None if the deque is empty let empty = DATA.front(&store)?; assert_eq!(None, empty); From 5592368e8a593d778864554ad2cb2edf6330d4e5 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 27 Sep 2022 11:02:59 +0200 Subject: [PATCH 13/17] Add test for iterator error --- packages/storage-plus/src/deque.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index 42832f527..2842c7ba2 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -271,7 +271,7 @@ mod tests { use crate::deque::Deque; use cosmwasm_std::testing::MockStorage; - use cosmwasm_std::StdResult; + use cosmwasm_std::{StdError, StdResult}; use serde::{Deserialize, Serialize}; #[test] @@ -531,4 +531,30 @@ mod tests { Ok(()) } + + #[test] + #[cfg(feature = "iterator")] + fn iterator_errors_when_item_missing() { + let mut store = MockStorage::new(); + + let deque = Deque::new("error_test"); + + deque.push_back(&mut store, &1u32).unwrap(); + // manually remove it + deque.remove_unchecked(&mut store, 0); + + let mut iter = deque.iter(&store).unwrap(); + + assert!( + matches!(iter.next(), Some(Err(StdError::NotFound { .. }))), + "iterator should error when item is missing" + ); + + let mut iter = deque.iter(&store).unwrap().rev(); + + assert!( + matches!(iter.next(), Some(Err(StdError::NotFound { .. }))), + "reverse iterator should error when item is missing" + ); + } } From 4616d332d863cad143b0317a8cb70516a3ff449e Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 27 Sep 2022 11:03:03 +0200 Subject: [PATCH 14/17] Add documentation about max deque size --- packages/storage-plus/src/deque.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index 2842c7ba2..d9115e637 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -10,6 +10,9 @@ const TAIL_KEY: &[u8] = b"t"; const HEAD_KEY: &[u8] = b"h"; /// A deque stores multiple items at the given key. It provides efficient FIFO and LIFO access. +/// +/// It has a maximum capacity of `u32::MAX - 1`. Make sure to never exceed that number when using this type. +/// If you do, the methods won't work as intended anymore. pub struct Deque<'a, T> { // prefix of the deque items namespace: &'a [u8], From b4f3fd34bc84c179b640515b3f9048ee6c2d15bb Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 27 Sep 2022 12:13:10 +0200 Subject: [PATCH 15/17] Add random access to deque --- packages/storage-plus/src/deque.rs | 75 ++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index d9115e637..07bc84609 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -93,7 +93,7 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { /// Gets the length of the deque. #[allow(clippy::len_without_is_empty)] pub fn len(&self, storage: &dyn Storage) -> StdResult { - Ok(self.tail(storage)?.wrapping_sub(self.head(storage)?)) + Ok(calc_len(self.head(storage)?, self.tail(storage)?)) } /// Returns `true` if the deque contains no elements. @@ -149,6 +149,22 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { storage.set(&full_key, &value.to_be_bytes()); } + /// Returns the value at the given position in the queue or `None` if the index is out of bounds + pub fn get(&self, storage: &dyn Storage, pos: u32) -> StdResult> { + let head = self.head(storage)?; + let tail = self.tail(storage)?; + + if pos >= calc_len(head, tail) { + // out of bounds + return Ok(None); + } + + let pos = head.wrapping_add(pos); + self.get_unchecked(storage, pos) + .and_then(|v| v.ok_or_else(|| StdError::not_found(format!("deque position {}", pos)))) + .map(Some) + } + /// Tries to get the value at the given position /// Used internally fn get_unchecked(&self, storage: &dyn Storage, pos: u32) -> StdResult> { @@ -173,6 +189,12 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { } } +// used internally to avoid additional storage loads +#[inline] +fn calc_len(head: u32, tail: u32) -> u32 { + tail.wrapping_sub(head) +} + #[cfg(feature = "iterator")] impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { pub fn iter(&self, storage: &'a dyn Storage) -> StdResult> { @@ -218,7 +240,7 @@ where } fn size_hint(&self) -> (usize, Option) { - let len = self.end.wrapping_sub(self.start) as usize; + let len = calc_len(self.start, self.end) as usize; (len, Some(len)) } @@ -228,7 +250,7 @@ where // Once `advance_by` is stabilized, we can implement that instead (`nth` calls it internally). fn nth(&mut self, n: usize) -> Option { // make sure that we don't skip past the end - if self.end.wrapping_sub(self.start) < n as u32 { + if calc_len(self.start, self.end) < n as u32 { // mark as empty self.start = self.end; } else { @@ -260,7 +282,7 @@ where // see [`DequeIter::nth`] fn nth_back(&mut self, n: usize) -> Option { // make sure that we don't skip past the start - if self.end.wrapping_sub(self.start) < n as u32 { + if calc_len(self.start, self.end) < n as u32 { // mark as empty self.end = self.start; } else { @@ -560,4 +582,49 @@ mod tests { "reverse iterator should error when item is missing" ); } + + #[test] + fn get() { + let mut store = MockStorage::new(); + + let deque = Deque::new("test"); + + deque.push_back(&mut store, &1u32).unwrap(); + deque.push_back(&mut store, &2).unwrap(); + + assert_eq!(deque.get(&store, 0).unwrap(), Some(1)); + assert_eq!(deque.get(&store, 1).unwrap(), Some(2)); + assert_eq!( + deque.get(&store, 2).unwrap(), + None, + "out of bounds access should return None" + ); + + // manually remove storage item + deque.remove_unchecked(&mut store, 1); + + assert!( + matches!(deque.get(&store, 1), Err(StdError::NotFound { .. })), + "missing deque item should error" + ); + + // start fresh + let deque = Deque::new("test2"); + + deque.push_back(&mut store, &0u32).unwrap(); + deque.push_back(&mut store, &1).unwrap(); + // push to front to move the head index + deque.push_front(&mut store, &u32::MAX).unwrap(); + deque.push_front(&mut store, &(u32::MAX - 1)).unwrap(); + + assert_eq!(deque.get(&store, 0).unwrap().unwrap(), u32::MAX - 1); + assert_eq!(deque.get(&store, 1).unwrap().unwrap(), u32::MAX); + assert_eq!(deque.get(&store, 2).unwrap().unwrap(), 0); + assert_eq!(deque.get(&store, 3).unwrap().unwrap(), 1); + assert_eq!( + deque.get(&store, 5).unwrap(), + None, + "out of bounds access should return None" + ); + } } From 48561bfb65dc09f67dec0b574ba0cb00e520faaa Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 27 Sep 2022 12:14:33 +0200 Subject: [PATCH 16/17] Rename Deque to VecDeque --- packages/storage-plus/src/deque.rs | 44 +++++++++++++++--------------- packages/storage-plus/src/lib.rs | 4 +-- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index 07bc84609..92ea1ebd6 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -13,14 +13,14 @@ const HEAD_KEY: &[u8] = b"h"; /// /// It has a maximum capacity of `u32::MAX - 1`. Make sure to never exceed that number when using this type. /// If you do, the methods won't work as intended anymore. -pub struct Deque<'a, T> { +pub struct VecDeque<'a, T> { // prefix of the deque items namespace: &'a [u8], // see https://doc.rust-lang.org/std/marker/struct.PhantomData.html#unused-type-parameters for why this is needed item_type: PhantomData, } -impl<'a, T> Deque<'a, T> { +impl<'a, T> VecDeque<'a, T> { pub const fn new(prefix: &'a str) -> Self { Self { namespace: prefix.as_bytes(), @@ -29,7 +29,7 @@ impl<'a, T> Deque<'a, T> { } } -impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { +impl<'a, T: Serialize + DeserializeOwned> VecDeque<'a, T> { /// Adds the given value to the end of the deque pub fn push_back(&self, storage: &mut dyn Storage, value: &T) -> StdResult<()> { // save value @@ -196,9 +196,9 @@ fn calc_len(head: u32, tail: u32) -> u32 { } #[cfg(feature = "iterator")] -impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { - pub fn iter(&self, storage: &'a dyn Storage) -> StdResult> { - Ok(DequeIter { +impl<'a, T: Serialize + DeserializeOwned> VecDeque<'a, T> { + pub fn iter(&self, storage: &'a dyn Storage) -> StdResult> { + Ok(VecDequeIter { deque: self, storage, start: self.head(storage)?, @@ -208,18 +208,18 @@ impl<'a, T: Serialize + DeserializeOwned> Deque<'a, T> { } #[cfg(feature = "iterator")] -pub struct DequeIter<'a, T> +pub struct VecDequeIter<'a, T> where T: Serialize + DeserializeOwned, { - deque: &'a Deque<'a, T>, + deque: &'a VecDeque<'a, T>, storage: &'a dyn Storage, start: u32, end: u32, } #[cfg(feature = "iterator")] -impl<'a, T> Iterator for DequeIter<'a, T> +impl<'a, T> Iterator for VecDequeIter<'a, T> where T: Serialize + DeserializeOwned, { @@ -261,7 +261,7 @@ where } #[cfg(feature = "iterator")] -impl<'a, T> DoubleEndedIterator for DequeIter<'a, T> +impl<'a, T> DoubleEndedIterator for VecDequeIter<'a, T> where T: Serialize + DeserializeOwned, { @@ -293,7 +293,7 @@ where } #[cfg(test)] mod tests { - use crate::deque::Deque; + use crate::deque::VecDeque; use cosmwasm_std::testing::MockStorage; use cosmwasm_std::{StdError, StdResult}; @@ -301,7 +301,7 @@ mod tests { #[test] fn push_and_pop() { - const PEOPLE: Deque = Deque::new("people"); + const PEOPLE: VecDeque = VecDeque::new("people"); let mut store = MockStorage::new(); // push some entries @@ -337,7 +337,7 @@ mod tests { #[test] fn length() { - let deque: Deque = Deque::new("test"); + let deque: VecDeque = VecDeque::new("test"); let mut store = MockStorage::new(); assert_eq!(deque.len(&store).unwrap(), 0); @@ -376,7 +376,7 @@ mod tests { #[test] #[cfg(feature = "iterator")] fn iterator() { - let deque: Deque = Deque::new("test"); + let deque: VecDeque = VecDeque::new("test"); let mut store = MockStorage::new(); // push some items @@ -402,7 +402,7 @@ mod tests { #[test] #[cfg(feature = "iterator")] fn reverse_iterator() { - let deque: Deque = Deque::new("test"); + let deque: VecDeque = VecDeque::new("test"); let mut store = MockStorage::new(); // push some items @@ -436,7 +436,7 @@ mod tests { #[test] fn wrapping() { - let deque: Deque = Deque::new("test"); + let deque: VecDeque = VecDeque::new("test"); let mut store = MockStorage::new(); // simulate deque that was pushed and popped `u32::MAX` times @@ -464,7 +464,7 @@ mod tests { #[test] #[cfg(feature = "iterator")] fn wrapping_iterator() { - let deque: Deque = Deque::new("test"); + let deque: VecDeque = VecDeque::new("test"); let mut store = MockStorage::new(); deque.set_head(&mut store, u32::MAX); @@ -487,7 +487,7 @@ mod tests { #[test] fn front_back() { - let deque: Deque = Deque::new("test"); + let deque: VecDeque = VecDeque::new("test"); let mut store = MockStorage::new(); assert_eq!(deque.back(&store).unwrap(), None); @@ -508,7 +508,7 @@ mod tests { pub age: i32, } - const DATA: Deque = Deque::new("data"); + const DATA: VecDeque = VecDeque::new("data"); #[test] #[cfg(feature = "iterator")] @@ -562,7 +562,7 @@ mod tests { fn iterator_errors_when_item_missing() { let mut store = MockStorage::new(); - let deque = Deque::new("error_test"); + let deque = VecDeque::new("error_test"); deque.push_back(&mut store, &1u32).unwrap(); // manually remove it @@ -587,7 +587,7 @@ mod tests { fn get() { let mut store = MockStorage::new(); - let deque = Deque::new("test"); + let deque = VecDeque::new("test"); deque.push_back(&mut store, &1u32).unwrap(); deque.push_back(&mut store, &2).unwrap(); @@ -609,7 +609,7 @@ mod tests { ); // start fresh - let deque = Deque::new("test2"); + let deque = VecDeque::new("test2"); deque.push_back(&mut store, &0u32).unwrap(); deque.push_back(&mut store, &1).unwrap(); diff --git a/packages/storage-plus/src/lib.rs b/packages/storage-plus/src/lib.rs index b9b65f806..014834bf8 100644 --- a/packages/storage-plus/src/lib.rs +++ b/packages/storage-plus/src/lib.rs @@ -18,9 +18,9 @@ mod snapshot; #[cfg(feature = "iterator")] pub use bound::{Bound, Bounder, PrefixBound, RawBound}; pub use de::KeyDeserialize; -pub use deque::Deque; +pub use deque::VecDeque; #[cfg(feature = "iterator")] -pub use deque::DequeIter; +pub use deque::VecDequeIter; pub use endian::Endian; #[cfg(feature = "iterator")] pub use indexed_map::{IndexList, IndexedMap}; From db6cab24ab8ac868a7a3aa333f259ac7b2d58b60 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 27 Sep 2022 12:41:07 +0200 Subject: [PATCH 17/17] Update docs --- packages/storage-plus/README.md | 10 ++++++++-- packages/storage-plus/src/deque.rs | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/storage-plus/README.md b/packages/storage-plus/README.md index e92069d2c..017dba7ba 100644 --- a/packages/storage-plus/README.md +++ b/packages/storage-plus/README.md @@ -636,11 +636,12 @@ the index key (the part that corresponds to the primary key, that is). So, to correctly use type-safe bounds over multi-indexes ranges, it is fundamental for this `PK` type to be correctly defined, so that it matches the primary key type, or its (typically owned) deserialization variant. -## Deque +## VecDeque -The usage of a [`Deque`](./src/deque.rs) is pretty straight-forward. +The usage of a [`VecDeque`](./src/deque.rs) is pretty straight-forward. Conceptually it works like a storage-backed version of Rust std's `VecDeque` and can be used as a queue or stack. It allows you to push and pop elements on both ends and also read the first or last element without mutating the deque. +You can also read a specific index directly. Example Usage: @@ -695,6 +696,11 @@ fn demo() -> StdResult<()> { let all: StdResult> = DATA.iter(&store)?.collect(); assert_eq!(all?, [p2, p1]); + // or access an index directly + assert_eq!(DATA.get(&store, 0)?, Some(p2)); + assert_eq!(DATA.get(&store, 1)?, Some(p1)); + assert_eq!(DATA.get(&store, 3)?, None); + Ok(()) } ``` \ No newline at end of file diff --git a/packages/storage-plus/src/deque.rs b/packages/storage-plus/src/deque.rs index 92ea1ebd6..6c51440cb 100644 --- a/packages/storage-plus/src/deque.rs +++ b/packages/storage-plus/src/deque.rs @@ -9,7 +9,8 @@ use crate::helpers::{may_deserialize, namespaces_with_key}; const TAIL_KEY: &[u8] = b"t"; const HEAD_KEY: &[u8] = b"h"; -/// A deque stores multiple items at the given key. It provides efficient FIFO and LIFO access. +/// A deque stores multiple items at the given key. It provides efficient FIFO and LIFO access, +/// as well as direct index access. /// /// It has a maximum capacity of `u32::MAX - 1`. Make sure to never exceed that number when using this type. /// If you do, the methods won't work as intended anymore. @@ -552,7 +553,12 @@ mod tests { DATA.push_front(&mut store, &p2)?; let all: StdResult> = DATA.iter(&store)?.collect(); - assert_eq!(all?, [p2, p1]); + assert_eq!(all?, [p2.clone(), p1.clone()]); + + // or access an index directly + assert_eq!(DATA.get(&store, 0)?, Some(p2)); + assert_eq!(DATA.get(&store, 1)?, Some(p1)); + assert_eq!(DATA.get(&store, 3)?, None); Ok(()) }