quick_cache/
linked_slab.rs1pub type Token = std::num::NonZeroU32;
2
3#[derive(Debug, Clone)]
4struct Entry<T> {
5 item: Option<T>,
6 next: Token,
9 prev: Token,
12}
13
14#[derive(Debug, Clone)]
16pub struct LinkedSlab<T> {
17 entries: Vec<Entry<T>>,
18 next_free: Token,
19}
20
21impl<T> LinkedSlab<T> {
22 pub fn with_capacity(capacity: usize) -> Self {
23 Self {
24 entries: Vec::with_capacity(capacity),
25 next_free: Token::new(1).unwrap(),
26 }
27 }
28
29 pub fn reserve(&mut self, additional: usize) {
34 self.entries.reserve(additional);
35 }
36
37 #[cfg(test)]
39 pub fn capacity(&self) -> usize {
40 self.entries.capacity()
41 }
42
43 #[cfg(fuzzing)]
44 pub fn len(&self) -> usize {
45 self.entries.iter().filter(|e| e.item.is_some()).count()
46 }
47
48 #[cfg(any(fuzzing, test))]
49 pub fn iter_entries(&self) -> impl Iterator<Item = &T> + '_ {
50 self.entries.iter().filter_map(|e| e.item.as_ref())
51 }
52
53 #[cfg(any(fuzzing, test))]
54 pub fn validate(&self) {
55 let mut freelist = std::collections::HashSet::new();
56 let mut next_free = self.next_free;
57 while next_free.get() as usize - 1 != self.entries.len() {
58 freelist.insert(next_free.get() as usize);
59 let e = &self.entries[next_free.get() as usize - 1];
60 assert!(e.item.is_none(), "{next_free} is in freelist but has item");
61 next_free = e.next;
62 }
63 for (i, e) in self.entries.iter().enumerate() {
64 if e.item.is_some() {
65 assert!(!freelist.contains(&(i + 1)));
66 assert!(!freelist.contains(&(e.prev.get() as usize)));
67 assert!(!freelist.contains(&(e.next.get() as usize)));
68 }
69 }
70 }
71
72 pub fn insert(&mut self, item: T) -> Token {
78 let token = self.next_free;
79 let idx = (token.get() - 1) as usize;
81 if idx < self.entries.len() {
82 let entry = &mut self.entries[idx];
83 self.next_free = entry.next;
84 (entry.prev, entry.next) = (token, token);
85 debug_assert!(entry.item.is_none());
86 entry.item = Some(item);
87 } else {
88 debug_assert_eq!(idx, self.entries.len());
89 self.next_free = Token::new(token.get().wrapping_add(1)).expect("Capacity overflow");
90 self.entries.push(Entry {
91 next: token,
92 prev: token,
93 item: Some(item),
94 });
95 }
96 token
97 }
98
99 #[inline]
101 pub fn get(&self, index: Token) -> Option<(&T, Token)> {
102 if let Some(entry) = self.entries.get((index.get() - 1) as usize) {
103 if let Some(v) = &entry.item {
104 return Some((v, entry.next));
105 }
106 }
107 None
108 }
109
110 #[inline]
112 pub fn get_mut(&mut self, index: Token) -> Option<(&mut T, Token)> {
113 if let Some(entry) = self.entries.get_mut((index.get() - 1) as usize) {
114 if let Some(v) = &mut entry.item {
115 return Some((v, entry.next));
116 }
117 }
118 None
119 }
120
121 #[inline]
123 pub unsafe fn get_unchecked(&self, index: Token) -> (&T, Token) {
124 unsafe {
125 let entry = self.entries.get_unchecked((index.get() - 1) as usize);
126 let v = entry.item.as_ref().unwrap_unchecked();
127 (v, entry.next)
128 }
129 }
130
131 #[inline]
133 pub unsafe fn get_mut_unchecked(&mut self, index: Token) -> (&mut T, Token) {
134 unsafe {
135 let entry = self.entries.get_unchecked_mut((index.get() - 1) as usize);
136 let v = entry.item.as_mut().unwrap_unchecked();
137 (v, entry.next)
138 }
139 }
140
141 pub fn link(&mut self, idx: Token, target_head: Option<Token>) -> Token {
148 let (prev, next) = if let Some(target_head) = target_head {
150 let head = &mut self.entries[(target_head.get() - 1) as usize];
151 debug_assert!(head.item.is_some());
152 if head.prev == target_head {
153 head.prev = idx;
155 head.next = idx;
156 (target_head, target_head)
157 } else {
158 let before_head_idx = head.prev;
159 head.prev = idx;
160 let before_head = &mut self.entries[(before_head_idx.get() - 1) as usize];
161 debug_assert!(before_head.item.is_some());
162 debug_assert_eq!(before_head.next, target_head);
163 before_head.next = idx;
164 (before_head_idx, target_head)
165 }
166 } else {
167 (idx, idx)
168 };
169
170 let entry = &mut self.entries[(idx.get() - 1) as usize];
171 debug_assert!(entry.item.is_some());
172 debug_assert_eq!(entry.next, idx);
173 debug_assert_eq!(entry.prev, idx);
174 (entry.prev, entry.next) = (prev, next);
175 next
176 }
177
178 pub fn unlink(&mut self, idx: Token) -> Option<Token> {
185 let entry = &mut self.entries[(idx.get() - 1) as usize];
187 debug_assert!(entry.item.is_some());
188 let (prev_idx, next_idx) = (entry.prev, entry.next);
189 if next_idx == idx {
190 debug_assert_eq!(prev_idx, idx);
191 None
193 } else {
194 (entry.prev, entry.next) = (idx, idx);
195 let next = &mut self.entries[(next_idx.get() - 1) as usize];
196 debug_assert!(next.item.is_some());
197 debug_assert_eq!(next.prev, idx);
198 next.prev = prev_idx;
199 let prev = &mut self.entries[(prev_idx.get() - 1) as usize];
200 debug_assert!(prev.item.is_some());
201 debug_assert_eq!(prev.next, idx);
202 prev.next = next_idx;
203 Some(next_idx)
204 }
205 }
206
207 pub fn remove(&mut self, idx: Token) -> Option<(T, Option<Token>)> {
213 let next = self.unlink(idx);
215 let entry = &mut self.entries[(idx.get() - 1) as usize];
216 let old_item = entry.item.take()?;
217 entry.next = self.next_free;
218 self.next_free = idx;
219 Some((old_item, next))
220 }
221
222 pub fn next_free(&self) -> Token {
224 self.next_free
225 }
226
227 pub fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
231 self.next_free = Token::new(1).unwrap();
232 self.entries.drain(..).flat_map(|i| i.item)
233 }
234
235 pub fn iter(&self) -> impl Iterator<Item = &'_ T> + '_ {
237 self.entries.iter().flat_map(|i| i.item.as_ref())
238 }
239
240 pub fn iter_from(
242 &self,
243 continuation: Option<Token>,
244 ) -> impl Iterator<Item = (Token, &'_ T)> + '_ {
245 let skip = continuation.map_or(0, |c| c.get() as usize);
246 self.entries
247 .iter()
248 .skip(skip)
249 .enumerate()
250 .filter_map(move |(i, e)| {
251 if let Some(item) = &e.item {
252 Some((Token::new((skip + i + 1) as u32)?, item))
253 } else {
254 None
255 }
256 })
257 }
258
259 pub fn memory_used(&self) -> usize {
264 self.entries.len() * std::mem::size_of::<Entry<T>>()
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn reserve_avoids_realloc() {
274 let mut slab: LinkedSlab<u64> = LinkedSlab::with_capacity(0);
275 slab.reserve(1000);
276 let cap = slab.entries.capacity();
277 assert!(cap >= 1000);
278 for i in 0..cap as u64 {
280 slab.insert(i);
281 }
282 assert_eq!(
283 slab.entries.capacity(),
284 cap,
285 "slab reallocated despite reserve"
286 );
287 }
288}