use super::bucket::{next_bucket, reduce};
use super::{
DenseKeyIndex, EMPTY_ROW, FindOutcome, FoundLocation, IndexInconsistency, PreparedRemoval,
PreparedVacancy,
};
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 location = match self.find(original, keys)? {
FindOutcome::Present { location, .. } => location,
FindOutcome::Absent => return Ok(PrepareReplaceOutcome::OriginalAbsent),
};
let row = location.row;
if original == replacement {
return Ok(PrepareReplaceOutcome::Unchanged { row });
}
match self.prepare_insert(replacement, keys)? {
super::PrepareInsertOutcome::Present { row } => {
Ok(PrepareReplaceOutcome::ReplacementPresent { row })
}
super::PrepareInsertOutcome::Vacant(vacancy) => self
.prepare_replace_found(location, vacancy, replacement, keys)
.map(PrepareReplaceOutcome::Prepared),
}
}
pub(in crate::storage::warp_table) fn prepare_replace_found<Key: IndexKey>(
&self,
location: FoundLocation,
_vacancy: PreparedVacancy,
replacement: Key,
keys: &[Key],
) -> Result<PreparedReplacement<Key>, IndexInconsistency> {
let removal = self.prepare_remove_found(location, keys, location.row)?;
Ok(PreparedReplacement {
removal,
replacement,
stored_row: location.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());
#[cfg(test)]
let mut remaining_probes = self.buckets.len();
loop {
#[cfg(test)]
{
assert_ne!(
remaining_probes, 0,
"prepared replacement exhausted its probe bound"
);
remaining_probes = remaining_probes.saturating_sub(1);
}
if self.buckets[bucket] == EMPTY_ROW {
self.buckets[bucket] = replacement.stored_row;
return ReplaceOutcome::Replaced { row };
}
bucket = next_bucket(bucket, self.buckets.len());
}
}
}