pipecircuit 0.0.23

Correct. Robust. Fast. Portable. Pick four.
Documentation
use super::super::{Capacity, IndexKey, WarpTable, WarpTableRow};
use super::view::creation_error;
use super::{ViewChange, ViewColumnStorageError, ViewCreationError};
use alloc::{collections::VecDeque, vec::Vec};

pub(super) struct Previous<Id, Value> {
    pub revision: u64,
    pub id: Id,
    pub before: Option<(u64, Value)>,
}

pub(super) struct RetainedChanges<Id, Value> {
    pub entries: VecDeque<Previous<Id, Value>>,
    pub capacity: Capacity,
    pub maximum_changes: usize,
    pub discarded_through: u64,
}

impl<Id, Value> RetainedChanges<Id, Value> {
    pub fn try_new(capacity: Capacity) -> Result<Self, ViewCreationError> {
        let maximum_changes = usize::try_from(capacity.value().get())
            .map_err(|_| ViewCreationError::CapacityOverflow)?;
        let mut entries = VecDeque::new();
        entries
            .try_reserve_exact(maximum_changes)
            .map_err(ViewCreationError::StorageUnavailable)?;
        Ok(Self {
            entries,
            capacity,
            maximum_changes,
            discarded_through: 0,
        })
    }

    pub fn reset(&mut self, through: u64) {
        self.entries.clear();
        self.discarded_through = through;
    }

    pub fn push(&mut self, change: Previous<Id, Value>) {
        if self.entries.len() == self.maximum_changes
            && let Some(discarded) = self.entries.pop_front()
        {
            self.discarded_through = discarded.revision;
        }
        self.entries.push_back(change);
    }
}

#[derive(WarpTableRow)]
#[index(primary = Id)]
pub(super) struct FirstChange<Id: IndexKey> {
    pub index: usize,
    pub marker: core::marker::PhantomData<Id>,
}

/// Reusable preparation space, shared across subscribers one delivery at a time.
/// Encode a prepared delivery once for subscribers with the same cursor and
/// representation; a subscriber needs only its admitted revision, not a table.
pub struct ViewDeliveryBuffer<Id: IndexKey, Value> {
    pub(super) first: WarpTable<FirstChange<Id>>,
    pub(super) changes: Vec<ViewChange<Id, Value>>,
    pub(super) maximum_rows: usize,
    pub(super) maximum_history: usize,
}

impl<Id: IndexKey, Value> ViewDeliveryBuffer<Id, Value> {
    pub(super) fn try_new(rows: Capacity, history: Capacity) -> Result<Self, ViewCreationError> {
        let first = WarpTable::try_new(history).map_err(|error| {
            creation_error(
                error,
                |FirstChangeColumnsCreationError::StorageUnavailable { column, source }| {
                    ViewColumnStorageError { column, source }
                },
                |never| match never {},
            )
        })?;
        let maximum_rows =
            usize::try_from(rows.value().get()).map_err(|_| ViewCreationError::CapacityOverflow)?;
        let maximum_history = usize::try_from(history.value().get())
            .map_err(|_| ViewCreationError::CapacityOverflow)?;
        let maximum_changes = maximum_rows
            .checked_mul(2)
            .ok_or(ViewCreationError::CapacityOverflow)?;
        let mut changes = Vec::new();
        changes
            .try_reserve_exact(maximum_changes)
            .map_err(ViewCreationError::StorageUnavailable)?;
        Ok(Self {
            first,
            changes,
            maximum_rows,
            maximum_history,
        })
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ViewDeliveryError {
    WrongView,
    FutureCursor,
    InsufficientBuffer,
}

pub struct ViewSubscription {
    pub(super) source: super::SourceId,
    pub(super) view: super::ViewId,
    pub(super) cursor: Option<super::ViewRevision>,
}

impl ViewSubscription {
    pub const fn admitted_through(&self) -> Option<super::ViewRevision> {
        self.cursor
    }

    /// Requests a fresh baseline after the carrier loses continuity.
    pub fn require_baseline(&mut self) {
        self.cursor = None;
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ViewAdmission {
    /// The carrier owns the complete delivery, including any remaining chunks.
    Admitted,
    /// No delivery was admitted; recompute from the unchanged cursor on retry.
    Backpressured,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ViewTransmitOutcome {
    Current { through: super::ViewRevision },
    Admitted { through: super::ViewRevision },
    Backpressured { through: super::ViewRevision },
}

#[derive(Debug, Eq, PartialEq)]
pub enum ViewTransmitError<TransportError> {
    Preparation(ViewDeliveryError),
    Transport(TransportError),
}