-
Notifications
You must be signed in to change notification settings - Fork 5.4k
StorageVec #2048
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
Changes from all commits
2a7e168
23f925f
81a0ea6
7985f55
28e9be3
6b629db
a4a9093
f09060f
9c2a914
37ed447
51961a7
c42229d
8b059b3
082ded9
f6315fa
9ddaf7d
b22e00d
049b162
cbfbedc
75c414b
0da645a
da514b7
5f50e40
c04be39
3ad6fca
6bcfac1
859b0f6
7092230
ed76626
f90d304
b192910
3293293
7f23319
0ad4143
1c32489
9dc6a5c
a106f1c
f5b78c8
d242c9c
c9ce95f
578d9e3
27979d8
d9f17e3
f51da9f
57e4aef
6e65c39
5796f40
bcae640
a6b6ab8
bfae279
2ea82e7
06805d8
5adddc5
a1b3843
4b52750
3d62d03
ab32539
b13a0a3
d514b19
d6601a2
e2d1deb
f39495d
01eb00a
7468ad6
114e498
477b5b0
de75364
a73f659
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,11 @@ | ||
| library r#storage; | ||
|
|
||
| use ::hash::sha256; | ||
| use ::assert::assert; | ||
| use ::context::registers::stack_ptr; | ||
| use ::hash::sha256; | ||
| use ::option::Option; | ||
| use ::result::Result; | ||
|
|
||
|
|
||
| /// Store a stack variable in storage. | ||
| #[storage(write)]pub fn store<T>(key: b256, value: T) { | ||
|
|
@@ -106,3 +110,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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if I call
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. then it would underflow and panic right?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But this should work, no? Shouldn't Inserting at index
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, even rust panics here
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
| } | ||
| } | ||
| 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-BAD4826C86E6CE3D' | ||
| dependencies = [] | ||
|
|
||
| [[package]] | ||
| name = 'std' | ||
| source = 'path+from-root-BAD4826C86E6CE3D' | ||
| dependencies = ['core'] | ||
|
|
||
| [[package]] | ||
| name = 'svec_b256' | ||
| source = 'root' | ||
| dependencies = ['std'] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| [project] | ||
| authors = ["Fuel Labs <[email protected]>"] | ||
| entry = "main.sw" | ||
| license = "Apache-2.0" | ||
| name = "svec_b256" | ||
|
|
||
| [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 b256_push(value: b256); | ||
| #[storage(read)] | ||
| fn b256_get(index: u64) -> b256; | ||
| #[storage(read, write)] | ||
| fn b256_pop() -> b256; | ||
| #[storage(read, write)] | ||
| fn b256_remove(index: u64) -> b256; | ||
| #[storage(read, write)] | ||
| fn b256_swap_remove(index: u64) -> b256; | ||
| #[storage(read, write)] | ||
| fn b256_insert(index: u64, value: b256); | ||
| #[storage(read)] | ||
| fn b256_len() -> u64; | ||
| #[storage(read)] | ||
| fn b256_is_empty() -> bool; | ||
| #[storage(write)] | ||
| fn b256_clear(); | ||
| } | ||
|
|
||
| storage { | ||
| my_vec: StorageVec<b256> = StorageVec {}, | ||
| } | ||
|
|
||
| impl MyContract for Contract { | ||
| #[storage(read, write)] | ||
| fn b256_push(value: b256) { | ||
| storage.my_vec.push(value); | ||
| } | ||
| #[storage(read)] | ||
| fn b256_get(index: u64) -> b256 { | ||
| storage.my_vec.get(index).unwrap() | ||
| } | ||
| #[storage(read, write)] | ||
| fn b256_pop() -> b256 { | ||
| storage.my_vec.pop().unwrap() | ||
| } | ||
| #[storage(read, write)] | ||
| fn b256_remove(index: u64) -> b256 { | ||
| storage.my_vec.remove(index) | ||
| } | ||
| #[storage(read, write)] | ||
| fn b256_swap_remove(index: u64) -> b256 { | ||
| storage.my_vec.swap_remove(index) | ||
| } | ||
| #[storage(read, write)] | ||
| fn b256_insert(index: u64, value: b256) { | ||
| storage.my_vec.insert(index, value); | ||
| } | ||
| #[storage(read)] | ||
| fn b256_len() -> u64 { | ||
| storage.my_vec.len() | ||
| } | ||
| #[storage(read)] | ||
| fn b256_is_empty() -> bool { | ||
| storage.my_vec.is_empty() | ||
| } | ||
| #[storage(write)] | ||
| fn b256_clear() { | ||
| storage.my_vec.clear(); | ||
| } | ||
| } |
| 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-112F6FFCFCECE066' | ||
| dependencies = [] | ||
|
|
||
| [[package]] | ||
| name = 'std' | ||
| source = 'path+from-root-112F6FFCFCECE066' | ||
| dependencies = ['core'] | ||
|
|
||
| [[package]] | ||
| name = 'svec_bool' | ||
| source = 'root' | ||
| dependencies = ['std'] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| [project] | ||
| authors = ["Fuel Labs <[email protected]>"] | ||
| entry = "main.sw" | ||
| license = "Apache-2.0" | ||
| name = "svec_bool" | ||
|
|
||
| [dependencies] | ||
| std = { path = "../../../../../../sway-lib-std" } |
Uh oh!
There was an error while loading. Please reload this page.