pipecircuit 0.0.2

Correct. Robust. Fast. Portable.
Documentation
use super::blocks::SlotLocation;
use super::key::checked_ordered_word;
use super::{RangeIndex, RangeIndexInconsistency, RangeIndexKey};
use crate::storage::warp_table::secondary_index::StableSlot;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RangeIndexRemoveError<Key> {
    OrderedWordOutsideWidth {
        key: Key,
        ordered_word: u64,
        bits: u32,
    },
    Inconsistent {
        key: Key,
        slot: StableSlot,
        reason: RangeIndexInconsistency,
    },
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub enum RangeIndexRemoveOutcome<Key> {
    Removed { key: Key, slot: StableSlot },
}

pub struct PreparedRangeIndexRemove<'index, Key: RangeIndexKey> {
    index: &'index mut RangeIndex<Key>,
    key: Key,
    word: u64,
    slot: StableSlot,
    location: SlotLocation,
    previous_member_count: u64,
    committed: bool,
}

impl<Key: RangeIndexKey> RangeIndex<Key> {
    pub fn try_prepare_remove(
        &mut self,
        key: Key,
        slot: StableSlot,
    ) -> Result<PreparedRangeIndexRemove<'_, Key>, RangeIndexRemoveError<Key>> {
        let word = checked_ordered_word(key).map_err(|outside| {
            RangeIndexRemoveError::OrderedWordOutsideWidth {
                key: outside.key,
                ordered_word: outside.ordered_word,
                bits: outside.bits,
            }
        })?;
        let location = self.validate_stored_word(key, word, slot)?;
        let previous_member_count = self.member_count;
        let Some(next_member_count) = self.member_count.checked_sub(1) else {
            return Err(inconsistent(
                key,
                slot,
                RangeIndexInconsistency::MemberCountMismatch {
                    members: self.member_count,
                },
            ));
        };
        self.write_slot(location, false, 0)
            .map_err(|reason| inconsistent(key, slot, reason))?;
        self.member_count = next_member_count;
        Ok(PreparedRangeIndexRemove {
            index: self,
            key,
            word,
            slot,
            location,
            previous_member_count,
            committed: false,
        })
    }

    pub(super) fn validate_stored_word(
        &self,
        key: Key,
        expected: u64,
        slot: StableSlot,
    ) -> Result<SlotLocation, RangeIndexRemoveError<Key>> {
        self.validate_storage()
            .map_err(|reason| inconsistent(key, slot, reason))?;
        let location = self
            .locate_slot(slot)
            .map_err(|reason| inconsistent(key, slot, reason))?;
        if !self
            .member_at(location)
            .map_err(|reason| inconsistent(key, slot, reason))?
        {
            return Err(inconsistent(
                key,
                slot,
                RangeIndexInconsistency::MemberAbsent,
            ));
        }
        let actual = self
            .word_at(location)
            .map_err(|reason| inconsistent(key, slot, reason))?;
        if actual != expected {
            return Err(inconsistent(
                key,
                slot,
                RangeIndexInconsistency::StoredWordMismatch { expected, actual },
            ));
        }
        Ok(location)
    }
}

impl<Key: RangeIndexKey> PreparedRangeIndexRemove<'_, Key> {
    pub fn commit(mut self) -> RangeIndexRemoveOutcome<Key> {
        self.committed = true;
        RangeIndexRemoveOutcome::Removed {
            key: self.key,
            slot: self.slot,
        }
    }
}

impl<Key: RangeIndexKey> Drop for PreparedRangeIndexRemove<'_, Key> {
    fn drop(&mut self) {
        if !self.committed {
            let rollback = self.index.write_slot(self.location, true, self.word);
            debug_assert!(rollback.is_ok());
            self.index.member_count = self.previous_member_count;
        }
    }
}

pub(super) fn inconsistent<Key>(
    key: Key,
    slot: StableSlot,
    reason: RangeIndexInconsistency,
) -> RangeIndexRemoveError<Key> {
    RangeIndexRemoveError::Inconsistent { key, slot, reason }
}