1pub const MAX_SLOTS: usize = 1 << 24;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
13pub enum SlabError {
14 #[error("capacity {0} out of range 1..={MAX_SLOTS}")]
16 Capacity(usize),
17 #[error("session slab exhausted")]
19 Exhausted,
20}
21
22struct Entry<T> {
23 value: Option<T>,
24 generation: u16,
25 next_free: u32,
26 occupied: bool,
27}
28
29pub struct SessionSlab<T> {
31 entries: Box<[Entry<T>]>,
32 free_head: u32,
33 free_count: usize,
34}
35
36impl<T> SessionSlab<T> {
37 pub fn new(capacity: usize) -> Result<Self, SlabError> {
42 if capacity == 0 || capacity > MAX_SLOTS {
43 return Err(SlabError::Capacity(capacity));
44 }
45 let mut entries = Vec::with_capacity(capacity);
46 for i in 0..capacity {
47 entries.push(Entry {
48 value: None,
49 generation: 0,
50 next_free: (i + 1) as u32,
51 occupied: false,
52 });
53 }
54 Ok(Self {
55 entries: entries.into_boxed_slice(),
56 free_head: 0,
57 free_count: capacity,
58 })
59 }
60
61 pub fn insert(&mut self, value: T) -> Result<(u32, u16), SlabError> {
66 if self.free_count == 0 {
67 return Err(SlabError::Exhausted);
68 }
69 let slot = self.free_head as usize;
70 let entry = &mut self.entries[slot];
71 self.free_head = entry.next_free;
72 entry.occupied = true;
73 entry.value = Some(value);
74 self.free_count -= 1;
75 Ok((slot as u32, entry.generation))
76 }
77
78 pub fn remove(&mut self, slot: u32) -> Option<T> {
81 let entry = self.entries.get_mut(slot as usize)?;
82 if !entry.occupied {
83 return None;
84 }
85 entry.occupied = false;
86 entry.generation = entry.generation.wrapping_add(1);
87 entry.next_free = self.free_head;
88 self.free_head = slot;
89 self.free_count += 1;
90 entry.value.take()
91 }
92
93 #[must_use]
95 pub fn get(&self, slot: u32) -> Option<&T> {
96 let e = self.entries.get(slot as usize)?;
97 if e.occupied { e.value.as_ref() } else { None }
98 }
99
100 pub fn get_mut(&mut self, slot: u32) -> Option<&mut T> {
102 let e = self.entries.get_mut(slot as usize)?;
103 if e.occupied { e.value.as_mut() } else { None }
104 }
105
106 #[must_use]
108 pub fn generation(&self, slot: u32) -> u16 {
109 self.entries.get(slot as usize).map_or(0, |e| e.generation)
110 }
111
112 #[must_use]
114 pub fn len(&self) -> usize {
115 self.entries.len() - self.free_count
116 }
117
118 #[must_use]
120 pub fn is_empty(&self) -> bool {
121 self.len() == 0
122 }
123
124 #[must_use]
126 pub fn matches(&self, slot: u32, generation: u16) -> bool {
127 self.entries
128 .get(slot as usize)
129 .is_some_and(|e| e.occupied && e.generation == generation)
130 }
131
132 #[must_use]
134 pub fn live(&self) -> Vec<(u32, u16)> {
135 self.entries
136 .iter()
137 .enumerate()
138 .filter(|(_, e)| e.occupied)
139 .map(|(i, e)| (i as u32, e.generation))
140 .collect()
141 }
142}
143
144pub trait GenerationBump<T> {
146 fn bump_generation(&mut self, slot: u32);
148}
149
150impl<T> GenerationBump<T> for SessionSlab<T> {
151 fn bump_generation(&mut self, slot: u32) {
152 if let Some(e) = self.entries.get_mut(slot as usize) {
153 e.generation = e.generation.wrapping_add(1);
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn insert_remove_reuse() {
164 let mut slab: SessionSlab<&str> = SessionSlab::new(4).expect("ok");
165 let (a, gen_a) = slab.insert("a").expect("fits");
166 let (b, _) = slab.insert("b").expect("fits");
167 assert_eq!(slab.get(a), Some(&"a"));
168 let old = slab.remove(a);
169 assert_eq!(old, Some("a"));
170 let (c, gen_c) = slab.insert("c").expect("reuses slot a");
171 assert_eq!(c, a);
172 assert_ne!(gen_c, gen_a);
173 assert!(slab.matches(a, gen_c));
174 assert!(!slab.matches(a, gen_a));
175 assert_eq!(slab.remove(b), Some("b"));
176 assert_eq!(slab.remove(c), Some("c"));
177 assert!(slab.is_empty());
178 }
179
180 #[test]
181 fn exhaustion() {
182 let mut slab: SessionSlab<u8> = SessionSlab::new(2).expect("ok");
183 slab.insert(1).expect("1");
184 slab.insert(2).expect("2");
185 assert_eq!(slab.insert(3), Err(SlabError::Exhausted));
186 }
187}