use std::fmt;
use std::num::NonZeroU32;
use std::ops::Bound;
use crate::key::Key;
use crate::map_index::{Dir, ShardSource, inspection_batch};
use crate::{DbError, DbResult};
fn bound_as_ref<K>(b: &Bound<K>) -> Bound<&K> {
match b {
Bound::Included(k) => Bound::Included(k),
Bound::Excluded(k) => Bound::Excluded(k),
Bound::Unbounded => Bound::Unbounded,
}
}
mod sealed {
pub trait Sealed {}
}
pub trait OrderedScan: sealed::Sealed + Copy {
type Key: Key;
type Row;
type Iter: Iterator<Item = Self::Row>;
type KeyIter: Iterator<Item = Self::Key>;
fn scan(self, start: Bound<Self::Key>, end: Bound<Self::Key>, dir: Dir) -> Self::Iter;
fn scan_keys(self, start: Bound<Self::Key>, end: Bound<Self::Key>, dir: Dir) -> Self::KeyIter;
fn peek_row_key(row: &Self::Row) -> Self::Key;
}
pub trait BulkScan: sealed::Sealed + OrderedScan {
type TryKeys: Iterator<Item = DbResult<Self::Key>>;
fn try_keys(self, start: Bound<Self::Key>, end: Bound<Self::Key>, dir: Dir) -> Self::TryKeys;
fn delete_key(&self, key: &Self::Key) -> DbResult<bool>;
fn reject_nontx_bulk(&self) -> DbResult<()>;
}
pub trait OwnedRows: OrderedScan {
type Val;
fn own(row: Self::Row) -> (Self::Key, Self::Val);
}
pub trait StrictRows: sealed::Sealed + BulkScan + OwnedRows {
type TryRows: Iterator<Item = DbResult<(Self::Key, Self::Val)>>;
fn try_rows(self, start: Bound<Self::Key>, end: Bound<Self::Key>, dir: Dir) -> Self::TryRows;
fn delete_if_unchanged(&self, key: &Self::Key, expected: &Self::Val) -> DbResult<bool>;
}
#[doc(hidden)]
pub struct InfallibleKeys<K, I> {
inner: I,
_key: std::marker::PhantomData<K>,
}
impl<K, I> InfallibleKeys<K, I> {
fn new(inner: I) -> Self {
Self {
inner,
_key: std::marker::PhantomData,
}
}
}
impl<K, I: Iterator<Item = K>> Iterator for InfallibleKeys<K, I> {
type Item = DbResult<K>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(Ok)
}
}
#[doc(hidden)]
pub struct InfallibleRows<K, V, I> {
inner: I,
_row: std::marker::PhantomData<(K, V)>,
}
impl<K, V, I> InfallibleRows<K, V, I> {
fn new(inner: I) -> Self {
Self {
inner,
_row: std::marker::PhantomData,
}
}
}
impl<K, V, I: Iterator<Item = (K, V)>> Iterator for InfallibleRows<K, V, I> {
type Item = DbResult<(K, V)>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(Ok)
}
}
#[doc(hidden)]
pub struct OwnedRowsIter<S, I> {
inner: I,
_scan: std::marker::PhantomData<S>,
}
impl<S, I> OwnedRowsIter<S, I> {
fn new(inner: I) -> Self {
Self {
inner,
_scan: std::marker::PhantomData,
}
}
}
impl<S: OwnedRows, I: Iterator<Item = S::Row>> Iterator for OwnedRowsIter<S, I> {
type Item = (S::Key, S::Val);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(S::own)
}
}
fn conditional_delete(result: DbResult<()>) -> DbResult<bool> {
match result {
Ok(()) => Ok(true),
Err(DbError::CasMismatch | DbError::KeyNotFound) => Ok(false),
Err(error) => Err(error),
}
}
#[cfg(feature = "typed-tree")]
fn reject_typed_bulk<K: Key, T, H: crate::hook::TypedWriteHook<K, T>>(
hook: &H,
collection: &'static str,
) -> DbResult<()> {
#[cfg(feature = "armour")]
if H::EMITS
&& let Some(targets) = hook.stream_targets()
{
return Err(DbError::ChangeStreamRequiresTx {
collection,
queue: targets.first_name(),
});
}
if H::CLAIMS
&& let Some(targets) = hook.claim_targets()
{
return Err(DbError::UniqueIndexRequiresTx {
collection,
namespace: targets.first_name(),
});
}
Ok(())
}
#[cfg(feature = "typed-tree")]
fn reject_bytes_bulk<K: Key, H: crate::hook::WriteHook<K>>(
hook: &H,
collection: &'static str,
) -> DbResult<()> {
#[cfg(feature = "armour")]
if H::EMITS
&& let Some(targets) = hook.stream_targets()
{
return Err(DbError::ChangeStreamRequiresTx {
collection,
queue: targets.first_name(),
});
}
if H::CLAIMS
&& let Some(targets) = hook.claim_targets()
{
return Err(DbError::UniqueIndexRequiresTx {
collection,
namespace: targets.first_name(),
});
}
Ok(())
}
fn collection_name(
marker: &std::sync::OnceLock<crate::counter_dirty::CounterDirty>,
) -> &'static str {
marker
.get()
.map(crate::counter_dirty::CounterDirty::name)
.unwrap_or("")
}
fn bulk_map_batch<S: ShardSource>(source: &S) -> usize {
inspection_batch(
NonZeroU32::new((crate::scope::bulk::CHUNK + 1) as u32).expect("bulk chunk is non-zero"),
source.shard_count(),
)
}
#[derive(Clone, Copy)]
pub struct TreeScan<C> {
pub(super) coll: C,
}
impl<C> fmt::Debug for TreeScan<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TreeScan").finish_non_exhaustive()
}
}
#[derive(Clone, Copy)]
pub struct MapScan<V> {
pub(super) view: V,
}
impl<V> fmt::Debug for MapScan<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MapScan").finish_non_exhaustive()
}
}
#[cfg(feature = "typed-tree")]
mod typed_tree {
use super::*;
use crate::codec::Codec;
use crate::hook::TypedWriteHook;
use crate::scope::iters::{TreeKeyIter, TreeRowIter};
use crate::typed_tree::TypedTree;
impl<K: Key, T: Send + Sync, C: Codec<T> + Sync, H: TypedWriteHook<K, T>> sealed::Sealed
for TreeScan<&TypedTree<K, T, C, H>>
{
}
impl<'a, K: Key, T: Send + Sync, C: Codec<T> + Sync, H: TypedWriteHook<K, T>> OrderedScan
for TreeScan<&'a TypedTree<K, T, C, H>>
{
type Key = K;
type Row = (K, crate::typed_tree::TypedRef<'a, T>);
type Iter = TreeRowIter<'a, K, T>;
type KeyIter = TreeKeyIter<'a, K, T>;
fn scan(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::Iter {
TreeRowIter::new(self.coll.range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn scan_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::KeyIter {
TreeKeyIter::new(self.coll.keys_range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn peek_row_key(row: &Self::Row) -> K {
row.0
}
}
impl<K: Key, T: Clone + Send + Sync, C: Codec<T> + Sync, H: TypedWriteHook<K, T>> OwnedRows
for TreeScan<&TypedTree<K, T, C, H>>
{
type Val = T;
fn own(row: Self::Row) -> (Self::Key, Self::Val) {
let (k, v) = row;
(k, v.clone_value())
}
}
impl<'a, K: Key, T: Send + Sync, C: Codec<T> + Sync, H: TypedWriteHook<K, T>> BulkScan
for TreeScan<&'a TypedTree<K, T, C, H>>
{
type TryKeys = InfallibleKeys<K, TreeKeyIter<'a, K, T>>;
fn try_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryKeys {
InfallibleKeys::new(self.scan_keys(start, end, dir))
}
fn delete_key(&self, key: &K) -> DbResult<bool> {
self.coll.delete(key).map(|old| old.is_some())
}
fn reject_nontx_bulk(&self) -> DbResult<()> {
reject_typed_bulk(&self.coll.hook, collection_name(&self.coll.counter_dirty))
}
}
impl<
'a,
K: Key,
T: Clone + PartialEq + Send + Sync,
C: Codec<T> + Sync,
H: TypedWriteHook<K, T>,
> StrictRows for TreeScan<&'a TypedTree<K, T, C, H>>
{
type TryRows = InfallibleRows<K, T, OwnedRowsIter<Self, TreeRowIter<'a, K, T>>>;
fn try_rows(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryRows {
InfallibleRows::new(OwnedRowsIter::new(self.scan(start, end, dir)))
}
fn delete_if_unchanged(&self, key: &K, expected: &T) -> DbResult<bool> {
conditional_delete(self.coll.compare_delete(key, expected))
}
}
}
#[cfg(feature = "typed-tree")]
mod typed_map {
use std::cmp::Eq;
use std::hash::Hash;
use super::*;
use crate::codec::Codec;
use crate::hook::TypedWriteHook;
use crate::map_index::MapIterView;
use crate::scope::iters::{MapKeyIter, MapRowIter};
use crate::typed_map::TypedMap;
use crate::typed_tree::TypedRef;
impl<
K: Key + Send + Sync + Hash + Eq,
T: Send + Sync,
C: Codec<T> + Sync,
H: TypedWriteHook<K, T>,
> sealed::Sealed for MapScan<&TypedMap<K, T, C, H>>
{
}
impl<
'a,
K: Key + Send + Sync + Hash + Eq,
T: Send + Sync,
C: Codec<T> + Sync,
H: TypedWriteHook<K, T>,
> OrderedScan for MapScan<&'a TypedMap<K, T, C, H>>
{
type Key = K;
type Row = (K, TypedRef<'a, T>);
type Iter = MapRowIter<'a, K, T, C, H>;
type KeyIter = MapKeyIter<'a, K, T, C, H>;
fn scan(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::Iter {
let view = MapIterView::new(self.view, dir);
MapRowIter::new(
self.view,
view.range_bounds(bound_as_ref(&start), bound_as_ref(&end)),
)
}
fn scan_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::KeyIter {
let view = MapIterView::new(self.view, dir);
MapKeyIter::new(view.keys_range_bounds(bound_as_ref(&start), bound_as_ref(&end)))
}
fn peek_row_key(row: &Self::Row) -> K {
row.0
}
}
impl<
K: Key + Send + Sync + Hash + Eq,
T: Clone + Send + Sync,
C: Codec<T> + Sync,
H: TypedWriteHook<K, T>,
> OwnedRows for MapScan<&TypedMap<K, T, C, H>>
{
type Val = T;
fn own(row: Self::Row) -> (Self::Key, Self::Val) {
let (k, r) = row;
(k, r.clone_value())
}
}
impl<
'a,
K: Key + Send + Sync + Hash + Eq,
T: Send + Sync,
C: Codec<T> + Sync,
H: TypedWriteHook<K, T>,
> BulkScan for MapScan<&'a TypedMap<K, T, C, H>>
{
type TryKeys = InfallibleKeys<K, MapKeyIter<'a, K, T, C, H>>;
fn try_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryKeys {
let view = MapIterView::with_batch(self.view, dir, bulk_map_batch(self.view));
InfallibleKeys::new(MapKeyIter::new(
view.keys_range_bounds(bound_as_ref(&start), bound_as_ref(&end)),
))
}
fn delete_key(&self, key: &K) -> DbResult<bool> {
self.view.delete(key).map(|old| old.is_some())
}
fn reject_nontx_bulk(&self) -> DbResult<()> {
reject_typed_bulk(&self.view.hook, collection_name(&self.view.counter_dirty))
}
}
impl<
'a,
K: Key + Send + Sync + Hash + Eq,
T: Clone + PartialEq + Send + Sync,
C: Codec<T> + Sync,
H: TypedWriteHook<K, T>,
> StrictRows for MapScan<&'a TypedMap<K, T, C, H>>
{
type TryRows = InfallibleRows<K, T, OwnedRowsIter<Self, MapRowIter<'a, K, T, C, H>>>;
fn try_rows(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryRows {
InfallibleRows::new(OwnedRowsIter::new(self.scan(start, end, dir)))
}
fn delete_if_unchanged(&self, key: &K, expected: &T) -> DbResult<bool> {
conditional_delete(self.view.compare_delete(key, expected))
}
}
}
#[cfg(all(feature = "fjall", feature = "typed-tree"))]
mod lsm {
use super::*;
use crate::CollectionMeta;
use crate::codec::Codec;
use crate::hook::TypedWriteHook;
use crate::lsm::{LsmTree, TryLsmIter, TryLsmKeyIter};
use crate::scope::iters::{LsmKeyIter, LsmRowIter};
impl<
K: Key + Ord,
T: CollectionMeta<SelfId = K> + Send + Sync,
C: Codec<T> + Clone,
H: TypedWriteHook<K, T>,
> sealed::Sealed for TreeScan<&LsmTree<K, T, C, H>>
{
}
impl<
'a,
K: Key + Ord,
T: CollectionMeta<SelfId = K> + Send + Sync,
C: Codec<T> + Clone,
H: TypedWriteHook<K, T>,
> OrderedScan for TreeScan<&'a LsmTree<K, T, C, H>>
{
type Key = K;
type Row = (K, T);
type Iter = LsmRowIter<'a, K, T, C>;
type KeyIter = LsmKeyIter<K>;
fn scan(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::Iter {
LsmRowIter::new(self.coll.range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn scan_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::KeyIter {
LsmKeyIter::new(self.coll.keys_range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn peek_row_key(row: &Self::Row) -> K {
row.0
}
}
impl<
K: Key + Ord,
T: CollectionMeta<SelfId = K> + Send + Sync,
C: Codec<T> + Clone,
H: TypedWriteHook<K, T>,
> OwnedRows for TreeScan<&LsmTree<K, T, C, H>>
{
type Val = T;
fn own(row: Self::Row) -> (Self::Key, Self::Val) {
row
}
}
impl<
K: Key + Ord,
T: CollectionMeta<SelfId = K> + Send + Sync,
C: Codec<T> + Clone,
H: TypedWriteHook<K, T>,
> BulkScan for TreeScan<&LsmTree<K, T, C, H>>
{
type TryKeys = TryLsmKeyIter<K>;
fn try_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryKeys {
self.coll
.try_keys_range_bounds_dir(bound_as_ref(&start), bound_as_ref(&end), dir)
}
fn delete_key(&self, key: &K) -> DbResult<bool> {
self.coll.delete(key)
}
fn reject_nontx_bulk(&self) -> DbResult<()> {
Ok(())
}
}
impl<
'a,
K: Key + Ord,
T: CollectionMeta<SelfId = K> + PartialEq + Send + Sync,
C: Codec<T> + Clone,
H: TypedWriteHook<K, T>,
> StrictRows for TreeScan<&'a LsmTree<K, T, C, H>>
{
type TryRows = TryLsmIter<'a, K, T, C>;
fn try_rows(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryRows {
self.coll
.try_range_bounds_dir(bound_as_ref(&start), bound_as_ref(&end), dir)
}
fn delete_if_unchanged(&self, key: &K, expected: &T) -> DbResult<bool> {
conditional_delete(self.coll.compare_delete(key, expected))
}
}
}
mod const_tree {
use super::*;
use crate::ConstTree;
use crate::durability::Durability;
use crate::hook::WriteHook;
use crate::scope::iters::{ConstTreeKeyIter, ConstTreeRowIter};
impl<K: Key, const V: usize, H: WriteHook<K>, D: Durability> sealed::Sealed
for TreeScan<&ConstTree<K, V, H, D>>
{
}
impl<'a, K: Key, const V: usize, H: WriteHook<K>, D: Durability> OrderedScan
for TreeScan<&'a ConstTree<K, V, H, D>>
{
type Key = K;
type Row = (K, [u8; V]);
type Iter = ConstTreeRowIter<'a, K, V, D::Loc>;
type KeyIter = ConstTreeKeyIter<'a, K, V, D::Loc>;
fn scan(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::Iter {
ConstTreeRowIter::new(self.coll.range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn scan_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::KeyIter {
ConstTreeKeyIter::new(self.coll.keys_range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn peek_row_key(row: &Self::Row) -> K {
row.0
}
}
impl<K: Key, const V: usize, H: WriteHook<K>, D: Durability> OwnedRows
for TreeScan<&ConstTree<K, V, H, D>>
{
type Val = [u8; V];
fn own(row: Self::Row) -> (Self::Key, Self::Val) {
row
}
}
impl<'a, K: Key, const V: usize, H: WriteHook<K>, D: Durability> BulkScan
for TreeScan<&'a ConstTree<K, V, H, D>>
{
type TryKeys = InfallibleKeys<K, ConstTreeKeyIter<'a, K, V, D::Loc>>;
fn try_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryKeys {
InfallibleKeys::new(self.scan_keys(start, end, dir))
}
fn delete_key(&self, key: &K) -> DbResult<bool> {
self.coll.delete(key).map(|old| old.is_some())
}
fn reject_nontx_bulk(&self) -> DbResult<()> {
#[cfg(feature = "typed-tree")]
{
reject_bytes_bulk(&self.coll.hook, collection_name(&self.coll.counter_dirty))
}
#[cfg(not(feature = "typed-tree"))]
Ok(())
}
}
impl<'a, K: Key, const V: usize, H: WriteHook<K>, D: Durability> StrictRows
for TreeScan<&'a ConstTree<K, V, H, D>>
{
type TryRows = InfallibleRows<K, [u8; V], ConstTreeRowIter<'a, K, V, D::Loc>>;
fn try_rows(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryRows {
InfallibleRows::new(self.scan(start, end, dir))
}
fn delete_if_unchanged(&self, key: &K, expected: &[u8; V]) -> DbResult<bool> {
conditional_delete(self.coll.compare_delete(key, expected))
}
}
}
mod const_map {
use std::cmp::Eq;
use std::hash::Hash;
use super::*;
use crate::ConstMap;
use crate::durability::Durability;
use crate::hook::WriteHook;
use crate::map_index::MapIterView;
use crate::scope::iters::{ConstMapKeyIter, ConstMapRowIter};
impl<K: Key + Send + Sync + Hash + Eq, const V: usize, H: WriteHook<K>, D: Durability>
sealed::Sealed for MapScan<&ConstMap<K, V, H, D>>
{
}
impl<'a, K: Key + Send + Sync + Hash + Eq, const V: usize, H: WriteHook<K>, D: Durability>
OrderedScan for MapScan<&'a ConstMap<K, V, H, D>>
{
type Key = K;
type Row = (K, [u8; V]);
type Iter = ConstMapRowIter<'a, K, V, H, D>;
type KeyIter = ConstMapKeyIter<'a, K, V, H, D>;
fn scan(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::Iter {
let view = MapIterView::new(self.view, dir);
ConstMapRowIter::new(view.range_bounds(bound_as_ref(&start), bound_as_ref(&end)))
}
fn scan_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::KeyIter {
let view = MapIterView::new(self.view, dir);
ConstMapKeyIter::new(view.keys_range_bounds(bound_as_ref(&start), bound_as_ref(&end)))
}
fn peek_row_key(row: &Self::Row) -> K {
row.0
}
}
impl<K: Key + Send + Sync + Hash + Eq, const V: usize, H: WriteHook<K>, D: Durability> OwnedRows
for MapScan<&ConstMap<K, V, H, D>>
{
type Val = [u8; V];
fn own(row: Self::Row) -> (Self::Key, Self::Val) {
row
}
}
impl<'a, K: Key + Send + Sync + Hash + Eq, const V: usize, H: WriteHook<K>, D: Durability>
BulkScan for MapScan<&'a ConstMap<K, V, H, D>>
{
type TryKeys = InfallibleKeys<K, ConstMapKeyIter<'a, K, V, H, D>>;
fn try_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryKeys {
let view = MapIterView::with_batch(self.view, dir, bulk_map_batch(self.view));
InfallibleKeys::new(ConstMapKeyIter::new(
view.keys_range_bounds(bound_as_ref(&start), bound_as_ref(&end)),
))
}
fn delete_key(&self, key: &K) -> DbResult<bool> {
self.view.delete(key).map(|old| old.is_some())
}
fn reject_nontx_bulk(&self) -> DbResult<()> {
#[cfg(feature = "typed-tree")]
{
reject_bytes_bulk(&self.view.hook, collection_name(&self.view.counter_dirty))
}
#[cfg(not(feature = "typed-tree"))]
Ok(())
}
}
impl<'a, K: Key + Send + Sync + Hash + Eq, const V: usize, H: WriteHook<K>, D: Durability>
StrictRows for MapScan<&'a ConstMap<K, V, H, D>>
{
type TryRows = InfallibleRows<K, [u8; V], ConstMapRowIter<'a, K, V, H, D>>;
fn try_rows(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryRows {
InfallibleRows::new(self.scan(start, end, dir))
}
fn delete_if_unchanged(&self, key: &K, expected: &[u8; V]) -> DbResult<bool> {
conditional_delete(self.view.compare_delete(key, expected))
}
}
}
#[cfg(feature = "typed-tree")]
mod zero_tree {
use super::*;
use crate::ZeroTree;
use crate::durability::Durability;
use crate::hook::TypedWriteHook;
use crate::scope::iters::{ZeroTreeKeyIter, ZeroTreeRowIter};
impl<
K: Key,
const V: usize,
T: Copy + zerocopy::IntoBytes + zerocopy::FromBytes + zerocopy::Immutable,
H: TypedWriteHook<K, T>,
D: Durability,
> sealed::Sealed for TreeScan<&ZeroTree<K, V, T, H, D>>
{
}
impl<
'a,
K: Key,
const V: usize,
T: Copy + zerocopy::IntoBytes + zerocopy::FromBytes + zerocopy::Immutable,
H: TypedWriteHook<K, T>,
D: Durability,
> OrderedScan for TreeScan<&'a ZeroTree<K, V, T, H, D>>
{
type Key = K;
type Row = (K, T);
type Iter = ZeroTreeRowIter<'a, K, V, T, D::Loc>;
type KeyIter = ZeroTreeKeyIter<'a, K, V, D::Loc>;
fn scan(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::Iter {
ZeroTreeRowIter::new(self.coll.range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn scan_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::KeyIter {
ZeroTreeKeyIter::new(self.coll.keys_range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn peek_row_key(row: &Self::Row) -> K {
row.0
}
}
impl<
K: Key,
const V: usize,
T: Copy + zerocopy::IntoBytes + zerocopy::FromBytes + zerocopy::Immutable,
H: TypedWriteHook<K, T>,
D: Durability,
> OwnedRows for TreeScan<&ZeroTree<K, V, T, H, D>>
{
type Val = T;
fn own(row: Self::Row) -> (Self::Key, Self::Val) {
row
}
}
impl<
'a,
K: Key,
const V: usize,
T: Copy + zerocopy::IntoBytes + zerocopy::FromBytes + zerocopy::Immutable,
H: TypedWriteHook<K, T>,
D: Durability,
> BulkScan for TreeScan<&'a ZeroTree<K, V, T, H, D>>
{
type TryKeys = InfallibleKeys<K, ZeroTreeKeyIter<'a, K, V, D::Loc>>;
fn try_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryKeys {
InfallibleKeys::new(self.scan_keys(start, end, dir))
}
fn delete_key(&self, key: &K) -> DbResult<bool> {
self.coll.delete(key).map(|old| old.is_some())
}
fn reject_nontx_bulk(&self) -> DbResult<()> {
let inner = self.coll.tx_inner();
reject_bytes_bulk(&inner.hook, collection_name(&inner.counter_dirty))
}
}
impl<
'a,
K: Key,
const V: usize,
T: Copy + zerocopy::IntoBytes + zerocopy::FromBytes + zerocopy::Immutable,
H: TypedWriteHook<K, T>,
D: Durability,
> StrictRows for TreeScan<&'a ZeroTree<K, V, T, H, D>>
{
type TryRows = InfallibleRows<K, T, ZeroTreeRowIter<'a, K, V, T, D::Loc>>;
fn try_rows(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryRows {
InfallibleRows::new(self.scan(start, end, dir))
}
fn delete_if_unchanged(&self, key: &K, expected: &T) -> DbResult<bool> {
conditional_delete(self.coll.compare_delete(key, expected))
}
}
}
#[cfg(feature = "typed-tree")]
mod zero_map {
use std::cmp::Eq;
use std::hash::Hash;
use super::*;
use crate::ZeroMap;
use crate::durability::Durability;
use crate::hook::TypedWriteHook;
use crate::map_index::MapIterView;
use crate::scope::iters::{ZeroMapKeyIter, ZeroMapRowIter};
impl<
K: Key + Send + Sync + Hash + Eq,
const V: usize,
T: Copy + zerocopy::IntoBytes + zerocopy::FromBytes + zerocopy::Immutable,
H: TypedWriteHook<K, T>,
D: Durability,
> sealed::Sealed for MapScan<&ZeroMap<K, V, T, H, D>>
{
}
impl<
'a,
K: Key + Send + Sync + Hash + Eq,
const V: usize,
T: Copy + zerocopy::IntoBytes + zerocopy::FromBytes + zerocopy::Immutable,
H: TypedWriteHook<K, T>,
D: Durability,
> OrderedScan for MapScan<&'a ZeroMap<K, V, T, H, D>>
{
type Key = K;
type Row = (K, T);
type Iter = ZeroMapRowIter<'a, K, V, T, H, D>;
type KeyIter = ZeroMapKeyIter<'a, K, V, T, H, D>;
fn scan(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::Iter {
let view = MapIterView::new(self.view, dir);
ZeroMapRowIter::new(view.range_bounds(bound_as_ref(&start), bound_as_ref(&end)))
}
fn scan_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::KeyIter {
let view = MapIterView::new(self.view, dir);
ZeroMapKeyIter::new(view.keys_range_bounds(bound_as_ref(&start), bound_as_ref(&end)))
}
fn peek_row_key(row: &Self::Row) -> K {
row.0
}
}
impl<
K: Key + Send + Sync + Hash + Eq,
const V: usize,
T: Copy + zerocopy::IntoBytes + zerocopy::FromBytes + zerocopy::Immutable,
H: TypedWriteHook<K, T>,
D: Durability,
> OwnedRows for MapScan<&ZeroMap<K, V, T, H, D>>
{
type Val = T;
fn own(row: Self::Row) -> (Self::Key, Self::Val) {
row
}
}
impl<
'a,
K: Key + Send + Sync + Hash + Eq,
const V: usize,
T: Copy + zerocopy::IntoBytes + zerocopy::FromBytes + zerocopy::Immutable,
H: TypedWriteHook<K, T>,
D: Durability,
> BulkScan for MapScan<&'a ZeroMap<K, V, T, H, D>>
{
type TryKeys = InfallibleKeys<K, ZeroMapKeyIter<'a, K, V, T, H, D>>;
fn try_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryKeys {
let view = MapIterView::with_batch(self.view, dir, bulk_map_batch(self.view));
InfallibleKeys::new(ZeroMapKeyIter::new(
view.keys_range_bounds(bound_as_ref(&start), bound_as_ref(&end)),
))
}
fn delete_key(&self, key: &K) -> DbResult<bool> {
self.view.delete(key).map(|old| old.is_some())
}
fn reject_nontx_bulk(&self) -> DbResult<()> {
let inner = self.view.tx_inner();
reject_bytes_bulk(&inner.hook, collection_name(&inner.counter_dirty))
}
}
impl<
'a,
K: Key + Send + Sync + Hash + Eq,
const V: usize,
T: Copy + zerocopy::IntoBytes + zerocopy::FromBytes + zerocopy::Immutable,
H: TypedWriteHook<K, T>,
D: Durability,
> StrictRows for MapScan<&'a ZeroMap<K, V, T, H, D>>
{
type TryRows = InfallibleRows<K, T, ZeroMapRowIter<'a, K, V, T, H, D>>;
fn try_rows(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryRows {
InfallibleRows::new(self.scan(start, end, dir))
}
fn delete_if_unchanged(&self, key: &K, expected: &T) -> DbResult<bool> {
conditional_delete(self.view.compare_delete(key, expected))
}
}
}
#[cfg(feature = "var-collections")]
mod var_tree {
use super::*;
use crate::VarTree;
use crate::hook::WriteHook;
use crate::scope::iters::{VarTreeKeyIter, VarTreeRowIter};
use crate::var_tree::TryVarIter;
impl<K: Key, H: WriteHook<K>> sealed::Sealed for TreeScan<&VarTree<K, H>> {}
impl<'a, K: Key, H: WriteHook<K>> OrderedScan for TreeScan<&'a VarTree<K, H>> {
type Key = K;
type Row = (K, crate::ByteView);
type Iter = VarTreeRowIter<'a, K, H>;
type KeyIter = VarTreeKeyIter<'a, K, H>;
fn scan(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::Iter {
VarTreeRowIter::new(self.coll.range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn scan_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::KeyIter {
VarTreeKeyIter::new(self.coll.keys_range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn peek_row_key(row: &Self::Row) -> K {
row.0
}
}
impl<K: Key, H: WriteHook<K>> OwnedRows for TreeScan<&VarTree<K, H>> {
type Val = crate::ByteView;
fn own(row: Self::Row) -> (Self::Key, Self::Val) {
row
}
}
impl<'a, K: Key, H: WriteHook<K>> BulkScan for TreeScan<&'a VarTree<K, H>> {
type TryKeys = InfallibleKeys<K, VarTreeKeyIter<'a, K, H>>;
fn try_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryKeys {
InfallibleKeys::new(self.scan_keys(start, end, dir))
}
fn delete_key(&self, key: &K) -> DbResult<bool> {
self.coll.delete(key)
}
fn reject_nontx_bulk(&self) -> DbResult<()> {
#[cfg(feature = "typed-tree")]
{
reject_bytes_bulk(&self.coll.hook, collection_name(&self.coll.counter_dirty))
}
#[cfg(not(feature = "typed-tree"))]
Ok(())
}
}
impl<'a, K: Key, H: WriteHook<K>> StrictRows for TreeScan<&'a VarTree<K, H>> {
type TryRows = crate::scope::DirRangeIter<TryVarIter<'a, K, H>>;
fn try_rows(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryRows {
self.coll
.try_range_bounds_dir(bound_as_ref(&start), bound_as_ref(&end), dir)
}
fn delete_if_unchanged(&self, key: &K, expected: &crate::ByteView) -> DbResult<bool> {
conditional_delete(self.coll.compare_delete(key, expected.as_ref()))
}
}
}
#[cfg(feature = "var-collections")]
mod var_map {
use std::cmp::Eq;
use std::hash::Hash;
use super::*;
use crate::VarMap;
use crate::hook::WriteHook;
use crate::map_index::MapIterView;
use crate::scope::iters::{VarMapKeyIter, VarMapRowIter};
use crate::var_map::VarMapTryIter;
impl<K: Key + Send + Sync + Hash + Eq, H: WriteHook<K>> sealed::Sealed for MapScan<&VarMap<K, H>> {}
impl<'a, K: Key + Send + Sync + Hash + Eq, H: WriteHook<K>> OrderedScan
for MapScan<&'a VarMap<K, H>>
{
type Key = K;
type Row = (K, crate::ByteView);
type Iter = VarMapRowIter<'a, K, H>;
type KeyIter = VarMapKeyIter<'a, K, H>;
fn scan(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::Iter {
let view = MapIterView::new(self.view, dir);
VarMapRowIter::new(view.range_bounds(bound_as_ref(&start), bound_as_ref(&end)))
}
fn scan_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::KeyIter {
let view = MapIterView::new(self.view, dir);
VarMapKeyIter::new(view.keys_range_bounds(bound_as_ref(&start), bound_as_ref(&end)))
}
fn peek_row_key(row: &Self::Row) -> K {
row.0
}
}
impl<K: Key + Send + Sync + Hash + Eq, H: WriteHook<K>> OwnedRows for MapScan<&VarMap<K, H>> {
type Val = crate::ByteView;
fn own(row: Self::Row) -> (Self::Key, Self::Val) {
row
}
}
impl<'a, K: Key + Send + Sync + Hash + Eq, H: WriteHook<K>> BulkScan for MapScan<&'a VarMap<K, H>> {
type TryKeys = InfallibleKeys<K, VarMapKeyIter<'a, K, H>>;
fn try_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryKeys {
let view = MapIterView::with_batch(self.view, dir, bulk_map_batch(self.view));
InfallibleKeys::new(VarMapKeyIter::new(
view.keys_range_bounds(bound_as_ref(&start), bound_as_ref(&end)),
))
}
fn delete_key(&self, key: &K) -> DbResult<bool> {
self.view.delete(key)
}
fn reject_nontx_bulk(&self) -> DbResult<()> {
#[cfg(feature = "typed-tree")]
{
reject_bytes_bulk(&self.view.hook, collection_name(&self.view.counter_dirty))
}
#[cfg(not(feature = "typed-tree"))]
Ok(())
}
}
impl<'a, K: Key + Send + Sync + Hash + Eq, H: WriteHook<K>> StrictRows
for MapScan<&'a VarMap<K, H>>
{
type TryRows = VarMapTryIter<'a, K, H>;
fn try_rows(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryRows {
self.view.inspection_range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
bulk_map_batch(self.view),
)
}
fn delete_if_unchanged(&self, key: &K, expected: &crate::ByteView) -> DbResult<bool> {
conditional_delete(self.view.compare_delete(key, expected.as_ref()))
}
}
}
#[cfg(all(feature = "typed-tree", feature = "var-collections"))]
mod var_typed_tree {
use super::*;
use crate::VarTypedTree;
use crate::codec::Codec;
use crate::hook::TypedWriteHook;
use crate::scope::iters::{VarTypedTreeKeyIter, VarTypedTreeRowIter};
use crate::var_typed_tree::TryVarTypedIter;
impl<K: Key, T: Send + Sync, C: Codec<T> + Clone, H: TypedWriteHook<K, T>> sealed::Sealed
for TreeScan<&VarTypedTree<K, T, C, H>>
{
}
impl<'a, K: Key, T: Send + Sync, C: Codec<T> + Clone, H: TypedWriteHook<K, T>> OrderedScan
for TreeScan<&'a VarTypedTree<K, T, C, H>>
{
type Key = K;
type Row = (K, T);
type Iter = VarTypedTreeRowIter<'a, K, T, C, H>;
type KeyIter = VarTypedTreeKeyIter<'a, K, T, C, H>;
fn scan(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::Iter {
VarTypedTreeRowIter::new(self.coll.range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn scan_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::KeyIter {
VarTypedTreeKeyIter::new(self.coll.keys_range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
))
}
fn peek_row_key(row: &Self::Row) -> K {
row.0
}
}
impl<K: Key, T: Send + Sync, C: Codec<T> + Clone, H: TypedWriteHook<K, T>> OwnedRows
for TreeScan<&VarTypedTree<K, T, C, H>>
{
type Val = T;
fn own(row: Self::Row) -> (Self::Key, Self::Val) {
row
}
}
impl<'a, K: Key, T: Send + Sync, C: Codec<T> + Clone, H: TypedWriteHook<K, T>> BulkScan
for TreeScan<&'a VarTypedTree<K, T, C, H>>
{
type TryKeys = InfallibleKeys<K, VarTypedTreeKeyIter<'a, K, T, C, H>>;
fn try_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryKeys {
InfallibleKeys::new(self.scan_keys(start, end, dir))
}
fn delete_key(&self, key: &K) -> DbResult<bool> {
self.coll.delete(key)
}
fn reject_nontx_bulk(&self) -> DbResult<()> {
let inner = self.coll.as_inner();
reject_bytes_bulk(&inner.hook, collection_name(&inner.counter_dirty))
}
}
impl<'a, K: Key, T: Send + Sync, C: Codec<T> + Clone, H: TypedWriteHook<K, T>> StrictRows
for TreeScan<&'a VarTypedTree<K, T, C, H>>
{
type TryRows = TryVarTypedIter<'a, K, T, C, H>;
fn try_rows(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryRows {
self.coll
.try_range_bounds_dir(bound_as_ref(&start), bound_as_ref(&end), dir)
}
fn delete_if_unchanged(&self, key: &K, expected: &T) -> DbResult<bool> {
conditional_delete(self.coll.compare_delete(key, expected))
}
}
}
#[cfg(all(feature = "typed-tree", feature = "var-collections"))]
mod var_typed_map {
use std::cmp::Eq;
use std::hash::Hash;
use super::*;
use crate::VarTypedMap;
use crate::codec::Codec;
use crate::hook::TypedWriteHook;
use crate::map_index::MapIterView;
use crate::scope::iters::{VarTypedMapKeyIter, VarTypedMapRowIter};
use crate::var_typed_map::VarTypedMapTryIter;
impl<
K: Key + Send + Sync + Hash + Eq,
T: Send + Sync,
C: Codec<T> + Clone + Sync,
H: TypedWriteHook<K, T>,
> sealed::Sealed for MapScan<&VarTypedMap<K, T, C, H>>
{
}
impl<
'a,
K: Key + Send + Sync + Hash + Eq,
T: Send + Sync,
C: Codec<T> + Clone + Sync,
H: TypedWriteHook<K, T>,
> OrderedScan for MapScan<&'a VarTypedMap<K, T, C, H>>
{
type Key = K;
type Row = (K, T);
type Iter = VarTypedMapRowIter<'a, K, T, C, H>;
type KeyIter = VarTypedMapKeyIter<'a, K, T, C, H>;
fn scan(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::Iter {
let view = MapIterView::new(self.view.as_inner(), dir);
VarTypedMapRowIter::new(
self.view.codec(),
view.range_bounds(bound_as_ref(&start), bound_as_ref(&end)),
)
}
fn scan_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::KeyIter {
let view = MapIterView::new(self.view.as_inner(), dir);
VarTypedMapKeyIter::new(
view.keys_range_bounds(bound_as_ref(&start), bound_as_ref(&end)),
)
}
fn peek_row_key(row: &Self::Row) -> K {
row.0
}
}
impl<
K: Key + Send + Sync + Hash + Eq,
T: Send + Sync,
C: Codec<T> + Clone + Sync,
H: TypedWriteHook<K, T>,
> OwnedRows for MapScan<&VarTypedMap<K, T, C, H>>
{
type Val = T;
fn own(row: Self::Row) -> (Self::Key, Self::Val) {
row
}
}
impl<
'a,
K: Key + Send + Sync + Hash + Eq,
T: Send + Sync,
C: Codec<T> + Clone + Sync,
H: TypedWriteHook<K, T>,
> BulkScan for MapScan<&'a VarTypedMap<K, T, C, H>>
{
type TryKeys = InfallibleKeys<K, VarTypedMapKeyIter<'a, K, T, C, H>>;
fn try_keys(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryKeys {
let source = self.view.as_inner();
let view = MapIterView::with_batch(source, dir, bulk_map_batch(source));
InfallibleKeys::new(VarTypedMapKeyIter::new(
view.keys_range_bounds(bound_as_ref(&start), bound_as_ref(&end)),
))
}
fn delete_key(&self, key: &K) -> DbResult<bool> {
self.view.delete(key)
}
fn reject_nontx_bulk(&self) -> DbResult<()> {
let inner = self.view.as_inner();
reject_bytes_bulk(&inner.hook, collection_name(&inner.counter_dirty))
}
}
impl<
'a,
K: Key + Send + Sync + Hash + Eq,
T: Send + Sync,
C: Codec<T> + Clone + Sync,
H: TypedWriteHook<K, T>,
> StrictRows for MapScan<&'a VarTypedMap<K, T, C, H>>
{
type TryRows = VarTypedMapTryIter<'a, K, T, C, H>;
fn try_rows(self, start: Bound<K>, end: Bound<K>, dir: Dir) -> Self::TryRows {
let source = self.view.as_inner();
self.view.inspection_range_bounds_dir(
bound_as_ref(&start),
bound_as_ref(&end),
dir,
bulk_map_batch(source),
)
}
fn delete_if_unchanged(&self, key: &K, expected: &T) -> DbResult<bool> {
conditional_delete(self.view.compare_delete(key, expected))
}
}
}
#[cfg(all(test, feature = "typed-tree"))]
mod shape_gate {
use super::*;
use crate::scope::Scope;
fn _uses_iter<'l, S: OrderedScan + 'l, const N: usize>(s: Scope<'l, S, N>) {
let _ = s.iter();
}
fn _page_by_shape<'l, S: OrderedScan + 'l, const N: usize>(
s: Scope<'l, S, N>,
mut f: impl FnMut(S::Row) -> Option<()>,
) {
for row in s.iter() {
let _ = f(row);
}
}
#[test]
fn scope_debug_formats_without_collection_debug() {
use crate::const_tree::ConstTree;
use armour_core::{KeyScheme, KeyType};
use zerocopy::{FromBytes, Immutable, IntoBytes};
#[derive(Clone, Copy, FromBytes, IntoBytes, Immutable)]
#[repr(C)]
struct NoDebugKey {
a: u64,
b: u64,
}
crate::impl_key_zerocopy!(NoDebugKey, KeyScheme::Typed(&[KeyType::U64, KeyType::U64]));
fn assert_debug<T: fmt::Debug>(_: &T) {}
fn check<'l>(s: &Scope<'l, TreeScan<&'l ConstTree<NoDebugKey, 8>>, 1>) {
assert_debug(s);
}
let _ = check;
}
#[test]
fn scope_compiles_for_typed_tree() {
use crate::typed_tree::TypedTree;
fn check_iter<'l>(
s: Scope<'l, TreeScan<&'l TypedTree<[u8; 8], u64, crate::ZerocopyCodec>>, 0>,
) {
_uses_iter(s);
}
fn check_page<'l>(
s: Scope<'l, TreeScan<&'l TypedTree<[u8; 8], u64, crate::ZerocopyCodec>>, 0>,
) {
_page_by_shape(s, |_| None);
}
let _ = (check_iter, check_page);
}
#[test]
fn scope_compiles_for_typed_map() {
use crate::typed_map::TypedMap;
fn check_iter<'l>(
s: Scope<'l, MapScan<&'l TypedMap<[u8; 8], u64, crate::ZerocopyCodec>>, 0>,
) {
_uses_iter(s);
}
fn check_page<'l>(
s: Scope<'l, MapScan<&'l TypedMap<[u8; 8], u64, crate::ZerocopyCodec>>, 0>,
) {
_page_by_shape(s, |_| None);
}
let _ = (check_iter, check_page);
}
#[cfg(feature = "fjall")]
#[test]
fn scope_compiles_for_lsm_tree() {
use crate::CollectionMeta;
use crate::RapiraCodec;
use crate::lsm::LsmTree;
#[derive(Clone, rapira::Rapira, armour_core::GetType)]
struct Row {
id: [u8; 8],
}
impl CollectionMeta for Row {
type SelfId = [u8; 8];
const NAME: &'static str = "shape_gate_lsm";
const VERSION: u16 = 1;
}
fn check_iter<'l>(s: Scope<'l, TreeScan<&'l LsmTree<[u8; 8], Row, RapiraCodec>>, 0>) {
_uses_iter(s);
}
fn check_page<'l>(s: Scope<'l, TreeScan<&'l LsmTree<[u8; 8], Row, RapiraCodec>>, 0>) {
_page_by_shape(s, |_| None);
}
let _ = (check_iter, check_page);
}
}