pipecircuit 0.0.25

Correct. Robust. Fast. Portable. Pick four.
Documentation
use super::super::{
    WarpTable, WarpTableBacking, WarpTableGetAtOutcome, WarpTableRow,
    inconsistency::invariant_violation,
    stable_slots::{StableSlotResolveOutcome, StableSlotResolver},
};
use crate::storage::{
    StableSlot,
    query_workspace::{
        MembershipOperand, MembershipOperation, QueryWorkspaceError, WorkspaceMembership,
    },
    range_index::{RangeIndex, RangeIndexBounds, RangeIndexKey, RangeIndexQueryError},
};
use core::ops::DerefMut;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CallerSelectionError<Source> {
    Source(Source),
    DifferentTables,
    Workspace(QueryWorkspaceError),
}

pub trait CallerSelectionOperand<Indexes> {
    type Error;
    fn indexes(&self) -> &Indexes;
    fn write(
        self,
        membership: &mut WorkspaceMembership<'_>,
        operation: Option<MembershipOperation>,
    ) -> Result<(), Self::Error>;
}

pub struct CallerMembershipLookup<'index, Indexes> {
    indexes: &'index Indexes,
    operand: MembershipOperand<'index>,
}
impl<'index, Indexes> CallerMembershipLookup<'index, Indexes> {
    pub fn new(indexes: &'index Indexes, operand: MembershipOperand<'index>) -> Self {
        Self { indexes, operand }
    }
}
impl<Indexes> CallerSelectionOperand<Indexes> for CallerMembershipLookup<'_, Indexes> {
    type Error = QueryWorkspaceError;
    fn indexes(&self) -> &Indexes {
        self.indexes
    }
    fn write(
        self,
        membership: &mut WorkspaceMembership<'_>,
        operation: Option<MembershipOperation>,
    ) -> Result<(), Self::Error> {
        match operation {
            None => membership.copy_from(self.operand),
            Some(operation) => membership.apply(operation, self.operand),
        }
    }
}

pub struct CallerRangeLookup<'index, Indexes, Key: RangeIndexKey, Storage: DerefMut<Target = [u64]>>
{
    indexes: &'index Indexes,
    range: &'index RangeIndex<Key, Storage>,
    bounds: RangeIndexBounds<Key>,
}
impl<'index, Indexes, Key: RangeIndexKey, Storage: DerefMut<Target = [u64]>>
    CallerRangeLookup<'index, Indexes, Key, Storage>
{
    pub fn new(
        indexes: &'index Indexes,
        range: &'index RangeIndex<Key, Storage>,
        bounds: impl core::ops::RangeBounds<Key>,
    ) -> Self {
        Self {
            indexes,
            range,
            bounds: RangeIndexBounds::from_range(bounds),
        }
    }
}
impl<Indexes, Key: RangeIndexKey, Storage: DerefMut<Target = [u64]>> CallerSelectionOperand<Indexes>
    for CallerRangeLookup<'_, Indexes, Key, Storage>
{
    type Error = RangeIndexQueryError<Key>;
    fn indexes(&self) -> &Indexes {
        self.indexes
    }
    fn write(
        self,
        membership: &mut WorkspaceMembership<'_>,
        operation: Option<MembershipOperation>,
    ) -> Result<(), Self::Error> {
        let bounds = (self.bounds.start().cloned(), self.bounds.end().cloned());
        match operation {
            None => self.range.find_into(bounds, membership),
            Some(operation) => self.range.combine_into(bounds, membership, operation),
        }
        .map(|_bounds| ())
    }
}

pub struct CallerSelection<
    'table,
    'workspace,
    Row: WarpTableRow,
    Brand,
    Backing: WarpTableBacking<Row>,
> {
    pub(super) table: &'table WarpTable<Row, Brand, Backing>,
    pub(super) membership: WorkspaceMembership<'workspace>,
}

pub fn bind_caller_selection<
    'table,
    'workspace,
    Row: WarpTableRow,
    Brand,
    Backing: WarpTableBacking<Row>,
    Operand: CallerSelectionOperand<Backing::Indexes>,
>(
    table: &'table WarpTable<Row, Brand, Backing>,
    mut membership: WorkspaceMembership<'workspace>,
    operand: Operand,
) -> Result<
    CallerSelection<'table, 'workspace, Row, Brand, Backing>,
    CallerSelectionError<Operand::Error>,
> {
    if !core::ptr::eq(&table.indexes, operand.indexes()) {
        return Err(CallerSelectionError::DifferentTables);
    }
    if membership.capacity() != table.maximum_rows {
        return Err(CallerSelectionError::Workspace(
            QueryWorkspaceError::DifferentCapacities {
                left: table.maximum_rows,
                right: membership.capacity(),
            },
        ));
    }
    operand
        .write(&mut membership, None)
        .map_err(CallerSelectionError::Source)?;
    Ok(CallerSelection { table, membership })
}

impl<'table, 'workspace, Row: WarpTableRow, Brand, Backing: WarpTableBacking<Row>>
    CallerSelection<'table, 'workspace, Row, Brand, Backing>
{
    pub fn len(&self) -> u64 {
        self.membership.len()
    }
    pub fn is_empty(&self) -> bool {
        self.membership.is_empty()
    }
    pub fn and<Operand: CallerSelectionOperand<Backing::Indexes>>(
        self,
        find: impl FnOnce(&'table Backing::Indexes) -> Operand,
    ) -> Result<Self, CallerSelectionError<Operand::Error>> {
        self.apply(MembershipOperation::Intersection, find)
    }
    pub fn or<Operand: CallerSelectionOperand<Backing::Indexes>>(
        self,
        find: impl FnOnce(&'table Backing::Indexes) -> Operand,
    ) -> Result<Self, CallerSelectionError<Operand::Error>> {
        self.apply(MembershipOperation::Union, find)
    }
    pub fn and_not<Operand: CallerSelectionOperand<Backing::Indexes>>(
        self,
        find: impl FnOnce(&'table Backing::Indexes) -> Operand,
    ) -> Result<Self, CallerSelectionError<Operand::Error>> {
        self.apply(MembershipOperation::Difference, find)
    }
    pub fn xor<Operand: CallerSelectionOperand<Backing::Indexes>>(
        self,
        find: impl FnOnce(&'table Backing::Indexes) -> Operand,
    ) -> Result<Self, CallerSelectionError<Operand::Error>> {
        self.apply(MembershipOperation::SymmetricDifference, find)
    }
    fn apply<Operand: CallerSelectionOperand<Backing::Indexes>>(
        mut self,
        operation: MembershipOperation,
        find: impl FnOnce(&'table Backing::Indexes) -> Operand,
    ) -> Result<Self, CallerSelectionError<Operand::Error>> {
        let operand = find(&self.table.indexes);
        if !core::ptr::eq(&self.table.indexes, operand.indexes()) {
            return Err(CallerSelectionError::DifferentTables);
        }
        operand
            .write(&mut self.membership, Some(operation))
            .map_err(CallerSelectionError::Source)?;
        Ok(self)
    }
    pub fn rows(&self) -> impl DoubleEndedIterator<Item = Row::Ref<'table>> + '_
    where
        Backing::Indexes: StableSlotResolver,
    {
        self.membership.slots().map(|slot| {
            let row = resolve(&self.table.indexes, slot);
            match self.table.get_at(row) {
                WarpTableGetAtOutcome::Present { row, .. } => row,
                WarpTableGetAtOutcome::Absent { dense_row } => invariant_violation(dense_row),
            }
        })
    }
    pub fn rows_in_dense_order<'storage>(
        &self,
        storage: &'storage mut [usize],
    ) -> Result<
        super::super::DenseSelectionRows<'table, 'storage, Row, Brand, Backing>,
        super::super::DenseSelectionStorageTooSmall,
    >
    where
        Backing::Indexes: StableSlotResolver,
    {
        let rows = super::super::selection_rows::dense_rows(
            self.table,
            self.len(),
            self.membership.slots().map(StableSlot::new),
            storage,
        )?;
        Ok(super::super::DenseSelectionRows::new(self.table, rows))
    }
    pub fn into_workspace(self) -> WorkspaceMembership<'workspace> {
        self.membership
    }
}

fn resolve(indexes: &impl StableSlotResolver, slot: u32) -> usize {
    let StableSlotResolveOutcome::Resolved { dense_row, .. } = indexes
        .resolve_stable_slot(StableSlot::new(slot))
        .unwrap_or_else(|reason| invariant_violation(reason));
    dense_row
}

impl<'storage, Row: WarpTableRow, Columns: super::CallerWarpTableColumns<Row>, Brand>
    super::CallerWarpTable<'storage, Row, Columns, Brand>
{
    pub fn query_in<'table, 'workspace>(
        &'table self,
        membership: WorkspaceMembership<'workspace>,
    ) -> CallerQuery<'table, 'workspace, Row, Brand, super::CallerStorage<'storage, Row, Columns>>
    {
        CallerQuery {
            table: self,
            membership,
        }
    }
    pub fn update_selected_in<'workspace, Error, Update>(
        &mut self,
        membership: WorkspaceMembership<'workspace>,
        select: impl for<'table> FnOnce(
            CallerQuery<
                'table,
                'workspace,
                Row,
                Brand,
                super::CallerStorage<'storage, Row, Columns>,
            >,
        ) -> Result<
            CallerSelection<
                'table,
                'workspace,
                Row,
                Brand,
                super::CallerStorage<'storage, Row, Columns>,
            >,
            Error,
        >,
        mut update: Update,
    ) -> Result<u64, CallerSelectionError<Error>>
    where
        Columns::Indexes: StableSlotResolver,
        Columns: CallerWarpTableUpdate<Row, Update>,
    {
        let selected = select(CallerQuery {
            table: self,
            membership,
        })
        .map_err(CallerSelectionError::Source)?;
        if !core::ptr::eq(self, selected.table) {
            return Err(CallerSelectionError::DifferentTables);
        }
        let membership = selected.into_workspace();
        let count = membership.len();
        for slot in membership.slots() {
            let row = resolve(&self.indexes, slot);
            let id = super::super::get::stored_id(self.ids.as_slice(), row);
            Columns::update(&mut self.columns, id, &mut self.indexes, row, &mut update);
        }
        Ok(count)
    }
}

pub struct CallerQuery<'table, 'workspace, Row: WarpTableRow, Brand, Backing: WarpTableBacking<Row>>
{
    table: &'table WarpTable<Row, Brand, Backing>,
    membership: WorkspaceMembership<'workspace>,
}
impl<'table, 'workspace, Row: WarpTableRow, Brand, Backing: WarpTableBacking<Row>>
    CallerQuery<'table, 'workspace, Row, Brand, Backing>
{
    pub fn find<Operand: CallerSelectionOperand<Backing::Indexes>>(
        self,
        find: impl FnOnce(&'table Backing::Indexes) -> Operand,
    ) -> Result<
        CallerSelection<'table, 'workspace, Row, Brand, Backing>,
        CallerSelectionError<Operand::Error>,
    > {
        bind_caller_selection(self.table, self.membership, find(&self.table.indexes))
    }
}

#[doc(hidden)]
pub trait CallerWarpTableUpdate<Row: WarpTableRow, Update>:
    super::CallerWarpTableColumns<Row>
{
    fn update(
        &mut self,
        id: &Row::Id,
        indexes: &mut Self::Indexes,
        row: usize,
        update: &mut Update,
    );
}