pipecircuit 0.0.17

Correct. Robust. Fast. Portable. Pick four.
Documentation
use super::bucket::{next_bucket, reduce};
use super::{DenseKeyIndex, EMPTY_ROW, IndexInconsistency};
use crate::storage::warp_table::IndexKey;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::storage::warp_table) struct PreparedVacancy {
    pub(super) bucket: usize,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::storage::warp_table) struct PreparedCommit {
    vacancy: PreparedVacancy,
    row: usize,
    stored_row: u32,
}

impl PreparedVacancy {
    pub(in crate::storage::warp_table) const fn bucket(self) -> usize {
        self.bucket
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub(in crate::storage::warp_table) enum PrepareInsertOutcome {
    Vacant(PreparedVacancy),
    Present { row: usize },
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub(in crate::storage::warp_table) enum CommitOutcome {
    Committed { row: usize },
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::storage::warp_table) enum CommitInconsistency {
    RowUnrepresentable {
        vacancy: PreparedVacancy,
        row: usize,
    },
    RowReserved {
        vacancy: PreparedVacancy,
        row: usize,
    },
    VacancyUnavailable {
        vacancy: PreparedVacancy,
        row: usize,
    },
    VacancyOccupied {
        vacancy: PreparedVacancy,
        row: usize,
        occupying_row: u32,
    },
}

impl DenseKeyIndex {
    pub(in crate::storage::warp_table) fn prepare_insert<Key: IndexKey>(
        &self,
        key: Key,
        keys: &[Key],
    ) -> Result<PrepareInsertOutcome, IndexInconsistency> {
        let mut bucket = reduce(key.index_word(), self.buckets.len());
        for _ in 0..self.buckets.len() {
            let Some(stored_row) = self.buckets.get(bucket).copied() else {
                return Err(IndexInconsistency::BucketUnavailable { bucket });
            };
            if stored_row == EMPTY_ROW {
                return Ok(PrepareInsertOutcome::Vacant(PreparedVacancy { bucket }));
            }
            let Ok(row) = usize::try_from(stored_row) else {
                return Err(IndexInconsistency::StoredRowUnrepresentable { stored_row });
            };
            let Some(stored_key) = keys.get(row).copied() else {
                return Err(IndexInconsistency::KeyUnavailable { row });
            };
            if stored_key == key {
                return Ok(PrepareInsertOutcome::Present { row });
            }
            bucket = next_bucket(bucket, self.buckets.len());
        }
        Err(IndexInconsistency::ProbeLimitReached)
    }

    #[cfg(test)]
    pub(in crate::storage::warp_table) fn commit(
        &mut self,
        vacancy: PreparedVacancy,
        row: usize,
    ) -> Result<CommitOutcome, CommitInconsistency> {
        let prepared = self.prepare_commit(vacancy, row)?;
        Ok(self.commit_prepared(prepared))
    }

    pub(in crate::storage::warp_table) fn prepare_commit(
        &self,
        vacancy: PreparedVacancy,
        row: usize,
    ) -> Result<PreparedCommit, CommitInconsistency> {
        let Ok(stored_row) = u32::try_from(row) else {
            return Err(CommitInconsistency::RowUnrepresentable { vacancy, row });
        };
        if stored_row == EMPTY_ROW {
            return Err(CommitInconsistency::RowReserved { vacancy, row });
        }
        let Some(stored) = self.buckets.get(vacancy.bucket) else {
            return Err(CommitInconsistency::VacancyUnavailable { vacancy, row });
        };
        if *stored != EMPTY_ROW {
            return Err(CommitInconsistency::VacancyOccupied {
                vacancy,
                row,
                occupying_row: *stored,
            });
        }
        Ok(PreparedCommit {
            vacancy,
            row,
            stored_row,
        })
    }

    #[allow(
        clippy::indexing_slicing,
        reason = "the prepared commit proves its bucket is present and vacant"
    )]
    pub(in crate::storage::warp_table) fn commit_prepared(
        &mut self,
        prepared: PreparedCommit,
    ) -> CommitOutcome {
        self.buckets[prepared.vacancy.bucket] = prepared.stored_row;
        CommitOutcome::Committed { row: prepared.row }
    }
}