quick_cache/
linked_slab.rs

1pub type Token = std::num::NonZeroU32;
2
3#[derive(Debug, Clone)]
4struct Entry<T> {
5    item: Option<T>,
6    /// The next item in the list (possibly itself).
7    /// When `item` is None, `next` points ot the next free token in the slab.
8    next: Token,
9    /// The previous item in the list (possibly itself).
10    /// Unused if `item` is None.
11    prev: Token,
12}
13
14/// A slab like structure that also maintains circular lists with its items.
15#[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    #[cfg(fuzzing)]
30    pub fn len(&self) -> usize {
31        self.entries.iter().filter(|e| e.item.is_some()).count()
32    }
33
34    #[cfg(any(fuzzing, test))]
35    pub fn iter_entries(&self) -> impl Iterator<Item = &T> + '_ {
36        self.entries.iter().filter_map(|e| e.item.as_ref())
37    }
38
39    #[cfg(any(fuzzing, test))]
40    pub fn validate(&self) {
41        let mut freelist = std::collections::HashSet::new();
42        let mut next_free = self.next_free;
43        while next_free.get() as usize - 1 != self.entries.len() {
44            freelist.insert(next_free.get() as usize);
45            let e = &self.entries[next_free.get() as usize - 1];
46            assert!(e.item.is_none(), "{next_free} is in freelist but has item");
47            next_free = e.next;
48        }
49        for (i, e) in self.entries.iter().enumerate() {
50            if e.item.is_some() {
51                assert!(!freelist.contains(&(i + 1)));
52                assert!(!freelist.contains(&(e.prev.get() as usize)));
53                assert!(!freelist.contains(&(e.next.get() as usize)));
54            }
55        }
56    }
57
58    /// Inserts a new entry in the list.
59    /// Initially, the item will belong to a list only containing itself.
60    ///
61    /// # Panics
62    /// Panics if number of items exceed `u32::MAX - 1`.
63    pub fn insert(&mut self, item: T) -> Token {
64        let token = self.next_free;
65        // eprintln!("linkedslab::insert token {token} head {head:?}");
66        let idx = (token.get() - 1) as usize;
67        if idx < self.entries.len() {
68            let entry = &mut self.entries[idx];
69            self.next_free = entry.next;
70            (entry.prev, entry.next) = (token, token);
71            debug_assert!(entry.item.is_none());
72            entry.item = Some(item);
73        } else {
74            debug_assert_eq!(idx, self.entries.len());
75            self.next_free = Token::new(token.get().wrapping_add(1)).expect("Capacity overflow");
76            self.entries.push(Entry {
77                next: token,
78                prev: token,
79                item: Some(item),
80            });
81        }
82        token
83    }
84
85    /// Gets an entry and a token to the next entry.
86    #[inline]
87    pub fn get(&self, index: Token) -> Option<(&T, Token)> {
88        if let Some(entry) = self.entries.get((index.get() - 1) as usize) {
89            if let Some(v) = &entry.item {
90                return Some((v, entry.next));
91            }
92        }
93        None
94    }
95
96    /// Gets an entry and a token to the next entry.
97    #[inline]
98    pub fn get_mut(&mut self, index: Token) -> Option<(&mut T, Token)> {
99        if let Some(entry) = self.entries.get_mut((index.get() - 1) as usize) {
100            if let Some(v) = &mut entry.item {
101                return Some((v, entry.next));
102            }
103        }
104        None
105    }
106
107    /// Gets an entry and a token to the next entry w/o checking, thus unsafe.
108    #[inline]
109    pub unsafe fn get_unchecked(&self, index: Token) -> (&T, Token) {
110        unsafe {
111            let entry = self.entries.get_unchecked((index.get() - 1) as usize);
112            let v = entry.item.as_ref().unwrap_unchecked();
113            (v, entry.next)
114        }
115    }
116
117    /// Gets an entry and a token to the next entry w/o checking, thus unsafe.
118    #[inline]
119    pub unsafe fn get_mut_unchecked(&mut self, index: Token) -> (&mut T, Token) {
120        unsafe {
121            let entry = self.entries.get_unchecked_mut((index.get() - 1) as usize);
122            let v = entry.item.as_mut().unwrap_unchecked();
123            (v, entry.next)
124        }
125    }
126
127    /// Links an entry before `target_head`. Returns the item next to the linked item,
128    /// which is either the item itself or `target_head`.
129    ///
130    /// # Panics
131    /// Panics on out of bounds access.
132    /// Panics (in debug mode) if linking an absent entry.
133    pub fn link(&mut self, idx: Token, target_head: Option<Token>) -> Token {
134        // eprintln!("linkedslab::link {idx} head {target_head:?}");
135        let (prev, next) = if let Some(target_head) = target_head {
136            let head = &mut self.entries[(target_head.get() - 1) as usize];
137            debug_assert!(head.item.is_some());
138            if head.prev == target_head {
139                // existing list has a single item linking to itself
140                head.prev = idx;
141                head.next = idx;
142                (target_head, target_head)
143            } else {
144                let before_head_idx = head.prev;
145                head.prev = idx;
146                let before_head = &mut self.entries[(before_head_idx.get() - 1) as usize];
147                debug_assert!(before_head.item.is_some());
148                debug_assert_eq!(before_head.next, target_head);
149                before_head.next = idx;
150                (before_head_idx, target_head)
151            }
152        } else {
153            (idx, idx)
154        };
155
156        let entry = &mut self.entries[(idx.get() - 1) as usize];
157        debug_assert!(entry.item.is_some());
158        debug_assert_eq!(entry.next, idx);
159        debug_assert_eq!(entry.prev, idx);
160        (entry.prev, entry.next) = (prev, next);
161        next
162    }
163
164    /// Unlinks an entry without removing it from the ring
165    /// Returns next item in the list, if not self.
166    ///
167    /// # Panics
168    /// Panics on out of bounds access.
169    /// Panics (in debug mode) if unlinking an absent entry.
170    pub fn unlink(&mut self, idx: Token) -> Option<Token> {
171        // eprintln!("linkedslab::unlink {idx}");
172        let entry = &mut self.entries[(idx.get() - 1) as usize];
173        debug_assert!(entry.item.is_some());
174        let (prev_idx, next_idx) = (entry.prev, entry.next);
175        if next_idx == idx {
176            debug_assert_eq!(prev_idx, idx);
177            // single item list, nothing to do
178            None
179        } else {
180            (entry.prev, entry.next) = (idx, idx);
181            let next = &mut self.entries[(next_idx.get() - 1) as usize];
182            debug_assert!(next.item.is_some());
183            debug_assert_eq!(next.prev, idx);
184            next.prev = prev_idx;
185            let prev = &mut self.entries[(prev_idx.get() - 1) as usize];
186            debug_assert!(prev.item.is_some());
187            debug_assert_eq!(prev.next, idx);
188            prev.next = next_idx;
189            Some(next_idx)
190        }
191    }
192
193    /// Unlinks and removes the entry from the ring.
194    /// Returns next item in the list, if not self.
195    ///
196    /// # Panics
197    /// Panics on out of bounds access.
198    pub fn remove(&mut self, idx: Token) -> Option<(T, Option<Token>)> {
199        // eprintln!("linkedslab::remove {idx}");
200        let next = self.unlink(idx);
201        let entry = &mut self.entries[(idx.get() - 1) as usize];
202        let old_item = entry.item.take()?;
203        entry.next = self.next_free;
204        self.next_free = idx;
205        Some((old_item, next))
206    }
207
208    /// The Token that will be returned by the next call to `insert()`
209    pub fn next_free(&self) -> Token {
210        self.next_free
211    }
212
213    /// Drains all items from the slab.
214    ///
215    /// The slab will be emptied even if the returned iterator isn't fully consumed.
216    pub fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
217        self.next_free = Token::new(1).unwrap();
218        self.entries.drain(..).flat_map(|i| i.item)
219    }
220
221    /// Iterator for the items in the slab
222    pub fn iter(&self) -> impl Iterator<Item = &'_ T> + '_ {
223        self.entries.iter().flat_map(|i| i.item.as_ref())
224    }
225
226    /// Iterator for the items in the slab, starting after a given token.
227    pub fn iter_from(
228        &self,
229        continuation: Option<Token>,
230    ) -> impl Iterator<Item = (Token, &'_ T)> + '_ {
231        let skip = continuation.map_or(0, |c| c.get() as usize);
232        self.entries
233            .iter()
234            .skip(skip)
235            .enumerate()
236            .filter_map(move |(i, e)| {
237                if let Some(item) = &e.item {
238                    Some((Token::new((skip + i + 1) as u32)?, item))
239                } else {
240                    None
241                }
242            })
243    }
244
245    /// Get the memory used by LinkedSlab
246    ///
247    /// It should be noted that if cache key or value is some type like `Vec<T>`,
248    /// the memory allocated in the heap will not be counted.
249    pub fn memory_used(&self) -> usize {
250        self.entries.len() * size_of::<Entry<T>>()
251    }
252}