Skip to main content

oxihuman_core/
slab_allocator.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan) / SPDX-License-Identifier: Apache-2.0
2#![allow(dead_code)]
3
4//! A slab allocator that manages fixed-size slots with O(1) alloc/free using a free list.
5
6/// Handle returned by the slab allocator.
7#[allow(dead_code)]
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub struct SlabKey {
10    index: u32,
11    generation: u32,
12}
13
14#[allow(dead_code)]
15impl SlabKey {
16    pub fn index(&self) -> u32 {
17        self.index
18    }
19    pub fn generation(&self) -> u32 {
20        self.generation
21    }
22}
23
24#[allow(dead_code)]
25#[derive(Debug)]
26enum SlabEntry<T> {
27    Occupied {
28        value: T,
29        generation: u32,
30    },
31    Vacant {
32        next_free: Option<u32>,
33        generation: u32,
34    },
35}
36
37/// A slab allocator with generational keys.
38#[allow(dead_code)]
39#[derive(Debug)]
40pub struct SlabAllocator<T> {
41    entries: Vec<SlabEntry<T>>,
42    free_head: Option<u32>,
43    count: usize,
44}
45
46#[allow(dead_code)]
47impl<T> SlabAllocator<T> {
48    pub fn new() -> Self {
49        Self {
50            entries: Vec::new(),
51            free_head: None,
52            count: 0,
53        }
54    }
55
56    pub fn with_capacity(cap: usize) -> Self {
57        Self {
58            entries: Vec::with_capacity(cap),
59            free_head: None,
60            count: 0,
61        }
62    }
63
64    pub fn insert(&mut self, value: T) -> SlabKey {
65        self.count += 1;
66        if let Some(idx) = self.free_head {
67            let i = idx as usize;
68            match &self.entries[i] {
69                SlabEntry::Vacant {
70                    next_free,
71                    generation,
72                } => {
73                    let gen = *generation + 1;
74                    let nf = *next_free;
75                    self.entries[i] = SlabEntry::Occupied {
76                        value,
77                        generation: gen,
78                    };
79                    self.free_head = nf;
80                    SlabKey {
81                        index: idx,
82                        generation: gen,
83                    }
84                }
85                _ => unreachable!(),
86            }
87        } else {
88            let idx = self.entries.len() as u32;
89            self.entries.push(SlabEntry::Occupied {
90                value,
91                generation: 1,
92            });
93            SlabKey {
94                index: idx,
95                generation: 1,
96            }
97        }
98    }
99
100    pub fn remove(&mut self, key: SlabKey) -> Option<T> {
101        let i = key.index as usize;
102        if i >= self.entries.len() {
103            return None;
104        }
105        match &self.entries[i] {
106            SlabEntry::Occupied { generation, .. } if *generation == key.generation => {
107                let gen = *generation;
108                let old = std::mem::replace(
109                    &mut self.entries[i],
110                    SlabEntry::Vacant {
111                        next_free: self.free_head,
112                        generation: gen,
113                    },
114                );
115                self.free_head = Some(key.index);
116                self.count -= 1;
117                match old {
118                    SlabEntry::Occupied { value, .. } => Some(value),
119                    _ => unreachable!(),
120                }
121            }
122            _ => None,
123        }
124    }
125
126    pub fn get(&self, key: SlabKey) -> Option<&T> {
127        match self.entries.get(key.index as usize)? {
128            SlabEntry::Occupied { value, generation } if *generation == key.generation => {
129                Some(value)
130            }
131            _ => None,
132        }
133    }
134
135    pub fn get_mut(&mut self, key: SlabKey) -> Option<&mut T> {
136        match self.entries.get_mut(key.index as usize)? {
137            SlabEntry::Occupied { value, generation } if *generation == key.generation => {
138                Some(value)
139            }
140            _ => None,
141        }
142    }
143
144    pub fn contains(&self, key: SlabKey) -> bool {
145        self.get(key).is_some()
146    }
147
148    pub fn len(&self) -> usize {
149        self.count
150    }
151    pub fn is_empty(&self) -> bool {
152        self.count == 0
153    }
154    pub fn capacity(&self) -> usize {
155        self.entries.len()
156    }
157}
158
159impl<T> Default for SlabAllocator<T> {
160    fn default() -> Self {
161        Self::new()
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    #[test]
170    fn test_insert_get() {
171        let mut slab = SlabAllocator::new();
172        let k = slab.insert(42);
173        assert_eq!(slab.get(k), Some(&42));
174    }
175
176    #[test]
177    fn test_remove() {
178        let mut slab = SlabAllocator::new();
179        let k = slab.insert(10);
180        assert_eq!(slab.remove(k), Some(10));
181        assert_eq!(slab.get(k), None);
182    }
183
184    #[test]
185    fn test_generation() {
186        let mut slab = SlabAllocator::new();
187        let k1 = slab.insert(1);
188        slab.remove(k1);
189        let k2 = slab.insert(2);
190        assert_eq!(slab.get(k1), None);
191        assert_eq!(slab.get(k2), Some(&2));
192        assert_eq!(k1.index(), k2.index()); // reused slot
193    }
194
195    #[test]
196    fn test_multiple() {
197        let mut slab = SlabAllocator::new();
198        let a = slab.insert("a");
199        let b = slab.insert("b");
200        let c = slab.insert("c");
201        assert_eq!(slab.len(), 3);
202        slab.remove(b);
203        assert_eq!(slab.len(), 2);
204        assert!(!slab.contains(b));
205        assert!(slab.contains(a));
206        assert!(slab.contains(c));
207    }
208
209    #[test]
210    fn test_get_mut() {
211        let mut slab = SlabAllocator::new();
212        let k = slab.insert(100);
213        *slab.get_mut(k).expect("should succeed") = 200;
214        assert_eq!(slab.get(k), Some(&200));
215    }
216
217    #[test]
218    fn test_empty() {
219        let slab: SlabAllocator<i32> = SlabAllocator::new();
220        assert!(slab.is_empty());
221    }
222
223    #[test]
224    fn test_remove_invalid() {
225        let mut slab: SlabAllocator<i32> = SlabAllocator::new();
226        let fake = SlabKey {
227            index: 99,
228            generation: 1,
229        };
230        assert_eq!(slab.remove(fake), None);
231    }
232
233    #[test]
234    fn test_capacity() {
235        let mut slab = SlabAllocator::new();
236        slab.insert(1);
237        slab.insert(2);
238        assert_eq!(slab.capacity(), 2);
239    }
240
241    #[test]
242    fn test_reuse_chain() {
243        let mut slab = SlabAllocator::new();
244        let a = slab.insert(1);
245        let b = slab.insert(2);
246        slab.remove(a);
247        slab.remove(b);
248        let c = slab.insert(3);
249        let d = slab.insert(4);
250        assert_eq!(slab.len(), 2);
251        assert!(slab.contains(c));
252        assert!(slab.contains(d));
253    }
254
255    #[test]
256    fn test_with_capacity() {
257        let slab: SlabAllocator<i32> = SlabAllocator::with_capacity(100);
258        assert!(slab.is_empty());
259    }
260}