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;