use super::{SlotLocation, StableSlot, StableSlots, StableSlotsInconsistency};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub enum StableSlotRowRelocation {
Stable,
Relocated {
slot: StableSlot,
from: usize,
to: usize,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub enum StableSlotRemoveOutcome {
Removed {
slot: StableSlot,
relocation: StableSlotRowRelocation,
},
}
pub struct PreparedStableSlotRemove<'slots> {
slots: &'slots mut StableSlots,
row: usize,
relocated_row: u32,
removed_slot: StableSlot,
removed_slot_index: usize,
relocated: Option<(StableSlot, usize)>,
last_row: usize,
}
impl StableSlots {
pub fn try_prepare_remove(
&mut self,
row: usize,
) -> Result<PreparedStableSlotRemove<'_>, StableSlotsInconsistency> {
let last_row = self
.row_to_slot
.len()
.checked_sub(1)
.ok_or(StableSlotsInconsistency::RowUnavailable { row })?;
let removed_slot = self
.row_to_slot
.get(row)
.copied()
.ok_or(StableSlotsInconsistency::RowUnavailable { row })?;
let removed_slot_index = self.validate_location(removed_slot, row)?;
let relocated_row =
u32::try_from(row).map_err(|_| StableSlotsInconsistency::RowUnrepresentable { row })?;
let relocated = if row == last_row {
None
} else {
let moved_slot = self
.row_to_slot
.get(last_row)
.copied()
.ok_or(StableSlotsInconsistency::RowUnavailable { row: last_row })?;
if moved_slot == removed_slot {
return Err(StableSlotsInconsistency::DuplicateSlot { slot: moved_slot });
}
let moved_slot_index = self.validate_location(moved_slot, last_row)?;
Some((moved_slot, moved_slot_index))
};
Ok(PreparedStableSlotRemove {
slots: self,
row,
relocated_row,
removed_slot,
removed_slot_index,
relocated,
last_row,
})
}
pub fn remove(
&mut self,
row: usize,
) -> Result<StableSlotRemoveOutcome, StableSlotsInconsistency> {
Ok(self.try_prepare_remove(row)?.commit())
}
}
impl PreparedStableSlotRemove<'_> {
pub const fn slot(&self) -> StableSlot {
self.removed_slot
}
#[allow(
clippy::indexing_slicing,
reason = "preparation validated distinct slot indexes and dense rows"
)]
pub fn commit(self) -> StableSlotRemoveOutcome {
if let Some((_, moved_slot_index)) = self.relocated {
self.slots.slot_locations[moved_slot_index] =
SlotLocation::Occupied(self.relocated_row);
}
self.slots.slot_locations[self.removed_slot_index] =
SlotLocation::Free(self.slots.first_free);
self.slots.first_free = Some(self.removed_slot);
self.slots.row_to_slot.swap_remove(self.row);
let relocation = match self.relocated {
Some((slot, _)) => StableSlotRowRelocation::Relocated {
slot,
from: self.last_row,
to: self.row,
},
None => StableSlotRowRelocation::Stable,
};
StableSlotRemoveOutcome::Removed {
slot: self.removed_slot,
relocation,
}
}
}