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

StorageVec #2048

Merged
merged 68 commits into from
Jul 14, 2022
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
2a7e168
Initial commit - storagevec
SwayStar123 Jun 19, 2022
23f925f
added an error and comments and documentation
SwayStar123 Jun 19, 2022
81a0ea6
fixed the order of the Result types
SwayStar123 Jun 19, 2022
7985f55
added suggested functions
SwayStar123 Jun 19, 2022
28e9be3
removed unncessary code
SwayStar123 Jun 19, 2022
6b629db
renamed remove_index
SwayStar123 Jun 19, 2022
a4a9093
added swap remove and removed unncessary annotations
SwayStar123 Jun 19, 2022
f09060f
moved storage_vec to storage and started on tests
SwayStar123 Jun 22, 2022
9c2a914
built some of the contract -- WIP
SwayStar123 Jun 22, 2022
37ed447
made it so swap_remove returns the removed element
SwayStar123 Jun 22, 2022
51961a7
removed extra ) and added all the test functions
SwayStar123 Jun 22, 2022
c42229d
fixed a syntax error
SwayStar123 Jun 22, 2022
8b059b3
changed store to storage
SwayStar123 Jun 22, 2022
082ded9
removed all other types for now
SwayStar123 Jun 22, 2022
f6315fa
made storagevecerror public
SwayStar123 Jun 22, 2022
9ddaf7d
removed unncessary code to streamline bugfixing
SwayStar123 Jun 22, 2022
b22e00d
fixed annotations
SwayStar123 Jun 22, 2022
049b162
made all the test contracts
SwayStar123 Jun 22, 2022
cbfbedc
changed build.sh to include all the storage_vec projs
SwayStar123 Jun 23, 2022
75c414b
updated fuels version
SwayStar123 Jun 23, 2022
0da645a
unwrapped all results and options in the contract
SwayStar123 Jun 30, 2022
da514b7
reduced fuels version
SwayStar123 Jun 30, 2022
5f50e40
merge master to storage_vec branch (#2183)
SwayStar123 Jun 30, 2022
c04be39
merge master to storage_vec (#2184)
SwayStar123 Jun 30, 2022
3ad6fca
removed unncessary use
SwayStar123 Jun 30, 2022
6bcfac1
Merge branch 'master' into storage_vec-swaystar123
SwayStar123 Jun 30, 2022
859b0f6
added one test
SwayStar123 Jun 30, 2022
7092230
deleted all contracts except u8s
SwayStar123 Jun 30, 2022
ed76626
fixed bug in pop
SwayStar123 Jun 30, 2022
f90d304
failing test for some reason
SwayStar123 Jun 30, 2022
b192910
.
SwayStar123 Jun 30, 2022
3293293
fixed some brackets
SwayStar123 Jun 30, 2022
7f23319
fixed a mistake of > where it should be >=
SwayStar123 Jul 2, 2022
0ad4143
added 2 remaining tests
SwayStar123 Jul 2, 2022
1c32489
Update test/src/sdk-harness/test_artifacts/storage_vec/svec_u8/Cargo.…
SwayStar123 Jul 4, 2022
9dc6a5c
removed storagevecerrors
SwayStar123 Jul 4, 2022
a106f1c
assert was the other way round
SwayStar123 Jul 4, 2022
f5b78c8
added a variable for repeated code
SwayStar123 Jul 4, 2022
d242c9c
merged use
SwayStar123 Jul 4, 2022
c9ce95f
formatting
SwayStar123 Jul 4, 2022
578d9e3
expanded the testing
SwayStar123 Jul 6, 2022
27979d8
rearranged file structure
SwayStar123 Jul 6, 2022
d9f17e3
cargo fmt
SwayStar123 Jul 6, 2022
f51da9f
adjusted assert for removes
SwayStar123 Jul 7, 2022
57e4aef
removed non test and shortened the contract code
SwayStar123 Jul 7, 2022
6e65c39
added cant_get test
SwayStar123 Jul 7, 2022
5796f40
added a todo
SwayStar123 Jul 7, 2022
bcae640
Improved documentation
SwayStar123 Jul 9, 2022
a6b6ab8
updated tests with preconditional tests
SwayStar123 Jul 9, 2022
bfae279
added initializer
SwayStar123 Jul 9, 2022
2ea82e7
Merge branch 'master' into storage_vec-swaystar123
SwayStar123 Jul 9, 2022
06805d8
updated functions with sdk release
SwayStar123 Jul 9, 2022
5adddc5
mdae build.sh more generic
SwayStar123 Jul 9, 2022
a1b3843
fixed build script
SwayStar123 Jul 9, 2022
4b52750
fix 2 electric boogaloo
SwayStar123 Jul 9, 2022
3d62d03
updated remove and insert tests
SwayStar123 Jul 11, 2022
ab32539
added len checks
SwayStar123 Jul 11, 2022
b13a0a3
FINALLY ADDED TESTS FOR ALL TYPES OMG
SwayStar123 Jul 11, 2022
d514b19
Merge branch 'master' into storage_vec-swaystar123
SwayStar123 Jul 11, 2022
d6601a2
Merge branch 'master' into storage_vec-swaystar123
SwayStar123 Jul 12, 2022
e2d1deb
alphabetical order
SwayStar123 Jul 13, 2022
f39495d
Merge branch 'storage_vec-swaystar123' of https://github.com/FuelLabs…
SwayStar123 Jul 13, 2022
01eb00a
cargo fmt + changed the b256 consts
SwayStar123 Jul 13, 2022
7468ad6
removed unncessary len checks
SwayStar123 Jul 13, 2022
114e498
Merge branch 'master' into storage_vec-swaystar123
SwayStar123 Jul 13, 2022
477b5b0
Merge branch 'master' into storage_vec-swaystar123
adlerjohn Jul 14, 2022
de75364
Merge branch 'master' into storage_vec-swaystar123
mohammadfawaz Jul 14, 2022
a73f659
Merge branch 'master' into storage_vec-swaystar123
SwayStar123 Jul 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 189 additions & 0 deletions sway-lib-std/src/storage.sw
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
library r#storage;

use ::assert::assert;
SwayStar123 marked this conversation as resolved.
Show resolved Hide resolved
use ::hash::sha256;
use ::option::Option;
use ::result::Result;
use ::context::registers::stack_ptr;

/// Store a stack variable in storage.
Expand Down Expand Up @@ -106,3 +109,189 @@ impl<K, V> StorageMap<K, V> {
get::<V>(key)
}
}

/// A persistant vector struct
pub struct StorageVec<V> {}

impl<V> StorageVec<V> {
/// Appends the value to the end of the vector
SwayStar123 marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Arguments
///
/// * `value` - The item being added to the end of the vector
#[storage(read, write)]
pub fn push(self, value: V) {
// The length of the vec is stored in the __get_storage_key() slot
let len = get::<u64>(__get_storage_key());

// Storing the value at the current length index (if this is the first item, starts off at 0)
let key = sha256((len, __get_storage_key()));
SwayStar123 marked this conversation as resolved.
Show resolved Hide resolved
store::<V>(key, value);

// Incrementing the length
store(__get_storage_key(), len + 1);
}

/// Removes the last element of the vector and returns it, None if empty
#[storage(read, write)]
pub fn pop(self) -> Option<V> {
let len = get::<u64>(__get_storage_key());
// if the length is 0, there is no item to pop from the vec
if len == 0 {
return Option::None;
}
adlerjohn marked this conversation as resolved.
Show resolved Hide resolved

// reduces len by 1, effectively removing the last item in the vec
store(__get_storage_key(), len - 1);

let key = sha256((len - 1, __get_storage_key()));
SwayStar123 marked this conversation as resolved.
Show resolved Hide resolved
Option::Some::<V>(get::<V>(key))
}

/// Gets the value in the given index, None if index is out of bounds
///
/// # Arguments
///
/// * `index` - The index of the vec to retrieve the item from
#[storage(read)]
pub fn get(self, index: u64) -> Option<V> {
let len = get::<u64>(__get_storage_key());
// if the index is larger or equal to len, there is no item to return
if len <= index {
return Option::None;
}

let key = sha256((index, __get_storage_key()));
Option::Some::<V>(get::<V>(key))
}

/// Removes the element in the given index and moves all the element in the following indexes
/// Down one index. Also returns the element
///
/// # WARNING
///
/// Expensive for larger vecs
///
/// # Arguments
///
/// * `index` - The index of the vec to remove the item from
///
/// # Reverts
///
/// Reverts if index is larger or equal to length of the vec
#[storage(read, write)]
pub fn remove(self, index: u64) -> V {
let len = get::<u64>(__get_storage_key());
// if the index is larger or equal to len, there is no item to remove
assert(index < len);

// gets the element before removing it, so it can be returned
let removed_element = get::<V>(sha256((index, __get_storage_key())));

// for every element in the vec with an index greater than the input index,
// shifts the index for that element down one
let mut count = index + 1;
while count < len {
// gets the storage location for the previous index
let key = sha256((count - 1, __get_storage_key()));
// moves the element of the current index into the previous index
store::<V>(key, get::<V>(sha256((count, __get_storage_key()))));

count += 1;
}

// decrements len by 1
store(__get_storage_key(), len - 1);

removed_element
}

/// Removes the element at the specified index and fills it with the last element
/// Does not preserve ordering. Also returns the element
///
/// # Arguments
///
/// * `index` - The index of the vec to remove the item from
///
/// # Reverts
///
/// Reverts if index is larger or equal to length of the vec
#[storage(read, write)]
pub fn swap_remove(self, index: u64) -> V {
let len = get::<u64>(__get_storage_key());
// if the index is larger or equal to len, there is no item to remove
assert(index < len);

let hash_of_to_be_removed = sha256((index, __get_storage_key()));
// gets the element before removing it, so it can be returned
let element_to_be_removed = get::<V>(hash_of_to_be_removed);

let last_element = get::<V>(sha256((len - 1, __get_storage_key())));
store::<V>(hash_of_to_be_removed, last_element);

// decrements len by 1
store(__get_storage_key(), len - 1);

element_to_be_removed
}

/// Inserts the value at the given index, moving the current index's value aswell as the following's
/// Up one index
///
/// # WARNING
///
/// Expensive for larger vecs
///
/// # Arguments
///
/// * `index` - The index of the vec to insert the item into
/// * `value` - The value to insert into the vec
///
/// # Reverts
///
/// Reverts if index is larger than length of the vec
#[storage(read, write)]
pub fn insert(self, index: u64, value: V) {
let len = get::<u64>(__get_storage_key());
// if the index is larger than len, there is no space to insert
assert(index <= len);

// for every element in the vec with an index larger than the input index,
// move the element up one index.
// performed in reverse to prevent data overwriting
let mut count = len-1;
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if I call insert() on an empty vector (i.e. len = 0) and set index = 0? Wouldn't len - 1 cause a problem?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch

Copy link
Member Author

Choose a reason for hiding this comment

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

then it would underflow and panic right?

Copy link
Contributor

Choose a reason for hiding this comment

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

But this should work, no? Shouldn't Inserting at index len (even if len is zero) exhibit the same behavior as push?

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope, even rust panics here

Copy link
Contributor

@mohammadfawaz mohammadfawaz Jul 14, 2022

Choose a reason for hiding this comment

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

Hmm this is not panicking for me: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c05a852a5b60fb1f13fa37bfd783b1f3

Even then, I think relying on an underflow to get a panic is an antipattern. The better practice is to do all your checking in separate code for safety and readability even if that means larger bytecode. The optimizer can then do its magic!

Copy link
Contributor

Choose a reason for hiding this comment

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

As this PR is basically ready aside from this, I won't block the merge but I suggest you create "P: critical" issue tracking the above and hopefully we'll get to it asap.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah sorry my bad I looked at the wrong thing, yeah i guess this works in rust.

After sway starts allowing methods to call other methods ill just add a if len == 0 before the while loop there to early return with a push

while count >= index {
let key = sha256((count + 1, __get_storage_key()));
// shifts all the values up one index
store::<V>(key, get::<V>(sha256((count, __get_storage_key()))));

count -= 1
}

// inserts the value into the now unused index
let key = sha256((index, __get_storage_key()));
store::<V>(key, value);

// increments len by 1
store(__get_storage_key(), len + 1);
}

/// Returns the length of the vector
#[storage(read)]
pub fn len(self) -> u64 {
get::<u64>(__get_storage_key())
}

/// Checks whether the len is 0 or not
#[storage(read)]
pub fn is_empty(self) -> bool {
let len = get::<u64>(__get_storage_key());
len == 0
}

/// Sets the len to 0
#[storage(write)]
pub fn clear(self) {
store(__get_storage_key(), 0);
}
}
2 changes: 1 addition & 1 deletion test/src/sdk-harness/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ while true; do
fi
done

test_dirs="${base_dir}/test_artifacts/* ${base_dir}/test_projects/*"
test_dirs="${base_dir}/test_artifacts/* ${base_dir}/test_projects/* ${base_dir}/test_artifacts/*/*"

for test_dir in $test_dirs; do
if [[ -f "${test_dir}/Forc.toml" ]]; then
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out
target
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[[package]]
name = 'core'
source = 'path+from-root-0A9A4F1E1D370290'
dependencies = []

[[package]]
name = 'std'
source = 'path+from-root-0A9A4F1E1D370290'
dependencies = ['core']

[[package]]
name = 'svec_u8'
source = 'root'
dependencies = ['std']
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "svec_u8"

[dependencies]
std = { path = "../../../../../../sway-lib-std" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
contract;

use std::{option::Option, result::Result, storage::StorageVec};

abi MyContract {
#[storage(read, write)]
fn u8_push(value: u8);
#[storage(read)]
fn u8_get(index: u64) -> u8;
#[storage(read, write)]
fn u8_pop() -> u8;
#[storage(read, write)]
fn u8_remove(index: u64) -> u8;
#[storage(read, write)]
fn u8_swap_remove(index: u64) -> u8;
#[storage(read, write)]
fn u8_insert(index: u64, value: u8);
#[storage(read)]
fn u8_len() -> u64;
#[storage(read)]
fn u8_is_empty() -> bool;
#[storage(write)]
fn u8_clear();
}

storage {
my_vec: StorageVec<u8> = StorageVec {},
}

impl MyContract for Contract {
#[storage(read, write)]
fn u8_push(value: u8) {
storage.my_vec.push(value);
}
#[storage(read)]
fn u8_get(index: u64) -> u8 {
storage.my_vec.get(index).unwrap()
}
#[storage(read, write)]
fn u8_pop() -> u8 {
storage.my_vec.pop().unwrap()
}
#[storage(read, write)]
fn u8_remove(index: u64) -> u8 {
storage.my_vec.remove(index)
}
#[storage(read, write)]
fn u8_swap_remove(index: u64) -> u8 {
storage.my_vec.swap_remove(index)
}
#[storage(read, write)]
fn u8_insert(index: u64, value: u8) {
storage.my_vec.insert(index, value);
}
#[storage(read)]
fn u8_len() -> u64 {
storage.my_vec.len()
}
#[storage(read)]
fn u8_is_empty() -> bool {
storage.my_vec.is_empty()
}
#[storage(write)]
fn u8_clear() {
storage.my_vec.clear();
}
}
1 change: 1 addition & 0 deletions test/src/sdk-harness/test_projects/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ mod registers;
mod script_data;
mod storage;
mod storage_map;
mod storage_vec;
mod token_ops;
mod tx_fields;
1 change: 1 addition & 0 deletions test/src/sdk-harness/test_projects/storage_vec/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod u8;
Loading