pipecircuit 0.0.24

Correct. Robust. Fast. Portable. Pick four.
Documentation
use super::rows::{StableSlotResolver, resolve};
use crate::storage::warp_table::secondary_index::StableSlot;
use crate::storage::warp_table::{WarpTable, WarpTableRow};
use crate::storage::{
    membership::BucketMembership,
    query_workspace::{
        MembershipOperand, MembershipOperation, QueryWorkspaceError, WorkspaceMembership,
    },
};

pub trait WarpTableMembershipRow: WarpTableRow {
    type MembershipQuery<'table, Brand: 'table>
    where
        Self: 'table;
    fn membership_query<'table, Brand: 'table>(
        table: &'table WarpTable<Self, Brand>,
    ) -> Self::MembershipQuery<'table, Brand>
    where
        Self: 'table;
}

pub struct MembershipLookup<'table, Row: WarpTableRow, Brand, Key> {
    table: &'table WarpTable<Row, Brand>,
    key: Key,
    members: Option<BucketMembership<'table>>,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WorkspaceSelectionError {
    SelectionAlreadyStarted,
    Workspace(QueryWorkspaceError),
    DifferentTables,
    DenseStorage(super::DenseSelectionStorageTooSmall),
}
impl From<QueryWorkspaceError> for WorkspaceSelectionError {
    fn from(reason: QueryWorkspaceError) -> Self {
        Self::Workspace(reason)
    }
}

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

impl<Row: WarpTableMembershipRow, Brand> WarpTable<Row, Brand> {
    pub fn query_memberships(&self) -> Row::MembershipQuery<'_, Brand> {
        Row::membership_query(self)
    }
}

impl<'table, Row: WarpTableRow, Brand, Key> MembershipLookup<'table, Row, Brand, Key> {
    pub fn key(&self) -> &Key {
        &self.key
    }
    pub const fn is_present(&self) -> bool {
        self.members.is_some()
    }
    pub fn len(&self) -> usize {
        self.members.map_or(0, |members| members.len())
    }
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
    fn operand(&self) -> MembershipOperand<'table> {
        MembershipOperand::from_index(self.table.maximum_rows, self.members)
    }
    pub fn in_workspace<'workspace>(
        self,
        mut membership: WorkspaceMembership<'workspace>,
    ) -> Result<WorkspaceSelection<'table, 'workspace, Row, Brand>, WorkspaceSelectionError> {
        membership.copy_from(self.operand())?;
        Ok(WorkspaceSelection {
            table: self.table,
            membership,
        })
    }
}

impl<'table, 'workspace, Row: WarpTableMembershipRow, Brand: 'table>
    WorkspaceSelection<'table, 'workspace, Row, Brand>
{
    pub fn len(&self) -> u64 {
        self.membership.len()
    }
    pub fn is_empty(&self) -> bool {
        self.membership.is_empty()
    }
    pub fn and<Key>(
        self,
        find: impl FnOnce(
            Row::MembershipQuery<'table, Brand>,
        ) -> MembershipLookup<'table, Row, Brand, Key>,
    ) -> Result<Self, WorkspaceSelectionError> {
        self.apply(MembershipOperation::Intersection, find)
    }
    pub fn or<Key>(
        self,
        find: impl FnOnce(
            Row::MembershipQuery<'table, Brand>,
        ) -> MembershipLookup<'table, Row, Brand, Key>,
    ) -> Result<Self, WorkspaceSelectionError> {
        self.apply(MembershipOperation::Union, find)
    }
    pub fn and_not<Key>(
        self,
        find: impl FnOnce(
            Row::MembershipQuery<'table, Brand>,
        ) -> MembershipLookup<'table, Row, Brand, Key>,
    ) -> Result<Self, WorkspaceSelectionError> {
        self.apply(MembershipOperation::Difference, find)
    }
    pub fn xor<Key>(
        self,
        find: impl FnOnce(
            Row::MembershipQuery<'table, Brand>,
        ) -> MembershipLookup<'table, Row, Brand, Key>,
    ) -> Result<Self, WorkspaceSelectionError> {
        self.apply(MembershipOperation::SymmetricDifference, find)
    }
    fn apply<Key>(
        mut self,
        operation: MembershipOperation,
        find: impl FnOnce(
            Row::MembershipQuery<'table, Brand>,
        ) -> MembershipLookup<'table, Row, Brand, Key>,
    ) -> Result<Self, WorkspaceSelectionError> {
        let right = find(Row::membership_query(self.table));
        if !core::ptr::eq(self.table, right.table) {
            return Err(WorkspaceSelectionError::DifferentTables);
        }
        self.membership.apply(operation, right.operand())?;
        Ok(self)
    }
    pub fn rows(&self) -> impl DoubleEndedIterator<Item = Row::Ref<'table>> + '_
    where
        Row::Indexes: StableSlotResolver,
    {
        self.membership
            .slots()
            .map(|slot| resolve(self.table, StableSlot::new(slot)))
    }
    pub fn ids(&self) -> impl DoubleEndedIterator<Item = Row::Id> + '_
    where
        Row::Indexes: StableSlotResolver,
    {
        self.membership.slots().map(|slot| {
            let crate::storage::warp_table::secondary_index::StableSlotResolveOutcome::Resolved {
                dense_row,
                ..
            } = match self
                .table
                .__indexes()
                .resolve_stable_slot(StableSlot::new(slot))
            {
                Ok(resolved) => resolved,
                Err(reason) => {
                    crate::storage::warp_table::inconsistency::invariant_violation(reason)
                }
            };
            match self.table.ids().get(dense_row) {
                Some(id) => *id,
                None => crate::storage::warp_table::inconsistency::invariant_violation(
                    "selected identity missing",
                ),
            }
        })
    }
    pub fn rows_in_dense_order<'storage>(
        &self,
        storage: &'storage mut [usize],
    ) -> Result<
        super::DenseSelectionRows<'table, 'storage, Row, Brand>,
        super::DenseSelectionStorageTooSmall,
    >
    where
        Row::Indexes: StableSlotResolver,
    {
        let rows = super::dense::dense_rows(
            self.table,
            self.len(),
            self.membership.slots().map(StableSlot::new),
            storage,
        )?;
        Ok(super::DenseSelectionRows::new(self.table, rows))
    }

    pub fn into_workspace(self) -> WorkspaceMembership<'workspace> {
        self.membership
    }
}

impl<Row: WarpTableMembershipRow, Brand> WarpTable<Row, Brand>
where
    Row::Indexes: StableSlotResolver,
{
    pub fn update_memberships<'workspace>(
        &mut self,
        membership: WorkspaceMembership<'workspace>,
        select: impl for<'table> FnOnce(
            &'table WarpTable<Row, Brand>,
            WorkspaceMembership<'workspace>,
        ) -> Result<
            WorkspaceSelection<'table, 'workspace, Row, Brand>,
            WorkspaceSelectionError,
        >,
        mut update: impl FnMut(Row::Mut<'_>),
    ) -> Result<u64, WorkspaceSelectionError> {
        let selected = select(self, membership)?;
        if !core::ptr::eq(self, selected.table) {
            return Err(WorkspaceSelectionError::DifferentTables);
        }
        let membership = selected.into_workspace();
        let rows = membership.len();
        for slot in membership.slots() {
            let crate::storage::warp_table::secondary_index::StableSlotResolveOutcome::Resolved {
                dense_row,
                ..
            } = match self.__indexes().resolve_stable_slot(StableSlot::new(slot)) {
                Ok(resolved) => resolved,
                Err(reason) => {
                    crate::storage::warp_table::inconsistency::invariant_violation(reason)
                }
            };
            self.update_resolved(dense_row, &mut update);
        }
        Ok(rows)
    }
    pub fn update_memberships_in_dense_order<'workspace>(
        &mut self,
        membership: WorkspaceMembership<'workspace>,
        storage: &mut [usize],
        select: impl for<'table> FnOnce(
            &'table WarpTable<Row, Brand>,
            WorkspaceMembership<'workspace>,
        ) -> Result<
            WorkspaceSelection<'table, 'workspace, Row, Brand>,
            WorkspaceSelectionError,
        >,
        mut update: impl FnMut(Row::Mut<'_>),
    ) -> Result<u64, WorkspaceSelectionError> {
        let selected = select(self, membership)?;
        if !core::ptr::eq(self, selected.table) {
            return Err(WorkspaceSelectionError::DifferentTables);
        }
        let membership = selected.into_workspace();
        let rows = membership.len();
        let dense =
            super::dense::dense_rows(self, rows, membership.slots().map(StableSlot::new), storage)
                .map_err(WorkspaceSelectionError::DenseStorage)?;
        for row in dense {
            self.update_resolved(*row, &mut update);
        }
        Ok(rows)
    }
}

pub fn bind_membership_lookup<'table, Row: WarpTableRow, Brand, Key>(
    table: &'table WarpTable<Row, Brand>,
    key: Key,
    members: Option<BucketMembership<'table>>,
) -> MembershipLookup<'table, Row, Brand, Key> {
    MembershipLookup {
        table,
        key,
        members,
    }
}

impl<'table, Row: WarpTableRow, Brand> super::Selection<'table, Row, Brand> {
    pub fn in_workspace<'workspace>(
        self,
        mut membership: WorkspaceMembership<'workspace>,
    ) -> Result<WorkspaceSelection<'table, 'workspace, Row, Brand>, WorkspaceSelectionError> {
        membership.copy_chunks(self.table.maximum_rows, self.membership.read().chunks())?;
        Ok(WorkspaceSelection {
            table: self.table,
            membership,
        })
    }
}
impl<'table, Row: WarpTableRow, Brand, Key>
    super::SecondaryIndexLookupOutcome<'table, Row, Brand, Key>
{
    pub fn in_workspace<'workspace>(
        self,
        membership: WorkspaceMembership<'workspace>,
    ) -> Result<WorkspaceSelection<'table, 'workspace, Row, Brand>, WorkspaceSelectionError> {
        self.into_selection().in_workspace(membership)
    }
}
impl<'table, Row: WarpTableRow, Brand, Key>
    super::RangeIndexLookupOutcome<'table, Row, Brand, Key>
{
    pub fn in_workspace<'workspace>(
        self,
        membership: WorkspaceMembership<'workspace>,
    ) -> Result<WorkspaceSelection<'table, 'workspace, Row, Brand>, WorkspaceSelectionError> {
        self.into_selection().in_workspace(membership)
    }
}

#[cfg(test)]
mod tests;