1use std::marker::PhantomData;
2
3use sim_lib_control::AdmissionLimit;
4
5use crate::ValueWidthPolicy;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8struct Span {
9 start: usize,
10 width: usize,
11}
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum SlotError {
16 Overflow {
18 slot: usize,
20 width: usize,
22 limit: usize,
24 },
25 Uninitialized {
27 slot: usize,
29 },
30 ZeroWidth {
32 slot: usize,
34 },
35}
36
37pub struct SlotFile<P: ValueWidthPolicy> {
43 values: Vec<Option<P::Value>>,
44 occupancy: Vec<Option<Span>>,
45 _policy: PhantomData<P>,
46}
47
48impl<P: ValueWidthPolicy> SlotFile<P> {
49 pub fn new(limit: AdmissionLimit) -> Self {
51 Self {
52 values: (0..limit.0).map(|_| None).collect(),
53 occupancy: vec![None; limit.0],
54 _policy: PhantomData,
55 }
56 }
57
58 pub fn limit(&self) -> usize {
60 self.occupancy.len()
61 }
62
63 pub fn load(&self, slot: usize) -> Result<&P::Value, SlotError> {
65 self.values
66 .get(slot)
67 .and_then(Option::as_ref)
68 .ok_or(SlotError::Uninitialized { slot })
69 }
70
71 pub fn store(&mut self, slot: usize, value: P::Value) -> Result<(), SlotError> {
73 let width = P::width(&value);
74 if width == 0 {
75 return Err(SlotError::ZeroWidth { slot });
76 }
77 let end = slot
78 .checked_add(width)
79 .filter(|end| *end <= self.limit())
80 .ok_or(SlotError::Overflow {
81 slot,
82 width,
83 limit: self.limit(),
84 })?;
85
86 let mut overlaps = self.occupancy[slot..end]
87 .iter()
88 .flatten()
89 .copied()
90 .collect::<Vec<_>>();
91 overlaps.sort_unstable_by_key(|span| span.start);
92 overlaps.dedup();
93 for span in overlaps {
94 self.release_span(span);
95 }
96
97 let span = Span { start: slot, width };
98 self.values[slot] = Some(value);
99 self.occupancy[slot..end].fill(Some(span));
100 Ok(())
101 }
102
103 pub fn release(&mut self, slot: usize) -> Result<P::Value, SlotError> {
105 let span = self
106 .occupancy
107 .get(slot)
108 .copied()
109 .flatten()
110 .ok_or(SlotError::Uninitialized { slot })?;
111 self.release_span(span)
112 .ok_or(SlotError::Uninitialized { slot })
113 }
114
115 pub fn is_initialized(&self, slot: usize) -> bool {
117 self.occupancy.get(slot).is_some_and(Option::is_some)
118 }
119
120 pub fn visit_values(&self, mut visit: impl FnMut(&P::Value)) {
122 for value in self.values.iter().flatten() {
123 visit(value);
124 }
125 }
126
127 fn release_span(&mut self, span: Span) -> Option<P::Value> {
128 self.occupancy[span.start..span.start + span.width].fill(None);
129 self.values[span.start].take()
130 }
131}