kozan_primitives/arena/
storage.rs1use core::mem::MaybeUninit;
16
17pub struct Storage<T> {
28 slots: Vec<MaybeUninit<T>>,
29 initialized: Vec<bool>,
30}
31
32impl<T> Storage<T> {
33 #[must_use]
35 pub fn new() -> Self {
36 Self {
37 slots: Vec::new(),
38 initialized: Vec::new(),
39 }
40 }
41
42 #[inline]
44 #[must_use]
45 pub fn len(&self) -> usize {
46 self.slots.len()
47 }
48
49 #[inline]
51 #[must_use]
52 pub fn is_empty(&self) -> bool {
53 self.slots.is_empty()
54 }
55
56 pub fn grow(&mut self, index: u32) {
58 let needed = index as usize + 1;
59 if needed > self.slots.len() {
60 self.slots.reserve(needed - self.slots.len());
61 self.initialized.reserve(needed - self.initialized.len());
62 while self.slots.len() < needed {
63 self.slots.push(MaybeUninit::uninit());
64 self.initialized.push(false);
65 }
66 }
67 }
68
69 #[inline]
72 pub fn set(&mut self, index: u32, value: T) {
73 self.grow(index);
74 let i = index as usize;
75 if self.initialized[i] {
76 unsafe {
77 self.slots[i].assume_init_drop();
78 }
79 }
80 self.slots[i] = MaybeUninit::new(value);
81 self.initialized[i] = true;
82 }
83
84 #[inline]
86 #[must_use]
87 pub fn is_initialized(&self, index: u32) -> bool {
88 let i = index as usize;
89 i < self.initialized.len() && self.initialized[i]
90 }
91
92 #[inline]
94 #[must_use]
95 pub fn get(&self, index: u32) -> Option<&T> {
96 if !self.is_initialized(index) {
97 return None;
98 }
99 Some(unsafe { self.slots.get_unchecked(index as usize).assume_init_ref() })
100 }
101
102 #[inline]
104 pub fn get_mut(&mut self, index: u32) -> Option<&mut T> {
105 if !self.is_initialized(index) {
106 return None;
107 }
108 Some(unsafe {
109 self.slots
110 .get_unchecked_mut(index as usize)
111 .assume_init_mut()
112 })
113 }
114
115 #[inline]
121 #[must_use]
122 pub unsafe fn get_unchecked(&self, index: u32) -> &T {
123 debug_assert!(
124 self.is_initialized(index),
125 "Storage::get_unchecked on uninitialized slot {index}"
126 );
127 unsafe { self.slots.get_unchecked(index as usize).assume_init_ref() }
128 }
129
130 #[inline]
136 pub unsafe fn get_unchecked_mut(&mut self, index: u32) -> &mut T {
137 debug_assert!(
138 self.is_initialized(index),
139 "Storage::get_unchecked_mut on uninitialized slot {index}"
140 );
141 unsafe {
142 self.slots
143 .get_unchecked_mut(index as usize)
144 .assume_init_mut()
145 }
146 }
147
148 pub fn take(&mut self, index: u32) -> Option<T> {
151 if !self.is_initialized(index) {
152 return None;
153 }
154 let i = index as usize;
155 self.initialized[i] = false;
156 Some(unsafe { self.slots.get_unchecked(i).assume_init_read() })
157 }
158
159 pub fn clear(&mut self) {
161 for i in 0..self.initialized.len() {
162 if self.initialized[i] {
163 unsafe {
164 self.slots[i].assume_init_drop();
165 }
166 self.initialized[i] = false;
167 }
168 }
169 }
170
171 pub fn iter(&self) -> impl Iterator<Item = (u32, &T)> + '_ {
173 self.initialized
174 .iter()
175 .enumerate()
176 .filter_map(move |(i, &init)| {
177 if init {
178 Some((i as u32, unsafe { self.slots[i].assume_init_ref() }))
180 } else {
181 None
182 }
183 })
184 }
185
186 pub fn clear_slot(&mut self, index: u32) {
189 let i = index as usize;
190 if i < self.initialized.len() && self.initialized[i] {
191 unsafe {
192 self.slots[i].assume_init_drop();
193 }
194 self.initialized[i] = false;
195 }
196 }
197}
198
199impl<T> Default for Storage<T> {
200 fn default() -> Self {
201 Self::new()
202 }
203}
204
205impl<T: Clone> Clone for Storage<T> {
206 fn clone(&self) -> Self {
207 let mut new = Self::new();
208 for (id, val) in self.iter() {
209 new.set(id, val.clone());
210 }
211 new
212 }
213}
214
215impl<T> Drop for Storage<T> {
216 fn drop(&mut self) {
217 for i in 0..self.initialized.len() {
218 if self.initialized[i] {
219 unsafe {
220 self.slots[i].assume_init_drop();
221 }
222 }
223 }
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230
231 #[test]
232 fn new_is_empty() {
233 let s = Storage::<u32>::new();
234 assert_eq!(s.len(), 0);
235 assert!(s.is_empty());
236 assert!(!s.is_initialized(0));
237 }
238
239 #[test]
240 fn set_and_get() {
241 let mut s = Storage::new();
242 s.set(0, 42u32);
243 assert!(s.is_initialized(0));
244 assert_eq!(s.get(0), Some(&42));
245 }
246
247 #[test]
248 fn get_uninitialized_returns_none() {
249 let mut s = Storage::<u32>::new();
250 s.grow(5);
251 assert_eq!(s.get(2), None);
252 assert_eq!(s.get_mut(2), None);
253 }
254
255 #[test]
256 fn set_grows_storage() {
257 let mut s = Storage::new();
258 s.set(5, 99u32);
259 assert_eq!(s.len(), 6);
260 assert!(!s.is_initialized(0));
261 assert!(!s.is_initialized(4));
262 assert_eq!(s.get(5), Some(&99));
263 }
264
265 #[test]
266 fn set_overwrites() {
267 let mut s = Storage::new();
268 s.set(0, String::from("old"));
269 s.set(0, String::from("new"));
270 assert_eq!(s.get(0).map(|s| s.as_str()), Some("new"));
271 }
272
273 #[test]
274 fn get_mut_modifies() {
275 let mut s = Storage::new();
276 s.set(0, 10u32);
277 *s.get_mut(0).unwrap() = 20;
278 assert_eq!(s.get(0), Some(&20));
279 }
280
281 #[test]
282 fn take_removes() {
283 let mut s = Storage::new();
284 s.set(0, String::from("hello"));
285 let val = s.take(0);
286 assert_eq!(val.as_deref(), Some("hello"));
287 assert!(!s.is_initialized(0));
288 }
289
290 #[test]
291 fn take_uninitialized_returns_none() {
292 let mut s = Storage::<u32>::new();
293 s.grow(5);
294 assert_eq!(s.take(2), None);
295 }
296
297 #[test]
298 fn clear_slot_drops_value() {
299 use std::sync::atomic::{AtomicU32, Ordering};
300 static DROPS: AtomicU32 = AtomicU32::new(0);
301
302 struct Tracked;
303 impl Drop for Tracked {
304 fn drop(&mut self) {
305 DROPS.fetch_add(1, Ordering::Relaxed);
306 }
307 }
308
309 DROPS.store(0, Ordering::Relaxed);
310 let mut s = Storage::new();
311 s.set(0, Tracked);
312 assert_eq!(DROPS.load(Ordering::Relaxed), 0);
313
314 s.clear_slot(0);
315 assert_eq!(DROPS.load(Ordering::Relaxed), 1);
316 assert!(!s.is_initialized(0));
317 }
318
319 #[test]
320 fn clear_uninitialized_is_noop() {
321 let mut s = Storage::<u32>::new();
322 s.grow(5);
323 s.clear_slot(3);
324 }
325
326 #[test]
327 fn drop_cleans_up_all() {
328 use std::sync::atomic::{AtomicU32, Ordering};
329 static DROPS: AtomicU32 = AtomicU32::new(0);
330
331 struct Tracked;
332 impl Drop for Tracked {
333 fn drop(&mut self) {
334 DROPS.fetch_add(1, Ordering::Relaxed);
335 }
336 }
337
338 DROPS.store(0, Ordering::Relaxed);
339 {
340 let mut s = Storage::new();
341 s.set(0, Tracked);
342 s.set(2, Tracked);
343 s.set(4, Tracked);
344 }
345 assert_eq!(DROPS.load(Ordering::Relaxed), 3);
346 }
347
348 #[test]
349 fn set_overwrite_drops_old() {
350 use std::sync::atomic::{AtomicU32, Ordering};
351 static DROPS: AtomicU32 = AtomicU32::new(0);
352
353 struct Tracked;
354 impl Drop for Tracked {
355 fn drop(&mut self) {
356 DROPS.fetch_add(1, Ordering::Relaxed);
357 }
358 }
359
360 DROPS.store(0, Ordering::Relaxed);
361 let mut s = Storage::new();
362 s.set(0, Tracked);
363 assert_eq!(DROPS.load(Ordering::Relaxed), 0);
364 s.set(0, Tracked);
365 assert_eq!(DROPS.load(Ordering::Relaxed), 1);
366 }
367
368 #[test]
369 fn iter_yields_initialized_slots() {
370 let mut s = Storage::new();
371 s.set(0, 10u32);
372 s.set(2, 20);
373 s.set(4, 30);
374 let pairs: Vec<(u32, u32)> = s.iter().map(|(i, &v)| (i, v)).collect();
375 assert_eq!(pairs, vec![(0, 10), (2, 20), (4, 30)]);
376 }
377
378 #[test]
379 fn iter_empty_storage() {
380 let s = Storage::<u32>::new();
381 assert_eq!(s.iter().count(), 0);
382 }
383
384 #[test]
385 fn clone_preserves_values() {
386 let mut s = Storage::new();
387 s.set(0, 10u32);
388 s.set(3, 30);
389 s.set(5, 50);
390
391 let c = s.clone();
392 assert_eq!(c.get(0), Some(&10));
393 assert_eq!(c.get(1), None);
394 assert_eq!(c.get(3), Some(&30));
395 assert_eq!(c.get(5), Some(&50));
396 }
397
398 #[test]
399 fn clone_is_independent() {
400 let mut s = Storage::new();
401 s.set(0, String::from("hello"));
402 let mut c = s.clone();
403 c.set(0, String::from("world"));
404 assert_eq!(s.get(0).map(String::as_str), Some("hello"));
405 assert_eq!(c.get(0).map(String::as_str), Some("world"));
406 }
407
408 #[test]
409 #[should_panic(expected = "uninitialized slot")]
410 #[cfg(debug_assertions)]
411 fn unchecked_get_panics_in_debug() {
412 let mut s = Storage::<u32>::new();
413 s.grow(5);
414 unsafe {
415 let _ = s.get_unchecked(2);
416 }
417 }
418}