pipecircuit 0.0.25

Correct. Robust. Fast. Portable. Pick four.
Documentation
mod unique;
pub use unique::{CallerUniqueLookup, CallerUniqueLookupError};
mod bulk;
pub use bulk::{CallerBulkUpdateError, CallerWarpTableBulkUpdate};
mod ranking;
pub use ranking::{CallerOrderingLevel, CallerRanking, CallerRankingError};
mod selection;
use core::num::NonZeroU64;
pub use selection::{
    CallerMembershipLookup, CallerQuery, CallerRangeLookup, CallerSelection, CallerSelectionError,
    CallerSelectionOperand, CallerWarpTableUpdate, bind_caller_selection,
};

use super::dense_key_index::{DenseKeyIndex, required_buckets};
use super::inconsistency::invariant_violation;
use super::{
    Capacity, ColumnLease, WarpTable, WarpTableBacking, WarpTableInconsistency, WarpTableRow,
    WarpTableRowGetError, WarpTableRowGetOutcome, WarpTableRowReplaceError,
    WarpTableRowReplaceOutcome,
};

pub trait CallerWarpTableColumns<Row: WarpTableRow> {
    type Indexes: super::WarpTableIndexes<Row>;
    type Mut<'row>
    where
        Self: 'row,
        Row: 'row;
    fn validate_indexes(
        indexes: &Self::Indexes,
        maximum_rows: usize,
    ) -> Result<(), CallerStorageError>;
    fn validate(&self, maximum_rows: usize) -> Result<(), CallerStorageError>;
    fn columns_mut(&mut self) -> Row::ColumnsMut<'_>;
    fn append(&mut self, row: Row);
    fn swap_remove(&mut self, row: usize) -> Row;
    fn get<'row>(
        &'row self,
        id: &'row Row::Id,
        row: usize,
    ) -> Result<WarpTableRowGetOutcome<Row::Ref<'row>>, WarpTableRowGetError>
    where
        Row: 'row;
    fn rows<'row>(
        &'row self,
        ids: &'row [Row::Id],
    ) -> Result<Row::Rows<'row>, WarpTableRowGetError>
    where
        Row: 'row;

    fn fold_rows<'row, Acc, Fold>(
        &'row self,
        ids: &'row [Row::Id],
        start: usize,
        initial: Acc,
        mut fold: Fold,
    ) -> Result<Acc, WarpTableRowGetError>
    where
        Row: 'row,
        Fold: FnMut(Acc, Row::Ref<'row>) -> Acc,
    {
        ids.iter()
            .enumerate()
            .try_fold(initial, |acc, (offset, id)| {
                let WarpTableRowGetOutcome::Present { row } =
                    self.get(id, start.saturating_add(offset))?;
                Ok(fold(acc, row))
            })
    }

    fn get_mut<'row>(
        &'row mut self,
        id: &'row Row::Id,
        indexes: &'row mut Self::Indexes,
        row: usize,
    ) -> Result<WarpTableRowGetOutcome<Self::Mut<'row>>, WarpTableRowGetError>
    where
        Row: 'row,
        Self: 'row;
    fn replace(
        &mut self,
        row: usize,
        replacement: Row,
    ) -> Result<WarpTableRowReplaceOutcome<Row>, WarpTableRowReplaceError<Row>>;
}

pub type CallerWarpTable<'storage, Row, Columns, Brand = Row> =
    WarpTable<Row, Brand, CallerStorage<'storage, Row, Columns>>;

pub struct CallerStorage<'storage, Row: WarpTableRow, Columns: CallerWarpTableColumns<Row>> {
    pub ids: ColumnLease<'storage, Row::Id>,
    pub columns: Columns,
    pub indexes: Columns::Indexes,
    pub primary_buckets: &'storage mut [u32],
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CallerStorageError {
    CapacityUnrepresentable {
        capacity: Capacity,
    },
    PrimaryIndexCapacityOverflow {
        maximum_rows: usize,
    },
    IdsNotEmpty {
        rows: usize,
    },
    IdCapacityInsufficient {
        required: usize,
        available: usize,
    },
    ColumnNotEmpty {
        column: &'static str,
        rows: usize,
    },
    ColumnCapacityInsufficient {
        column: &'static str,
        required: usize,
        available: usize,
    },
    MembershipLayout {
        field: &'static str,
        reason: crate::storage::membership::MembershipIndexLayoutError,
    },
    IndexNotEmpty {
        field: &'static str,
    },
    IndexCapacityMismatch {
        field: &'static str,
        required: usize,
        available: usize,
    },
    PrimaryIndexCapacityInsufficient {
        required: usize,
        available: usize,
    },
}

pub struct CallerStorageRefusal<'storage, Row: WarpTableRow, Columns: CallerWarpTableColumns<Row>> {
    pub storage: CallerStorage<'storage, Row, Columns>,
    pub reason: CallerStorageError,
}

impl Capacity {
    pub fn primary_index_buckets(self) -> Result<usize, CallerStorageError> {
        let maximum_rows = represent_capacity::<usize>(self)?;
        primary_bucket_count(maximum_rows)
    }
}

fn represent_capacity<Index: TryFrom<u32>>(
    capacity: Capacity,
) -> Result<Index, CallerStorageError> {
    Index::try_from(capacity.value().get())
        .map_err(|_| CallerStorageError::CapacityUnrepresentable { capacity })
}

fn primary_bucket_count(maximum_rows: usize) -> Result<usize, CallerStorageError> {
    required_buckets(maximum_rows)
        .ok_or(CallerStorageError::PrimaryIndexCapacityOverflow { maximum_rows })
}

impl<'storage, Row: WarpTableRow, Columns: CallerWarpTableColumns<Row>, Brand>
    WarpTable<Row, Brand, CallerStorage<'storage, Row, Columns>>
{
    pub fn from_storage(
        capacity: Capacity,
        storage: CallerStorage<'storage, Row, Columns>,
    ) -> Result<
        CallerWarpTable<'storage, Row, Columns, Brand>,
        CallerStorageRefusal<'storage, Row, Columns>,
    > {
        let dimensions = (|| {
            let maximum_rows = represent_capacity::<usize>(capacity)?;
            let bucket_count = primary_bucket_count(maximum_rows)?;
            if !storage.ids.is_empty() {
                return Err(CallerStorageError::IdsNotEmpty {
                    rows: storage.ids.len(),
                });
            }
            if storage.ids.capacity() < maximum_rows {
                return Err(CallerStorageError::IdCapacityInsufficient {
                    required: maximum_rows,
                    available: storage.ids.capacity(),
                });
            }
            storage.columns.validate(maximum_rows)?;
            Columns::validate_indexes(&storage.indexes, maximum_rows)?;
            if storage.primary_buckets.len() < bucket_count {
                return Err(CallerStorageError::PrimaryIndexCapacityInsufficient {
                    required: bucket_count,
                    available: storage.primary_buckets.len(),
                });
            }
            Ok(maximum_rows)
        })();
        let maximum_rows = match dimensions {
            Ok(maximum_rows) => maximum_rows,
            Err(reason) => return Err(CallerStorageRefusal { storage, reason }),
        };
        let CallerStorage {
            ids,
            columns,
            indexes,
            primary_buckets,
        } = storage;
        Ok(WarpTable {
            capacity,
            maximum_rows,
            len: 0,
            next_id: Some(NonZeroU64::MIN),
            ids,
            columns,
            indexes,
            primary_index: DenseKeyIndex::from_buckets(primary_buckets),
            #[cfg(feature = "secondary-indexes")]
            bulk_workspace: None,
            #[cfg(feature = "secondary-indexes")]
            query_storage: core::cell::OnceCell::new(),
            #[cfg(feature = "secondary-indexes")]
            mutable_snapshot: None,
            brand: core::marker::PhantomData,
        })
    }
}

impl<'storage, Row: WarpTableRow, Columns: CallerWarpTableColumns<Row>> WarpTableBacking<Row>
    for CallerStorage<'storage, Row, Columns>
{
    type Indexes = Columns::Indexes;
    type Mut<'row>
        = Columns::Mut<'row>
    where
        Self: 'row,
        Row: 'row;
    type Ids = ColumnLease<'storage, Row::Id>;
    type Columns = Columns;
    type Buckets = &'storage mut [u32];

    fn ids(ids: &Self::Ids) -> &[Row::Id] {
        ids.as_slice()
    }

    fn append_id(ids: &mut Self::Ids, id: Row::Id) {
        if ids.push(id).is_err() {
            invariant_violation(WarpTableInconsistency::IdStorageFull {
                capacity: ids.capacity(),
            });
        }
    }

    fn remove_id(ids: &mut Self::Ids, row: usize) {
        if ids.swap_remove(row).is_none() {
            invariant_violation(WarpTableInconsistency::IdUnavailable { row });
        }
    }

    fn columns_mut(columns: &mut Self::Columns) -> Row::ColumnsMut<'_> {
        columns.columns_mut()
    }

    fn append(columns: &mut Self::Columns, row: Row) {
        columns.append(row);
    }

    fn swap_remove(columns: &mut Self::Columns, row: usize) -> Row {
        columns.swap_remove(row)
    }

    fn get<'row>(
        id: &'row Row::Id,
        columns: &'row Self::Columns,
        row: usize,
    ) -> Result<WarpTableRowGetOutcome<Row::Ref<'row>>, WarpTableRowGetError>
    where
        Row: 'row,
    {
        columns.get(id, row)
    }

    fn rows<'row>(
        ids: &'row [Row::Id],
        columns: &'row Self::Columns,
    ) -> Result<Row::Rows<'row>, WarpTableRowGetError>
    where
        Row: 'row,
    {
        columns.rows(ids)
    }

    fn fold_rows<'row, Acc, Fold>(
        ids: &'row [Row::Id],
        columns: &'row Self::Columns,
        start: usize,
        initial: Acc,
        fold: Fold,
    ) -> Result<Acc, WarpTableRowGetError>
    where
        Row: 'row,
        Fold: FnMut(Acc, Row::Ref<'row>) -> Acc,
    {
        columns.fold_rows(ids, start, initial, fold)
    }

    fn get_mut<'row>(
        id: &'row Row::Id,
        columns: &'row mut Self::Columns,
        indexes: &'row mut Self::Indexes,
        row: usize,
    ) -> Result<WarpTableRowGetOutcome<Self::Mut<'row>>, WarpTableRowGetError>
    where
        Row: 'row,
        Self: 'row,
    {
        columns.get_mut(id, indexes, row)
    }

    fn replace(
        columns: &mut Self::Columns,
        row: usize,
        replacement: Row,
    ) -> Result<WarpTableRowReplaceOutcome<Row>, WarpTableRowReplaceError<Row>> {
        columns.replace(row, replacement)
    }
}

#[doc(hidden)]
pub fn validate_caller_column<T>(
    column: &'static str,
    storage: &ColumnLease<'_, T>,
    maximum_rows: usize,
) -> Result<(), CallerStorageError> {
    if !storage.is_empty() {
        return Err(CallerStorageError::ColumnNotEmpty {
            column,
            rows: storage.len(),
        });
    }
    if storage.capacity() < maximum_rows {
        return Err(CallerStorageError::ColumnCapacityInsufficient {
            column,
            required: maximum_rows,
            available: storage.capacity(),
        });
    }
    Ok(())
}

#[cfg(test)]
mod tests;

#[cfg(feature = "alloc")]
impl<Row: WarpTableRow, Brand> WarpTable<Row, Brand> {
    pub fn try_from_storage<'storage, Columns: CallerWarpTableColumns<Row>>(
        capacity: Capacity,
        storage: CallerStorage<'storage, Row, Columns>,
    ) -> Result<
        CallerWarpTable<'storage, Row, Columns, Brand>,
        CallerStorageRefusal<'storage, Row, Columns>,
    > {
        CallerWarpTable::from_storage(capacity, storage)
    }
}

#[doc(hidden)]
pub fn validate_caller_index(
    field: &'static str,
    required: usize,
    available: usize,
    empty: bool,
) -> Result<(), CallerStorageError> {
    if !empty {
        return Err(CallerStorageError::IndexNotEmpty { field });
    }
    if available != required {
        return Err(CallerStorageError::IndexCapacityMismatch {
            field,
            required,
            available,
        });
    }
    Ok(())
}