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

StorageVec #2048

merged 68 commits into from
Jul 14, 2022

Conversation

SwayStar123
Copy link
Member

Making a vector for use in storage, with many utility functions

@SwayStar123
Copy link
Member Author

closes #2035

@SwayStar123 SwayStar123 marked this pull request as draft June 19, 2022 09:21
@SwayStar123 SwayStar123 requested a review from adlerjohn June 19, 2022 09:22
@SwayStar123 SwayStar123 assigned Braqzen and SwayStar123 and unassigned Braqzen Jun 19, 2022
@SwayStar123 SwayStar123 requested a review from Braqzen June 19, 2022 09:22
@Braqzen Braqzen added enhancement New feature or request good first issue Good for newcomers lib: std Standard library labels Jun 19, 2022
@Braqzen Braqzen requested a review from nfurfaro June 19, 2022 11:08
sway-lib-std/src/vec.sw Outdated Show resolved Hide resolved
sway-lib-std/src/vec.sw Outdated Show resolved Hide resolved
sway-lib-std/src/vec.sw Outdated Show resolved Hide resolved
sway-lib-std/src/vec.sw Outdated Show resolved Hide resolved
sway-lib-std/src/vec.sw Outdated Show resolved Hide resolved
sway-lib-std/src/vec.sw Outdated Show resolved Hide resolved
sway-lib-std/src/vec.sw Outdated Show resolved Hide resolved
sway-lib-std/src/vec.sw Outdated Show resolved Hide resolved
@mohammadfawaz
Copy link
Contributor

mohammadfawaz commented Jun 19, 2022

Nice work! 🚀

Even though I like seeing all the utility functions you have here, I recommend starting with only the core ones:

  • pub fn push(mut self, value: T)
  • pub fn get(self, index: u64) -> Option<T>
  • pub fn clear(mut self)
  • pub fn is_empty(self) -> bool
  • pub fn len(self) -> u64

It would be weird to have all the extra utility functions for StorageVec before having them for Vec. Also, writing a test for all these utilities at once for all possible data types is going to take forever. The simple list above will be easier to test, review, and document well. There are some non-obvious things going (like why is len stored in __get_storage_key(), etc.).

If you want, keep this PR open with only the core functions, and create a separate PR based on this one that adds the other stuff. We will prioritize reviewing and getting this one in first with its tests, and then focus on the other stuff in the other PR.

@SwayStar123
Copy link
Member Author

SwayStar123 commented Jun 19, 2022

Nice work! 🚀

Even though I like seeing all the utility functions you have here, I recommend starting with only the core ones:

  • pub fn push(mut self, value: T)
  • pub fn get(self, index: u64) -> Option<T>
  • pub fn clear(mut self)
  • pub fn is_empty(self) -> bool
  • pub fn len(self) -> u64

It would be weird to have all the extra utility functions for StorageVec before having them for Vec. Also, writing a test for all these utilities at once for all possible data types is going to take forever. The simple list above will be easier to test, review, and document well. There are some non-obvious things going (like why is len stored in __get_storage_key(), etc.).

If you want, keep this PR open with only the core functions, and create a separate PR based on this one that adds the other stuff. We will prioritize reviewing and getting this one in first with its tests, and then focus on the other stuff in the other PR.

Added clear(), len(), is_empty()

My reasoning for adding those specific functions was that those functions are whats actually blocking my app dev, which is why i started working on a storagevec. These two (remove, insert) functions seem pretty basic and very useful so im thinking of keeping them and potentially even adding swap_remove for gas efficiency. After that ill work on testing

@adlerjohn adlerjohn removed the good first issue Good for newcomers label Jun 19, 2022
@adlerjohn
Copy link
Contributor

and potentially even adding swap_remove for gas efficiency

To echo @mohammadfawaz's suggestion: smaller atomic PRs are easier to review and faster to merge. It's an art to hold yourself back and break down your work into pieces, but learning to do this well will pay dividends.

sway-lib-std/src/storage.sw Show resolved Hide resolved
sway-lib-std/src/storage.sw Show resolved Hide resolved
sway-lib-std/src/storage.sw Show resolved Hide resolved
Copy link
Contributor

@Braqzen Braqzen left a comment

Choose a reason for hiding this comment

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

  • can_insert() - Given that the entire vector is being changed which assertion do you think is needed before & after the insertion?
    • The same applies to can_remove()
  • can_remove() tests the remove() method which uses a loop. The current test checks a single value unlike insert() which also has a loop but its test checks the other values. What do you think can go wrong in a method that uses a loop to shift its values but only a single value is checked?
  • There are missing tests for every other built-in type

Copy link
Contributor

@Braqzen Braqzen left a comment

Choose a reason for hiding this comment

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

  • can_remove(), can_swap_remove() & can_insert() have len checks at the beginning of the test. Those should be removed because there is a len check just before each assert_eq!() at the start. We do not need to check each and every operation from the other implemented methods. We only care about the snapshot of the state just before we perform our test. It does not matter how we got there, all that matters is that we know what the state is so when we perform a call to the method we are testing we know that is the only thing that could be changing the state. Could you remove the len check just before the pushing to the vec occurs?
  • Tests look reasonable aside from the duplication. This could probably be transformed to be a single contract that uses generics and likewise the Rust side too. This would mean that each test may have slightly different constants etc. defined somewhere but it would significantly cut down on the code. Is there something blocking a generic implementation?

test/src/sdk-harness/test_projects/storage_vec/mod.rs Outdated Show resolved Hide resolved
test/src/sdk-harness/test_projects/storage_vec/b256/mod.rs Outdated Show resolved Hide resolved
@Braqzen Braqzen dismissed their stale review July 12, 2022 14:03

Don't see an immediate reason to continue to block when code duplication is the only nuisance

Copy link
Contributor

@mohammadfawaz mohammadfawaz left a comment

Choose a reason for hiding this comment

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

This is really impressive work. Well done!

sway-lib-std/src/storage.sw Show resolved Hide resolved
// 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

@SwayStar123
Copy link
Member Author

  • can_remove(), can_swap_remove() & can_insert() have len checks at the beginning of the test. Those should be removed because there is a len check just before each assert_eq!() at the start. We do not need to check each and every operation from the other implemented methods. We only care about the snapshot of the state just before we perform our test. It does not matter how we got there, all that matters is that we know what the state is so when we perform a call to the method we are testing we know that is the only thing that could be changing the state. Could you remove the len check just before the pushing to the vec occurs?
  • Tests look reasonable aside from the duplication. This could probably be transformed to be a single contract that uses generics and likewise the Rust side too. This would mean that each test may have slightly different constants etc. defined somewhere but it would significantly cut down on the code. Is there something blocking a generic implementation?

ABIs will never support generics, so this will never be possible

Copy link
Contributor

@adlerjohn adlerjohn left a comment

Choose a reason for hiding this comment

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

Due to the general lacking of being able to call functions properly when they're in the wrong order, this is fine for now. Can you file an issue to abstract away key calculation into a function?

@SwayStar123 SwayStar123 enabled auto-merge (squash) July 14, 2022 12:49
@SwayStar123 SwayStar123 merged commit 1ea6e8b into master Jul 14, 2022
@SwayStar123 SwayStar123 deleted the storage_vec-swaystar123 branch July 14, 2022 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request lib: std Standard library
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Implement Vec in storage, potentially starting with a new type StorageVec
5 participants