use std::marker::PhantomData;
use sim_lib_control::AdmissionLimit;
use crate::ValueWidthPolicy;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Span {
start: usize,
width: usize,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SlotError {
Overflow {
slot: usize,
width: usize,
limit: usize,
},
Uninitialized {
slot: usize,
},
ZeroWidth {
slot: usize,
},
}
pub struct SlotFile<P: ValueWidthPolicy> {
values: Vec<Option<P::Value>>,
occupancy: Vec<Option<Span>>,
_policy: PhantomData<P>,
}
impl<P: ValueWidthPolicy> SlotFile<P> {
pub fn new(limit: AdmissionLimit) -> Self {
Self {
values: (0..limit.0).map(|_| None).collect(),
occupancy: vec![None; limit.0],
_policy: PhantomData,
}
}
pub fn limit(&self) -> usize {
self.occupancy.len()
}
pub fn load(&self, slot: usize) -> Result<&P::Value, SlotError> {
self.values
.get(slot)
.and_then(Option::as_ref)
.ok_or(SlotError::Uninitialized { slot })
}
pub fn store(&mut self, slot: usize, value: P::Value) -> Result<(), SlotError> {
let width = P::width(&value);
if width == 0 {
return Err(SlotError::ZeroWidth { slot });
}
let end = slot
.checked_add(width)
.filter(|end| *end <= self.limit())
.ok_or(SlotError::Overflow {
slot,
width,
limit: self.limit(),
})?;
let mut overlaps = self.occupancy[slot..end]
.iter()
.flatten()
.copied()
.collect::<Vec<_>>();
overlaps.sort_unstable_by_key(|span| span.start);
overlaps.dedup();
for span in overlaps {
self.release_span(span);
}
let span = Span { start: slot, width };
self.values[slot] = Some(value);
self.occupancy[slot..end].fill(Some(span));
Ok(())
}
pub fn release(&mut self, slot: usize) -> Result<P::Value, SlotError> {
let span = self
.occupancy
.get(slot)
.copied()
.flatten()
.ok_or(SlotError::Uninitialized { slot })?;
self.release_span(span)
.ok_or(SlotError::Uninitialized { slot })
}
pub fn is_initialized(&self, slot: usize) -> bool {
self.occupancy.get(slot).is_some_and(Option::is_some)
}
pub fn visit_values(&self, mut visit: impl FnMut(&P::Value)) {
for value in self.values.iter().flatten() {
visit(value);
}
}
fn release_span(&mut self, span: Span) -> Option<P::Value> {
self.occupancy[span.start..span.start + span.width].fill(None);
self.values[span.start].take()
}
}