use core::iter::FusedIterator;
use super::{SlotLocation, StableSlot, StableSlots, StableSlotsInconsistency};
use crate::storage::warp_table::secondary_index::{SlotSet, SlotSetSlots};
pub trait StableSlotResolver {
fn resolve_stable_slot(
&self,
slot: StableSlot,
) -> Result<StableSlotResolveOutcome, StableSlotsInconsistency>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub enum StableSlotResolveOutcome {
Resolved { slot: StableSlot, dense_row: usize },
}
#[must_use]
pub struct StableSlotResolutions<'slots> {
slots: SlotSetSlots<'slots>,
stable_slots: &'slots StableSlots,
}
impl Iterator for StableSlotResolutions<'_> {
type Item = Result<StableSlotResolveOutcome, StableSlotsInconsistency>;
fn next(&mut self) -> Option<Self::Item> {
self.slots
.next()
.map(|slot| self.stable_slots.resolve(slot))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.slots.size_hint()
}
}
impl DoubleEndedIterator for StableSlotResolutions<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.slots
.next_back()
.map(|slot| self.stable_slots.resolve(slot))
}
}
#[cfg(target_pointer_width = "64")]
impl ExactSizeIterator for StableSlotResolutions<'_> {}
impl FusedIterator for StableSlotResolutions<'_> {}
impl StableSlots {
pub fn resolve_slots<'slots>(
&'slots self,
slots: &'slots SlotSet,
) -> StableSlotResolutions<'slots> {
StableSlotResolutions {
slots: slots.slots(),
stable_slots: self,
}
}
pub fn resolve(
&self,
slot: StableSlot,
) -> Result<StableSlotResolveOutcome, StableSlotsInconsistency> {
let slot_index = usize::try_from(slot.value())
.map_err(|_| StableSlotsInconsistency::SlotUnrepresentable { slot })?;
match self
.slot_locations
.get(slot_index)
.ok_or(StableSlotsInconsistency::SlotUnavailable { slot })?
{
SlotLocation::Occupied(stored_row) => {
let dense_row = usize::try_from(*stored_row).map_err(|_| {
StableSlotsInconsistency::StoredRowUnrepresentable {
slot,
stored_row: *stored_row,
}
})?;
Ok(StableSlotResolveOutcome::Resolved { slot, dense_row })
}
SlotLocation::Free(_) => Err(StableSlotsInconsistency::SlotVacant { slot }),
}
}
pub fn slot(&self, row: usize) -> Result<StableSlot, StableSlotsInconsistency> {
let slot = self
.row_to_slot
.get(row)
.copied()
.ok_or(StableSlotsInconsistency::RowUnavailable { row })?;
self.validate_location(slot, row)?;
Ok(slot)
}
pub(super) fn validate_location(
&self,
slot: StableSlot,
expected_row: usize,
) -> Result<usize, StableSlotsInconsistency> {
let expected = u32::try_from(expected_row)
.map_err(|_| StableSlotsInconsistency::RowUnrepresentable { row: expected_row })?;
let slot_index = usize::try_from(slot.value())
.map_err(|_| StableSlotsInconsistency::SlotUnrepresentable { slot })?;
match self
.slot_locations
.get(slot_index)
.ok_or(StableSlotsInconsistency::SlotUnavailable { slot })?
{
SlotLocation::Occupied(actual) if *actual == expected => Ok(slot_index),
SlotLocation::Occupied(actual) => Err(StableSlotsInconsistency::SlotRowMismatch {
slot,
expected,
actual: *actual,
}),
SlotLocation::Free(_) => Err(StableSlotsInconsistency::SlotVacant { slot }),
}
}
}