Skip to main content

jstd/
registry.rs

1//! Strongly typed identifiers and registries.
2//!
3//! This module provides:
4//! - `Identifier`: a trait for strongly typed IDs backed by `usize`
5//! - `Identified<Id, T>`: a value paired with its typed ID
6//! - `Registry<Id, T>`: a typed vector indexed by `Id`
7//!
8//! # Example
9//! ```
10//! use jstd::Identifier;
11//! use jstd::registry::Registry;
12//!
13//! #[derive(Identifier)]
14//! struct NodeId(usize);
15//!
16//! let mut registry = Registry::<NodeId, &str>::default();
17//! let a = registry.push("a");
18//! let b = registry.push("b");
19//!
20//! assert_eq!(usize::from(a), 0);
21//! assert_eq!(usize::from(b), 1);
22//! assert_eq!(registry[a], "a");
23//! assert_eq!(registry[b], "b");
24//! ```
25use std::{
26    fmt::{Debug, Display},
27    hash::Hash,
28    marker::PhantomData,
29    ops::{Deref, DerefMut, Index, IndexMut},
30    slice, vec,
31};
32
33use crate::intern::Intern;
34use serde::{Deserialize, Deserializer, Serialize, Serializer, ser::SerializeSeq};
35
36/// Typed identifier trait used by [`Registry`].
37///
38/// Any `Identifier` is expected to be a light wrapper over `usize`.
39pub trait Identifier: Copy + Hash + From<usize> + Into<usize> + Eq + Ord + std::fmt::Debug {}
40
41impl Identifier for usize {}
42
43/// A value paired with its typed identifier.
44pub struct Identified<Id: Identifier, T> {
45    pub id: Id,
46    pub inner: T,
47}
48
49impl<Id: Identifier, T> Identified<Id, T> {
50    /// Creates a new identified wrapper.
51    pub fn new(id: Id, data: T) -> Self {
52        Self { id, inner: data }
53    }
54}
55
56impl<Id: Identifier, T> Deref for Identified<Id, T> {
57    type Target = T;
58
59    fn deref(&self) -> &Self::Target {
60        &self.inner
61    }
62}
63
64impl<Id: Identifier, T> DerefMut for Identified<Id, T> {
65    fn deref_mut(&mut self) -> &mut Self::Target {
66        &mut self.inner
67    }
68}
69
70impl<'a, Id: Identifier, T> Identified<Id, &'a mut T> {
71    /// Converts `Identified<Id, &mut T>` into `Identified<Id, &T>`.
72    pub fn immutable(self) -> Identified<Id, &'a T> {
73        Identified::new(self.id, &*self.inner)
74    }
75}
76
77impl<Id: Identifier, T: Display> Display for Identified<Id, T> {
78    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79        self.inner.fmt(f)
80    }
81}
82
83impl<Id: Identifier, T: Debug> Debug for Identified<Id, T> {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        self.inner.fmt(f)
86    }
87}
88
89/// A strongly typed vector
90///
91/// # Example
92/// ```
93/// use jstd::Identifier;
94/// use jstd::registry::Registry;
95///
96/// #[derive(Identifier)]
97/// struct ItemId(usize);
98///
99/// let mut reg = Registry::<ItemId, i32>::default();
100/// let id = reg.push(10);
101/// reg[id] += 5;
102///
103/// assert_eq!(reg[id], 15);
104/// assert_eq!(reg.len(), 1);
105/// assert!(!reg.is_empty());
106/// ```
107/// Segmented, **append-only-stable** backing store: element `n` lives in chunk
108/// `k = floor(log2(n + 1))` at offset `n + 1 - 2^k`, so chunk `k` holds exactly
109/// `2^k` elements. Each chunk is allocated once at its full capacity and never
110/// reallocated, so **an element's address is stable for the life of the
111/// registry** even as later `push`es grow the store (growing appends new chunks;
112/// it never moves existing elements). Growing the outer `Vec<Vec<T>>` moves the
113/// chunk *headers*, not their heap buffers. This stability is what lets the
114/// literal/type interners hand out `&T` references that outlive a mint (see
115/// `qcode`'s `RwLock`-wrapped interners). Chunk sizes double, so a small registry
116/// stays cheap (chunks 1, 2, 4, …) and a large one needs few chunks.
117///
118/// The public API is identical to a flat `Vec`-backed registry: ids are dense
119/// `0..len` and index in insertion order.
120pub struct Registry<Id: Identifier, T> {
121    chunks: Vec<Vec<T>>,
122    len: usize,
123    _marker: PhantomData<Id>,
124}
125
126/// `(chunk, offset)` for global index `n`.
127#[inline]
128fn locate(n: usize) -> (usize, usize) {
129    let m = n + 1;
130    let k = (usize::BITS - 1 - m.leading_zeros()) as usize;
131    (k, m - (1 << k))
132}
133
134impl<Id: Identifier, T: Serialize> Serialize for Registry<Id, T> {
135    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
136    where
137        S: Serializer,
138    {
139        // Same wire format as the old flat registry: a single length-prefixed
140        // sequence of elements in id order. The length must be given up front —
141        // a `collect_seq` over the chunk-`Flatten` iterator has no exact length,
142        // which length-prefixed formats (bincode) reject.
143        let mut seq = serializer.serialize_seq(Some(self.len))?;
144        for e in self.chunks.iter().flatten() {
145            seq.serialize_element(e)?;
146        }
147        seq.end()
148    }
149}
150
151impl<'de, Id: Identifier, T: Deserialize<'de>> Deserialize<'de> for Registry<Id, T> {
152    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
153    where
154        D: Deserializer<'de>,
155    {
156        Ok(Vec::<T>::deserialize(deserializer)?.into_iter().collect())
157    }
158}
159
160impl<Id: Identifier, T: Clone> Clone for Registry<Id, T> {
161    fn clone(&self) -> Self {
162        // Rebuild through `push` so each chunk is reallocated at its full
163        // capacity (a derived `Vec` clone would shrink the last chunk to its
164        // length and break the never-realloc stability invariant on next push).
165        self.chunks.iter().flatten().cloned().collect()
166    }
167}
168
169impl<Id: Identifier, T: Debug> Debug for Registry<Id, T> {
170    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171        f.debug_list()
172            .entries(self.chunks.iter().flatten())
173            .finish()
174    }
175}
176
177impl<Id: Identifier, T: PartialEq> PartialEq for Registry<Id, T> {
178    fn eq(&self, other: &Self) -> bool {
179        self.len == other.len
180            && self
181                .chunks
182                .iter()
183                .flatten()
184                .eq(other.chunks.iter().flatten())
185    }
186}
187
188impl<Id: Identifier, T: Eq> Eq for Registry<Id, T> {}
189
190impl<Id: Identifier, T> Registry<Id, T> {
191    /// Pushes a value and returns its typed identifier.
192    pub fn push(&mut self, e: T) -> Id {
193        let n = self.len;
194        let (k, offset) = locate(n);
195        if offset == 0 && self.chunks.len() == k {
196            // First element of a fresh chunk `k`; earlier chunks are already
197            // full. A chunk `truncate` emptied is still there, at its old
198            // capacity, and is reused rather than shadowed by a new one.
199            self.chunks.push(Vec::with_capacity(1 << k));
200        }
201        self.chunks[k].push(e);
202        self.len += 1;
203        n.into()
204    }
205
206    /// Returns the number of elements.
207    pub fn len(&self) -> usize {
208        self.len
209    }
210
211    /// Drops every element with an id of `len` or above, keeping the chunks
212    /// already allocated so the next pushes reuse them.
213    ///
214    /// The ids below `len` and their elements' addresses are unchanged. This
215    /// is the one way a registry shrinks: for a construction that appended
216    /// elements it then has to take back, and for a scratch store that starts
217    /// its ids over.
218    pub fn truncate(&mut self, len: usize) {
219        while self.len > len {
220            let (k, offset) = locate(self.len - 1);
221            self.chunks[k].truncate(offset);
222            self.len -= 1;
223        }
224    }
225
226    /// Returns `true` if the registry contains no elements.
227    pub fn is_empty(&self) -> bool {
228        self.len == 0
229    }
230
231    #[inline]
232    #[track_caller]
233    fn at(&self, n: usize) -> &T {
234        debug_assert!(
235            n < self.len,
236            "registry index {n} out of bounds for length {}",
237            self.len
238        );
239        let (k, offset) = locate(n);
240        &self.chunks[k][offset]
241    }
242
243    #[inline]
244    #[track_caller]
245    fn at_mut(&mut self, n: usize) -> &mut T {
246        debug_assert!(
247            n < self.len,
248            "registry index {n} out of bounds for length {}",
249            self.len
250        );
251        let (k, offset) = locate(n);
252        &mut self.chunks[k][offset]
253    }
254
255    /// Replaces the element at `id`, returning the previous value. The id (and
256    /// every element's stable address) is unchanged. Used to *check out* an
257    /// element — swap in a sentinel, own the original, swap it back later —
258    /// without disturbing any other id.
259    ///
260    /// # Panics
261    /// Panics if `id` is out of bounds.
262    #[track_caller]
263    pub fn replace(&mut self, id: Id, value: T) -> T {
264        std::mem::replace(self.at_mut(id.into()), value)
265    }
266
267    /// Returns an immutable identified view of an element.
268    ///
269    /// # Panics
270    /// Panics if `id` is out of bounds.
271    #[track_caller]
272    pub fn get(&self, id: Id) -> Identified<Id, &T> {
273        Identified::new(id, self.at(id.into()))
274    }
275
276    /// Returns a mutable identified view of an element.
277    ///
278    /// # Panics
279    /// Panics if `id` is out of bounds.
280    #[track_caller]
281    pub fn get_mut(&mut self, id: Id) -> Identified<Id, &mut T> {
282        Identified::new(id, self.at_mut(id.into()))
283    }
284
285    /// Iterates immutably over `(id, value)` as [`Identified`] items.
286    ///
287    /// # Example
288    /// ```
289    /// use jstd::Identifier;
290    /// use jstd::registry::Registry;
291    ///
292    /// #[derive(Identifier)]
293    /// struct Id(usize);
294    ///
295    /// let mut reg = Registry::<Id, &str>::default();
296    /// reg.push("x");
297    /// reg.push("y");
298    ///
299    /// let ids: Vec<usize> = reg.iter().map(|item| usize::from(item.id)).collect();
300    /// let vals: Vec<&str> = reg.iter().map(|item| **item).collect();
301    ///
302    /// assert_eq!(ids, vec![0, 1]);
303    /// assert_eq!(vals, vec!["x", "y"]);
304    /// ```
305    pub fn iter(&self) -> Iter<'_, Id, T> {
306        Iter {
307            iter: self.chunks.iter().flatten(),
308            index: 0,
309            _marker: PhantomData,
310        }
311    }
312
313    /// Iterates mutably over `(id, value)` as [`Identified`] items.
314    ///
315    /// # Example
316    /// ```
317    /// use jstd::Identifier;
318    /// use jstd::registry::Registry;
319    ///
320    /// #[derive(Identifier)]
321    /// struct Id(usize);
322    ///
323    /// let mut reg = Registry::<Id, i32>::default();
324    /// reg.push(1);
325    /// reg.push(2);
326    ///
327    /// for mut item in reg.iter_mut() {
328    ///     **item += 10;
329    /// }
330    ///
331    /// assert_eq!(reg[Id::from(0)], 11);
332    /// assert_eq!(reg[Id::from(1)], 12);
333    /// ```
334    pub fn iter_mut(&mut self) -> IterMut<'_, Id, T> {
335        IterMut {
336            iter: self.chunks.iter_mut().flatten(),
337            index: 0,
338            _marker: PhantomData,
339        }
340    }
341
342    /// Borrows the elements at `ids` mutably and disjointly, returned in the same
343    /// order as `ids`.
344    ///
345    /// This is the disjoint-`&mut`-slice primitive the parallel function-pass
346    /// driver uses to hand each worker its own body straight out of the registry,
347    /// without the checkout/checkin swap (context-split stage 5b-ii, see
348    /// `docs/plans/context-split/05b-plan.md` §2.2). Because every returned
349    /// reference comes from a *distinct* [`iter_mut`](Self::iter_mut) slot, the
350    /// borrows are provably non-overlapping and no `unsafe` is required.
351    ///
352    /// `ids` must be **distinct** and in bounds; the returned vector has one
353    /// reference per requested id, positionally aligned with `ids`.
354    ///
355    /// # Panics
356    /// Panics if `ids` contains a duplicate id or an out-of-bounds id.
357    pub fn select_mut(&mut self, ids: &[Id]) -> Vec<&mut T> {
358        // Map each requested global index to its position in `ids`, asserting
359        // distinctness and bounds up front so a caller bug is a loud panic, never
360        // a silently-shortened result.
361        let mut want: std::collections::HashMap<usize, usize> =
362            std::collections::HashMap::with_capacity(ids.len());
363        for (pos, id) in ids.iter().enumerate() {
364            let n: usize = (*id).into();
365            assert!(
366                n < self.len,
367                "select_mut: id index {n} out of bounds (len {})",
368                self.len
369            );
370            let prev = want.insert(n, pos);
371            assert!(prev.is_none(), "select_mut: duplicate id index {n}");
372        }
373
374        // One `iter_mut` pass: each disjoint `&mut T` is routed to its requested
375        // output slot. `iter_mut` yields ids in ascending global order; `want`
376        // restores the caller's order.
377        let mut slots: Vec<Option<&mut T>> = (0..ids.len()).map(|_| None).collect();
378        for item in self.iter_mut() {
379            let n: usize = item.id.into();
380            if let Some(&pos) = want.get(&n) {
381                slots[pos] = Some(item.inner);
382            }
383        }
384        slots
385            .into_iter()
386            .map(|slot| slot.expect("select_mut: requested id had no backing slot"))
387            .collect()
388    }
389}
390
391pub struct Iter<'a, Id: Identifier, T> {
392    iter: std::iter::Flatten<slice::Iter<'a, Vec<T>>>,
393    index: usize,
394    _marker: PhantomData<Id>,
395}
396
397impl<'a, Id: Identifier, T> Iterator for Iter<'a, Id, T> {
398    type Item = Identified<Id, &'a T>;
399
400    fn next(&mut self) -> Option<Self::Item> {
401        let value = self.iter.next()?;
402        let id = Id::from(self.index);
403        self.index += 1;
404
405        Some(Identified::new(id, value))
406    }
407}
408
409pub struct IterMut<'a, Id: Identifier, T> {
410    iter: std::iter::Flatten<slice::IterMut<'a, Vec<T>>>,
411    index: usize,
412    _marker: PhantomData<Id>,
413}
414
415impl<'a, Id: Identifier, T> Iterator for IterMut<'a, Id, T> {
416    type Item = Identified<Id, &'a mut T>;
417
418    fn next(&mut self) -> Option<Self::Item> {
419        let value = self.iter.next()?;
420        let id = Id::from(self.index);
421        self.index += 1;
422
423        Some(Identified::new(id, value))
424    }
425}
426
427pub struct IntoIter<Id: Identifier, T> {
428    iter: std::iter::Flatten<vec::IntoIter<Vec<T>>>,
429    index: usize,
430    _marker: PhantomData<Id>,
431}
432
433impl<Id: Identifier, T> Iterator for IntoIter<Id, T> {
434    type Item = Identified<Id, T>;
435
436    fn next(&mut self) -> Option<Self::Item> {
437        let value = self.iter.next()?;
438        let id = Id::from(self.index);
439        self.index += 1;
440
441        Some(Identified::new(id, value))
442    }
443}
444
445impl<Id: Identifier, T> Default for Registry<Id, T> {
446    fn default() -> Self {
447        Self {
448            chunks: Vec::new(),
449            len: 0,
450            _marker: PhantomData,
451        }
452    }
453}
454
455impl<'a, Id: Identifier, T> IntoIterator for &'a Registry<Id, T> {
456    type Item = Identified<Id, &'a T>;
457
458    type IntoIter = Iter<'a, Id, T>;
459
460    fn into_iter(self) -> Self::IntoIter {
461        self.iter()
462    }
463}
464
465impl<'a, Id: Identifier, T> IntoIterator for &'a mut Registry<Id, T> {
466    type Item = Identified<Id, &'a mut T>;
467
468    type IntoIter = IterMut<'a, Id, T>;
469
470    fn into_iter(self) -> Self::IntoIter {
471        self.iter_mut()
472    }
473}
474
475impl<Id: Identifier, T> IntoIterator for Registry<Id, T> {
476    type Item = Identified<Id, T>;
477
478    type IntoIter = IntoIter<Id, T>;
479
480    fn into_iter(self) -> Self::IntoIter {
481        IntoIter {
482            iter: self.chunks.into_iter().flatten(),
483            index: 0,
484            _marker: PhantomData,
485        }
486    }
487}
488
489impl<Id: Identifier, T> FromIterator<T> for Registry<Id, T> {
490    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
491        let mut reg = Self::default();
492        for e in iter {
493            reg.push(e);
494        }
495        reg
496    }
497}
498
499impl<Id: Identifier, T> Index<Id> for Registry<Id, T> {
500    type Output = T;
501
502    #[track_caller]
503    fn index(&self, index: Id) -> &Self::Output {
504        self.at(index.into())
505    }
506}
507
508impl<Id: Identifier, T> IndexMut<Id> for Registry<Id, T> {
509    #[track_caller]
510    fn index_mut(&mut self, index: Id) -> &mut Self::Output {
511        self.at_mut(index.into())
512    }
513}
514
515impl<Id: Identifier, T: Intern> Intern for Registry<Id, T> {
516    type Static = Registry<Id, T::Static>;
517
518    fn intern(self, pool: &mut super::intern::StringPool) -> Self::Static {
519        self.into_iter()
520            .map(|item| item.inner.intern(pool))
521            .collect()
522    }
523}
524
525#[cfg(test)]
526mod tests {
527    use super::*;
528    use jstd_derive::Identifier;
529
530    #[derive(Identifier)]
531    struct Id(usize);
532
533    /// Segment math places element `n` in chunk `floor(log2(n+1))`.
534    #[test]
535    fn truncate_keeps_earlier_ids_and_chunk_capacity() {
536        let mut registry: Registry<Id, u32> = (0..7).collect();
537        let capacity: Vec<usize> = registry.chunks.iter().map(Vec::capacity).collect();
538
539        registry.truncate(2);
540        assert_eq!(registry.len(), 2);
541        assert_eq!(registry[Id(1)], 1);
542        assert_eq!(registry.chunks[0].capacity(), capacity[0]);
543        assert_eq!(registry.chunks[1].capacity(), capacity[1]);
544        assert!(registry.chunks[2].is_empty());
545
546        assert_eq!(registry.push(9), Id(2));
547        assert_eq!(registry.push(10), Id(3));
548        assert_eq!(registry.chunks[2].capacity(), capacity[2]);
549
550        registry.truncate(0);
551        assert!(registry.is_empty());
552        registry.truncate(5);
553        assert!(registry.is_empty());
554    }
555
556    /// Pushing after a truncate refills the chunks that are already there; it
557    /// does not append a fresh chunk every time the registry starts over,
558    /// which would retain one more set of capacities per epoch.
559    #[test]
560    fn pushing_after_truncate_reuses_the_emptied_chunks() {
561        let mut registry: Registry<Id, u32> = (0..7).collect();
562        let chunks = registry.chunks.len();
563        let capacity: Vec<usize> = registry.chunks.iter().map(Vec::capacity).collect();
564        for epoch in 0..100 {
565            registry.truncate(0);
566            for i in 0..7 {
567                assert_eq!(registry.push(i + epoch), Id(i as usize));
568            }
569            assert_eq!(registry.chunks.len(), chunks, "epoch {epoch}");
570            let now: Vec<usize> = registry.chunks.iter().map(Vec::capacity).collect();
571            assert_eq!(now, capacity, "epoch {epoch}");
572            assert_eq!(registry[Id(6)], 6 + epoch);
573        }
574        // Growing past the old length still appends the next chunk.
575        for i in 7..15 {
576            registry.push(i);
577        }
578        assert_eq!(registry.chunks.len(), chunks + 1);
579        assert_eq!(registry[Id(14)], 14);
580    }
581
582    #[test]
583    fn locate_matches_doubling_layout() {
584        assert_eq!(locate(0), (0, 0)); // chunk 0 (size 1)
585        assert_eq!(locate(1), (1, 0)); // chunk 1 (size 2)
586        assert_eq!(locate(2), (1, 1));
587        assert_eq!(locate(3), (2, 0)); // chunk 2 (size 4)
588        assert_eq!(locate(6), (2, 3));
589        assert_eq!(locate(7), (3, 0)); // chunk 3 (size 8)
590    }
591
592    /// Dense ids, `len`, and index order survive spanning many chunks.
593    #[test]
594    fn push_index_iter_across_chunks() {
595        let mut reg = Registry::<Id, usize>::default();
596        let ids: Vec<Id> = (0..1000).map(|v| reg.push(v)).collect();
597        assert_eq!(reg.len(), 1000);
598        for (i, &id) in ids.iter().enumerate() {
599            assert_eq!(usize::from(id), i);
600            assert_eq!(reg[id], i);
601        }
602        let seen: Vec<usize> = reg.iter().map(|item| *item.inner).collect();
603        assert_eq!(seen, (0..1000).collect::<Vec<_>>());
604    }
605
606    /// The core invariant: an element's address is stable across later pushes
607    /// (later pushes only append new chunks; existing chunks never reallocate).
608    #[test]
609    fn element_address_is_stable_across_pushes() {
610        let mut reg = Registry::<Id, usize>::default();
611        let first = reg.push(42);
612        let addr = &reg[first] as *const usize;
613        for v in 0..10_000 {
614            reg.push(v);
615        }
616        assert_eq!(
617            &reg[first] as *const usize, addr,
618            "address moved after growth"
619        );
620        assert_eq!(reg[first], 42);
621    }
622
623    /// `replace` swaps contents in place, returning the old value and leaving the
624    /// slot's address (and all other ids) untouched.
625    #[test]
626    fn replace_swaps_in_place() {
627        let mut reg = Registry::<Id, i32>::default();
628        let a = reg.push(1);
629        let b = reg.push(2);
630        let addr_b = &reg[b] as *const i32;
631        let old = reg.replace(b, 99);
632        assert_eq!(old, 2);
633        assert_eq!(reg[b], 99);
634        assert_eq!(reg[a], 1, "other ids untouched");
635        assert_eq!(&reg[b] as *const i32, addr_b, "slot address stable");
636        assert_eq!(reg.len(), 2, "len unchanged");
637    }
638
639    #[test]
640    #[cfg(debug_assertions)]
641    #[should_panic(expected = "registry index 0 out of bounds for length 0")]
642    fn indexing_reports_requested_index_and_length() {
643        let reg = Registry::<Id, usize>::default();
644        let _ = reg[Id::from(0)];
645    }
646
647    /// `select_mut` returns disjoint mutable borrows in the caller's id order
648    /// (not ascending id order), spanning multiple chunks, and lets each be
649    /// written independently.
650    #[test]
651    fn select_mut_disjoint_in_input_order() {
652        let mut reg = Registry::<Id, usize>::default();
653        let ids: Vec<Id> = (0..100).map(|v| reg.push(v)).collect();
654
655        // Deliberately out of order and spanning chunk boundaries.
656        let picked = [ids[7], ids[0], ids[63], ids[64], ids[2]];
657        let refs = reg.select_mut(&picked);
658        assert_eq!(refs.len(), picked.len());
659        // Order matches `picked`, not ascending id order.
660        assert_eq!(
661            refs.iter().map(|r| **r).collect::<Vec<_>>(),
662            vec![7, 0, 63, 64, 2]
663        );
664        // Disjoint: mutate every borrow, then observe all writes landed.
665        for r in refs {
666            *r += 1000;
667        }
668        for &id in &picked {
669            assert_eq!(reg[id], usize::from(id) + 1000);
670        }
671        // Untouched ids are unchanged.
672        assert_eq!(reg[ids[1]], 1);
673    }
674
675    /// A duplicate id in the request is a loud panic (would otherwise alias).
676    #[test]
677    #[should_panic(expected = "duplicate id")]
678    fn select_mut_rejects_duplicates() {
679        let mut reg = Registry::<Id, usize>::default();
680        let a = reg.push(1);
681        reg.push(2);
682        let _ = reg.select_mut(&[a, a]);
683    }
684
685    /// Rebuilding from a flat element sequence (the path `Deserialize` and
686    /// `Clone` take through `FromIterator`) reproduces a chunked registry equal to
687    /// the original, spanning several chunks.
688    #[test]
689    fn rebuild_from_flat_sequence() {
690        let reg: Registry<Id, i32> = (0..300).collect();
691        let flat: Vec<i32> = reg.iter().map(|item| *item.inner).collect();
692        let back: Registry<Id, i32> = flat.into_iter().collect();
693        assert_eq!(reg, back);
694        assert_eq!(back.len(), 300);
695    }
696
697    /// Clone preserves contents and the never-realloc stability invariant: a
698    /// clone's last (partial) chunk must still absorb a push without moving its
699    /// existing elements.
700    #[test]
701    fn clone_preserves_stability() {
702        let mut reg = Registry::<Id, usize>::default();
703        for v in 0..5 {
704            reg.push(v); // ends mid-chunk (chunk 2 holds indices 3,4 of capacity 4)
705        }
706        let mut cloned = reg.clone();
707        assert_eq!(reg, cloned);
708        let last = Id::from(4);
709        let addr = &cloned[last] as *const usize;
710        cloned.push(99); // fills the partial chunk without reallocating it
711        assert_eq!(
712            &cloned[last] as *const usize, addr,
713            "clone's chunk reallocated"
714        );
715    }
716
717    #[test]
718    fn public_views_iterators_and_serialization_preserve_values() {
719        let mut reg = Registry::<Id, i32>::default();
720        assert!(reg.is_empty());
721        let first = reg.push(10);
722        let second = reg.push(20);
723        assert_eq!(reg.get(first).id, first);
724        assert_eq!(**reg.get(second), 20);
725
726        let mut mutable = reg.get_mut(first);
727        **mutable += 5;
728        let immutable = mutable.immutable();
729        assert_eq!(immutable.id, first);
730        assert_eq!(**immutable, 15);
731        assert_eq!(format!("{immutable}"), "15");
732        assert_eq!(format!("{immutable:?}"), "15");
733
734        let mut borrowed = (&reg).into_iter();
735        assert_eq!(borrowed.size_hint(), (0, None));
736        assert_eq!(borrowed.next().unwrap().id, first);
737        assert_eq!(borrowed.next().unwrap().id, second);
738        assert!(borrowed.next().is_none());
739
740        let owned: Vec<_> = reg.clone().into_iter().map(|entry| entry.inner).collect();
741        assert_eq!(owned, [15, 20]);
742        assert_eq!(format!("{reg:?}"), "[15, 20]");
743
744        let bytes = bincode::serde::encode_to_vec(&reg, bincode::config::standard()).unwrap();
745        let (decoded, used): (Registry<Id, i32>, _) =
746            bincode::serde::decode_from_slice(&bytes, bincode::config::standard()).unwrap();
747        assert_eq!(used, bytes.len());
748        assert_eq!(decoded, reg);
749    }
750}