Skip to main content

ogeom_core/
arena.rs

1//! Typed generational arenas.
2//!
3//! Topology lives in arenas rather than behind reference counting: see
4//! `docs/DATA_MODEL.md` ยง11. Keys are small, `Copy`, comparable and hashable,
5//! which is what makes stable entity identity possible at all.
6//!
7//! Slots are generational: freeing a slot bumps its generation, so a stale key
8//! fails to resolve instead of silently aliasing whatever was allocated there
9//! next. That failure mode is worth eight bytes per key in a kernel where the
10//! alternative is a wrong answer rather than a crash. A slot dropped by
11//! [`Arena::retain`] is never handed out again at all.
12//!
13//! # Keys are scoped to the arena that issued them
14//!
15//! A generation catches a key that has outlived its slot. It cannot catch a key
16//! from a *different* arena, because index 3 generation 0 means something in
17//! every arena, so a handle from one document resolved against another comes
18//! back with whatever sits at that index, and answers confidently about the
19//! wrong entity. Nothing about the result says so.
20//!
21//! Every arena therefore takes an identifier the first time something is put
22//! in it, every key it issues carries that identifier, and every lookup
23//! compares it. A foreign key resolves to `None`, exactly as a stale one does.
24//! The cost is four bytes per key and one comparison per lookup, against a
25//! whole class of silent wrong answers.
26//!
27//! Cloning an arena keeps its identifier, because a clone is the same document
28//! and handles into it should keep working. Identifiers are per-process and are
29//! never serialized: a document read back from a file is a new arena with a new
30//! identifier, and the reader re-stamps the handles it read.
31
32use core::fmt;
33use core::hash::{Hash, Hasher};
34use core::marker::PhantomData;
35use core::sync::atomic::{AtomicU32, Ordering};
36
37use crate::collections::FastMap;
38
39/// Hands out arena identifiers.
40///
41/// Starts at one so that zero can mean *unscoped*: the state of a key built
42/// by a deserializer that does not yet know which arena it will belong to.
43static NEXT_SCOPE: AtomicU32 = AtomicU32::new(1);
44
45/// An identifier that no arena in this process shares.
46fn next_scope() -> u32 {
47    NEXT_SCOPE.fetch_add(1, Ordering::Relaxed)
48}
49
50/// The identifier a key carries before it has been bound to an arena.
51///
52/// A key with this scope resolves in no arena at all. That is deliberate: a
53/// handle read from a file is meaningless until the reader says which document
54/// it belongs to.
55pub const UNSCOPED: u32 = 0;
56
57/// A handle into an [`Arena<T>`].
58///
59/// Phantom-typed, so a `Key<Face>` cannot be used to index an `Arena<Edge>`.
60/// The marker is `fn() -> T` so the key stays `Copy`, `Send` and `Sync`
61/// regardless of `T`.
62pub struct Key<T> {
63    index: u32,
64    generation: u32,
65    scope: u32,
66    marker: PhantomData<fn() -> T>,
67}
68
69impl<T> Key<T> {
70    const fn new(index: u32, generation: u32, scope: u32) -> Self {
71        Self {
72            index,
73            generation,
74            scope,
75            marker: PhantomData,
76        }
77    }
78
79    /// Which arena issued this key.
80    ///
81    /// [`UNSCOPED`] for a key that has not been bound to one.
82    #[must_use]
83    pub const fn scope(self) -> u32 {
84        self.scope
85    }
86
87    /// This key, bound to the arena with the given identifier.
88    ///
89    /// For a deserializer, which rebuilds handles before it has an arena to
90    /// bind them to. Nothing else should need it: a key that came from an arena
91    /// already names the right one, and moving a key between arenas is the
92    /// mistake the scope exists to catch.
93    #[must_use]
94    pub const fn with_scope(self, scope: u32) -> Self {
95        Self { scope, ..self }
96    }
97
98    /// Position of the slot this key refers to.
99    #[must_use]
100    pub const fn index(self) -> u32 {
101        self.index
102    }
103
104    /// Generation stamp, used to detect a key outliving its slot.
105    #[must_use]
106    pub const fn generation(self) -> u32 {
107        self.generation
108    }
109
110    /// A key naming a given slot, for reading a document back from a file.
111    ///
112    /// Deliberately narrow. Forging a handle is precisely what generations
113    /// exist to prevent, and [`Arena::insert`] is what issues one within a
114    /// process. But a file records the handles a document was written with, and
115    /// a reader that could not rebuild them would have to renumber everything,
116    /// which is to say, hand back a different document.
117    ///
118    /// A key made this way is not trusted: it resolves through [`Arena::get`]
119    /// like any other, so a stale or out-of-range one comes back `None` rather
120    /// than aliasing whatever sits at that index.
121    #[must_use]
122    pub const fn from_parts(index: u32, generation: u32) -> Self {
123        Self::new(index, generation, UNSCOPED)
124    }
125}
126
127// Derived impls would demand `T: Clone` and friends. The key holds no `T`.
128impl<T> Clone for Key<T> {
129    fn clone(&self) -> Self {
130        *self
131    }
132}
133impl<T> Copy for Key<T> {}
134impl<T> PartialEq for Key<T> {
135    fn eq(&self, other: &Self) -> bool {
136        self.index == other.index
137            && self.generation == other.generation
138            && self.scope == other.scope
139    }
140}
141impl<T> Eq for Key<T> {}
142/// The hash leaves the scope out. A scope is drawn from a process-wide
143/// counter, so it is a different number for the same document depending on
144/// how many arenas the process filled before, on which threads; hashed, it
145/// would reorder every table keyed by handles with it, and a stage that walks
146/// one would answer differently from one run to the next. Equality still
147/// compares the scope, so keys of two arenas never merge in a table: at
148/// worst they share a bucket.
149impl<T> Hash for Key<T> {
150    fn hash<H: Hasher>(&self, state: &mut H) {
151        self.index.hash(state);
152        self.generation.hash(state);
153    }
154}
155impl<T> PartialOrd for Key<T> {
156    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
157        Some(self.cmp(other))
158    }
159}
160impl<T> Ord for Key<T> {
161    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
162        (self.scope, self.index, self.generation).cmp(&(other.scope, other.index, other.generation))
163    }
164}
165impl<T> fmt::Debug for Key<T> {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        write!(f, "Key({}v{}@{})", self.index, self.generation, self.scope)
168    }
169}
170
171/// A live value, with the slot it sits in and the generation it was stored
172/// at.
173#[derive(Debug, Clone)]
174struct Entry<T> {
175    slot: u32,
176    generation: u32,
177    value: T,
178}
179
180/// A generational arena of `T`.
181///
182/// Live values are stored packed, in two runs. The *tail* holds every slot
183/// from some index on, each one occupied, so its values are found by
184/// arithmetic; an arena that has only been appended to is all tail. The
185/// values before it are *scattered*: slots whose neighbours were dropped,
186/// found through a map from slot to position. An arena holds what is live
187/// and nothing for the slots it no longer uses, so cloning one costs what
188/// it holds, however many slots it has handed out.
189#[derive(Debug, Clone)]
190pub struct Arena<T> {
191    /// The scattered values, then the tail's, in slot order unless
192    /// `ordered` says otherwise.
193    entries: Vec<Entry<T>>,
194    /// The first slot of the tail.
195    tail: u32,
196    /// Where the tail starts in `entries`: the number of scattered values.
197    tail_at: u32,
198    /// Slot to position in `entries`, for the scattered values.
199    scattered: Option<FastMap<u32, u32>>,
200    /// Whether `entries` runs in slot order. Removing a value and refilling
201    /// a freed slot can break the order; iterating mutably restores it.
202    ordered: bool,
203    /// Freed slots for `insert` to reuse, each with the generation it takes
204    /// next. A slot dropped by [`Arena::retain`] is never reused.
205    free: Vec<(u32, u32)>,
206    /// How many slots have been handed out: the slot a fresh insert takes.
207    next_slot: u32,
208    /// Which arena this is. [`UNSCOPED`] until the first insert, because
209    /// `new` is `const` and a counter cannot be read from one, and an arena
210    /// with nothing in it has issued no keys to disagree with.
211    scope: u32,
212}
213
214impl<T> Default for Arena<T> {
215    fn default() -> Self {
216        Self::new()
217    }
218}
219
220impl<T> Arena<T> {
221    /// An empty arena.
222    #[must_use]
223    pub const fn new() -> Self {
224        Self {
225            entries: Vec::new(),
226            tail: 0,
227            tail_at: 0,
228            scattered: None,
229            ordered: true,
230            free: Vec::new(),
231            next_slot: 0,
232            scope: UNSCOPED,
233        }
234    }
235
236    /// An empty arena with room for `capacity` entries.
237    #[must_use]
238    pub fn with_capacity(capacity: usize) -> Self {
239        Self {
240            entries: Vec::with_capacity(capacity),
241            ..Self::new()
242        }
243    }
244
245    /// Number of live entries.
246    #[must_use]
247    pub const fn len(&self) -> usize {
248        self.entries.len()
249    }
250
251    /// Whether there are no live entries.
252    #[must_use]
253    pub const fn is_empty(&self) -> bool {
254        self.entries.is_empty()
255    }
256
257    /// Which arena this is, for stamping keys that were rebuilt elsewhere.
258    ///
259    /// [`UNSCOPED`] until the first insert.
260    #[must_use]
261    pub const fn scope(&self) -> u32 {
262        self.scope
263    }
264
265    /// Whether a key was issued by this arena.
266    ///
267    /// Distinct from [`Arena::contains`], which also asks whether the slot is
268    /// still live. This asks only whether the key belongs here at all, which is
269    /// the question a caller wants when reporting *why* a lookup failed.
270    #[must_use]
271    pub const fn issued(&self, key: Key<T>) -> bool {
272        key.scope == self.scope
273    }
274
275    /// The slot the next [`Arena::insert`] takes, at generation zero, when
276    /// it takes a fresh one; `None` while a freed slot waits to be reused.
277    ///
278    /// The precondition for extending the arena by offset, where a caller
279    /// predicts the keys of entries it is about to append: the `k`-th
280    /// insert from here lands at this index plus `k`.
281    #[must_use]
282    pub fn next_index(&self) -> Option<u32> {
283        self.free.is_empty().then_some(self.next_slot)
284    }
285
286    /// The live key at slot `index`, if the slot holds a value.
287    #[must_use]
288    pub fn key_at(&self, index: u32) -> Option<Key<T>> {
289        let entry = self.entries.get(self.position(index)?)?;
290        (entry.slot == index).then(|| Key::new(index, entry.generation, self.scope))
291    }
292
293    /// Insert a value, returning its key.
294    ///
295    /// The first insert is what fixes the arena's identity, since [`Arena::new`]
296    /// is `const` and cannot read a counter. That is safe because an arena with
297    /// nothing in it has issued no keys to disagree with.
298    ///
299    /// # Panics
300    ///
301    /// If the arena exceeds `u32::MAX` slots. A single model reaching four
302    /// billion topological entities is a bug elsewhere, not a case to handle.
303    #[allow(clippy::expect_used, reason = "documented panic; see # Panics")]
304    pub fn insert(&mut self, value: T) -> Key<T> {
305        if self.scope == UNSCOPED {
306            self.scope = next_scope();
307        }
308        if let Some((slot, generation)) = self.free.pop() {
309            self.scatter_tail();
310            let at = u32::try_from(self.entries.len()).expect("arena exceeded u32::MAX slots");
311            self.ordered &= self.entries.last().is_none_or(|last| last.slot < slot);
312            self.entries.push(Entry {
313                slot,
314                generation,
315                value,
316            });
317            self.scattered
318                .get_or_insert_with(FastMap::default)
319                .insert(slot, at);
320            self.tail_at = at + 1;
321            return Key::new(slot, generation, self.scope);
322        }
323        let slot = self.next_slot;
324        self.next_slot = slot.checked_add(1).expect("arena exceeded u32::MAX slots");
325        self.entries.push(Entry {
326            slot,
327            generation: 0,
328            value,
329        });
330        Key::new(slot, 0, self.scope)
331    }
332
333    /// Where slot `index`'s value would sit in `entries`: past the end for
334    /// a slot never handed out, `None` for a scattered slot with no value.
335    #[inline]
336    fn position(&self, index: u32) -> Option<usize> {
337        if index >= self.tail {
338            Some((index - self.tail) as usize + self.tail_at as usize)
339        } else {
340            self.scattered.as_ref()?.get(&index).map(|&at| at as usize)
341        }
342    }
343
344    /// The position of `key`'s value, if the key is live here.
345    #[inline]
346    fn live(&self, key: Key<T>) -> Option<usize> {
347        if key.scope != self.scope {
348            return None;
349        }
350        let at = self.position(key.index)?;
351        let entry = self.entries.get(at)?;
352        (entry.slot == key.index && entry.generation == key.generation).then_some(at)
353    }
354
355    /// Borrow the value behind `key`, or `None` if the key is stale.
356    #[must_use]
357    #[inline]
358    pub fn get(&self, key: Key<T>) -> Option<&T> {
359        let at = self.live(key)?;
360        self.entries.get(at).map(|entry| &entry.value)
361    }
362
363    /// Mutably borrow the value behind `key`, or `None` if the key is stale.
364    #[inline]
365    pub fn get_mut(&mut self, key: Key<T>) -> Option<&mut T> {
366        let at = self.live(key)?;
367        self.entries.get_mut(at).map(|entry| &mut entry.value)
368    }
369
370    /// Whether `key` resolves to a live entry.
371    #[must_use]
372    pub fn contains(&self, key: Key<T>) -> bool {
373        self.live(key).is_some()
374    }
375
376    /// Move the tail's values into the scattered ones, leaving the tail
377    /// empty and starting at the next fresh slot.
378    #[allow(
379        clippy::cast_possible_truncation,
380        reason = "`insert` keeps positions within u32"
381    )]
382    fn scatter_tail(&mut self) {
383        let from = self.tail_at as usize;
384        if from < self.entries.len() {
385            let map = self.scattered.get_or_insert_with(FastMap::default);
386            for (at, entry) in self.entries.iter().enumerate().skip(from) {
387                map.insert(entry.slot, at as u32);
388            }
389        }
390        self.tail = self.next_slot;
391        self.tail_at = self.entries.len() as u32;
392    }
393
394    /// Remove and return the value behind `key`, if it is live.
395    ///
396    /// The slot's generation is bumped, invalidating every outstanding copy of
397    /// `key`, and a later insert reuses the slot.
398    #[allow(
399        clippy::cast_possible_truncation,
400        reason = "`insert` keeps positions within u32"
401    )]
402    pub fn remove(&mut self, key: Key<T>) -> Option<T> {
403        self.live(key)?;
404        self.scatter_tail();
405        let map = self.scattered.as_mut()?;
406        let at = map.remove(&key.index)? as usize;
407        let entry = self.entries.swap_remove(at);
408        if let Some(moved) = self.entries.get(at) {
409            map.insert(moved.slot, at as u32);
410            self.ordered = false;
411        }
412        self.tail_at -= 1;
413        // Saturating rather than wrapping: a slot recycled 4 billion times stops
414        // being reusable, which is strictly better than handing out a generation
415        // that collides with a key someone still holds.
416        let next = entry.generation.saturating_add(1);
417        if next != u32::MAX {
418            self.free.push((key.index, next));
419        }
420        Some(entry.value)
421    }
422
423    /// Keep the entries `keep` answers `true` for and drop the rest.
424    ///
425    /// Every kept key still resolves to its value. A dropped key fails to
426    /// resolve, and its slot is never handed out again, so no later entry
427    /// answers to it. What is dropped stops costing memory: the arena holds
428    /// the kept values, packed.
429    #[allow(
430        clippy::cast_possible_truncation,
431        reason = "`insert` keeps positions within u32"
432    )]
433    pub fn retain(&mut self, mut keep: impl FnMut(Key<T>, &T) -> bool) {
434        let scope = self.scope;
435        self.entries
436            .retain(|entry| keep(Key::new(entry.slot, entry.generation, scope), &entry.value));
437        if !self.ordered {
438            self.entries.sort_unstable_by_key(|entry| entry.slot);
439            self.ordered = true;
440        }
441        // The longest run of entries ending at the last slot handed out,
442        // every slot in it occupied, stays the tail; the rest are scattered.
443        let mut tail_len = 0_usize;
444        while tail_len < self.entries.len() {
445            let entry = &self.entries[self.entries.len() - 1 - tail_len];
446            if u64::from(entry.slot) + 1 + tail_len as u64 != u64::from(self.next_slot) {
447                break;
448            }
449            tail_len += 1;
450        }
451        let tail_at = self.entries.len() - tail_len;
452        self.tail = self.next_slot - tail_len as u32;
453        self.tail_at = tail_at as u32;
454        self.scattered = (tail_at > 0).then(|| {
455            self.entries[..tail_at]
456                .iter()
457                .enumerate()
458                .map(|(at, entry)| (entry.slot, at as u32))
459                .collect()
460        });
461        self.entries.shrink_to_fit();
462    }
463
464    /// The positions of `entries`, in slot order.
465    fn slot_order(&self) -> impl Iterator<Item = usize> + '_ {
466        let sorted = (!self.ordered).then(|| {
467            let mut order: Vec<usize> = (0..self.entries.len()).collect();
468            order.sort_unstable_by_key(|&at| self.entries[at].slot);
469            order
470        });
471        (0..self.entries.len()).map(move |k| sorted.as_ref().map_or(k, |order| order[k]))
472    }
473
474    /// Put `entries` in slot order, where removals have disturbed it.
475    #[allow(
476        clippy::cast_possible_truncation,
477        reason = "`insert` keeps positions within u32"
478    )]
479    fn sort(&mut self) {
480        if self.ordered {
481            return;
482        }
483        self.scatter_tail();
484        self.entries.sort_unstable_by_key(|entry| entry.slot);
485        if let Some(map) = &mut self.scattered {
486            for (at, entry) in self.entries.iter().enumerate() {
487                map.insert(entry.slot, at as u32);
488            }
489        }
490        self.ordered = true;
491    }
492
493    /// Iterate over live `(key, &value)` pairs, in slot order.
494    pub fn iter(&self) -> impl Iterator<Item = (Key<T>, &T)> {
495        let scope = self.scope;
496        self.slot_order().map(move |at| {
497            let entry = &self.entries[at];
498            (Key::new(entry.slot, entry.generation, scope), &entry.value)
499        })
500    }
501
502    /// Iterate over live `(key, &mut value)` pairs, in slot order.
503    pub fn iter_mut(&mut self) -> impl Iterator<Item = (Key<T>, &mut T)> {
504        self.sort();
505        let scope = self.scope;
506        self.entries.iter_mut().map(move |entry| {
507            (
508                Key::new(entry.slot, entry.generation, scope),
509                &mut entry.value,
510            )
511        })
512    }
513
514    /// Iterate over live values.
515    pub fn values(&self) -> impl Iterator<Item = &T> {
516        self.iter().map(|(_, v)| v)
517    }
518
519    /// Consume the arena, yielding its live values in index order.
520    ///
521    /// For appending one arena's contents onto another: the receiving arena
522    /// hands out its own keys, so the values travel bare.
523    pub fn into_values(mut self) -> impl Iterator<Item = T> {
524        self.sort();
525        self.entries.into_iter().map(|entry| entry.value)
526    }
527
528    /// Whether the arena has only ever been appended to: every slot occupied,
529    /// every generation zero.
530    ///
531    /// When this holds, [`Arena::len`] is also the next index [`Arena::insert`]
532    /// will hand out.
533    #[must_use]
534    pub fn is_dense(&self) -> bool {
535        self.entries.len() == self.next_slot as usize
536            && self.entries.iter().all(|entry| entry.generation == 0)
537    }
538
539    /// Remove every entry. Existing keys go stale, and their slots are not
540    /// handed out again.
541    pub fn clear(&mut self) {
542        self.retain(|_, _| false);
543    }
544}
545
546impl<T> core::ops::Index<Key<T>> for Arena<T> {
547    type Output = T;
548
549    /// # Panics
550    ///
551    /// If the key is stale. Use [`Arena::get`] where that is a possibility.
552    #[allow(
553        clippy::expect_used,
554        reason = "Index cannot return Result; see # Panics"
555    )]
556    fn index(&self, key: Key<T>) -> &T {
557        self.get(key).expect("stale arena key")
558    }
559}
560
561impl<T> core::ops::IndexMut<Key<T>> for Arena<T> {
562    /// # Panics
563    ///
564    /// If the key is stale. Use [`Arena::get_mut`] where that is a possibility.
565    #[allow(
566        clippy::expect_used,
567        reason = "IndexMut cannot return Result; see # Panics"
568    )]
569    fn index_mut(&mut self, key: Key<T>) -> &mut T {
570        self.get_mut(key).expect("stale arena key")
571    }
572}
573
574#[cfg(test)]
575#[allow(clippy::unwrap_used)]
576mod tests {
577    use super::*;
578
579    #[test]
580    fn insert_and_get() {
581        let mut a = Arena::new();
582        let k1 = a.insert("one");
583        let k2 = a.insert("two");
584        assert_eq!(a.get(k1), Some(&"one"));
585        assert_eq!(a.get(k2), Some(&"two"));
586        assert_eq!(a.len(), 2);
587    }
588
589    #[test]
590    fn removed_key_goes_stale_and_does_not_alias() {
591        let mut a = Arena::new();
592        let old = a.insert(1_u32);
593        assert_eq!(a.remove(old), Some(1));
594
595        // The slot is reused, but the old key must not resolve to the new value.
596        let new = a.insert(2_u32);
597        assert_eq!(new.index(), old.index(), "slot should have been reused");
598        assert_eq!(a.get(new), Some(&2));
599        assert_eq!(a.get(old), None, "stale key aliased a live entry");
600        assert!(!a.contains(old));
601    }
602
603    #[test]
604    fn double_remove_is_none() {
605        let mut a = Arena::new();
606        let k = a.insert(7_u8);
607        assert_eq!(a.remove(k), Some(7));
608        assert_eq!(a.remove(k), None);
609        assert_eq!(a.len(), 0);
610    }
611
612    #[test]
613    fn iteration_skips_holes() {
614        let mut a = Arena::new();
615        let keys: Vec<_> = (0..5_u32).map(|i| a.insert(i)).collect();
616        a.remove(keys[1]);
617        a.remove(keys[3]);
618        let live: Vec<_> = a.values().copied().collect();
619        assert_eq!(live, vec![0, 2, 4]);
620        assert_eq!(a.len(), 3);
621    }
622
623    #[test]
624    fn into_values_yields_values_in_index_order() {
625        let mut a = Arena::new();
626        for i in 0..5_u32 {
627            a.insert(i * 10);
628        }
629        let values: Vec<_> = a.into_values().collect();
630        assert_eq!(values, vec![0, 10, 20, 30, 40]);
631    }
632
633    #[test]
634    fn an_arena_that_never_removed_is_dense() {
635        let mut a = Arena::new();
636        assert!(a.is_dense(), "an empty arena has no holes");
637        for i in 0..4_u32 {
638            a.insert(i);
639        }
640        assert!(a.is_dense());
641    }
642
643    #[test]
644    fn a_removal_makes_an_arena_not_dense() {
645        let mut a = Arena::new();
646        let keys: Vec<_> = (0..3_u32).map(|i| a.insert(i)).collect();
647        a.remove(keys[1]);
648        assert!(!a.is_dense(), "a vacant slot is a hole");
649
650        // Refilling the slot does not restore density either: the recycled
651        // entry sits at a bumped generation, so `len` no longer predicts the
652        // keys of future appends alone.
653        a.insert(9);
654        assert!(!a.is_dense(), "a recycled slot is off generation zero");
655    }
656
657    #[test]
658    fn clear_invalidates_every_key() {
659        let mut a = Arena::new();
660        let keys: Vec<_> = (0..4_u32).map(|i| a.insert(i)).collect();
661        a.clear();
662        assert!(a.is_empty());
663        assert!(keys.iter().all(|&k| a.get(k).is_none()));
664    }
665
666    #[test]
667    fn retained_keys_resolve_and_dropped_slots_are_never_handed_out_again() {
668        let mut a = Arena::new();
669        let keys: Vec<_> = (0..10_u32).map(|i| a.insert(i)).collect();
670        a.retain(|_, v| v % 3 == 0);
671        assert_eq!(a.len(), 4);
672        for (i, key) in keys.iter().enumerate() {
673            let want = (i % 3 == 0).then_some(u32::try_from(i).unwrap());
674            assert_eq!(a.get(*key).copied(), want, "slot {i}");
675        }
676        assert_eq!(a.next_index(), Some(10), "a fresh insert appends");
677        let fresh = a.insert(10);
678        assert_eq!(fresh.index(), 10);
679        assert!(keys.iter().all(|&k| k != fresh));
680        assert_eq!(
681            a.values().copied().collect::<Vec<_>>(),
682            vec![0, 3, 6, 9, 10]
683        );
684        assert_eq!(a.key_at(3), Some(keys[3]));
685        assert_eq!(a.key_at(4), None);
686
687        // A clone answers to the same keys, and a removal from the retained
688        // arena still refills its slot under a new generation.
689        let copy = a.clone();
690        assert!(keys.iter().all(|&k| copy.get(k) == a.get(k)));
691        assert_eq!(a.remove(keys[6]), Some(6));
692        assert_eq!(a.next_index(), None, "the freed slot is refilled first");
693        let refill = a.insert(60);
694        assert_eq!(refill.index(), 6);
695        assert_eq!(a.get(keys[6]), None);
696        assert_eq!(
697            a.iter().map(|(k, v)| (k.index(), *v)).collect::<Vec<_>>(),
698            vec![(0, 0), (3, 3), (6, 60), (9, 9), (10, 10)]
699        );
700    }
701
702    #[test]
703    fn keys_are_hashable_and_distinct() {
704        use crate::collections::FastSet;
705        let mut a = Arena::new();
706        let set: FastSet<_> = (0..64_u32).map(|i| a.insert(i)).collect();
707        assert_eq!(set.len(), 64);
708    }
709}
710
711#[cfg(test)]
712#[allow(clippy::unwrap_used)]
713mod scope_tests {
714    use super::*;
715
716    #[test]
717    fn a_key_from_one_arena_does_not_resolve_in_another() {
718        // The whole reason the scope exists. Index 0 generation 0 means
719        // something in every arena, so without it this lookup succeeds and
720        // answers about the wrong value, confidently, with nothing about the
721        // result to say so.
722        let mut a: Arena<&str> = Arena::new();
723        let mut b: Arena<&str> = Arena::new();
724        let here = a.insert("in a");
725        let there = b.insert("in b");
726
727        assert_eq!(a.get(here), Some(&"in a"));
728        assert_eq!(b.get(there), Some(&"in b"));
729        assert_eq!(here.index(), there.index(), "the same slot in both");
730        assert_eq!(a.get(there), None, "a foreign key must not resolve");
731        assert_eq!(b.get(here), None);
732        assert!(!a.issued(there));
733    }
734
735    #[test]
736    fn foreign_keys_are_refused_by_every_route_in() {
737        let mut a: Arena<u32> = Arena::new();
738        let mut b: Arena<u32> = Arena::new();
739        let key = a.insert(1);
740        b.insert(2);
741
742        assert!(!b.contains(key));
743        assert_eq!(b.get_mut(key), None);
744        assert_eq!(b.remove(key), None, "and it must not remove something else");
745        assert_eq!(b.len(), 1, "nothing was taken out");
746    }
747
748    #[test]
749    fn keys_from_different_arenas_are_not_equal_and_do_not_collide() {
750        // Equality and hashing have to agree with resolution, or a map keyed on
751        // handles merges entries from two documents.
752        use crate::collections::FastSet;
753        let mut a: Arena<u32> = Arena::new();
754        let mut b: Arena<u32> = Arena::new();
755        let here = a.insert(1);
756        let there = b.insert(2);
757
758        assert_ne!(here, there);
759        let mut set = FastSet::default();
760        set.insert(here);
761        set.insert(there);
762        assert_eq!(set.len(), 2, "two documents' handles collided in a map");
763    }
764
765    #[test]
766    fn a_table_of_keys_walks_in_the_same_order_whatever_the_scope() {
767        // Two arenas filled alike stand for one document built in two
768        // processes: the same slots, different identifiers. A table of their
769        // keys must walk in the same slot order, or whatever walks it
770        // depends on what else the process built first.
771        use crate::collections::FastSet;
772        let walk = |skip: usize| {
773            for _ in 0..skip {
774                Arena::<u8>::new().insert(0);
775            }
776            let mut a: Arena<u32> = Arena::new();
777            let set: FastSet<Key<u32>> = (0..200_u32).map(|i| a.insert(i)).collect();
778            set.iter().map(|k| k.index()).collect::<Vec<_>>()
779        };
780        let first = walk(0);
781        for skip in [1, 2, 3, 17, 1000] {
782            assert_eq!(walk(skip), first, "{skip} arenas filled before");
783        }
784    }
785
786    #[test]
787    fn an_unscoped_key_resolves_nowhere_until_it_is_bound() {
788        // What a deserializer builds. It names a slot but no arena, and a
789        // handle that names no arena is meaningless until someone says which.
790        let mut a: Arena<u32> = Arena::new();
791        let real = a.insert(7);
792        let loose: Key<u32> = Key::from_parts(real.index(), real.generation());
793
794        assert_eq!(loose.scope(), UNSCOPED);
795        assert_eq!(a.get(loose), None);
796        assert_eq!(a.get(loose.with_scope(a.scope())), Some(&7));
797    }
798
799    #[test]
800    fn a_clone_answers_to_the_originals_handles() {
801        // A clone is the same document (a snapshot), so handles into it keep
802        // working. If it took a fresh identifier, every handle a caller held
803        // would silently stop resolving after a clone.
804        let mut a: Arena<u32> = Arena::new();
805        let key = a.insert(5);
806        let copy = a.clone();
807        assert_eq!(copy.get(key), Some(&5));
808    }
809
810    #[test]
811    fn an_empty_arena_has_issued_nothing_to_disagree_with() {
812        // `new` is const, so the identifier cannot be taken until the first
813        // insert. That is safe precisely because an arena with nothing in it
814        // has handed out no keys.
815        let empty: Arena<u32> = Arena::new();
816        assert_eq!(empty.scope(), UNSCOPED);
817        let mut used: Arena<u32> = Arena::new();
818        used.insert(1);
819        assert_ne!(used.scope(), UNSCOPED);
820    }
821}