Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Deque #807

Merged
merged 17 commits into from
Sep 27, 2022
Prev Previous commit
Next Next commit
Add deque to readme
  • Loading branch information
chipshort committed Sep 27, 2022
commit c2062cb7ebce26eb3278260543ea5a09aaf6e7e5
63 changes: 63 additions & 0 deletions packages/storage-plus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
chipshort marked this conversation as resolved.
Show resolved Hide resolved
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<Data> = Deque::new("data");

fn demo() -> StdResult<()> {
let mut store = MockStorage::new();

// read methods return Option<T>, so None if the deque is empty
chipshort marked this conversation as resolved.
Show resolved Hide resolved
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice examples!

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<Vec<_>> = DATA.iter(&store)?.collect();
assert_eq!(all?, [p2, p1]);

Ok(())
}
```
59 changes: 57 additions & 2 deletions packages/storage-plus/src/deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<T>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Should we just name it get_unchecked? It feels like array accessors in std are usually named get, not get_at.
  2. Since it costs us nothing, would you want to provide a public and checked get variant of this, and then call the type VecDeque, like you suggested before? I think it makes a lot of sense.

let prefixed_key = namespaces_with_key(&[self.namespace], &pos.to_be_bytes());
Expand All @@ -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());
Expand Down Expand Up @@ -271,6 +271,7 @@ mod tests {

use cosmwasm_std::testing::MockStorage;
use cosmwasm_std::StdResult;
use serde::{Deserialize, Serialize};

#[test]
fn push_and_pop() {
Expand Down Expand Up @@ -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<Data> = Deque::new("data");

#[test]
fn readme_works() -> StdResult<()> {
let mut store = MockStorage::new();

// read methods return Option<T>, 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<Vec<_>> = DATA.iter(&store)?.collect();
assert_eq!(all?, [p2, p1]);

Ok(())
}
}