Skip to content

Commit

Permalink
feat(corelib): storage vectors iterators (#6941)
Browse files Browse the repository at this point in the history
  • Loading branch information
julio4 authored Jan 16, 2025
1 parent 1ad3115 commit 57c7421
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 8 deletions.
16 changes: 14 additions & 2 deletions corelib/src/starknet/storage.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@
//! address (the `sn_keccak` hash of the variable name) combined with the mapping keys or vector
//! indices.
//! See their respective module documentation for more details.

use core::hash::HashStateTrait;
#[allow(unused_imports)]
use core::pedersen::HashState;
Expand All @@ -161,7 +160,10 @@ mod sub_pointers;
pub use sub_pointers::{SubPointers, SubPointersForward, SubPointersMut, SubPointersMutForward};

mod vec;
use vec::{MutableVecIndexView, VecIndexView};
use vec::{
MutableVecIndexView, MutableVecIntoIterRange, PathableMutableVecIntoIterRange,
PathableVecIntoIterRange, VecIndexView, VecIntoIterRange,
};
pub use vec::{MutableVecTrait, Vec, VecTrait};

/// A pointer to an address in storage, can be used to read and write values, if the generic type
Expand Down Expand Up @@ -539,3 +541,13 @@ trait MutableTrait<T> {
impl MutableImpl<T> of MutableTrait<Mutable<T>> {
type InnerType = T;
}

/// Trait for turning collection of values into an iterator over a specific range.
pub trait IntoIterRange<T> {
type IntoIter;
impl Iterator: Iterator<Self::IntoIter>;
/// Creates an iterator over a range from a collection.
fn into_iter_range(self: T, range: core::ops::Range<u64>) -> Self::IntoIter;
/// Creates an iterator over the full range of a collection.
fn into_iter_full_range(self: T) -> Self::IntoIter;
}
110 changes: 106 additions & 4 deletions corelib/src/starknet/storage/vec.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@
//! arr
//! }
//! ```

use core::Option;
use core::ops::Range;
use super::{
Mutable, StorageAsPath, StorageAsPointer, StoragePath, StoragePathTrait, StoragePathUpdateTrait,
StoragePointer0Offset, StoragePointerReadAccess, StoragePointerWriteAccess,
IntoIterRange, Mutable, StorageAsPath, StorageAsPointer, StoragePath, StoragePathTrait,
StoragePathUpdateTrait, StoragePointer0Offset, StoragePointerReadAccess,
StoragePointerWriteAccess,
};

/// Represents a dynamic array in contract storage.
Expand Down Expand Up @@ -396,3 +396,105 @@ pub impl MutableVecIndexView<
(*self).at(index)
}
}

/// An iterator struct over a `Vec` in storage.
#[derive(Drop)]
pub struct VecIter<T, impl VecTraitImpl: VecTrait<T>> {
vec: T,
current_index: crate::ops::RangeIterator<u64>,
}

impl VecIterator<T, impl VecTraitImpl: VecTrait<T>, +Drop<T>, +Copy<T>> of Iterator<VecIter<T>> {
type Item = StoragePath<VecTraitImpl::ElementType>;
fn next(ref self: VecIter<T>) -> Option<Self::Item> {
self.vec.get(self.current_index.next()?)
}
}

// Implement `IntoIterRange` for `StoragePath<Vec<T>>`
pub impl VecIntoIterRange<
T, impl VecTraitImpl: VecTrait<StoragePath<Vec<T>>>,
> of IntoIterRange<StoragePath<Vec<T>>> {
type IntoIter = VecIter<StoragePath<Vec<T>>, VecTraitImpl>;
#[inline]
fn into_iter_range(self: StoragePath<Vec<T>>, range: Range<u64>) -> Self::IntoIter {
VecIter { current_index: range.into_iter(), vec: self }
}
#[inline]
fn into_iter_full_range(self: StoragePath<Vec<T>>) -> Self::IntoIter {
VecIter { current_index: (0..core::num::traits::Bounded::MAX).into_iter(), vec: self }
}
}

/// Implement `IntoIterRange` for any type that implements StorageAsPath into a storage path
/// that implements VecTrait.
pub impl PathableVecIntoIterRange<
T,
+Destruct<T>,
impl PathImpl: StorageAsPath<T>,
impl VecTraitImpl: VecTrait<StoragePath<PathImpl::Value>>,
> of IntoIterRange<T> {
type IntoIter = VecIter<StoragePath<PathImpl::Value>, VecTraitImpl>;
#[inline]
fn into_iter_range(self: T, range: Range<u64>) -> Self::IntoIter {
VecIter { current_index: range.into_iter(), vec: self.as_path() }
}
#[inline]
fn into_iter_full_range(self: T) -> Self::IntoIter {
let vec = self.as_path();
VecIter { current_index: (0..core::num::traits::Bounded::MAX).into_iter(), vec }
}
}

/// An iterator struct over a `Mutable<Vec>` in storage.
#[derive(Drop)]
struct MutableVecIter<T, impl MutVecTraitImpl: MutableVecTrait<T>> {
vec: T,
current_index: crate::ops::RangeIterator<u64>,
}

impl MutableVecIterator<
T, +Drop<T>, +Copy<T>, impl MutVecTraitImpl: MutableVecTrait<T>,
> of Iterator<MutableVecIter<T>> {
type Item = StoragePath<Mutable<MutVecTraitImpl::ElementType>>;
fn next(ref self: MutableVecIter<T>) -> Option<Self::Item> {
self.vec.get(self.current_index.next()?)
}
}

// Implement `IntoIterRange` for `StoragePath<Mutable<Vec<T>>>`
pub impl MutableVecIntoIterRange<
T, impl MutVecTraitImpl: MutableVecTrait<StoragePath<Mutable<Vec<T>>>>,
> of IntoIterRange<StoragePath<Mutable<Vec<T>>>> {
type IntoIter = MutableVecIter<StoragePath<Mutable<Vec<T>>>, MutVecTraitImpl>;
#[inline]
fn into_iter_range(self: StoragePath<Mutable<Vec<T>>>, range: Range<u64>) -> Self::IntoIter {
MutableVecIter { current_index: range.into_iter(), vec: self }
}
#[inline]
fn into_iter_full_range(self: StoragePath<Mutable<Vec<T>>>) -> Self::IntoIter {
MutableVecIter {
current_index: (0..core::num::traits::Bounded::MAX).into_iter(), vec: self,
}
}
}

/// Implement `IntoIterRange` for any type that implements StorageAsPath into a storage path
/// that implements MutableVecTrait.
pub impl PathableMutableVecIntoIterRange<
T,
+Destruct<T>,
impl PathImpl: StorageAsPath<T>,
impl MutVecTraitImpl: MutableVecTrait<StoragePath<PathImpl::Value>>,
> of IntoIterRange<T> {
type IntoIter = MutableVecIter<StoragePath<PathImpl::Value>, MutVecTraitImpl>;
#[inline]
fn into_iter_range(self: T, range: Range<u64>) -> Self::IntoIter {
MutableVecIter { current_index: range.into_iter(), vec: self.as_path() }
}
#[inline]
fn into_iter_full_range(self: T) -> Self::IntoIter {
let vec = self.as_path();
MutableVecIter { current_index: (0..core::num::traits::Bounded::MAX).into_iter(), vec }
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use starknet::storage::{
MutableVecTrait, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess,
IntoIterRange, MutableVecTrait, StoragePathEntry, StoragePointerReadAccess,
StoragePointerWriteAccess,
};


#[starknet::contract]
mod contract_with_map {
use starknet::storage::Map;
Expand Down Expand Up @@ -32,6 +32,51 @@ fn test_simple_member_write_to_map() {
assert_eq!(vec_entry.read(), 1);
}

#[test]
fn test_vec_iter() {
let mut mut_state = contract_with_vec::contract_state_for_testing();
for i in 0..9_usize {
mut_state.simple.append().write(i);
};

let state = @contract_with_vec::contract_state_for_testing();
let mut i = 0;
for entry in state.simple.into_iter_full_range() {
assert_eq!(entry.read(), i);
i += 1;
};
assert_eq!(i, 9);

let mut i = 2;
for entry in state.simple.into_iter_range(2..5) {
assert_eq!(entry.read(), i);
i += 1;
};
assert_eq!(i, 5);
}

#[test]
fn test_mut_vec_iter() {
let mut mut_state = contract_with_vec::contract_state_for_testing();
for i in 0..9_usize {
mut_state.simple.append().write(i);
};

let mut i = 0;
for entry in mut_state.simple.into_iter_full_range() {
assert_eq!(entry.read(), i);
i += 1;
};
assert_eq!(i, 9);

let mut i = 2;
for entry in mut_state.simple.into_iter_range(2..5) {
assert_eq!(entry.read(), i);
i += 1;
};
assert_eq!(i, 5);
}

#[test]
fn test_simple_member_write_to_vec() {
let mut map_contract_state = contract_with_map::contract_state_for_testing();
Expand Down

0 comments on commit 57c7421

Please sign in to comment.