pipecircuit 0.0.18

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

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::storage::warp_table) struct PreparedReplacement<Key> {
    removal: PreparedRemoval<Key>,
    replacement: Key,
    stored_row: u32,
}

impl<Key> PreparedReplacement<Key> {
    pub(in crate::storage::warp_table) const fn row(&self) -> usize {
        self.removal.row
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub(in crate::storage::warp_table) enum PrepareReplaceOutcome<Key> {
    Prepared(PreparedReplacement<Key>),
    Unchanged { row: usize },
    OriginalAbsent,
    ReplacementPresent { row: usize },
}

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

impl DenseKeyIndex {
    pub(in crate::storage::warp_table) fn prepare_replace<Key: IndexKey>(
        &self,
        original: Key,
        replacement: Key,
        keys: &[Key],
    ) -> Result<PrepareReplaceOutcome<Key>, IndexInconsistency> {
        let (row, stored_row) = match self.find(original, keys)? {
            FindOutcome::Present {
                row, stored_row, ..
            } => (row, stored_row),
            FindOutcome::Absent => return Ok(PrepareReplaceOutcome::OriginalAbsent),
        };
        if original == replacement {
            return Ok(PrepareReplaceOutcome::Unchanged { row });
        }
        if let FindOutcome::Present { row, .. } = self.find(replacement, keys)? {
            return Ok(PrepareReplaceOutcome::ReplacementPresent { row });
        }
        let removal = match self.prepare_remove(original, keys, row)? {
            PrepareRemoveOutcome::Prepared(removal) => removal,
            PrepareRemoveOutcome::Absent => return Ok(PrepareReplaceOutcome::OriginalAbsent),
        };
        Ok(PrepareReplaceOutcome::Prepared(PreparedReplacement {
            removal,
            replacement,
            stored_row,
        }))
    }

    #[allow(
        clippy::indexing_slicing,
        reason = "replacement preparation proves a removable entry and a spare bucket"
    )]
    pub(in crate::storage::warp_table) fn commit_replace<Key: IndexKey>(
        &mut self,
        replacement: PreparedReplacement<Key>,
        keys: &[Key],
    ) -> ReplaceOutcome {
        let row = replacement.removal.row;
        self.commit_remove(replacement.removal, keys);

        let mut bucket = reduce(replacement.replacement.index_word(), self.buckets.len());
        loop {
            if self.buckets[bucket] == EMPTY_ROW {
                self.buckets[bucket] = replacement.stored_row;
                return ReplaceOutcome::Replaced { row };
            }
            bucket = next_bucket(bucket, self.buckets.len());
        }
    }
}