armdb 0.14.1

sharded bitcask key-value storage optimized for NVMe
Documentation
use std::ops::Bound;
use std::sync::Arc;

use crate::key::Key;
use crate::map_index::Dir;
use crate::scope::key_math;

type KeyIter<S, K> =
    for<'a> fn(&'a S, Bound<&'a K>, Bound<&'a K>, Dir) -> Box<dyn Iterator<Item = K> + 'a>;

/// Result of selecting source operations for a transactional range deletion.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Selection {
    /// Number of source delete operations queued in the returned transaction.
    pub queued: usize,
    /// Whether this selection observed its range exhausted.
    ///
    /// This is false when the caller limit or the transaction's remaining source
    /// operation capacity truncated selection. It is not a snapshot: a key
    /// inserted after this walk can survive even when `done` is true.
    pub done: bool,
}

/// Arc-owned typed prefix/range builder for transactional deletion.
///
/// A scope is bound to the exact collection it was constructed from, so it
/// cannot accidentally enqueue deletes against another collection with the
/// same key type.
pub struct TxScope<S, K: Key, const N: usize> {
    pub(super) collection: Arc<S>,
    pub(super) prefix_key: K,
    pub(super) start: Bound<K>,
    pub(super) end: Bound<K>,
    pub(super) dir: Dir,
    keys: KeyIter<S, K>,
}

impl<S, K: Key, const N: usize> TxScope<S, K, N> {
    pub(crate) fn from_parts(collection: Arc<S>, key: &K, dir: Dir, keys: KeyIter<S, K>) -> Self {
        const { key_math::assert_scope_n::<K>(N) };
        let (start, end) = key_math::scope_bounds_n::<K, N>(key);
        Self {
            collection,
            prefix_key: key_math::prefix_key(key, N),
            start,
            end,
            dir,
            keys,
        }
    }

    /// Narrow the scan to fields `[N..M)`. Last call wins.
    pub fn range<const M: usize>(mut self, start: Bound<&K>, end: Bound<&K>) -> Self {
        const { key_math::assert_range_m::<K>(N, M) };
        (self.start, self.end) =
            key_math::saturate_range_bounds(&self.prefix_key, N, M, start, end);
        self
    }

    /// Select keys in ascending order.
    ///
    /// Map and LSM families reverse cheaply; a skiplist Tree can pay `O(log n)`
    /// per row when ascending opposes its configured storage order.
    pub fn asc(mut self) -> Self {
        self.dir = Dir::Asc;
        self
    }

    /// Select keys in descending order.
    ///
    /// Map and LSM families reverse cheaply; a skiplist Tree can pay `O(log n)`
    /// per row when descending opposes its configured storage order.
    pub fn desc(mut self) -> Self {
        self.dir = Dir::Desc;
        self
    }

    pub(crate) fn keys(&self) -> Box<dyn Iterator<Item = K> + '_> {
        (self.keys)(
            self.collection.as_ref(),
            bound_as_ref(&self.start),
            bound_as_ref(&self.end),
            self.dir,
        )
    }
}

fn bound_as_ref<K>(bound: &Bound<K>) -> Bound<&K> {
    match bound {
        Bound::Included(key) => Bound::Included(key),
        Bound::Excluded(key) => Bound::Excluded(key),
        Bound::Unbounded => Bound::Unbounded,
    }
}