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

feat(collections): unstable new vector implementation #402

Merged
merged 45 commits into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
1e90139
add unstable feature
austinabell May 4, 2021
1446ad0
feat: setup unstable vec collection and basic implementations
austinabell May 4, 2021
babd92e
setup framework for caching values (wip and unsafe)
austinabell May 4, 2021
c2e39b8
fmt and update pop impl
austinabell May 4, 2021
6efbc64
Merge branch 'master' into unstable_vec
mikedotexe May 4, 2021
13e0662
implement load methods
austinabell May 4, 2021
63de2af
implement swap remove
austinabell May 4, 2021
b884f32
finish base implementation
austinabell May 4, 2021
e5e1ca8
implement iterator functionality
austinabell May 5, 2021
b35a49b
replace casting as potential panic
austinabell May 5, 2021
4f360b8
implement index trait impls
austinabell May 5, 2021
38cc873
port over existing tests
austinabell May 5, 2021
486cd46
swap some tests to use new APIs to test
austinabell May 5, 2021
5aed715
update debug
austinabell May 5, 2021
c0d088d
remove duplicate extend and fix modified match (oops)
austinabell May 6, 2021
8fb7324
this is ugly but seems to solve unsoundness issue
austinabell May 6, 2021
e56b207
Merge branch 'master' of github.com:near/near-sdk-rs into unstable_vec
austinabell May 19, 2021
a6ba75b
migrate vec to store module
austinabell May 19, 2021
75ffd20
Merge branch 'master' of github.com:near/near-sdk-rs into unstable_vec
austinabell Jun 4, 2021
1335407
refactor storage load deserialization to fix bug and cleanup
austinabell Jun 4, 2021
b378bf4
add safety doc on load
austinabell Jun 4, 2021
7a7de54
refactor: update cache interface to avoid unsafe usages
austinabell Jun 11, 2021
e67dc3f
update docs
austinabell Jun 11, 2021
c51b1ce
move stable map to utils
austinabell Jun 11, 2021
fffc91f
fmt
austinabell Jun 11, 2021
39648fa
Merge branch 'master' into unstable_vec
austinabell Jun 11, 2021
82eb8fa
test and fix iterator logic and panic message
austinabell Jun 16, 2021
2f602b9
switch iterator range to use ops::Range
austinabell Jun 16, 2021
7dc6049
update lifetime cast on iter mut function
austinabell Jun 16, 2021
9e54bb6
reuse buffer for serialization on flush (thanks matklad)
austinabell Jun 16, 2021
f9b94f8
switch get mut inner to check index within bounds
austinabell Jun 16, 2021
e531d1c
combine debug impls
austinabell Jun 16, 2021
f6bc152
switch fmt function call to be more explicit
austinabell Jun 16, 2021
4b0da03
remove into conversions
austinabell Jun 16, 2021
032a968
update prefix to be boxed slice to minimize type size
austinabell Jun 16, 2021
a3d344f
Merge branch 'master' into unstable_vec
austinabell Jun 23, 2021
c0cb627
fmt
austinabell Jun 23, 2021
29b0fdf
fix debug derives and iter exports
austinabell Jul 19, 2021
6b89716
Merge branch 'master' of github.com:near/near-sdk-rs into unstable_vec
austinabell Jul 20, 2021
b8380c3
Merge branch 'master' into unstable_vec
austinabell Jul 24, 2021
78875b8
Merge branch 'master' into unstable_vec
austinabell Aug 8, 2021
2b5f8f5
switch to new panic sig
austinabell Aug 8, 2021
00acbb6
rebuild wasm
austinabell Aug 8, 2021
482ab02
Merge branch 'master' of github.com:near/near-sdk-rs into unstable_vec
austinabell Sep 7, 2021
9e0f9ec
update docs
austinabell Sep 7, 2021
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
6 changes: 5 additions & 1 deletion near-sdk/src/store/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
mod lazy;
mod lazy_option;
pub use lazy::Lazy;

mod lazy_option;
pub use lazy_option::LazyOption;

pub mod vec;
pub use vec::Vector;
72 changes: 72 additions & 0 deletions near-sdk/src/store/vec/impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use borsh::{BorshDeserialize, BorshSerialize};

use super::iter::{Iter, IterMut};
use super::{Vector, ERR_INDEX_OUT_OF_BOUNDS};
use crate::env;

impl<T> Drop for Vector<T>
where
T: BorshSerialize,
{
fn drop(&mut self) {
self.flush()
}
}

impl<'a, T> IntoIterator for &'a Vector<T>
where
T: BorshSerialize + BorshDeserialize,
{
type Item = &'a T;
type IntoIter = Iter<'a, T>;

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

impl<'a, T> IntoIterator for &'a mut Vector<T>
where
T: BorshSerialize + BorshDeserialize,
{
type Item = &'a mut T;
type IntoIter = IterMut<'a, T>;

fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}

impl<T> Extend<T> for Vector<T>
where
T: BorshSerialize + BorshDeserialize,
{
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = T>,
{
for item in iter {
self.push(item)
}
}
}

impl<T> core::ops::Index<u32> for Vector<T>
where
T: BorshSerialize + BorshDeserialize,
{
type Output = T;

fn index(&self, index: u32) -> &Self::Output {
self.get(index).unwrap_or_else(|| env::panic_str(ERR_INDEX_OUT_OF_BOUNDS))
}
}

impl<T> core::ops::IndexMut<u32> for Vector<T>
where
T: BorshSerialize + BorshDeserialize,
{
fn index_mut(&mut self, index: u32) -> &mut Self::Output {
self.get_mut(index).unwrap_or_else(|| env::panic_str(ERR_INDEX_OUT_OF_BOUNDS))
}
}
158 changes: 158 additions & 0 deletions near-sdk/src/store/vec/iter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
use borsh::{BorshDeserialize, BorshSerialize};
use core::{iter::FusedIterator, ops::Range};

use super::{Vector, ERR_INDEX_OUT_OF_BOUNDS};
use crate::env;

/// An interator over references to each element in the stored vector.
#[derive(Debug)]
pub struct Iter<'a, T>
where
T: BorshSerialize + BorshDeserialize,
{
/// Underlying vector to iterate through
vec: &'a Vector<T>,
/// Range of indices to iterate.
range: Range<u32>,
}

impl<'a, T> Iter<'a, T>
where
T: BorshSerialize + BorshDeserialize,
{
pub(super) fn new(vec: &'a Vector<T>) -> Self {
Self { vec, range: Range { start: 0, end: vec.len() } }
}

/// Returns number of elements left to iterate.
fn remaining(&self) -> usize {
self.range.len()
}
}

impl<'a, T> Iterator for Iter<'a, T>
where
T: BorshSerialize + BorshDeserialize,
{
type Item = &'a T;

fn next(&mut self) -> Option<Self::Item> {
<Self as Iterator>::nth(self, 0)
}

fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.remaining();
(remaining, Some(remaining))
}

fn count(self) -> usize {
self.remaining()
}

fn nth(&mut self, n: usize) -> Option<Self::Item> {
let idx = self.range.nth(n)?;
Some(self.vec.get(idx).unwrap_or_else(|| env::panic_str(ERR_INDEX_OUT_OF_BOUNDS)))
}
}

impl<'a, T> ExactSizeIterator for Iter<'a, T> where T: BorshSerialize + BorshDeserialize {}
impl<'a, T> FusedIterator for Iter<'a, T> where T: BorshSerialize + BorshDeserialize {}

impl<'a, T> DoubleEndedIterator for Iter<'a, T>
where
T: BorshSerialize + BorshDeserialize,
{
fn next_back(&mut self) -> Option<Self::Item> {
<Self as DoubleEndedIterator>::nth_back(self, 0)
}

fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
let idx = self.range.nth_back(n)?;
Some(self.vec.get(idx).unwrap_or_else(|| env::panic_str(ERR_INDEX_OUT_OF_BOUNDS)))
}
}

/// An iterator over exclusive references to each element of a stored vector.
#[derive(Debug)]
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want the Debug here? Genuinely don't know but saw this in near-sdk/src/collections/vector.rs and thought I'd mentioned it, seeing if this is a good pattern to follow:
#[cfg_attr(not(feature = "expensive-debug"), derive(Debug))]

#[cfg_attr(not(feature = "expensive-debug"), derive(Debug))]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, since derived, it would use this pattern for when the feature is enabled. There is nothing in the iterator that would be different here

pub struct IterMut<'a, T>
where
T: BorshSerialize + BorshDeserialize,
{
/// Mutable reference to vector used to iterate through.
vec: &'a mut Vector<T>,
/// Range of indices to iterate.
range: Range<u32>,
}

impl<'a, T> IterMut<'a, T>
where
T: BorshSerialize + BorshDeserialize,
{
/// Creates a new iterator for the given storage vector.
pub(crate) fn new(vec: &'a mut Vector<T>) -> Self {
let end = vec.len();
Self { vec, range: Range { start: 0, end } }
}

/// Returns the amount of remaining elements to yield by the iterator.
fn remaining(&self) -> usize {
self.range.len()
}
}

impl<'a, T> IterMut<'a, T>
where
T: BorshSerialize + BorshDeserialize,
{
fn get_mut<'b>(&'b mut self, at: u32) -> Option<&'a mut T> {
self.vec.get_mut(at).map(|value| {
//* SAFETY: The lifetime can be swapped here because we can assert that the iterator
//* will only give out one mutable reference for every individual item
//* during the iteration, and there is no overlap. This must be checked
//* that no element in this iterator is ever revisited during iteration.
unsafe { &mut *(value as *mut T) }
})
}
}

impl<'a, T> Iterator for IterMut<'a, T>
where
T: BorshSerialize + BorshDeserialize,
{
type Item = &'a mut T;

fn next(&mut self) -> Option<Self::Item> {
<Self as Iterator>::nth(self, 0)
}

fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.remaining();
(remaining, Some(remaining))
}

fn count(self) -> usize {
self.remaining()
}

fn nth(&mut self, n: usize) -> Option<Self::Item> {
let idx = self.range.nth(n)?;
Some(self.get_mut(idx).unwrap_or_else(|| env::panic_str(ERR_INDEX_OUT_OF_BOUNDS)))
}
}

impl<'a, T> ExactSizeIterator for IterMut<'a, T> where T: BorshSerialize + BorshDeserialize {}
impl<'a, T> FusedIterator for IterMut<'a, T> where T: BorshSerialize + BorshDeserialize {}

impl<'a, T> DoubleEndedIterator for IterMut<'a, T>
where
T: BorshSerialize + BorshDeserialize,
{
fn next_back(&mut self) -> Option<Self::Item> {
<Self as DoubleEndedIterator>::nth_back(self, 0)
}

fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
let idx = self.range.nth_back(n)?;
Some(self.get_mut(idx).unwrap_or_else(|| env::panic_str(ERR_INDEX_OUT_OF_BOUNDS)))
}
}
Loading