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,
);
}