pipecircuit 0.0.20

Correct. Robust. Fast. Portable. Pick four.
Documentation
use super::blocks::SlotLocation;
use super::inconsistency::{increment_members, represent_members};
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 RangeIndexInsertError<Key> {
    Full {
        key: Key,
        slot: StableSlot,
        maximum_rows: usize,
    },
    AlreadyPresent {
        key: Key,
        slot: StableSlot,
    },
    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 RangeIndexInsertOutcome<Key> {
    Inserted { key: Key, slot: StableSlot },
}

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

impl<Key: RangeIndexKey> RangeIndex<Key> {
    pub fn try_prepare_insert(
        &mut self,
        key: Key,
        slot: StableSlot,
    ) -> Result<PreparedRangeIndexInsert<'_, Key>, RangeIndexInsertError<Key>> {
        let words_per_block = self
            .validated_words_per_block()
            .map_err(|reason| inconsistent(key, slot, reason))?;
        let word = checked_ordered_word(key).map_err(|outside| {
            RangeIndexInsertError::OrderedWordOutsideWidth {
                key: outside.key,
                ordered_word: outside.ordered_word,
                bits: outside.bits,
            }
        })?;
        let maximum_rows = self.maximum_rows;
        let previous_member_count = self.member_count;
        let location = self.locate_slot(slot, words_per_block);
        if location
            .as_ref()
            .is_ok_and(|(location, _)| location.member())
        {
            return Err(RangeIndexInsertError::AlreadyPresent { key, slot });
        }
        let members = represent_members::<usize>(previous_member_count)
            .map_err(|reason| inconsistent(key, slot, reason))?;
        if members >= maximum_rows {
            return Err(RangeIndexInsertError::Full {
                key,
                slot,
                maximum_rows,
            });
        }
        let (mut location, member_count) =
            location.map_err(|reason| inconsistent(key, slot, reason))?;
        let stale_word = location.word();
        if stale_word != 0 {
            return Err(inconsistent(
                key,
                slot,
                RangeIndexInconsistency::StaleBitMembership {
                    bit: stale_word.trailing_zeros(),
                },
            ));
        }
        let next_member_count = increment_members(previous_member_count)
            .map_err(|reason| inconsistent(key, slot, reason))?;
        location.write(true, word);
        *member_count = next_member_count;
        Ok(PreparedRangeIndexInsert {
            member_count,
            key,
            slot,
            location,
            previous_member_count,
            committed: false,
        })
    }
}

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

impl<Key: RangeIndexKey> Drop for PreparedRangeIndexInsert<'_, Key> {
    fn drop(&mut self) {
        if !self.committed {
            self.location.write(false, 0);
            *self.member_count = self.previous_member_count;
        }
    }
}

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