pipecircuit 0.0.25

Correct. Robust. Fast. Portable. Pick four.
Documentation
#[cfg(feature = "alloc")]
use super::AllocatedStableSlots;
use super::inconsistency;
use super::{StableSlotColumn, StableSlotsBacking};

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<Backing: StableSlotsBacking> StableSlots<Backing> {
    pub fn try_prepare_insert(
        &mut self,
    ) -> Result<PreparedStableSlotInsert<'_, Backing>, 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::<Backing>(slot_locations, first_free, slot)?,
            None => prepare_new::<Backing>(slot_locations)?,
        };
        let slot = insertion.slot();
        Ok(PreparedStableSlotInsert {
            row_to_slot,
            insertion,
            slot,
            row,
        })
    }
}

fn prepare_reuse<'slots, Backing: StableSlotsBacking>(
    slot_locations: &'slots mut [SlotLocation],
    first_free: &'slots mut Option<StableSlot>,
    slot: StableSlot,
) -> Result<StableSlotInsertion<'slots, Backing>, 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<Backing: StableSlotsBacking>(
    slot_locations: &mut Backing::Locations,
) -> Result<StableSlotInsertion<'_, Backing>, StableSlotInsertError> {
    let slot =
        inconsistency::new_slot(slot_locations.len()).map_err(StableSlotInsertError::from)?;
    Ok(StableSlotInsertion::New {
        slot_locations,
        slot,
    })
}

enum StableSlotInsertion<'slots, Backing: StableSlotsBacking> {
    New {
        slot_locations: &'slots mut Backing::Locations,
        slot: StableSlot,
    },
    Reused {
        location: &'slots mut SlotLocation,
        first_free: &'slots mut Option<StableSlot>,
        next_free: Option<StableSlot>,
        slot: StableSlot,
    },
}

impl<Backing: StableSlotsBacking> StableSlotInsertion<'_, Backing> {
    const fn slot(&self) -> StableSlot {
        match self {
            Self::New { slot, .. } | Self::Reused { slot, .. } => *slot,
        }
    }
}

pub struct PreparedStableSlotInsert<
    'slots,
    #[cfg(feature = "alloc")] Backing: StableSlotsBacking = AllocatedStableSlots,
    #[cfg(not(feature = "alloc"))] Backing: StableSlotsBacking,
> {
    row_to_slot: &'slots mut Backing::Rows,
    insertion: StableSlotInsertion<'slots, Backing>,
    slot: StableSlot,
    row: u32,
}

impl<Backing: StableSlotsBacking> PreparedStableSlotInsert<'_, Backing> {
    pub const fn slot(&self) -> StableSlot {
        self.slot
    }

    pub fn commit(self) -> StableSlot {
        match self.insertion {
            StableSlotInsertion::New { slot_locations, .. } => {
                slot_locations.append_admitted(SlotLocation::Occupied(self.row));
            }
            StableSlotInsertion::Reused {
                location,
                first_free,
                next_free,
                ..
            } => {
                *location = SlotLocation::Occupied(self.row);
                *first_free = next_free;
            }
        }
        self.row_to_slot.append_admitted(self.slot);
        self.slot
    }
}

impl From<StableSlotsInconsistency> for StableSlotInsertError {
    fn from(reason: StableSlotsInconsistency) -> Self {
        Self::Inconsistent { reason }
    }
}