use super::inconsistency;
use alloc::vec::Vec;
use super::{SlotLocation, StableSlot, StableSlots, StableSlotsInconsistency};
#[derive(
::core::clone::Clone,
::core::marker::Copy,
::core::fmt::Debug,
::core::cmp::Eq,
::core::cmp::PartialEq,
)]
pub enum StableSlotInsertError {
Full { maximum_rows: usize },
Inconsistent { reason: StableSlotsInconsistency },
}
impl StableSlots {
pub fn try_prepare_insert(
&mut self,
) -> Result<PreparedStableSlotInsert<'_>, StableSlotInsertError> {
let Self {
maximum_rows,
row_to_slot,
slot_locations,
first_free,
} = self;
if row_to_slot.len() >= *maximum_rows {
return Err(StableSlotInsertError::Full {
maximum_rows: *maximum_rows,
});
}
let row = inconsistency::row(row_to_slot.len()).map_err(StableSlotInsertError::from)?;
let insertion = match *first_free {
Some(slot) => prepare_reuse(slot_locations, first_free, slot)?,
None => prepare_new(slot_locations)?,
};
let slot = insertion.slot();
Ok(PreparedStableSlotInsert {
row_to_slot,
insertion,
slot,
row,
})
}
}
fn prepare_reuse<'slots>(
slot_locations: &'slots mut [SlotLocation],
first_free: &'slots mut Option<StableSlot>,
slot: StableSlot,
) -> Result<StableSlotInsertion<'slots>, StableSlotInsertError> {
let slot_index =
inconsistency::slot_index::<usize>(slot).map_err(StableSlotInsertError::from)?;
let location =
slot_locations
.get_mut(slot_index)
.ok_or(StableSlotInsertError::Inconsistent {
reason: StableSlotsInconsistency::SlotUnavailable { slot },
})?;
let next_free = match location {
SlotLocation::Free(next_free) => *next_free,
SlotLocation::Occupied(row) => {
return Err(StableSlotInsertError::Inconsistent {
reason: StableSlotsInconsistency::SlotOccupied { slot, row: *row },
});
}
};
Ok(StableSlotInsertion::Reused {
location,
first_free,
next_free,
slot,
})
}
fn prepare_new(
slot_locations: &mut Vec<SlotLocation>,
) -> Result<StableSlotInsertion<'_>, StableSlotInsertError> {
let slot =
inconsistency::new_slot(slot_locations.len()).map_err(StableSlotInsertError::from)?;
Ok(StableSlotInsertion::New {
slot_locations,
slot,
})
}
enum StableSlotInsertion<'slots> {
New {
slot_locations: &'slots mut Vec<SlotLocation>,
slot: StableSlot,
},
Reused {
location: &'slots mut SlotLocation,
first_free: &'slots mut Option<StableSlot>,
next_free: Option<StableSlot>,
slot: StableSlot,
},
}
impl StableSlotInsertion<'_> {
const fn slot(&self) -> StableSlot {
match self {
Self::New { slot, .. } | Self::Reused { slot, .. } => *slot,
}
}
}
pub struct PreparedStableSlotInsert<'slots> {
row_to_slot: &'slots mut Vec<StableSlot>,
insertion: StableSlotInsertion<'slots>,
slot: StableSlot,
row: u32,
}
impl PreparedStableSlotInsert<'_> {
pub const fn slot(&self) -> StableSlot {
self.slot
}
pub fn commit(self) -> StableSlot {
match self.insertion {
StableSlotInsertion::New { slot_locations, .. } => {
slot_locations.push(SlotLocation::Occupied(self.row));
}
StableSlotInsertion::Reused {
location,
first_free,
next_free,
..
} => {
*location = SlotLocation::Occupied(self.row);
*first_free = next_free;
}
}
self.row_to_slot.push(self.slot);
self.slot
}
}
impl From<StableSlotsInconsistency> for StableSlotInsertError {
fn from(reason: StableSlotsInconsistency) -> Self {
Self::Inconsistent { reason }
}
}