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> {
member_count: &'index mut u64,
key: Key,
word: u64,
slot: StableSlot,
location: SlotLocation<'index>,
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 (mut location, member_count) = self
.validate_stored_word(word, slot)
.map_err(|reason| inconsistent(key, slot, reason))?;
let previous_member_count = *member_count;
let Some(next_member_count) = previous_member_count.checked_sub(1) else {
return Err(inconsistent(
key,
slot,
RangeIndexInconsistency::MemberCountMismatch {
members: previous_member_count,
},
));
};
location.write(false, 0);
*member_count = next_member_count;
Ok(PreparedRangeIndexRemove {
member_count,
key,
word,
slot,
location,
previous_member_count,
committed: false,
})
}
pub(super) fn validate_stored_word(
&mut self,
expected: u64,
slot: StableSlot,
) -> Result<(SlotLocation<'_>, &mut u64), RangeIndexInconsistency> {
let words_per_block = self.validated_words_per_block()?;
let (location, member_count) = self.locate_slot(slot, words_per_block)?;
if !location.member() {
return Err(RangeIndexInconsistency::MemberAbsent);
}
let actual = location.word();
if actual != expected {
return Err(RangeIndexInconsistency::StoredWordMismatch { expected, actual });
}
Ok((location, member_count))
}
}
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 {
self.location.write(true, self.word);
*self.member_count = self.previous_member_count;
}
}
}
pub(super) fn inconsistent<Key>(
key: Key,
slot: StableSlot,
reason: RangeIndexInconsistency,
) -> RangeIndexRemoveError<Key> {
RangeIndexRemoveError::Inconsistent { key, slot, reason }
}