From 1e1ab989abcdda4cd78f6098a244a089cb68079f Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Wed, 30 Oct 2024 22:21:11 +0100 Subject: [PATCH 1/5] feat: inclusive/exclusive bounds --- packages/cw-storey/src/backend.rs | 216 +++++++++++++++++----- packages/mocks/src/backend.rs | 81 +++++--- packages/storey-storage/src/storage.rs | 34 ++-- packages/storey/src/containers/column.rs | 28 +-- packages/storey/src/containers/map/mod.rs | 6 +- packages/storey/src/containers/mod.rs | 70 ++++--- packages/storey/src/storage/branch.rs | 132 +++++++------ 7 files changed, 379 insertions(+), 188 deletions(-) diff --git a/packages/cw-storey/src/backend.rs b/packages/cw-storey/src/backend.rs index 8c2bd54..c6d512b 100644 --- a/packages/cw-storey/src/backend.rs +++ b/packages/cw-storey/src/backend.rs @@ -1,3 +1,5 @@ +use std::ops::Bound; + use storey::storage::{IterableStorage, RevIterableStorage, StorageBackend, StorageBackendMut}; /// A wrapper around a type implementing [`cosmwasm_std::Storage`] that integrates it with [`storey`]. @@ -42,18 +44,34 @@ where type ValuesIterator<'a> = Box> + 'a> where Self: 'a; type PairsIterator<'a> = Box, Vec)> + 'a> where Self: 'a; - fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a> { - self.0 - .range_keys(start, end, cosmwasm_std::Order::Ascending) + fn keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::KeysIterator<'a> { + let (start, end) = bounds_to_option(start, end); + + self.0.range_keys( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Ascending, + ) } - fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a> { - self.0 - .range_values(start, end, cosmwasm_std::Order::Ascending) + fn values<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::ValuesIterator<'a> { + let (start, end) = bounds_to_option(start, end); + + self.0.range_values( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Ascending, + ) } - fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a> { - self.0.range(start, end, cosmwasm_std::Order::Ascending) + fn pairs<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::PairsIterator<'a> { + let (start, end) = bounds_to_option(start, end); + + self.0.range( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Ascending, + ) } } @@ -65,18 +83,34 @@ where type ValuesIterator<'a> = Box> + 'a> where Self: 'a; type PairsIterator<'a> = Box, Vec)> + 'a> where Self: 'a; - fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a> { - self.0 - .range_keys(start, end, cosmwasm_std::Order::Ascending) + fn keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::KeysIterator<'a> { + let (start, end) = bounds_to_option(start, end); + + self.0.range_keys( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Ascending, + ) } - fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a> { - self.0 - .range_values(start, end, cosmwasm_std::Order::Ascending) + fn values<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::ValuesIterator<'a> { + let (start, end) = bounds_to_option(start, end); + + self.0.range_values( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Ascending, + ) } - fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a> { - self.0.range(start, end, cosmwasm_std::Order::Ascending) + fn pairs<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::PairsIterator<'a> { + let (start, end) = bounds_to_option(start, end); + + self.0.range( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Ascending, + ) } } @@ -88,30 +122,42 @@ where type RevValuesIterator<'a> = Box> + 'a> where Self: 'a; type RevPairsIterator<'a> = Box, Vec)> + 'a> where Self: 'a; - fn rev_keys<'a>( - &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, - ) -> Self::RevKeysIterator<'a> { - self.0 - .range_keys(start, end, cosmwasm_std::Order::Descending) + fn rev_keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::RevKeysIterator<'a> { + let (start, end) = bounds_to_option(start, end); + + self.0.range_keys( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Descending, + ) } fn rev_values<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevValuesIterator<'a> { - self.0 - .range_values(start, end, cosmwasm_std::Order::Descending) + let (start, end) = bounds_to_option(start, end); + + self.0.range_values( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Descending, + ) } fn rev_pairs<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevPairsIterator<'a> { - self.0.range(start, end, cosmwasm_std::Order::Descending) + let (start, end) = bounds_to_option(start, end); + + self.0.range( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Descending, + ) } } @@ -123,29 +169,111 @@ where type RevValuesIterator<'a> = Box> + 'a> where Self: 'a; type RevPairsIterator<'a> = Box, Vec)> + 'a> where Self: 'a; - fn rev_keys<'a>( - &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, - ) -> Self::RevKeysIterator<'a> { - self.0 - .range_keys(start, end, cosmwasm_std::Order::Descending) + fn rev_keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::RevKeysIterator<'a> { + let (start, end) = bounds_to_option(start, end); + + self.0.range_keys( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Descending, + ) } fn rev_values<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevValuesIterator<'a> { - self.0 - .range_values(start, end, cosmwasm_std::Order::Descending) + let (start, end) = bounds_to_option(start, end); + + self.0.range_values( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Descending, + ) } fn rev_pairs<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevPairsIterator<'a> { - self.0.range(start, end, cosmwasm_std::Order::Descending) + let (start, end) = bounds_to_option(start, end); + + self.0.range( + start.as_deref(), + end.as_deref(), + cosmwasm_std::Order::Descending, + ) + } +} + +fn bounds_to_option(start: Bound<&[u8]>, end: Bound<&[u8]>) -> (Option>, Option>) { + let start = match start { + Bound::Included(key) => Some(key.to_vec()), + Bound::Excluded(key) => { + let mut key = key.to_vec(); + key.push(0); + Some(key) + } + Bound::Unbounded => None, + }; + + let end = match end { + Bound::Included(key) => { + let mut key = key.to_vec(); + key.push(0); + Some(key) + } + Bound::Excluded(key) => Some(key.to_vec()), + Bound::Unbounded => None, + }; + + (start, end) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bounds() { + let mut cw_storage = cosmwasm_std::MemoryStorage::new(); + let mut storage = CwStorage(&mut cw_storage); + + storage.set(b"key1", b"value1"); + storage.set(b"key2", b"value2"); + storage.set(b"key3", b"value3"); + + let keys: Vec> = storage + .keys(Bound::Included(b"key1"), Bound::Excluded(b"key3")) + .collect(); + assert_eq!(keys, vec![b"key1".to_vec(), b"key2".to_vec()]); + + let keys: Vec> = storage + .keys(Bound::Excluded(b"key1"), Bound::Included(b"key3")) + .collect(); + assert_eq!(keys, vec![b"key2".to_vec(), b"key3".to_vec()]); + } + + #[test] + fn test_unbounded() { + let mut cw_storage = cosmwasm_std::MemoryStorage::new(); + let mut storage = CwStorage(&mut cw_storage); + + storage.set(b"key1", b"value1"); + storage.set(b"key2", b"value2"); + storage.set(b"key3", b"value3"); + + let keys: Vec> = storage.keys(Bound::Unbounded, Bound::Unbounded).collect(); + assert_eq!( + keys, + vec![b"key1".to_vec(), b"key2".to_vec(), b"key3".to_vec()] + ); + + let keys: Vec> = storage + .keys(Bound::Unbounded, Bound::Excluded(b"key3")) + .collect(); + assert_eq!(keys, vec![b"key1".to_vec(), b"key2".to_vec()]); } } diff --git a/packages/mocks/src/backend.rs b/packages/mocks/src/backend.rs index 413be91..f2c5a4b 100644 --- a/packages/mocks/src/backend.rs +++ b/packages/mocks/src/backend.rs @@ -1,4 +1,4 @@ -use std::{cell::UnsafeCell, collections::BTreeMap}; +use std::{cell::UnsafeCell, collections::BTreeMap, ops::Bound}; use storey_storage::{IterableStorage, RevIterableStorage, StorageBackend, StorageBackendMut}; @@ -59,7 +59,7 @@ impl IterableStorage for TestStorage { type ValuesIterator<'a> = Box> + 'a>; type PairsIterator<'a> = Box, Vec)> + 'a>; - fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a> { + fn keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::KeysIterator<'a> { let start = start.map(|x| x.to_vec()); let end = end.map(|x| x.to_vec()); @@ -72,7 +72,7 @@ impl IterableStorage for TestStorage { ) } - fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a> { + fn values<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::ValuesIterator<'a> { let start = start.map(|x| x.to_vec()); let end = end.map(|x| x.to_vec()); @@ -85,7 +85,7 @@ impl IterableStorage for TestStorage { ) } - fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a> { + fn pairs<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::PairsIterator<'a> { let start = start.map(|x| x.to_vec()); let end = end.map(|x| x.to_vec()); @@ -103,38 +103,43 @@ impl RevIterableStorage for TestStorage { type RevValuesIterator<'a> = Box> + 'a>; type RevPairsIterator<'a> = Box, Vec)> + 'a>; - fn rev_keys<'a>( - &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, - ) -> Self::RevKeysIterator<'a> { + fn rev_keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::RevKeysIterator<'a> { Box::new(self.keys(start, end).rev()) } fn rev_values<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevValuesIterator<'a> { Box::new(self.values(start, end).rev()) } fn rev_pairs<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevPairsIterator<'a> { Box::new(self.pairs(start, end).rev()) } } -fn check_bounds(v: &[u8], start: Option<&Vec>, end: Option<&Vec>) -> bool { - if let Some(start) = start { +fn check_bounds(v: &[u8], start: Bound<&Vec>, end: Bound<&Vec>) -> bool { + if let Bound::Included(start) = start { if v < start { return false; } + } else if let Bound::Excluded(start) = start { + if v <= start { + return false; + } } - if let Some(end) = end { + + if let Bound::Included(end) = end { + if v > end { + return false; + } + } else if let Bound::Excluded(end) = end { if v >= end { return false; } @@ -158,28 +163,32 @@ mod tests { storage.set(&[1, 1], b"quux"); storage.set(&[2], b"qux"); - let keys: Vec<_> = storage.keys(None, None).collect(); + let keys: Vec<_> = storage.keys(Bound::Unbounded, Bound::Unbounded).collect(); assert_eq!( keys, vec![vec![0], vec![1], vec![1, 0], vec![1, 1], vec![2]] ); - let some_keys: Vec<_> = storage.keys(Some(&[1]), Some(&[2])).collect(); + let some_keys: Vec<_> = storage + .keys(Bound::Included(&[1]), Bound::Excluded(&[2])) + .collect(); assert_eq!(some_keys, vec![vec![1], vec![1, 0], vec![1, 1]]); - let values: Vec<_> = storage.values(None, None).collect(); + let values: Vec<_> = storage.values(Bound::Unbounded, Bound::Unbounded).collect(); assert_eq!( values.iter().collect::>(), vec![&b"bar"[..], b"baz", b"qux", b"quux", b"qux"] ); - let some_values: Vec<_> = storage.values(Some(&[1]), Some(&[2])).collect(); + let some_values: Vec<_> = storage + .values(Bound::Included(&[1]), Bound::Excluded(&[2])) + .collect(); assert_eq!( some_values.iter().collect::>(), vec![&b"baz"[..], b"qux", b"quux"] ); - let pairs: Vec<_> = storage.pairs(None, None).collect(); + let pairs: Vec<_> = storage.pairs(Bound::Unbounded, Bound::Unbounded).collect(); assert_eq!( pairs, vec![ @@ -191,7 +200,9 @@ mod tests { ] ); - let some_pairs: Vec<_> = storage.pairs(Some(&[1]), Some(&[2])).collect(); + let some_pairs: Vec<_> = storage + .pairs(Bound::Included(&[1]), Bound::Excluded(&[2])) + .collect(); assert_eq!( some_pairs, vec![ @@ -201,28 +212,38 @@ mod tests { ] ); - let rev_keys: Vec<_> = storage.rev_keys(None, None).collect(); + let rev_keys: Vec<_> = storage + .rev_keys(Bound::Unbounded, Bound::Unbounded) + .collect(); assert_eq!( rev_keys, vec![vec![2], vec![1, 1], vec![1, 0], vec![1], vec![0]] ); - let some_rev_keys: Vec<_> = storage.rev_keys(Some(&[1]), Some(&[2])).collect(); - assert_eq!(some_rev_keys, vec![vec![1, 1], vec![1, 0], vec![1]]); + let some_rev_keys: Vec<_> = storage + .rev_keys(Bound::Excluded(&[1]), Bound::Excluded(&[2])) + .collect(); + assert_eq!(some_rev_keys, vec![vec![1, 1], vec![1, 0]]); - let rev_values: Vec<_> = storage.rev_values(None, None).collect(); + let rev_values: Vec<_> = storage + .rev_values(Bound::Unbounded, Bound::Unbounded) + .collect(); assert_eq!( rev_values.iter().collect::>(), vec![&b"qux"[..], b"quux", b"qux", b"baz", b"bar"] ); - let some_rev_values: Vec<_> = storage.rev_values(Some(&[1]), Some(&[2])).collect(); + let some_rev_values: Vec<_> = storage + .rev_values(Bound::Included(&[1]), Bound::Excluded(&[2])) + .collect(); assert_eq!( some_rev_values.iter().collect::>(), vec![&b"quux"[..], b"qux", b"baz"] ); - let rev_pairs: Vec<_> = storage.rev_pairs(None, None).collect(); + let rev_pairs: Vec<_> = storage + .rev_pairs(Bound::Unbounded, Bound::Unbounded) + .collect(); assert_eq!( rev_pairs, vec![ @@ -234,7 +255,9 @@ mod tests { ] ); - let some_rev_pairs: Vec<_> = storage.rev_pairs(Some(&[1]), Some(&[2])).collect(); + let some_rev_pairs: Vec<_> = storage + .rev_pairs(Bound::Included(&[1]), Bound::Excluded(&[2])) + .collect(); assert_eq!( some_rev_pairs, vec![ diff --git a/packages/storey-storage/src/storage.rs b/packages/storey-storage/src/storage.rs index 28c8f2f..2a1d5e3 100644 --- a/packages/storey-storage/src/storage.rs +++ b/packages/storey-storage/src/storage.rs @@ -1,3 +1,5 @@ +use std::ops::Bound; + /// A read interface for binary key-value storage. pub trait Storage { /// Get the value of the key. @@ -60,7 +62,7 @@ pub trait IterableStorage { /// If both `start` and `end` are `None`, the iterator should iterate over all keys. /// /// The range is inclusive for `start` and exclusive for `end`. - fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a>; + fn keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::KeysIterator<'a>; /// Get an iterator over values. /// @@ -71,7 +73,7 @@ pub trait IterableStorage { /// If both `start` and `end` are `None`, the iterator should iterate over all keys. /// /// The range is inclusive for `start` and exclusive for `end`. - fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a>; + fn values<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::ValuesIterator<'a>; /// Get an iterator over key-value pairs. /// @@ -82,7 +84,7 @@ pub trait IterableStorage { /// If both `start` and `end` are `None`, the iterator should iterate over all keys. /// /// The range is inclusive for `start` and exclusive for `end`. - fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a>; + fn pairs<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::PairsIterator<'a>; } impl IterableStorage for &T { @@ -99,7 +101,7 @@ impl IterableStorage for &T { /// If both `start` and `end` are `None`, the iterator should iterate over all keys. /// /// The range is inclusive for `start` and exclusive for `end`. - fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a> { + fn keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::KeysIterator<'a> { (**self).keys(start, end) } @@ -112,7 +114,7 @@ impl IterableStorage for &T { /// If both `start` and `end` are `None`, the iterator should iterate over all keys. /// /// The range is inclusive for `start` and exclusive for `end`. - fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a> { + fn values<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::ValuesIterator<'a> { (**self).values(start, end) } @@ -125,7 +127,7 @@ impl IterableStorage for &T { /// If both `start` and `end` are `None`, the iterator should iterate over all keys. /// /// The range is inclusive for `start` and exclusive for `end`. - fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a> { + fn pairs<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::PairsIterator<'a> { (**self).pairs(start, end) } } @@ -135,15 +137,15 @@ impl IterableStorage for &mut T { type ValuesIterator<'a> = T::ValuesIterator<'a> where Self: 'a; type PairsIterator<'a> = T::PairsIterator<'a> where Self: 'a; - fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a> { + fn keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::KeysIterator<'a> { (**self).keys(start, end) } - fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a> { + fn values<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::ValuesIterator<'a> { (**self).values(start, end) } - fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a> { + fn pairs<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::PairsIterator<'a> { (**self).pairs(start, end) } } @@ -167,19 +169,15 @@ pub trait RevIterableStorage { where Self: 'a; - fn rev_keys<'a>( - &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, - ) -> Self::RevKeysIterator<'a>; + fn rev_keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::RevKeysIterator<'a>; fn rev_values<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevValuesIterator<'a>; fn rev_pairs<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevPairsIterator<'a>; } diff --git a/packages/storey/src/containers/column.rs b/packages/storey/src/containers/column.rs index c93a255..2bb9efe 100644 --- a/packages/storey/src/containers/column.rs +++ b/packages/storey/src/containers/column.rs @@ -471,6 +471,8 @@ pub enum LenError { #[cfg(test)] mod tests { + use std::ops::Bound; + use crate::containers::{BoundedRevIterableAccessor as _, RevIterableAccessor as _}; use super::*; @@ -605,21 +607,21 @@ mod tests { // start and end set assert_eq!( access - .bounded_pairs(Some(2), Some(5)) + .bounded_pairs(Bound::Included(2), Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![(2, 42), (4, 1)] ); assert_eq!( access - .bounded_keys(Some(2), Some(5)) + .bounded_keys(Bound::Included(2), Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![2, 4] ); assert_eq!( access - .bounded_values(Some(2), Some(5)) + .bounded_values(Bound::Included(2), Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![42, 1] @@ -628,21 +630,21 @@ mod tests { // end unset assert_eq!( access - .bounded_pairs(Some(2), None) + .bounded_pairs(Bound::Included(2), Bound::Unbounded) .collect::, _>>() .unwrap(), vec![(2, 42), (4, 1), (5, 2)] ); assert_eq!( access - .bounded_keys(Some(2), None) + .bounded_keys(Bound::Included(2), Bound::Unbounded) .collect::, _>>() .unwrap(), vec![2, 4, 5] ); assert_eq!( access - .bounded_values(Some(2), None) + .bounded_values(Bound::Included(2), Bound::Unbounded) .collect::, _>>() .unwrap(), vec![42, 1, 2] @@ -651,21 +653,21 @@ mod tests { // start unset assert_eq!( access - .bounded_pairs(None, Some(5)) + .bounded_pairs(Bound::Included(1), Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![(1, 1337), (2, 42), (4, 1)] ); assert_eq!( access - .bounded_keys(None, Some(5)) + .bounded_keys(Bound::Included(1), Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![1, 2, 4] ); assert_eq!( access - .bounded_values(None, Some(5)) + .bounded_values(Bound::Included(1), Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![1337, 42, 1] @@ -689,21 +691,21 @@ mod tests { // start and end set assert_eq!( access - .bounded_rev_pairs(Some(2), Some(5)) + .bounded_rev_pairs(Bound::Included(2), Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![(4, 1), (2, 42)] ); assert_eq!( access - .bounded_rev_keys(Some(2), Some(5)) + .bounded_rev_keys(Bound::Included(2), Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![4, 2] ); assert_eq!( access - .bounded_rev_values(Some(2), Some(5)) + .bounded_rev_values(Bound::Included(2), Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![1, 42] @@ -712,7 +714,7 @@ mod tests { // end unset assert_eq!( access - .bounded_rev_pairs(Some(2), None) + .bounded_rev_pairs(Bound::Included(2), Bound::Unbounded) .collect::, _>>() .unwrap(), vec![(5, 2), (4, 1), (2, 42)] diff --git a/packages/storey/src/containers/map/mod.rs b/packages/storey/src/containers/map/mod.rs index 8f5a606..8b1f8cd 100644 --- a/packages/storey/src/containers/map/mod.rs +++ b/packages/storey/src/containers/map/mod.rs @@ -346,6 +346,8 @@ where #[cfg(test)] mod tests { + use std::ops::Bound; + use super::*; use crate::containers::Item; @@ -388,7 +390,7 @@ mod tests { access.entry_mut("baz").set(&69).unwrap(); let items = access - .bounded_pairs(Some("bar"), Some("bazz")) + .bounded_pairs(Bound::Included("bar"), Bound::Excluded("bazz")) .collect::, _>>() .unwrap(); assert_eq!( @@ -431,7 +433,7 @@ mod tests { access.entry_mut(&4).entry_mut("quux").set(&69).unwrap(); let items = access - .bounded_pairs(Some(&2), Some(&4)) + .bounded_pairs(Bound::Included(&2), Bound::Excluded(&4)) .collect::, _>>() .unwrap(); diff --git a/packages/storey/src/containers/mod.rs b/packages/storey/src/containers/mod.rs index 0b00fda..1a3caaa 100644 --- a/packages/storey/src/containers/mod.rs +++ b/packages/storey/src/containers/mod.rs @@ -6,7 +6,7 @@ pub mod common; mod item; pub mod map; -use std::marker::PhantomData; +use std::{marker::PhantomData, ops::Bound}; pub use column::{Column, ColumnAccess}; pub use item::{Item, ItemAccess}; @@ -89,7 +89,7 @@ pub trait IterableAccessor: Sized { &self, ) -> StorableIter::PairsIterator<'_>> { StorableIter { - inner: self.storage().pairs(None, None), + inner: self.storage().pairs(Bound::Unbounded, Bound::Unbounded), phantom: PhantomData, } } @@ -99,7 +99,7 @@ pub trait IterableAccessor: Sized { &self, ) -> StorableKeys::KeysIterator<'_>> { StorableKeys { - inner: self.storage().keys(None, None), + inner: self.storage().keys(Bound::Unbounded, Bound::Unbounded), phantom: PhantomData, } } @@ -110,7 +110,7 @@ pub trait IterableAccessor: Sized { ) -> StorableValues::ValuesIterator<'_>> { StorableValues { - inner: self.storage().values(None, None), + inner: self.storage().values(Bound::Unbounded, Bound::Unbounded), phantom: PhantomData, } } @@ -127,7 +127,7 @@ where ) -> StorableIter::RevPairsIterator<'_>> { StorableIter { - inner: self.storage().rev_pairs(None, None), + inner: self.storage().rev_pairs(Bound::Unbounded, Bound::Unbounded), phantom: PhantomData, } } @@ -138,7 +138,7 @@ where ) -> StorableKeys::RevKeysIterator<'_>> { StorableKeys { - inner: self.storage().rev_keys(None, None), + inner: self.storage().rev_keys(Bound::Unbounded, Bound::Unbounded), phantom: PhantomData, } } @@ -149,7 +149,9 @@ where ) -> StorableValues::RevValuesIterator<'_>> { StorableValues { - inner: self.storage().rev_values(None, None), + inner: self + .storage() + .rev_values(Bound::Unbounded, Bound::Unbounded), phantom: PhantomData, } } @@ -181,8 +183,8 @@ pub trait BoundedIterableAccessor: IterableAccessor { /// Iterate over key-value pairs in this collection, respecting the given bounds. fn bounded_pairs( &self, - start: Option, - end: Option, + start: Bound, + end: Bound, ) -> StorableIter::PairsIterator<'_>> where B: BoundFor, @@ -191,7 +193,10 @@ pub trait BoundedIterableAccessor: IterableAccessor { let end = end.map(|b| b.into_bytes()); StorableIter { - inner: self.storage().pairs(start.as_deref(), end.as_deref()), + inner: self.storage().pairs( + start.as_ref().map(|b| b.as_slice()), + end.as_ref().map(|b| b.as_slice()), + ), phantom: PhantomData, } } @@ -199,8 +204,8 @@ pub trait BoundedIterableAccessor: IterableAccessor { /// Iterate over keys in this collection, respecting the given bounds. fn bounded_keys( &self, - start: Option, - end: Option, + start: Bound, + end: Bound, ) -> StorableKeys::KeysIterator<'_>> where B: BoundFor, @@ -209,7 +214,10 @@ pub trait BoundedIterableAccessor: IterableAccessor { let end = end.map(|b| b.into_bytes()); StorableKeys { - inner: self.storage().keys(start.as_deref(), end.as_deref()), + inner: self.storage().keys( + start.as_ref().map(|b| b.as_slice()), + end.as_ref().map(|b| b.as_slice()), + ), phantom: PhantomData, } } @@ -217,8 +225,8 @@ pub trait BoundedIterableAccessor: IterableAccessor { /// Iterate over values in this collection, respecting the given bounds. fn bounded_values( &self, - start: Option, - end: Option, + start: Bound, + end: Bound, ) -> StorableValues::ValuesIterator<'_>> where B: BoundFor, @@ -227,7 +235,10 @@ pub trait BoundedIterableAccessor: IterableAccessor { let end = end.map(|b| b.into_bytes()); StorableValues { - inner: self.storage().values(start.as_deref(), end.as_deref()), + inner: self.storage().values( + start.as_ref().map(|b| b.as_slice()), + end.as_ref().map(|b| b.as_slice()), + ), phantom: PhantomData, } } @@ -250,8 +261,8 @@ where /// Iterate over key-value pairs in this collection in reverse order, respecting the given bounds. fn bounded_rev_pairs( &self, - start: Option, - end: Option, + start: Bound, + end: Bound, ) -> StorableIter::RevPairsIterator<'_>> where B: BoundFor, @@ -260,7 +271,10 @@ where let end = end.map(|b| b.into_bytes()); StorableIter { - inner: self.storage().rev_pairs(start.as_deref(), end.as_deref()), + inner: self.storage().rev_pairs( + start.as_ref().map(|b| b.as_slice()), + end.as_ref().map(|b| b.as_slice()), + ), phantom: PhantomData, } } @@ -268,8 +282,8 @@ where /// Iterate over keys in this collection in reverse order, respecting the given bounds. fn bounded_rev_keys( &self, - start: Option, - end: Option, + start: Bound, + end: Bound, ) -> StorableKeys::RevKeysIterator<'_>> where B: BoundFor, @@ -278,7 +292,10 @@ where let end = end.map(|b| b.into_bytes()); StorableKeys { - inner: self.storage().rev_keys(start.as_deref(), end.as_deref()), + inner: self.storage().rev_keys( + start.as_ref().map(|b| b.as_slice()), + end.as_ref().map(|b| b.as_slice()), + ), phantom: PhantomData, } } @@ -286,8 +303,8 @@ where /// Iterate over values in this collection in reverse order, respecting the given bounds. fn bounded_rev_values( &self, - start: Option, - end: Option, + start: Bound, + end: Bound, ) -> StorableValues::RevValuesIterator<'_>> where B: BoundFor, @@ -296,7 +313,10 @@ where let end = end.map(|b| b.into_bytes()); StorableValues { - inner: self.storage().rev_values(start.as_deref(), end.as_deref()), + inner: self.storage().rev_values( + start.as_ref().map(|b| b.as_slice()), + end.as_ref().map(|b| b.as_slice()), + ), phantom: PhantomData, } } diff --git a/packages/storey/src/storage/branch.rs b/packages/storey/src/storage/branch.rs index eefbab4..38428c7 100644 --- a/packages/storey/src/storage/branch.rs +++ b/packages/storey/src/storage/branch.rs @@ -1,3 +1,5 @@ +use std::ops::Bound; + use crate::storage::{IterableStorage, RevIterableStorage, Storage, StorageMut}; /// A type representing a storage namespace created by applying a prefix to all keys. @@ -76,7 +78,7 @@ impl IterableStorage for StorageBranch<&S> { type ValuesIterator<'a> = S::ValuesIterator<'a> where Self: 'a; type PairsIterator<'a> = BranchKVIter> where Self: 'a; - fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a> { + fn keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::KeysIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); BranchKeysIter { @@ -88,7 +90,7 @@ impl IterableStorage for StorageBranch<&S> { } } - fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a> { + fn values<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::ValuesIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); self.backend.values( @@ -97,7 +99,7 @@ impl IterableStorage for StorageBranch<&S> { ) } - fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a> { + fn pairs<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::PairsIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); BranchKVIter { @@ -115,7 +117,7 @@ impl IterableStorage for StorageBranch<&mut S> { type ValuesIterator<'a> = S::ValuesIterator<'a> where Self: 'a; type PairsIterator<'a> = BranchKVIter> where Self: 'a; - fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a> { + fn keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::KeysIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); BranchKeysIter { @@ -127,7 +129,7 @@ impl IterableStorage for StorageBranch<&mut S> { } } - fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a> { + fn values<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::ValuesIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); self.backend.values( @@ -136,7 +138,7 @@ impl IterableStorage for StorageBranch<&mut S> { ) } - fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a> { + fn pairs<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::PairsIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); BranchKVIter { @@ -154,11 +156,7 @@ impl RevIterableStorage for StorageBranch<&S> { type RevValuesIterator<'a> = S::RevValuesIterator<'a> where Self: 'a; type RevPairsIterator<'a> = BranchKVIter> where Self: 'a; - fn rev_keys<'a>( - &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, - ) -> Self::RevKeysIterator<'a> { + fn rev_keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::RevKeysIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); BranchKeysIter { @@ -172,8 +170,8 @@ impl RevIterableStorage for StorageBranch<&S> { fn rev_values<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevValuesIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); @@ -185,8 +183,8 @@ impl RevIterableStorage for StorageBranch<&S> { fn rev_pairs<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevPairsIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); @@ -205,11 +203,7 @@ impl RevIterableStorage for StorageBranch<&mut S> { type RevValuesIterator<'a> = S::RevValuesIterator<'a> where Self: 'a; type RevPairsIterator<'a> = BranchKVIter> where Self: 'a; - fn rev_keys<'a>( - &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, - ) -> Self::RevKeysIterator<'a> { + fn rev_keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::RevKeysIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); BranchKeysIter { @@ -223,8 +217,8 @@ impl RevIterableStorage for StorageBranch<&mut S> { fn rev_values<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevValuesIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); @@ -236,8 +230,8 @@ impl RevIterableStorage for StorageBranch<&mut S> { fn rev_pairs<'a>( &'a self, - start: Option<&[u8]>, - end: Option<&[u8]>, + start: Bound<&[u8]>, + end: Bound<&[u8]>, ) -> Self::RevPairsIterator<'a> { let (start, end) = sub_bounds(&self.prefix, start, end); @@ -253,25 +247,31 @@ impl RevIterableStorage for StorageBranch<&mut S> { fn sub_bounds( prefix: &[u8], - start: Option<&[u8]>, - end: Option<&[u8]>, -) -> (Option>, Option>) { + start: Bound<&[u8]>, + end: Bound<&[u8]>, +) -> (Bound>, Bound>) { if prefix.is_empty() { (start.map(|s| s.to_vec()), end.map(|s| s.to_vec())) } else { ( - Some( - start - .map(|s| [prefix, s].concat()) - .unwrap_or(prefix.to_vec()), - ), - Some(end.map(|e| [prefix, e].concat()).unwrap_or_else(|| { - let mut pref = prefix.to_vec(); - if let Some(x) = pref.last_mut() { - *x += 1; - } - pref - })), + // concat prefix and start if bounded + // return just the prefix if unbounded + if let Bound::Unbounded = start { + Bound::Included(prefix.to_vec()) + } else { + start.map(|s| [prefix, s].concat()) + }, + if let Bound::Unbounded = end { + Bound::Excluded({ + let mut pref = prefix.to_vec(); + if let Some(x) = pref.last_mut() { + *x += 1; + } + pref + }) + } else { + end.map(|e| [prefix, e].concat()) + }, ) } } @@ -337,43 +337,61 @@ mod tests { #[test] fn sub_bounds_no_prefix() { assert_eq!( - sub_bounds(&[], Some(b"foo"), Some(b"bar")), - (Some(b"foo".to_vec()), Some(b"bar".to_vec())) + sub_bounds(&[], Bound::Included(b"foo"), Bound::Excluded(b"bar")), + ( + Bound::Included(b"foo".to_vec()), + Bound::Excluded(b"bar".to_vec()) + ) ); assert_eq!( - sub_bounds(&[], Some(b"foo"), None), - (Some(b"foo".to_vec()), None) + sub_bounds(&[], Bound::Included(b"foo"), Bound::Unbounded), + (Bound::Included(b"foo".to_vec()), Bound::Unbounded) ); assert_eq!( - sub_bounds(&[], None, Some(b"bar")), - (None, Some(b"bar".to_vec())) + sub_bounds(&[], Bound::Unbounded, Bound::Excluded(b"bar")), + (Bound::Unbounded, Bound::Excluded(b"bar".to_vec())) ); - assert_eq!(sub_bounds(&[], None, None), (None, None)); + assert_eq!( + sub_bounds(&[], Bound::Unbounded, Bound::Unbounded), + (Bound::Unbounded, Bound::Unbounded) + ); } #[test] fn sub_bounds_with_prefix() { assert_eq!( - sub_bounds(b"foo", Some(b"bar"), Some(b"baz")), - (Some(b"foobar".to_vec()), Some(b"foobaz".to_vec())) + sub_bounds(b"foo", Bound::Included(b"bar"), Bound::Excluded(b"baz")), + ( + Bound::Included(b"foobar".to_vec()), + Bound::Excluded(b"foobaz".to_vec()) + ) ); assert_eq!( - sub_bounds(b"foo", Some(b"bar"), None), - (Some(b"foobar".to_vec()), Some(b"fop".to_vec())) + sub_bounds(b"foo", Bound::Included(b"bar"), Bound::Unbounded), + ( + Bound::Included(b"foobar".to_vec()), + Bound::Excluded(b"fop".to_vec()) + ) ); assert_eq!( - sub_bounds(b"foo", None, Some(b"baz")), - (Some(b"foo".to_vec()), Some(b"foobaz".to_vec())) + sub_bounds(b"foo", Bound::Unbounded, Bound::Excluded(b"baz")), + ( + Bound::Included(b"foo".to_vec()), + Bound::Excluded(b"foobaz".to_vec()) + ) ); assert_eq!( - sub_bounds(b"foo", None, None), - (Some(b"foo".to_vec()), Some(b"fop".to_vec())) + sub_bounds(b"foo", Bound::Unbounded, Bound::Unbounded), + ( + Bound::Included(b"foo".to_vec()), + Bound::Excluded(b"fop".to_vec()) + ) ); } @@ -385,7 +403,7 @@ mod tests { branch.set(b"bar", b"baz"); branch.set(b"qux", b"quux"); - let mut iter = branch.pairs(None, None); + let mut iter = branch.pairs(Bound::Unbounded, Bound::Unbounded); assert_eq!(iter.next(), Some((b"bar".to_vec(), b"baz".to_vec()))); assert_eq!(iter.next(), Some((b"qux".to_vec(), b"quux".to_vec()))); assert_eq!(iter.next(), None); @@ -399,7 +417,7 @@ mod tests { branch.set(b"bar", b"baz"); branch.set(b"qux", b"quux"); - let mut iter = branch.keys(None, None); + let mut iter = branch.keys(Bound::Unbounded, Bound::Unbounded); assert_eq!(iter.next(), Some(b"bar".to_vec())); assert_eq!(iter.next(), Some(b"qux".to_vec())); assert_eq!(iter.next(), None); @@ -413,7 +431,7 @@ mod tests { branch.set(b"bar", b"baz"); branch.set(b"qux", b"quux"); - let mut iter = branch.values(None, None); + let mut iter = branch.values(Bound::Unbounded, Bound::Unbounded); assert_eq!(iter.next(), Some(b"baz".to_vec())); assert_eq!(iter.next(), Some(b"quux".to_vec())); assert_eq!(iter.next(), None); From 2a5394b0f9a201ef1b2a4083a7121723c811c9f3 Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Wed, 30 Oct 2024 22:24:24 +0100 Subject: [PATCH 2/5] bump MSRV --- packages/storey/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/storey/Cargo.toml b/packages/storey/Cargo.toml index f0792cf..87c2055 100644 --- a/packages/storey/Cargo.toml +++ b/packages/storey/Cargo.toml @@ -4,7 +4,7 @@ description = "Storage abstractions for blockchains" readme = "../../README.md" version = "0.3.0" edition = "2021" -rust-version = "1.65" # https://caniuse.rs/features/generic_associated_types +rust-version = "1.77" authors.workspace = true license.workspace = true repository.workspace = true From aebd8f10ecd3c1592036ce24575f3752919fd3a6 Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Mon, 4 Nov 2024 18:24:02 +0100 Subject: [PATCH 3/5] update API docs --- packages/storey-storage/src/storage.rs | 82 +++++++++++--------------- packages/storey/src/containers/mod.rs | 12 ++++ 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/packages/storey-storage/src/storage.rs b/packages/storey-storage/src/storage.rs index 2a1d5e3..c032919 100644 --- a/packages/storey-storage/src/storage.rs +++ b/packages/storey-storage/src/storage.rs @@ -55,35 +55,29 @@ pub trait IterableStorage { /// Get an iterator over keys. /// - /// The iterator should iterate over keys in lexicographical order. + /// The iterator walks keys in lexicographical order. /// - /// If `start` is `None`, the iterator should start from the first key. - /// If `end` is `None`, the iterator should iterate until the last key. - /// If both `start` and `end` are `None`, the iterator should iterate over all keys. - /// - /// The range is inclusive for `start` and exclusive for `end`. + /// The [`Bound`] type is used to specify either end of the range - whether it should be + /// bounded at all, and if so, whether it should be inclusive or exclusive. See the + /// [`Bound`] documentation for more details. fn keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::KeysIterator<'a>; /// Get an iterator over values. /// - /// The iterator should iterate over values corresponding to keys in lexicographical order. - /// - /// If `start` is `None`, the iterator should start from the first key. - /// If `end` is `None`, the iterator should iterate until the last key. - /// If both `start` and `end` are `None`, the iterator should iterate over all keys. + /// The iterator walks values corresponding to keys in lexicographical order. /// - /// The range is inclusive for `start` and exclusive for `end`. + /// The [`Bound`] type is used to specify either end of the range - whether it should be + /// bounded at all, and if so, whether it should be inclusive or exclusive. See the + /// [`Bound`] documentation for more details. fn values<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::ValuesIterator<'a>; /// Get an iterator over key-value pairs. /// - /// The iterator should iterate over key-value pairs in lexicographical order. - /// - /// If `start` is `None`, the iterator should start from the first key. - /// If `end` is `None`, the iterator should iterate until the last key. - /// If both `start` and `end` are `None`, the iterator should iterate over all keys. + /// The iterator walks key-value pairs in lexicographical order. /// - /// The range is inclusive for `start` and exclusive for `end`. + /// The [`Bound`] type is used to specify either end of the range - whether it should be + /// bounded at all, and if so, whether it should be inclusive or exclusive. See the + /// [`Bound`] documentation for more details. fn pairs<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::PairsIterator<'a>; } @@ -92,41 +86,14 @@ impl IterableStorage for &T { type ValuesIterator<'a> = T::ValuesIterator<'a> where Self: 'a; type PairsIterator<'a> = T::PairsIterator<'a> where Self: 'a; - /// Get an iterator over keys. - /// - /// The iterator should iterate over keys in lexicographical order. - /// - /// If `start` is `None`, the iterator should start from the first key. - /// If `end` is `None`, the iterator should iterate until the last key. - /// If both `start` and `end` are `None`, the iterator should iterate over all keys. - /// - /// The range is inclusive for `start` and exclusive for `end`. fn keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::KeysIterator<'a> { (**self).keys(start, end) } - /// Get an iterator over values. - /// - /// The iterator should iterate over values corresponding to keys in lexicographical order. - /// - /// If `start` is `None`, the iterator should start from the first key. - /// If `end` is `None`, the iterator should iterate until the last key. - /// If both `start` and `end` are `None`, the iterator should iterate over all keys. - /// - /// The range is inclusive for `start` and exclusive for `end`. fn values<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::ValuesIterator<'a> { (**self).values(start, end) } - /// Get an iterator over key-value pairs. - /// - /// The iterator should iterate over key-value pairs in lexicographical order. - /// - /// If `start` is `None`, the iterator should start from the first key. - /// If `end` is `None`, the iterator should iterate until the last key. - /// If both `start` and `end` are `None`, the iterator should iterate over all keys. - /// - /// The range is inclusive for `start` and exclusive for `end`. fn pairs<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::PairsIterator<'a> { (**self).pairs(start, end) } @@ -152,7 +119,7 @@ impl IterableStorage for &mut T { /// Iteration interface for binary key-value storage in reverse order. /// -/// The iterator should iterate over key-value pairs in reverse lexicographical order of keys. +/// The iterator walks key-value pairs in reverse lexicographical order of keys. pub trait RevIterableStorage { /// The type of the iterator returned by [`rev_keys`](Self::rev_keys). type RevKeysIterator<'a>: Iterator> @@ -169,12 +136,35 @@ pub trait RevIterableStorage { where Self: 'a; + /// Get a reverse iterator over keys. + /// + /// The iterator walks keys in reverse lexicographical order. + /// + /// The [`Bound`] type is used to specify either end of the range - whether it should be + /// bounded at all, and if so, whether it should be inclusive or exclusive. See the + /// [`Bound`] documentation for more details. fn rev_keys<'a>(&'a self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Self::RevKeysIterator<'a>; + + /// Get a reverse iterator over values. + /// + /// The iterator walks values corresponding to keys in reverse lexicographical order. + /// + /// The [`Bound`] type is used to specify either end of the range - whether it should be + /// bounded at all, and if so, whether it should be inclusive or exclusive. See the + /// [`Bound`] documentation for more details. fn rev_values<'a>( &'a self, start: Bound<&[u8]>, end: Bound<&[u8]>, ) -> Self::RevValuesIterator<'a>; + + /// Get a reverse iterator over key-value pairs. + /// + /// The iterator walks key-value pairs in reverse lexicographical order. + /// + /// The [`Bound`] type is used to specify either end of the range - whether it should be + /// bounded at all, and if so, whether it should be inclusive or exclusive. See the + /// [`Bound`] documentation for more details. fn rev_pairs<'a>( &'a self, start: Bound<&[u8]>, diff --git a/packages/storey/src/containers/mod.rs b/packages/storey/src/containers/mod.rs index 1a3caaa..bbca95a 100644 --- a/packages/storey/src/containers/mod.rs +++ b/packages/storey/src/containers/mod.rs @@ -181,6 +181,8 @@ where /// in turn means the entries found between two string keys may not be the expected ones. pub trait BoundedIterableAccessor: IterableAccessor { /// Iterate over key-value pairs in this collection, respecting the given bounds. + /// + /// Either end of the range can be unbounded, inclusive, or exclusive. See [`Bound`] for more. fn bounded_pairs( &self, start: Bound, @@ -202,6 +204,8 @@ pub trait BoundedIterableAccessor: IterableAccessor { } /// Iterate over keys in this collection, respecting the given bounds. + /// + /// Either end of the range can be unbounded, inclusive, or exclusive. See [`Bound`] for more. fn bounded_keys( &self, start: Bound, @@ -223,6 +227,8 @@ pub trait BoundedIterableAccessor: IterableAccessor { } /// Iterate over values in this collection, respecting the given bounds. + /// + /// Either end of the range can be unbounded, inclusive, or exclusive. See [`Bound`] for more. fn bounded_values( &self, start: Bound, @@ -259,6 +265,8 @@ where Self::Storage: RevIterableStorage, { /// Iterate over key-value pairs in this collection in reverse order, respecting the given bounds. + /// + /// Either end of the range can be unbounded, inclusive, or exclusive. See [`Bound`] for more. fn bounded_rev_pairs( &self, start: Bound, @@ -280,6 +288,8 @@ where } /// Iterate over keys in this collection in reverse order, respecting the given bounds. + /// + /// Either end of the range can be unbounded, inclusive, or exclusive. See [`Bound`] for more. fn bounded_rev_keys( &self, start: Bound, @@ -301,6 +311,8 @@ where } /// Iterate over values in this collection in reverse order, respecting the given bounds. + /// + /// Either end of the range can be unbounded, inclusive, or exclusive. See [`Bound`] for more. fn bounded_rev_values( &self, start: Bound, From 6890883a02f540fbe34bcaecd9c9fb7a931ad4a1 Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Mon, 4 Nov 2024 18:37:12 +0100 Subject: [PATCH 4/5] add more tests for exclusive/inclusive bounds --- packages/storey/src/containers/column.rs | 33 ++++++++-- packages/storey/src/containers/map/mod.rs | 12 ++++ packages/storey/tests/iteration.rs | 78 ++++++++++++++++++++++- 3 files changed, 117 insertions(+), 6 deletions(-) diff --git a/packages/storey/src/containers/column.rs b/packages/storey/src/containers/column.rs index 2bb9efe..f40f639 100644 --- a/packages/storey/src/containers/column.rs +++ b/packages/storey/src/containers/column.rs @@ -604,6 +604,22 @@ mod tests { access.push(&2).unwrap(); access.remove(3).unwrap(); + assert_eq!( + access + .bounded_pairs(Bound::Excluded(2), Bound::Included(5)) + .collect::, _>>() + .unwrap(), + vec![(4, 1), (5, 2)] + ); + + assert_eq!( + access + .bounded_pairs(Bound::Excluded(1), Bound::Included(5)) + .collect::, _>>() + .unwrap(), + vec![(2, 42), (4, 1), (5, 2)] + ); + // start and end set assert_eq!( access @@ -681,11 +697,11 @@ mod tests { let column = Column::::new(0); let mut access = column.access(&mut storage); - access.push(&1337).unwrap(); - access.push(&42).unwrap(); - access.push(&9001).unwrap(); - access.push(&1).unwrap(); - access.push(&2).unwrap(); + access.push(&1337).unwrap(); //1 + access.push(&42).unwrap(); //2 + access.push(&9001).unwrap(); //3 (removed) + access.push(&1).unwrap(); //4 + access.push(&2).unwrap(); //5 access.remove(3).unwrap(); // start and end set @@ -696,6 +712,13 @@ mod tests { .unwrap(), vec![(4, 1), (2, 42)] ); + assert_eq!( + access + .bounded_rev_keys(Bound::Excluded(2), Bound::Excluded(5)) + .collect::, _>>() + .unwrap(), + vec![4] + ); assert_eq!( access .bounded_rev_keys(Bound::Included(2), Bound::Excluded(5)) diff --git a/packages/storey/src/containers/map/mod.rs b/packages/storey/src/containers/map/mod.rs index 8b1f8cd..32a950c 100644 --- a/packages/storey/src/containers/map/mod.rs +++ b/packages/storey/src/containers/map/mod.rs @@ -397,6 +397,18 @@ mod tests { items, vec![(("bar".to_string(), ()), 42), (("baz".to_string(), ()), 69)] ); + + let items = access + .bounded_pairs(Bound::Excluded("bar"), Bound::Included("foo")) + .collect::, _>>() + .unwrap(); + assert_eq!( + items, + vec![ + (("baz".to_string(), ()), 69), + (("foo".to_string(), ()), 1337) + ] + ); } #[test] diff --git a/packages/storey/tests/iteration.rs b/packages/storey/tests/iteration.rs index 4c43d90..022f5cb 100644 --- a/packages/storey/tests/iteration.rs +++ b/packages/storey/tests/iteration.rs @@ -1,4 +1,6 @@ -use storey::containers::{Item, IterableAccessor as _, Map}; +use std::ops::Bound; + +use storey::containers::{BoundedIterableAccessor, Item, IterableAccessor as _, Map}; use mocks::backend::TestStorage; use mocks::encoding::TestEncoding; @@ -44,3 +46,77 @@ fn map_of_map_iteration() { ] ); } + +#[test] +fn map_of_map_bounded_iteration() { + let mut storage = TestStorage::new(); + + let map = Map::>>::new(0); + let mut access = map.access(&mut storage); + + // populate with data + access.entry_mut("foo").entry_mut("bar").set(&1337).unwrap(); + access.entry_mut("foo").entry_mut("baz").set(&42).unwrap(); + access.entry_mut("foo").entry_mut("qux").set(&9001).unwrap(); + access + .entry_mut("qux") + .entry_mut("quux") + .set(&9001) + .unwrap(); + + // iterate over items under "foo" + let items = access + .entry("foo") + .bounded_pairs(Bound::Unbounded, Bound::Excluded("qux")) + .collect::, _>>() + .unwrap(); + assert_eq!( + items, + vec![ + (("bar".to_string(), ()), 1337), + (("baz".to_string(), ()), 42), + ] + ); + + // iterate over items under "foo" + let items = access + .entry("foo") + .bounded_pairs(Bound::Unbounded, Bound::Included("qux")) + .collect::, _>>() + .unwrap(); + assert_eq!( + items, + vec![ + (("bar".to_string(), ()), 1337), + (("baz".to_string(), ()), 42), + (("qux".to_string(), ()), 9001), + ] + ); + + let items = access + .entry("foo") + .bounded_pairs(Bound::Excluded("bar"), Bound::Unbounded) + .collect::, _>>() + .unwrap(); + assert_eq!( + items, + vec![ + (("baz".to_string(), ()), 42), + (("qux".to_string(), ()), 9001), + ] + ); + + let items = access + .entry("foo") + .bounded_pairs(Bound::Included("bar"), Bound::Unbounded) + .collect::, _>>() + .unwrap(); + assert_eq!( + items, + vec![ + (("bar".to_string(), ()), 1337), + (("baz".to_string(), ()), 42), + (("qux".to_string(), ()), 9001), + ] + ); +} From fdc255c5b1c50f66841680482f34083f7b039dbf Mon Sep 17 00:00:00 2001 From: Tomasz Kurcz Date: Tue, 12 Nov 2024 20:39:41 +0100 Subject: [PATCH 5/5] small fix (tests) --- packages/storey/src/containers/column.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/storey/src/containers/column.rs b/packages/storey/src/containers/column.rs index f40f639..6ba7887 100644 --- a/packages/storey/src/containers/column.rs +++ b/packages/storey/src/containers/column.rs @@ -669,21 +669,21 @@ mod tests { // start unset assert_eq!( access - .bounded_pairs(Bound::Included(1), Bound::Excluded(5)) + .bounded_pairs(Bound::Unbounded, Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![(1, 1337), (2, 42), (4, 1)] ); assert_eq!( access - .bounded_keys(Bound::Included(1), Bound::Excluded(5)) + .bounded_keys(Bound::Unbounded, Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![1, 2, 4] ); assert_eq!( access - .bounded_values(Bound::Included(1), Bound::Excluded(5)) + .bounded_values(Bound::Unbounded, Bound::Excluded(5)) .collect::, _>>() .unwrap(), vec![1337, 42, 1]