Skip to main content

vj_sim/
collections.rs

1//! SV queues, dynamic arrays and associative arrays (LRM 1800-2023 §7.5,
2//! §7.8, §7.10): variable-size collections held outside STATE.
3//!
4//! A collection is stored -- in STATE, a frame slot, or a register -- as a
5//! 64-bit handle: an entry index in the low 32 bits and the entry's version
6//! in the high 32. Handle 0 is an empty collection with no entry. Every
7//! operation that changes a collection returns its new handle, which the
8//! caller stores back: the version changes with each change, so the store
9//! is a value change other processes see (`wait (q.size() != 0)`).
10//!
11//! An entry's contents are shared copy-on-write: [`clone`] (an
12//! assignment `b = a`) makes a new entry sharing the data, and the first
13//! change to either copies it. [`release`] frees an entry whose handle
14//! is being overwritten.
15//!
16//! An element is the stored bits of its value -- codegen's packed
17//! `(unk << W) | val` form, as `elem_words` little-endian words -- so the
18//! store is independent of element types. Associative array keys are
19//! integers of up to 127 bits (ordered as signed or unsigned numbers), wider
20//! integers (unsigned), or strings (ordered lexicographically, §7.8.1).
21
22use std::collections::{BTreeMap, VecDeque};
23use std::ops::Bound;
24use std::sync::{Arc, Mutex, OnceLock};
25
26/// An associative array key.
27#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
28pub enum Key {
29    Int(i128),
30    /// Wider than 127 bits: the value's words, most significant first.
31    Big(Vec<u64>),
32    Str(Vec<u8>),
33}
34
35#[derive(Clone, Debug)]
36enum Data {
37    /// A queue or dynamic array: `len * stride` words, element 0 first.
38    Seq { stride: usize, words: VecDeque<u64> },
39    /// An associative array, and the value a missing key reads when it has
40    /// a `default:` (LRM 1800-2023 §7.9.11).
41    Map(BTreeMap<Key, Box<[u64]>>, Option<Box<[u64]>>),
42}
43
44impl Data {
45    fn len(&self) -> usize {
46        match self {
47            Data::Seq { stride, words } => words.len() / (*stride).max(1),
48            Data::Map(m, _) => m.len(),
49        }
50    }
51}
52
53/// A collection's contents, and -- when its elements are class handles
54/// (`objs`; the handle is word 0 of each element) -- the references it holds
55/// on those objects (LRM 1800-2023 §8.29: an object lives while any handle
56/// names it, and a collection element is a handle).
57///
58/// Every handle element owns one `objects` reference: cloning the contents
59/// (copy-on-write's unshare, a frozen constant's first change) retains each
60/// in the copy, and dropping them releases each. An operation that adds an
61/// element TAKES the caller's reference (the caller passes an owned value);
62/// one that removes an element releases it, except [`pop`], which hands it to
63/// its caller.
64///
65/// An associative array indexed by a class handle ([`KeyKind::Obj`],
66/// `key_objs`) owns a reference on each KEY object the same way: a key
67/// inserted by [`store`] is retained (the caller keeps its own reference; the
68/// key it passes is borrowed), and removing the key releases it.
69struct Held {
70    data: Data,
71    objs: bool,
72    /// Class handles inside each element that is not itself a handle (a struct
73    /// or fixed array of handles, [`Nested`]); each owns one reference too.
74    nested: Nested,
75    key_objs: bool,
76}
77
78/// The most class handles one collection element may hold inside a struct or
79/// fixed array (LRM 1800-2023 7.2, 7.4) before the collection stops owning
80/// them (codegen then retains on insert and never releases: a leak, never a
81/// premature free).
82pub const MAX_NESTED_OBJS: usize = 8;
83
84/// Where the class handles sit inside one collection element that is a struct
85/// or fixed-size array holding them (`pair_t pq[$]` with `typedef C pair_t[2]`,
86/// LRM 1800-2023 10.10): the value-bit offset of each 64-bit handle in the
87/// element's words. The collection owns one object reference per handle.
88#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89pub struct Nested {
90    pub n: u8,
91    pub at: [u32; MAX_NESTED_OBJS],
92}
93
94impl Nested {
95    /// No handle inside the elements.
96    pub const NONE: Nested = Nested {
97        n: 0,
98        at: [0; MAX_NESTED_OBJS],
99    };
100
101    fn slots(&self) -> &[u32] {
102        &self.at[..usize::from(self.n)]
103    }
104}
105
106/// Calls `f` with every class handle held by the element `elem`: its word 0
107/// when `objs`, and the handle at each [`Nested`] offset.
108fn elem_handles(objs: bool, nested: &Nested, elem: &[u64], mut f: impl FnMut(u64)) {
109    if objs && let Some(&w) = elem.first() {
110        f(w);
111    }
112    for &off in nested.slots() {
113        let h = bits64(elem, off as usize);
114        if h != 0 {
115            f(h);
116        }
117    }
118}
119
120impl Held {
121    /// Calls `f` with the handle word of every element, and (when
122    /// `with_default`) a map's `default:` value.
123    fn each_handle(&self, with_default: bool, mut f: impl FnMut(u64)) {
124        if !self.objs && self.nested.n == 0 {
125            return;
126        }
127        let (objs, nested) = (self.objs, self.nested);
128        match &self.data {
129            Data::Seq { stride, words } if nested.n == 0 => {
130                words.iter().step_by((*stride).max(1)).for_each(|&w| f(w))
131            }
132            Data::Seq { stride, words } => {
133                let stride = (*stride).max(1);
134                let mut buf = Vec::with_capacity(stride);
135                let mut it = words.iter().copied();
136                loop {
137                    buf.clear();
138                    buf.extend(it.by_ref().take(stride));
139                    if buf.len() < stride {
140                        break;
141                    }
142                    elem_handles(objs, &nested, &buf, &mut f);
143                }
144            }
145            Data::Map(m, dflt) => {
146                for v in m.values().chain(dflt.iter().filter(|_| with_default)) {
147                    elem_handles(objs, &nested, v, &mut f);
148                }
149            }
150        }
151    }
152
153    /// Calls `f` with every key of a class-handle-keyed map.
154    fn each_key(&self, mut f: impl FnMut(u64)) {
155        if !self.key_objs {
156            return;
157        }
158        if let Data::Map(m, _) = &self.data {
159            for k in m.keys() {
160                if let Key::Int(v) = k {
161                    f(*v as u64);
162                }
163            }
164        }
165    }
166
167    /// Releases every element's reference (before the elements are removed
168    /// wholesale; a map's `default:` stays, with its reference).
169    fn release_elements(&self) {
170        self.each_handle(false, crate::objects::release);
171    }
172}
173
174impl Clone for Held {
175    fn clone(&self) -> Held {
176        self.each_handle(true, crate::objects::retain);
177        self.each_key(crate::objects::retain);
178        Held {
179            data: self.data.clone(),
180            objs: self.objs,
181            nested: self.nested,
182            key_objs: self.key_objs,
183        }
184    }
185}
186
187impl Drop for Held {
188    fn drop(&mut self) {
189        self.each_handle(true, crate::objects::release);
190        self.each_key(crate::objects::release);
191    }
192}
193
194struct Entry {
195    version: u32,
196    data: Arc<Held>,
197    /// The key at `pos` of the last [`key_at`], for sequential walks.
198    cursor: Option<(usize, Key)>,
199    /// A constant (a declaration's initial value, shared by every instance
200    /// of its module): never changed in place -- a change makes a new
201    /// entry -- nor freed.
202    frozen: bool,
203}
204
205#[derive(Default)]
206struct Table {
207    /// Index 0 is unused (handle 0 is the empty collection).
208    entries: Vec<Option<Entry>>,
209    /// Each slot's last version, so a reused slot never repeats a handle.
210    versions: Vec<u32>,
211    free: Vec<u32>,
212    /// The element references open right now ([`ref_open`]); slot `k` is id
213    /// `k + 1`. Empty (and every hook below a no-op) unless a design passes
214    /// a queue or dynamic-array element by `ref`.
215    refs: Vec<Option<ElemRef>>,
216    free_refs: Vec<u32>,
217    n_refs: usize,
218}
219
220/// An open `ref` to element `index` of the collection in entry `entry`
221/// (LRM 1800-2023 13.5.2, 7.10.3). The structural operations keep `index`
222/// naming the same element; an operation that removes the element or the
223/// whole contents makes the reference outdated (`live == false`): the record
224/// stays until [`ref_close`] so its id is never reused under the callee.
225#[derive(Clone)]
226struct ElemRef {
227    entry: usize,
228    index: i64,
229    /// An associative array's element is named by its key (LRM 7.8); the
230    /// element need not exist yet (a reference to an absent key reads the
231    /// array's default and its first write creates the element).
232    key: Option<Key>,
233    live: bool,
234}
235
236fn table() -> &'static Mutex<Table> {
237    static T: OnceLock<Mutex<Table>> = OnceLock::new();
238    T.get_or_init(|| {
239        Mutex::new(Table {
240            entries: vec![None],
241            versions: vec![0],
242            free: Vec::new(),
243            refs: Vec::new(),
244            free_refs: Vec::new(),
245            n_refs: 0,
246        })
247    })
248}
249
250fn index(h: u64) -> usize {
251    (h & 0xffff_ffff) as usize
252}
253
254impl Table {
255    fn held(&self, h: u64) -> Option<&Held> {
256        self.entries.get(index(h))?.as_ref().map(|e| &*e.data)
257    }
258
259    fn data(&self, h: u64) -> Option<&Data> {
260        self.held(h).map(|e| &e.data)
261    }
262
263    fn alloc(&mut self, data: Held) -> u64 {
264        let i = match self.free.pop() {
265            Some(i) => i as usize,
266            None => {
267                self.entries.push(None);
268                self.versions.push(0);
269                self.entries.len() - 1
270            }
271        };
272        let version = self.versions[i].wrapping_add(1);
273        self.versions[i] = version;
274        self.entries[i] = Some(Entry {
275            version,
276            data: Arc::new(data),
277            cursor: None,
278            frozen: false,
279        });
280        (u64::from(version) << 32) | i as u64
281    }
282
283    /// Frees `h`'s entry -- only if `h` is that entry's *current* handle: a
284    /// stale handle (the slot since freed and reused, or the entry changed
285    /// in place) must not free the slot's new owner.
286    fn release(&mut self, h: u64) {
287        let i = index(h);
288        if i != 0
289            && self.entries.get(i).is_some_and(|e| {
290                e.as_ref()
291                    .is_some_and(|e| !e.frozen && u64::from(e.version) == h >> 32)
292            })
293        {
294            self.refs_outdated(i, 0);
295            self.entries[i] = None;
296            self.free.push(i as u32);
297        }
298    }
299
300    /// Every open reference into entry `entry` whose element is at or after
301    /// `from` is outdated (the elements were removed or the entry freed).
302    fn refs_outdated(&mut self, entry: usize, from: i64) {
303        if self.n_refs == 0 {
304            return;
305        }
306        for r in self.refs.iter_mut().flatten() {
307            if r.live && r.entry == entry && r.index >= from {
308                r.live = false;
309            }
310        }
311    }
312
313    /// Key `key` of the associative array `entry` was deleted: references to
314    /// that element are outdated (LRM 1800-2023 13.5.2); inserting other keys
315    /// never outdates one.
316    fn refs_key_removed(&mut self, entry: usize, key: &Key) {
317        if self.n_refs == 0 {
318            return;
319        }
320        for r in self.refs.iter_mut().flatten() {
321            if r.live && r.entry == entry && r.key.as_ref() == Some(key) {
322                r.live = false;
323            }
324        }
325    }
326
327    /// Element `i` of `entry` was removed: its references are outdated, those
328    /// of later elements move down one (LRM 1800-2023 7.10.3).
329    fn refs_removed(&mut self, entry: usize, i: i64) {
330        if self.n_refs == 0 {
331            return;
332        }
333        for rr in self.refs.iter_mut().flatten() {
334            if !rr.live || rr.entry != entry || rr.index < i {
335                continue;
336            }
337            if rr.index == i {
338                rr.live = false;
339            } else {
340                rr.index -= 1;
341            }
342        }
343    }
344
345    /// An element was inserted before position `i` of `entry`: later
346    /// elements' references follow them up one (an insert never outdates).
347    fn refs_inserted(&mut self, entry: usize, i: i64) {
348        if self.n_refs == 0 {
349            return;
350        }
351        for r in self.refs.iter_mut().flatten() {
352            if r.live && r.entry == entry && r.index >= i {
353                r.index += 1;
354            }
355        }
356    }
357
358    /// The contents of entry `from` moved to entry `to` (a constant's first
359    /// change makes a new entry): its references follow.
360    fn refs_moved(&mut self, from: usize, to: usize) {
361        if self.n_refs == 0 {
362            return;
363        }
364        for r in self.refs.iter_mut().flatten() {
365            if r.live && r.entry == from {
366                r.entry = to;
367            }
368        }
369    }
370
371    /// Changes `h`'s data with `f` (allocating an entry for the empty
372    /// handle, `empty` giving its initial data) and returns its new handle.
373    fn modify(&mut self, h: u64, empty: impl FnOnce() -> Held, f: impl FnOnce(&mut Held)) -> u64 {
374        let i = index(h);
375        match self.entries.get_mut(i).and_then(|e| e.as_mut()) {
376            Some(e) if e.frozen => {
377                let mut d = (*e.data).clone();
378                f(&mut d);
379                let new = self.alloc(d);
380                self.refs_moved(i, index(new));
381                new
382            }
383            Some(e) if i != 0 => {
384                f(Arc::make_mut(&mut e.data));
385                e.version = e.version.wrapping_add(1);
386                e.cursor = None;
387                self.versions[i] = e.version;
388                (u64::from(e.version) << 32) | i as u64
389            }
390            _ => {
391                let mut d = empty();
392                f(&mut d);
393                self.alloc(d)
394            }
395        }
396    }
397}
398
399/// What an operation works on: element size, and for an associative
400/// array, its key type.
401#[derive(Clone, Copy, Debug)]
402pub struct Shape {
403    pub elem_words: usize,
404    pub assoc: bool,
405    pub key: KeyKind,
406    /// The elements are class handles (word 0 of each): the collection owns
407    /// a reference on each object (see [`Held`]).
408    pub obj: bool,
409    /// Class handles inside each element that is not itself a handle (a
410    /// struct or fixed array of handles): the collection owns a reference on
411    /// each, exactly as for `obj` ([`Nested`]).
412    pub nested: Nested,
413}
414
415/// An associative array's key type.
416#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
417pub enum KeyKind {
418    /// An integral key of `width` bits.
419    Int {
420        width: u32,
421        signed: bool,
422    },
423    Str,
424    /// A class handle (LRM 1800-2023 §7.8.3, §8.29): compared as a 64-bit
425    /// unsigned number; the array owns a reference on each key's object.
426    Obj,
427}
428
429impl Shape {
430    fn empty(&self) -> Held {
431        self.held(if self.assoc {
432            Data::Map(BTreeMap::new(), None)
433        } else {
434            Data::Seq {
435                stride: self.elem_words.max(1),
436                words: VecDeque::new(),
437            }
438        })
439    }
440
441    fn held(&self, data: Data) -> Held {
442        Held {
443            data,
444            objs: self.obj,
445            nested: self.nested,
446            key_objs: self.key == KeyKind::Obj,
447        }
448    }
449
450    /// Gives up the reference an operation that did not store `val` was
451    /// handed on its element (the operation was a no-op: an out-of-range
452    /// write, an invalid key).
453    pub fn discard(&self, val: &[u64]) {
454        elem_handles(self.obj, &self.nested, val, crate::objects::release);
455    }
456
457    /// The words of a packed key value (`(unk << width) | val`): `None` if
458    /// any bit is unknown (such a key is invalid, §7.8.6).
459    pub fn key_from_bits(&self, bits: &[u64]) -> Option<Key> {
460        match self.key {
461            KeyKind::Str => Some(Key::Str(crate::strings::bytes(bits[0]).to_vec())),
462            KeyKind::Int { .. } | KeyKind::Obj => {
463                let (width, signed) = if let KeyKind::Int { width, signed } = self.key {
464                    (width, signed)
465                } else {
466                    // An object handle key: a 64-bit unsigned value.
467                    (64, false)
468                };
469                let w = width as usize;
470                let bit = |i: usize| (bits.get(i / 64).copied().unwrap_or(0) >> (i % 64)) & 1;
471                if (w..2 * w).any(|i| bit(i) != 0) {
472                    return None;
473                }
474                if w <= 127 {
475                    let mut v: i128 = 0;
476                    for i in 0..w {
477                        v |= (bit(i) as i128) << i;
478                    }
479                    if signed && w > 0 && bit(w - 1) == 1 {
480                        v -= 1i128 << w;
481                    }
482                    Some(Key::Int(v))
483                } else {
484                    let n = w.div_ceil(64);
485                    let mut words = vec![0u64; n];
486                    for i in 0..w {
487                        words[i / 64] |= bit(i) << (i % 64);
488                    }
489                    words.reverse();
490                    Some(Key::Big(words))
491                }
492            }
493        }
494    }
495
496    /// `key` as packed bits into `out` (a string key as its interned id).
497    pub fn key_to_bits(&self, key: &Key, out: &mut [u64]) {
498        out.fill(0);
499        match key {
500            Key::Str(s) => out[0] = crate::strings::intern(s),
501            Key::Int(v) => {
502                let width = match self.key {
503                    KeyKind::Int { width, .. } => width,
504                    KeyKind::Obj => 64,
505                    KeyKind::Str => return,
506                };
507                for i in 0..width as usize {
508                    if (v >> i.min(127)) & 1 == 1 {
509                        out[i / 64] |= 1 << (i % 64);
510                    }
511                }
512            }
513            Key::Big(words) => {
514                for (i, w) in words.iter().rev().enumerate() {
515                    if let Some(o) = out.get_mut(i) {
516                        *o = *w;
517                    }
518                }
519            }
520        }
521    }
522}
523
524/// The number of elements.
525pub fn size(h: u64) -> usize {
526    let t = table().lock().unwrap_or_else(|e| e.into_inner());
527    t.data(h).map_or(0, |d| d.len())
528}
529
530/// Element `i` of a queue or dynamic array into `out`: `false` (leaving
531/// `out` alone) when out of range.
532pub fn get(h: u64, i: i64, out: &mut [u64]) -> bool {
533    let t = table().lock().unwrap_or_else(|e| e.into_inner());
534    let Some(Data::Seq { stride, words }) = t.data(h) else {
535        return false;
536    };
537    let n = words.len() / stride;
538    if i < 0 || i as usize >= n {
539        return false;
540    }
541    let base = i as usize * stride;
542    for (k, o) in out.iter_mut().enumerate().take(*stride) {
543        *o = words[base + k];
544    }
545    true
546}
547
548/// The value at `key` of an associative array into `out`: `false` when
549/// absent.
550pub fn lookup(h: u64, key: &Key, out: &mut [u64]) -> bool {
551    let t = table().lock().unwrap_or_else(|e| e.into_inner());
552    let Some(Data::Map(m, dflt)) = t.data(h) else {
553        return false;
554    };
555    match m.get(key).or(dflt.as_ref()) {
556        Some(v) => {
557            let n = out.len().min(v.len());
558            out[..n].copy_from_slice(&v[..n]);
559            true
560        }
561        None => false,
562    }
563}
564
565/// Whether an associative array has `key`.
566pub fn exists(h: u64, key: &Key) -> bool {
567    let t = table().lock().unwrap_or_else(|e| e.into_inner());
568    matches!(t.data(h), Some(Data::Map(m, _)) if m.contains_key(key))
569}
570
571/// Writes element `i` of a queue or dynamic array; `i == size` of a queue
572/// appends (`q[$+1] = v`, §7.10.1). Any other out-of-range write is
573/// ignored and returns `h` unchanged.
574///
575/// Object elements: the collection takes `val`'s reference and releases the
576/// element it overwrites; an ignored write releases `val`.
577pub fn set(shape: &Shape, h: u64, i: i64, val: &[u64], queue: bool) -> u64 {
578    let n = size(h) as i64;
579    if i < 0 || i > n || (i == n && !queue) {
580        shape.discard(val);
581        return h;
582    }
583    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
584    t.modify(
585        h,
586        || shape.empty(),
587        |d| {
588            let Held {
589                data, objs, nested, ..
590            } = d;
591            if let Data::Seq { stride, words } = data {
592                let base = i as usize * *stride;
593                if base == words.len() {
594                    words.extend(val.iter().copied().take(*stride));
595                    words.resize(base + *stride, 0);
596                } else {
597                    if *objs || nested.n > 0 {
598                        let old: Vec<u64> = words.range(base..base + *stride).copied().collect();
599                        elem_handles(*objs, nested, &old, crate::objects::release);
600                    }
601                    for k in 0..*stride {
602                        words[base + k] = val.get(k).copied().unwrap_or(0);
603                    }
604                }
605            }
606        },
607    )
608}
609
610/// Writes (or creates) `key` of an associative array (object values: the
611/// collection takes `val`'s reference and releases the value it replaces).
612pub fn store(shape: &Shape, h: u64, key: Key, val: &[u64]) -> u64 {
613    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
614    let v: Box<[u64]> = val[..shape.elem_words.min(val.len())].into();
615    t.modify(
616        h,
617        || shape.empty(),
618        |d| {
619            let Held {
620                data,
621                objs,
622                nested,
623                key_objs,
624            } = d;
625            if let Data::Map(m, _) = data {
626                // A new key of a handle-keyed map takes its own reference.
627                if *key_objs
628                    && !m.contains_key(&key)
629                    && let Key::Int(k) = &key
630                {
631                    crate::objects::retain(*k as u64);
632                }
633                if let Some(old) = m.insert(key, v) {
634                    elem_handles(*objs, nested, &old, crate::objects::release);
635                }
636            }
637        },
638    )
639}
640
641/// Inserts `val` before element `i` (`i == size` appends); out-of-range
642/// positions are ignored (§7.10.2.2; object elements: the collection takes
643/// `val`'s reference, or releases it when ignored).
644pub fn insert(shape: &Shape, h: u64, i: i64, val: &[u64]) -> u64 {
645    let n = size(h) as i64;
646    if i < 0 || i > n {
647        shape.discard(val);
648        return h;
649    }
650    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
651    let new = t.modify(
652        h,
653        || shape.empty(),
654        |d| {
655            if let Data::Seq { stride, words } = &mut d.data {
656                let at = i as usize * *stride;
657                for k in (0..*stride).rev() {
658                    words.insert(at, val.get(k).copied().unwrap_or(0));
659                }
660            }
661        },
662    );
663    t.refs_inserted(index(new), i);
664    new
665}
666
667/// `push_front` / `push_back`.
668pub fn push(shape: &Shape, h: u64, val: &[u64], front: bool) -> u64 {
669    if front {
670        insert(shape, h, 0, val)
671    } else {
672        insert(shape, h, size(h) as i64, val)
673    }
674}
675
676/// `pop_front` / `pop_back`: the element into `out` (untouched when empty)
677/// and the new handle. An object element's reference moves to the caller
678/// (in `out`) rather than being released.
679pub fn pop(shape: &Shape, h: u64, out: &mut [u64], front: bool) -> (u64, bool) {
680    let n = size(h);
681    if n == 0 {
682        return (h, false);
683    }
684    let i = if front { 0 } else { n - 1 };
685    get(h, i as i64, out);
686    (remove_at(shape, h, i as i64, false), true)
687}
688
689/// Drops the elements of a queue beyond its first `keep` (a bounded queue's
690/// out-of-bounds elements, LRM 1800-2023 §7.10.5), warning when any go; an
691/// object element is released.
692pub fn truncate(shape: &Shape, h: u64, keep: usize) -> u64 {
693    if size(h) <= keep {
694        return h;
695    }
696    eprintln!(
697        "Warning: bounded queue exceeded its bound; elements beyond index {} discarded",
698        keep - 1
699    );
700    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
701    let new = t.modify(
702        h,
703        || shape.empty(),
704        |d| {
705            let Held {
706                data, objs, nested, ..
707            } = d;
708            if let Data::Seq { stride, words } = data {
709                let at = keep * *stride;
710                if *objs || nested.n > 0 {
711                    let tail: Vec<u64> = words.iter().skip(at).copied().collect();
712                    for elem in tail.chunks((*stride).max(1)) {
713                        elem_handles(*objs, nested, elem, crate::objects::release);
714                    }
715                }
716                words.truncate(at);
717            }
718        },
719    );
720    t.refs_outdated(index(new), keep as i64);
721    new
722}
723
724/// Removes element `i` of a queue (out of range: ignored); an object element
725/// is released.
726pub fn delete_at(shape: &Shape, h: u64, i: i64) -> u64 {
727    remove_at(shape, h, i, true)
728}
729
730/// [`delete_at`], releasing the element's object reference only when
731/// `release` (a pop hands it to its caller instead).
732fn remove_at(shape: &Shape, h: u64, i: i64, release: bool) -> u64 {
733    let n = size(h) as i64;
734    if i < 0 || i >= n {
735        return h;
736    }
737    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
738    let new = t.modify(
739        h,
740        || shape.empty(),
741        |d| {
742            let Held {
743                data, objs, nested, ..
744            } = d;
745            if let Data::Seq { stride, words } = data {
746                let at = i as usize * *stride;
747                if release && (*objs || nested.n > 0) {
748                    let old: Vec<u64> = words.range(at..at + *stride).copied().collect();
749                    elem_handles(*objs, nested, &old, crate::objects::release);
750                }
751                words.drain(at..at + *stride);
752            }
753        },
754    );
755    t.refs_removed(index(new), i);
756    new
757}
758
759/// Removes `key` of an associative array (an object value is released).
760pub fn delete_key(shape: &Shape, h: u64, key: &Key) -> u64 {
761    if !exists(h, key) {
762        return h;
763    }
764    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
765    let new = t.modify(
766        h,
767        || shape.empty(),
768        |d| {
769            let Held {
770                data,
771                objs,
772                nested,
773                key_objs,
774            } = d;
775            if let Data::Map(m, _) = data
776                && let Some(old) = m.remove(key)
777            {
778                if *key_objs && let Key::Int(k) = key {
779                    crate::objects::release(*k as u64);
780                }
781                elem_handles(*objs, nested, &old, crate::objects::release);
782            }
783        },
784    );
785    t.refs_key_removed(index(new), key);
786    new
787}
788
789/// Gives an associative array the value a missing key reads (`'{default:
790/// v}`); an object value is owned like any element, and replaces (releasing)
791/// an earlier default.
792pub fn set_default(shape: &Shape, h: u64, val: &[u64]) -> u64 {
793    let v: Box<[u64]> = val[..shape.elem_words.min(val.len())].into();
794    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
795    t.modify(
796        h,
797        || shape.empty(),
798        |d| {
799            let Held {
800                data, objs, nested, ..
801            } = d;
802            if let Data::Map(_, dflt) = data
803                && let Some(old) = dflt.replace(v)
804            {
805                elem_handles(*objs, nested, &old, crate::objects::release);
806            }
807        },
808    )
809}
810
811/// Removes every element (releasing the objects they name).
812pub fn clear(shape: &Shape, h: u64) -> u64 {
813    if h == 0 {
814        return 0;
815    }
816    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
817    let new = t.modify(
818        h,
819        || shape.empty(),
820        |d| {
821            d.release_elements();
822            d.each_key(crate::objects::release);
823            match &mut d.data {
824                Data::Map(m, _) => m.clear(),
825                Data::Seq { words, .. } => words.clear(),
826            }
827        },
828    );
829    t.refs_outdated(index(new), 0);
830    new
831}
832
833/// A new handle sharing `h`'s contents (an assignment's copy). The shared
834/// contents keep their one reference per object; the first change to either
835/// holder unshares them (and the copy takes its own references).
836pub fn clone(h: u64) -> u64 {
837    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
838    if t.entries
839        .get(index(h))
840        .and_then(|e| e.as_ref())
841        .is_some_and(|e| e.frozen)
842    {
843        return h;
844    }
845    match t.entries.get(index(h)).and_then(|e| e.as_ref()) {
846        Some(e) => {
847            let data = Arc::clone(&e.data);
848            let fresh = t.alloc(Held {
849                data: Data::Map(BTreeMap::new(), None),
850                objs: false,
851                nested: Nested::NONE,
852                key_objs: false,
853            });
854            if let Some(e) = t.entries[index(fresh)].as_mut() {
855                e.data = data;
856            }
857            fresh
858        }
859        None => 0,
860    }
861}
862
863/// Where the collection handles sit inside one element of a collection whose
864/// elements hold collections themselves (`int q[$][$]`, a struct with a queue
865/// field, ...): the bit offset of each handle within the element's value bits
866/// and the layout of the collection it names. Empty: no element holds one.
867#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
868pub struct Nest {
869    pub slots: Vec<(u32, Nest)>,
870}
871
872/// The 64 bits at bit `at` of an element's words (a handle may straddle two).
873fn bits64(words: &[u64], at: usize) -> u64 {
874    let (w, s) = (at / 64, at % 64);
875    let lo = words.get(w).copied().unwrap_or(0) >> s;
876    if s == 0 {
877        lo
878    } else {
879        lo | (words.get(w + 1).copied().unwrap_or(0) << (64 - s))
880    }
881}
882
883fn set_bits64(words: &mut [u64], at: usize, v: u64) {
884    let (w, s) = (at / 64, at % 64);
885    if s == 0 {
886        if let Some(x) = words.get_mut(w) {
887            *x = v;
888        }
889        return;
890    }
891    if let Some(x) = words.get_mut(w) {
892        *x = (*x & (u64::MAX >> (64 - s))) | (v << s);
893    }
894    if let Some(x) = words.get_mut(w + 1) {
895        *x = (*x & (u64::MAX << s)) | (v >> (64 - s));
896    }
897}
898
899/// Every element of `h` as owned words, with the key of an associative
900/// array's (`None` for a queue's), in order; `default:` values excluded.
901fn elem_list(h: u64) -> Vec<(Option<Key>, Vec<u64>)> {
902    let t = table().lock().unwrap_or_else(|e| e.into_inner());
903    match t.data(h) {
904        None => Vec::new(),
905        Some(Data::Seq { stride, words }) => words
906            .iter()
907            .copied()
908            .collect::<Vec<_>>()
909            .chunks((*stride).max(1))
910            .map(|c| (None, c.to_vec()))
911            .collect(),
912        Some(Data::Map(m, _)) => m
913            .iter()
914            .map(|(k, v)| (Some(k.clone()), v.to_vec()))
915            .collect(),
916    }
917}
918
919/// [`clone`] for a collection whose elements hold collections (`nest`): the
920/// copy gets a copy of every inner collection too (recursively), so later
921/// changes to the original's inner collections leave it alone -- a value copy
922/// (LRM 7.6), where [`clone`] alone would share the inner handles. Every
923/// handle made is pushed onto `minted`, for the caller to [`release`].
924pub fn clone_deep(h: u64, nest: &Nest, minted: &mut Vec<u64>) -> u64 {
925    let fresh = clone(h);
926    if fresh != 0 {
927        minted.push(fresh);
928    }
929    if fresh == 0 || nest.slots.is_empty() {
930        return fresh;
931    }
932    let mut inner: Vec<(usize, u64)> = Vec::new();
933    {
934        let t = table().lock().unwrap_or_else(|e| e.into_inner());
935        let Some(e) = t.entries.get(index(fresh)).and_then(|e| e.as_ref()) else {
936            return fresh;
937        };
938        if e.frozen {
939            return fresh;
940        }
941        let mut visit = |el: &[u64]| {
942            for (k, (off, _)) in nest.slots.iter().enumerate() {
943                let x = bits64(el, *off as usize);
944                if x != 0 {
945                    inner.push((k, x));
946                }
947            }
948        };
949        match &e.data.data {
950            Data::Seq { stride, words } => words
951                .iter()
952                .copied()
953                .collect::<Vec<_>>()
954                .chunks((*stride).max(1))
955                .for_each(&mut visit),
956            Data::Map(m, d) => m.values().chain(d.iter()).for_each(|v| visit(v)),
957        }
958    }
959    let mut copies: std::collections::HashMap<(usize, u64), u64> = std::collections::HashMap::new();
960    for (k, x) in inner {
961        copies
962            .entry((k, x))
963            .or_insert_with(|| clone_deep(x, &nest.slots[k].1, minted));
964    }
965    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
966    let Some(e) = t.entries.get_mut(index(fresh)).and_then(|e| e.as_mut()) else {
967        return fresh;
968    };
969    let held = Arc::make_mut(&mut e.data);
970    let mut rewrite = |el: &mut [u64]| {
971        for (k, (off, _)) in nest.slots.iter().enumerate() {
972            let x = bits64(el, *off as usize);
973            if let Some(&n) = copies.get(&(k, x)) {
974                set_bits64(el, *off as usize, n);
975            }
976        }
977    };
978    match &mut held.data {
979        Data::Seq { stride, words } => words
980            .make_contiguous()
981            .chunks_mut((*stride).max(1))
982            .for_each(&mut rewrite),
983        Data::Map(m, d) => m
984            .values_mut()
985            .map(|v| &mut v[..])
986            .chain(d.iter_mut().map(|v| &mut v[..]))
987            .for_each(rewrite),
988    }
989    fresh
990}
991
992/// [`equal`] by contents all the way down: the inner collections two
993/// elements name compare by their elements, not by handle (`nest` as for
994/// [`clone_deep`]).
995pub fn equal_nest(a: u64, b: u64, nest: &Nest) -> bool {
996    if nest.slots.is_empty() {
997        return equal(a, b);
998    }
999    let (x, y) = (elem_list(a), elem_list(b));
1000    x.len() == y.len()
1001        && x.iter().zip(&y).all(|((ka, ea), (kb, eb))| {
1002            if ka != kb || ea.len() != eb.len() {
1003                return false;
1004            }
1005            let (mut ea, mut eb) = (ea.clone(), eb.clone());
1006            let mut same = true;
1007            for (off, sub) in &nest.slots {
1008                let at = *off as usize;
1009                same &= equal_nest(bits64(&ea, at), bits64(&eb, at), sub);
1010                set_bits64(&mut ea, at, 0);
1011                set_bits64(&mut eb, at, 0);
1012            }
1013            same && ea == eb
1014        })
1015}
1016
1017/// How many collections are alive right now (a leak check for tests: the
1018/// count is process-global, so measure a before/after delta).
1019pub fn live_count() -> usize {
1020    let t = table().lock().unwrap_or_else(|e| e.into_inner());
1021    t.entries.iter().filter(|e| e.is_some()).count()
1022}
1023
1024/// Frees `h`'s entry (its variable is being overwritten).
1025pub fn release(h: u64) {
1026    table().lock().unwrap_or_else(|e| e.into_inner()).release(h);
1027}
1028
1029/// Opens a reference to element `i` of the queue or dynamic array `h` (a `ref`
1030/// argument bound to `h[i]`, LRM 1800-2023 13.5.2) and returns its id, 0 when
1031/// there is no such element. The caller first makes `h` its own entry
1032/// ([`ref_own`]). Until [`ref_close`] the structural operations track the
1033/// element: a delete / pop of it, a `clear`, a resize, an assignment to the
1034/// whole collection outdate the reference (7.10.3, 7.5.1, 13.5.2); other
1035/// elements being removed or inserted move the index it names.
1036pub fn ref_open(h: u64, i: i64) -> u64 {
1037    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1038    let e = index(h);
1039    let n = t.data(h).filter(|_| e != 0).map_or(0, |d| d.len() as i64);
1040    if i < 0 || i >= n {
1041        return 0;
1042    }
1043    t.ref_push(ElemRef {
1044        entry: e,
1045        index: i,
1046        key: None,
1047        live: true,
1048    })
1049}
1050
1051/// [`ref_open`] for key `key` of the associative array `h` (a `ref` argument
1052/// bound to `h[key]`, LRM 1800-2023 13.5.2, 7.8): the element is named by
1053/// its key, so inserting other keys never outdates the reference and deleting
1054/// this key does. The key need not exist: the reference then reads the
1055/// array's default and its first write creates the element (7.8.6). `h` must
1056/// be an entry ([`ref_own`] makes the empty handle one); 0 when it is not.
1057pub fn ref_open_key(h: u64, key: Key) -> u64 {
1058    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1059    let e = index(h);
1060    if e == 0 || !matches!(t.data(h), Some(Data::Map(..))) {
1061        return 0;
1062    }
1063    t.ref_push(ElemRef {
1064        entry: e,
1065        index: 0,
1066        key: Some(key),
1067        live: true,
1068    })
1069}
1070
1071impl Table {
1072    fn ref_push(&mut self, rec: ElemRef) -> u64 {
1073        let rec = Some(rec);
1074        self.n_refs += 1;
1075        match self.free_refs.pop() {
1076            Some(k) => {
1077                self.refs[k as usize] = rec;
1078                u64::from(k) + 1
1079            }
1080            None => {
1081                self.refs.push(rec);
1082                self.refs.len() as u64
1083            }
1084        }
1085    }
1086}
1087
1088/// Makes the collection `h` an entry of its own (a frozen constant's first
1089/// change would otherwise move it under an open reference); returns its
1090/// handle.
1091pub fn ref_own(shape: &Shape, h: u64) -> u64 {
1092    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1093    if h == 0 {
1094        // The empty associative array gets an entry a key reference can name.
1095        return if shape.assoc {
1096            t.modify(h, || shape.empty(), |_| {})
1097        } else {
1098            0
1099        };
1100    }
1101    if t.entries
1102        .get(index(h))
1103        .and_then(|e| e.as_ref())
1104        .is_some_and(|e| e.frozen)
1105    {
1106        t.modify(h, || shape.empty(), |_| {})
1107    } else {
1108        h
1109    }
1110}
1111
1112fn ref_rec(t: &Table, id: u64) -> Option<ElemRef> {
1113    t.refs.get(id.wrapping_sub(1) as usize).cloned().flatten()
1114}
1115
1116/// Element the open reference `id` names into `out`; `false` when `id` is 0,
1117/// outdated or closed (the caller keeps its own copy, which is what an
1118/// outdated reference's holder goes on using, 13.5.2).
1119pub fn ref_get(id: u64, out: &mut [u64]) -> bool {
1120    let t = table().lock().unwrap_or_else(|e| e.into_inner());
1121    let Some(r) = ref_rec(&t, id).filter(|r| r.live) else {
1122        return false;
1123    };
1124    match (&r.key, t.data(r.entry as u64)) {
1125        (Some(key), Some(Data::Map(m, dflt))) => match m.get(key).or(dflt.as_ref()) {
1126            Some(v) => {
1127                let n = out.len().min(v.len());
1128                out[..n].copy_from_slice(&v[..n]);
1129                true
1130            }
1131            None => false,
1132        },
1133        (None, _) => {
1134            drop(t);
1135            get(r.entry as u64, r.index, out)
1136        }
1137        _ => false,
1138    }
1139}
1140
1141/// Writes `val` to the element reference `id` names, in place (the
1142/// collection's handle is unchanged; a key reference to an absent key creates
1143/// the element, LRM 7.8.6). An outdated reference writes nothing: its changes
1144/// are not visible outside the subroutine (LRM 13.5.2).
1145pub fn ref_put(id: u64, val: &[u64]) {
1146    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1147    let Some(r) = ref_rec(&t, id).filter(|r| r.live) else {
1148        return;
1149    };
1150    let Some(e) = t.entries.get_mut(r.entry).and_then(|e| e.as_mut()) else {
1151        return;
1152    };
1153    let held = Arc::make_mut(&mut e.data);
1154    let key_objs = held.key_objs;
1155    match (&mut held.data, r.key) {
1156        (Data::Seq { stride, words }, None) => {
1157            let base = r.index as usize * *stride;
1158            if base + *stride <= words.len() {
1159                for k in 0..*stride {
1160                    words[base + k] = val.get(k).copied().unwrap_or(0);
1161                }
1162                e.cursor = None;
1163            }
1164        }
1165        (Data::Map(m, _), Some(key)) => {
1166            match m.get_mut(&key) {
1167                Some(slot) => {
1168                    let n = slot.len().min(val.len());
1169                    slot[..n].copy_from_slice(&val[..n]);
1170                }
1171                None => {
1172                    if key_objs && let Key::Int(k) = &key {
1173                        crate::objects::retain(*k as u64);
1174                    }
1175                    m.insert(key, val.into());
1176                }
1177            }
1178            e.cursor = None;
1179        }
1180        _ => {}
1181    }
1182}
1183
1184fn bit_at(w: &[u64], i: usize) -> bool {
1185    w.get(i / 64).is_some_and(|x| (x >> (i % 64)) & 1 != 0)
1186}
1187
1188fn set_bit(w: &mut [u64], i: usize, v: bool) {
1189    if let Some(x) = w.get_mut(i / 64) {
1190        let m = 1u64 << (i % 64);
1191        *x = if v { *x | m } else { *x & !m };
1192    }
1193}
1194
1195/// Part `[lo, lo + w)` of the `big_w`-bit element reference `id` names, into
1196/// `out` as a `w`-bit packed `(unk << w) | val` value (an element is
1197/// `(unk << big_w) | val`): a `ref` argument bound to a member / select of a
1198/// queue or dynamic-array element (`f(q[i].m)`, LRM 1800-2023 13.5.2).
1199/// `big_w == 0` names the whole element ([`ref_get`]). `false` when the
1200/// reference is 0, outdated or closed (`out` is then left alone).
1201pub fn ref_get_part(id: u64, lo: u64, w: u32, big_w: u32, out: &mut [u64]) -> bool {
1202    if crate::winref::is_window(id) {
1203        return crate::winref::get_part(id, lo, w, big_w, out);
1204    }
1205    if big_w == 0 {
1206        return ref_get(id, out);
1207    }
1208    let mut elem = vec![0u64; (2 * big_w as usize).div_ceil(64)];
1209    if !ref_get(id, &mut elem) {
1210        return false;
1211    }
1212    out.fill(0);
1213    for k in 0..w as usize {
1214        // A bit past the element (a select the caller flagged out of range)
1215        // reads as 0.
1216        let i = lo as usize + k;
1217        if i < big_w as usize {
1218            set_bit(out, k, bit_at(&elem, i));
1219            set_bit(out, w as usize + k, bit_at(&elem, big_w as usize + i));
1220        }
1221    }
1222    true
1223}
1224
1225/// Writes the `w`-bit packed `val` into part `[lo, lo + w)` of the element
1226/// reference `id` names, keeping the element's other bits as they are now
1227/// (the element may have changed through another path since this reference
1228/// was read). Nothing once the reference is outdated (13.5.2); `big_w == 0`
1229/// is [`ref_put`].
1230pub fn ref_put_part(id: u64, lo: u64, w: u32, big_w: u32, val: &[u64]) {
1231    if crate::winref::is_window(id) {
1232        return crate::winref::put_part(id, lo, w, big_w, val);
1233    }
1234    if big_w == 0 {
1235        return ref_put(id, val);
1236    }
1237    let mut elem = vec![0u64; (2 * big_w as usize).div_ceil(64)];
1238    if !ref_get(id, &mut elem) {
1239        return;
1240    }
1241    for k in 0..w as usize {
1242        let i = lo as usize + k;
1243        if i < big_w as usize {
1244            set_bit(&mut elem, i, bit_at(val, k));
1245            set_bit(&mut elem, big_w as usize + i, bit_at(val, w as usize + k));
1246        }
1247    }
1248    ref_put(id, &elem);
1249}
1250
1251/// Closes reference `id`, live or outdated.
1252pub fn ref_close(id: u64) {
1253    if crate::winref::is_window(id) {
1254        return crate::winref::close(id);
1255    }
1256    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1257    let k = id.wrapping_sub(1) as usize;
1258    if t.refs.get_mut(k).and_then(Option::take).is_some() {
1259        t.free_refs.push(k as u32);
1260        t.n_refs -= 1;
1261    }
1262}
1263
1264/// Whether `h` still names a live entry: its slot is occupied *at `h`'s
1265/// version*. Unlike [`size`] (which reads the slot by index alone), a freed
1266/// slot another thread has since reused does not count -- the table is
1267/// process-global, so a test asserting "freed" must not be fooled by a
1268/// parallel test's allocation taking the slot.
1269#[cfg(test)]
1270pub(crate) fn is_live(h: u64) -> bool {
1271    let t = table().lock().unwrap_or_else(|e| e.into_inner());
1272    t.entries
1273        .get(index(h))
1274        .and_then(|e| e.as_ref())
1275        .is_some_and(|e| u64::from(e.version) == h >> 32)
1276}
1277
1278/// A new sequence of `n` elements: element `i` is `src`'s element `i` if
1279/// it has one, else `dflt` (`new[n](src)`, §7.5.1). Object elements: the new
1280/// sequence takes its own reference on each (`dflt`'s included).
1281pub fn resized(shape: &Shape, src: u64, n: usize, dflt: &[u64]) -> u64 {
1282    let stride = shape.elem_words.max(1);
1283    let mut words = VecDeque::with_capacity(n * stride);
1284    {
1285        let t = table().lock().unwrap_or_else(|e| e.into_inner());
1286        if let Some(Data::Seq { words: w, .. }) = t.data(src) {
1287            words.extend(w.iter().copied().take(n * stride));
1288        }
1289    }
1290    while words.len() < n * stride {
1291        for k in 0..stride {
1292            words.push_back(dflt.get(k).copied().unwrap_or(0));
1293        }
1294    }
1295    retain_elems(shape, words.iter().copied());
1296    table()
1297        .lock()
1298        .unwrap_or_else(|e| e.into_inner())
1299        .alloc(shape.held(Data::Seq { stride, words }))
1300}
1301
1302/// Takes a reference on the object of each element of `words` (a copy of
1303/// elements some other holder keeps).
1304fn retain_elems(shape: &Shape, words: impl Iterator<Item = u64>) {
1305    if shape.nested.n == 0 {
1306        if shape.obj {
1307            words
1308                .step_by(shape.elem_words.max(1))
1309                .for_each(crate::objects::retain);
1310        }
1311        return;
1312    }
1313    let stride = shape.elem_words.max(1);
1314    let mut words = words;
1315    let mut buf = Vec::with_capacity(stride);
1316    loop {
1317        buf.clear();
1318        buf.extend(words.by_ref().take(stride));
1319        if buf.len() < stride {
1320            break;
1321        }
1322        elem_handles(shape.obj, &shape.nested, &buf, crate::objects::retain);
1323    }
1324}
1325
1326/// A new sequence of the given elements (`n * elem_words` words). Object
1327/// elements: the sequence TAKES the caller's reference on each.
1328pub fn from_words(shape: &Shape, elems: &[u64]) -> u64 {
1329    let stride = shape.elem_words.max(1);
1330    table()
1331        .lock()
1332        .unwrap_or_else(|e| e.into_inner())
1333        .alloc(shape.held(Data::Seq {
1334            stride,
1335            words: elems.iter().copied().collect(),
1336        }))
1337}
1338
1339/// [`from_words`] for elements some other holder still names: the new
1340/// sequence takes its own reference on each object.
1341pub fn from_copies(shape: &Shape, elems: &[u64]) -> u64 {
1342    retain_elems(shape, elems.iter().copied());
1343    from_words(shape, elems)
1344}
1345
1346/// A constant sequence of the given elements (`elem_words` words each):
1347/// a declaration's initial value (see `Entry::frozen`).
1348pub fn frozen_from_words(elem_words: usize, elems: &[u64]) -> u64 {
1349    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1350    let h = t.alloc(Held {
1351        data: Data::Seq {
1352            stride: elem_words.max(1),
1353            words: elems.iter().copied().collect(),
1354        },
1355        objs: false,
1356        nested: Nested::NONE,
1357        key_objs: false,
1358    });
1359    if let Some(e) = t.entries[index(h)].as_mut() {
1360        e.frozen = true;
1361    }
1362    h
1363}
1364
1365/// A constant associative array of the given entries (see
1366/// [`frozen_from_words`]).
1367pub fn frozen_map(entries: Vec<(Key, Vec<u64>)>, default: Option<Vec<u64>>) -> u64 {
1368    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1369    let h = t.alloc(Held {
1370        data: Data::Map(
1371            entries.into_iter().map(|(k, v)| (k, v.into())).collect(),
1372            default.map(Into::into),
1373        ),
1374        objs: false,
1375        nested: Nested::NONE,
1376        key_objs: false,
1377    });
1378    if let Some(e) = t.entries[index(h)].as_mut() {
1379        e.frozen = true;
1380    }
1381    h
1382}
1383
1384/// One constant collection (see [`Entry::frozen`]), as plain data.
1385#[derive(Clone, Debug, PartialEq, Eq)]
1386pub enum FrozenData {
1387    Seq {
1388        stride: usize,
1389        words: Vec<u64>,
1390    },
1391    Map {
1392        entries: Vec<(Key, Vec<u64>)>,
1393        default: Option<Vec<u64>>,
1394    },
1395}
1396
1397/// Every collection of this process, as the constants a declaration's initial
1398/// value names: entry 1 first, so entry `i` is handle `(1 << 32) | i`. The
1399/// handles are baked into a built design's cells, which is why it carries
1400/// these ([`frozen_preload`]). `Err` if the table holds anything else (a live
1401/// non-constant collection, or a freed slot), since its handles could not be
1402/// reproduced.
1403pub fn frozen_snapshot() -> Result<Vec<FrozenData>, String> {
1404    let t = table().lock().unwrap_or_else(|e| e.into_inner());
1405    t.entries[1..]
1406        .iter()
1407        .enumerate()
1408        .map(|(i, e)| {
1409            let Some(e) = e.as_ref().filter(|e| e.frozen && e.version == 1) else {
1410                return Err(format!(
1411                    "collection {} is not a constant of a declaration's initial value",
1412                    i + 1
1413                ));
1414            };
1415            Ok(plain(e))
1416        })
1417        .collect()
1418}
1419
1420fn plain(e: &Entry) -> FrozenData {
1421    match &e.data.data {
1422        Data::Seq { stride, words } => FrozenData::Seq {
1423            stride: *stride,
1424            words: words.iter().copied().collect(),
1425        },
1426        Data::Map(m, dflt) => FrozenData::Map {
1427            entries: m.iter().map(|(k, v)| (k.clone(), v.to_vec())).collect(),
1428            default: dflt.as_ref().map(|d| d.to_vec()),
1429        },
1430    }
1431}
1432
1433/// Makes `frozen` (a [`frozen_snapshot`]) entries `1..` of this process's
1434/// table, each at the handle it had when built. Fails if the table already
1435/// holds something else.
1436pub fn frozen_preload(frozen: &[FrozenData]) -> Result<(), String> {
1437    for (i, f) in frozen.iter().enumerate() {
1438        let want = (1u64 << 32) | (i as u64 + 1);
1439        let have = frozen_snapshot_entry(i + 1);
1440        if let Some(have) = have {
1441            if &have != f {
1442                return Err(format!(
1443                    "collection {} of the design cannot take its handle: this process \
1444                     already made a different one",
1445                    i + 1
1446                ));
1447            }
1448            continue;
1449        }
1450        let got = match f {
1451            FrozenData::Seq { stride, words } => frozen_from_words(*stride, words),
1452            FrozenData::Map { entries, default } => frozen_map(entries.clone(), default.clone()),
1453        };
1454        if got != want {
1455            return Err(format!(
1456                "collection {} of the design got handle {got:#x}, not {want:#x}",
1457                i + 1
1458            ));
1459        }
1460    }
1461    Ok(())
1462}
1463
1464/// Entry `i` as plain data, if it is a constant.
1465fn frozen_snapshot_entry(i: usize) -> Option<FrozenData> {
1466    let t = table().lock().unwrap_or_else(|e| e.into_inner());
1467    t.entries.get(i)?.as_ref().filter(|e| e.frozen).map(plain)
1468}
1469
1470/// A new associative array with `h`'s keys and `default:` value but the
1471/// values `flat` (`elem_words` words per key, in key order): what
1472/// `randomize()` writes back to a `rand` associative array, whose keys it
1473/// never changes (LRM 18.4). `None` when `h` is not an associative array or
1474/// `flat` does not have exactly one value per key. Integral elements only
1475/// (no object references are moved).
1476pub fn map_with_values(h: u64, elem_words: usize, flat: &[u64]) -> Option<u64> {
1477    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1478    let Some(Data::Map(m, dflt)) = t.data(h) else {
1479        return None;
1480    };
1481    if flat.len() != m.len() * elem_words {
1482        return None;
1483    }
1484    let keys: Vec<Key> = m.keys().cloned().collect();
1485    let dflt = dflt.clone();
1486    let map: BTreeMap<Key, Box<[u64]>> = keys
1487        .into_iter()
1488        .zip(flat.chunks(elem_words.max(1)))
1489        .map(|(k, v)| (k, v.into()))
1490        .collect();
1491    Some(t.alloc(Held {
1492        data: Data::Map(map, dflt),
1493        objs: false,
1494        nested: Nested::NONE,
1495        key_objs: false,
1496    }))
1497}
1498
1499/// `h` with every element of `other` appended (a concatenation's queue
1500/// operand); object elements: `h` takes its own reference on each.
1501pub fn append(shape: &Shape, h: u64, other: u64) -> u64 {
1502    let extra: Vec<u64> = {
1503        let t = table().lock().unwrap_or_else(|e| e.into_inner());
1504        match t.data(other) {
1505            Some(Data::Seq { words, .. }) => words.iter().copied().collect(),
1506            _ => Vec::new(),
1507        }
1508    };
1509    retain_elems(shape, extra.iter().copied());
1510    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1511    t.modify(
1512        h,
1513        || shape.empty(),
1514        |d| {
1515            if let Data::Seq { words, .. } = &mut d.data {
1516                words.extend(extra);
1517            }
1518        },
1519    )
1520}
1521
1522/// Elements `a..=b` as a new sequence, clamped to the sequence (an empty
1523/// result when `a > b`, §7.10.1).
1524pub fn slice(shape: &Shape, h: u64, a: i64, b: i64) -> u64 {
1525    let n = size(h) as i64;
1526    let a = a.max(0);
1527    let b = b.min(n - 1);
1528    let stride = shape.elem_words.max(1);
1529    let mut out = VecDeque::new();
1530    if a <= b {
1531        let t = table().lock().unwrap_or_else(|e| e.into_inner());
1532        if let Some(Data::Seq { words, .. }) = t.data(h) {
1533            out.extend(
1534                words
1535                    .range(a as usize * stride..(b as usize + 1) * stride)
1536                    .copied(),
1537            );
1538        }
1539    }
1540    retain_elems(shape, out.iter().copied());
1541    table()
1542        .lock()
1543        .unwrap_or_else(|e| e.into_inner())
1544        .alloc(shape.held(Data::Seq { stride, words: out }))
1545}
1546
1547/// Whether two collections hold the same elements (and keys).
1548pub fn equal(a: u64, b: u64) -> bool {
1549    let t = table().lock().unwrap_or_else(|e| e.into_inner());
1550    match (t.data(a), t.data(b)) {
1551        (None, None) => true,
1552        (Some(d), None) | (None, Some(d)) => d.len() == 0,
1553        (Some(Data::Seq { words: x, .. }), Some(Data::Seq { words: y, .. })) => x == y,
1554        (Some(Data::Map(x, _)), Some(Data::Map(y, _))) => x == y,
1555        _ => false,
1556    }
1557}
1558
1559/// The first / last key (`first`, `last`), or the key after / before `key`
1560/// (`next`, `prev`), of an associative array (§7.9.4-7.9.7).
1561pub fn neighbor(h: u64, key: Option<&Key>, forward: bool) -> Option<Key> {
1562    let t = table().lock().unwrap_or_else(|e| e.into_inner());
1563    let Some(Data::Map(m, _)) = t.data(h) else {
1564        return None;
1565    };
1566    let found = match (key, forward) {
1567        (None, true) => m.keys().next(),
1568        (None, false) => m.keys().next_back(),
1569        (Some(k), true) => m
1570            .range((Bound::Excluded(k.clone()), Bound::Unbounded))
1571            .next()
1572            .map(|(k, _)| k),
1573        (Some(k), false) => m
1574            .range((Bound::Unbounded, Bound::Excluded(k.clone())))
1575            .next_back()
1576            .map(|(k, _)| k),
1577    };
1578    found.cloned()
1579}
1580
1581/// The key at position `pos` of an associative array (a `foreach`
1582/// visits keys in order); sequential positions are O(log n) each.
1583pub fn key_at(h: u64, pos: usize) -> Option<Key> {
1584    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1585    let i = index(h);
1586    let e = t.entries.get_mut(i)?.as_mut()?;
1587    let Data::Map(m, _) = &e.data.data else {
1588        return None;
1589    };
1590    let key = match &e.cursor {
1591        Some((p, k)) if *p == pos => Some(k.clone()),
1592        Some((p, k)) if *p + 1 == pos => m
1593            .range((Bound::Excluded(k.clone()), Bound::Unbounded))
1594            .next()
1595            .map(|(k, _)| k.clone()),
1596        _ => m.keys().nth(pos).cloned(),
1597    };
1598    if let Some(k) = &key {
1599        e.cursor = Some((pos, k.clone()));
1600    }
1601    key
1602}
1603
1604/// The key at position `pos` of an associative array and its value into
1605/// `out` (an array method walking the entries in key order).
1606pub fn entry_at(h: u64, pos: usize, out: &mut [u64]) -> Option<Key> {
1607    let key = key_at(h, pos)?;
1608    lookup(h, &key, out);
1609    Some(key)
1610}
1611
1612/// `h` with its elements reordered: element `k` becomes the old element
1613/// `order[k]` (`order` a permutation of the positions).
1614pub fn permute(shape: &Shape, h: u64, order: &[usize]) -> u64 {
1615    let old = elements(h);
1616    if old.len() != order.len() || old.is_empty() {
1617        return h;
1618    }
1619    let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
1620    let new = t.modify(
1621        h,
1622        || shape.empty(),
1623        |d| {
1624            // The same elements in a new order: the references stay put.
1625            if let Data::Seq { words, .. } = &mut d.data {
1626                words.clear();
1627                for &k in order {
1628                    words.extend(old[k].iter().copied());
1629                }
1630            }
1631        },
1632    );
1633    // The LRM names no ordering method among the operations that keep or
1634    // outdate references (7.10.3): an open element reference would address
1635    // a different element afterwards, so it is outdated (its write is
1636    // dropped rather than landing on the wrong element).
1637    t.refs_outdated(index(new), 0);
1638    new
1639}
1640
1641/// Every element of `h` as words, element 0 (or the first key's value)
1642/// first -- for `%p`.
1643pub fn elements(h: u64) -> Vec<Vec<u64>> {
1644    let t = table().lock().unwrap_or_else(|e| e.into_inner());
1645    match t.data(h) {
1646        Some(Data::Seq { stride, words }) => words
1647            .iter()
1648            .copied()
1649            .collect::<Vec<_>>()
1650            .chunks(*stride)
1651            .map(<[u64]>::to_vec)
1652            .collect(),
1653        Some(Data::Map(m, _)) => m.values().map(|v| v.to_vec()).collect(),
1654        None => Vec::new(),
1655    }
1656}
1657
1658/// Every key of an associative array, in order -- for `%p`.
1659pub fn keys(h: u64) -> Vec<Key> {
1660    let t = table().lock().unwrap_or_else(|e| e.into_inner());
1661    match t.data(h) {
1662        Some(Data::Map(m, _)) => m.keys().cloned().collect(),
1663        _ => Vec::new(),
1664    }
1665}
1666
1667#[cfg(test)]
1668mod tests {
1669    use super::*;
1670
1671    const Q: Shape = Shape {
1672        elem_words: 1,
1673        assoc: false,
1674        key: KeyKind::Str,
1675        obj: false,
1676        nested: Nested::NONE,
1677    };
1678
1679    #[test]
1680    fn a_queue_pushes_pops_and_changes_handle_on_every_change() {
1681        let h = push(&Q, 0, &[3], false);
1682        let h2 = push(&Q, h, &[4], false);
1683        assert_ne!(h, h2, "each change is a new handle value");
1684        let h3 = push(&Q, h2, &[1], true);
1685        assert_eq!(size(h3), 3);
1686        let mut out = [0];
1687        assert!(get(h3, 2, &mut out));
1688        assert_eq!(out, [4]);
1689        let (h4, ok) = pop(&Q, h3, &mut out, true);
1690        assert!(ok);
1691        assert_eq!(out, [1]);
1692        assert_eq!(size(h4), 2);
1693    }
1694
1695    #[test]
1696    fn clone_deep_copies_inner_collections_and_equal_nest_compares_contents() {
1697        const OUTER: Shape = Shape {
1698            elem_words: 2,
1699            assoc: false,
1700            key: KeyKind::Str,
1701            obj: false,
1702            nested: Nested::NONE,
1703        };
1704        let nest = Nest {
1705            slots: vec![(0, Nest::default())],
1706        };
1707        let inner = push(&Q, push(&Q, 0, &[1], false), &[2], false);
1708        let outer = push(&OUTER, 0, &[inner, 0], false);
1709        let mut minted = Vec::new();
1710        let copy = clone_deep(outer, &nest, &mut minted);
1711        // The inner queue changes in place (same entry, new handle) and the
1712        // outer element is re-pointed at it, as the design's store does.
1713        let grown = push(&Q, inner, &[3], false);
1714        let outer = set(&OUTER, outer, 0, &[grown, 0], true);
1715        let mut got = [0, 0];
1716        assert!(get(copy, 0, &mut got));
1717        assert_eq!(size(got[0]), 2, "the copy keeps the old inner contents");
1718        assert!(!equal_nest(outer, copy, &nest), "contents differ");
1719        let again = clone_deep(outer, &nest, &mut minted);
1720        assert!(equal_nest(outer, again, &nest));
1721        minted.iter().for_each(|&h| release(h));
1722    }
1723
1724    fn queue_of(vals: &[u64]) -> u64 {
1725        vals.iter().fold(0, |h, v| push(&Q, h, &[*v], false))
1726    }
1727
1728    fn words(h: u64) -> Vec<u64> {
1729        (0..size(h) as i64)
1730            .map(|i| {
1731                let mut o = [0];
1732                get(h, i, &mut o);
1733                o[0]
1734            })
1735            .collect()
1736    }
1737
1738    #[test]
1739    fn an_element_reference_follows_other_elements_and_writes_through() {
1740        let h = queue_of(&[1, 2, 3]);
1741        let id = ref_open(h, 1);
1742        assert_ne!(id, 0);
1743        let mut out = [0];
1744        assert!(ref_get(id, &mut out));
1745        assert_eq!(out, [2]);
1746        let h = push(&Q, h, &[9], true); // 9 1 2 3: the element moves to 2
1747        let h = insert(&Q, h, 0, &[8]); // 8 9 1 2 3: and to 3
1748        let h = delete_at(&Q, h, 1); // 8 1 2 3: and back to 2
1749        assert!(ref_get(id, &mut out));
1750        assert_eq!(out, [2]);
1751        ref_put(id, &[10]);
1752        assert_eq!(words(h), [8, 1, 10, 3]);
1753        // A direct write is seen through the reference.
1754        let h = set(&Q, h, 2, &[11], true);
1755        assert!(ref_get(id, &mut out));
1756        assert_eq!(out, [11]);
1757        ref_close(id);
1758        assert!(!ref_get(id, &mut out), "a closed reference is gone");
1759        assert_eq!(words(h), [8, 1, 11, 3]);
1760    }
1761
1762    #[test]
1763    fn a_part_reference_reads_and_writes_only_its_bits() {
1764        // A 32-bit element is `(unk << 32) | val`; the part is bits [8, 16).
1765        let h = queue_of(&[0x0000_0F00_AABB_CCDD, 7]);
1766        let id = ref_open(h, 0);
1767        let mut out = [u64::MAX];
1768        assert!(ref_get_part(id, 8, 8, 32, &mut out));
1769        // val 0xCC, unk 0x0F: packed as (unk << 8) | val.
1770        assert_eq!(out, [0x0FCC]);
1771        ref_put_part(id, 8, 8, 32, &[0x01_55]);
1772        assert_eq!(words(h), [0x0000_0100_AABB_55DD, 7]);
1773        // `big_w == 0` is the whole element.
1774        assert!(ref_get_part(id, 0, 32, 0, &mut out));
1775        assert_eq!(out, [0x0000_0100_AABB_55DD]);
1776        ref_close(id);
1777        assert!(!ref_get_part(id, 8, 8, 32, &mut out));
1778    }
1779
1780    #[test]
1781    fn an_outdated_element_reference_drops_its_write_and_keeps_its_id() {
1782        // Deleting the element, popping it, truncating it away, clearing and
1783        // releasing the collection all outdate (LRM 7.10.3, 7.5.1).
1784        let mut out = [0];
1785        let h = queue_of(&[1, 2, 3]);
1786        let id = ref_open(h, 1);
1787        let h = delete_at(&Q, h, 1);
1788        assert!(!ref_get(id, &mut out));
1789        // The id is not reused while the callee holds it.
1790        let other = ref_open(h, 0);
1791        assert_ne!(other, id);
1792        ref_put(id, &[10]);
1793        assert_eq!(words(h), [1, 3]);
1794        ref_close(id);
1795        ref_close(other);
1796
1797        let h = queue_of(&[1, 2, 3]);
1798        let id = ref_open(h, 2);
1799        let (h, _) = pop(&Q, h, &mut out, false);
1800        ref_put(id, &[10]);
1801        assert_eq!(words(h), [1, 2]);
1802        ref_close(id);
1803
1804        let h = queue_of(&[1, 2, 3]);
1805        let id = ref_open(h, 2);
1806        let h = truncate(&Q, h, 2);
1807        ref_put(id, &[10]);
1808        assert_eq!(words(h), [1, 2]);
1809        ref_close(id);
1810
1811        let h = queue_of(&[1, 2, 3]);
1812        let id = ref_open(h, 0);
1813        let h = clear(&Q, h);
1814        let h = push(&Q, h, &[5], false);
1815        ref_put(id, &[10]);
1816        assert_eq!(words(h), [5]);
1817        ref_close(id);
1818
1819        // The collection is released (its variable was assigned).
1820        let h = queue_of(&[1, 2, 3]);
1821        let id = ref_open(h, 0);
1822        release(h);
1823        let other = queue_of(&[7, 8, 9]);
1824        ref_put(id, &[10]);
1825        assert_eq!(words(other), [7, 8, 9]);
1826        assert!(!ref_get(id, &mut out));
1827        ref_close(id);
1828    }
1829
1830    #[test]
1831    fn a_reference_outside_the_collection_is_never_open() {
1832        let h = queue_of(&[1]);
1833        assert_eq!(ref_open(h, 1), 0);
1834        assert_eq!(ref_open(h, -1), 0);
1835        assert_eq!(ref_open(0, 0), 0);
1836        ref_put(0, &[5]);
1837        assert_eq!(words(h), [1]);
1838    }
1839
1840    #[test]
1841    fn a_reference_survives_a_frozen_values_first_write() {
1842        let f = frozen_from_words(1, &[1, 2]);
1843        let f = ref_own(&Q, f);
1844        let id = ref_open(f, 1);
1845        let g = insert(&Q, f, 0, &[0]);
1846        ref_put(id, &[5]);
1847        assert_eq!(words(g), [0, 1, 5]);
1848        ref_close(id);
1849    }
1850
1851    #[test]
1852    fn a_clone_is_a_copy_on_write_value() {
1853        let a = push(&Q, 0, &[7], false);
1854        let b = clone(a);
1855        let b2 = set(&Q, b, 0, &[9], true);
1856        let mut out = [0];
1857        get(a, 0, &mut out);
1858        assert_eq!(out, [7], "changing the copy leaves the original");
1859        get(b2, 0, &mut out);
1860        assert_eq!(out, [9]);
1861    }
1862
1863    #[test]
1864    fn a_frozen_value_is_shared_and_never_changed_in_place() {
1865        let f = frozen_from_words(1, &[1, 2]);
1866        assert_eq!(clone(f), f, "a constant needs no copy");
1867        let a = push(&Q, f, &[3], false);
1868        assert_ne!(index(a), index(f), "a change makes a new entry");
1869        assert_eq!(size(f), 2);
1870        assert_eq!(size(a), 3);
1871        release(f);
1872        assert_eq!(size(f), 2, "a constant is never freed");
1873    }
1874
1875    #[test]
1876    fn associative_keys_are_ordered_and_walkable() {
1877        let s = Shape {
1878            elem_words: 1,
1879            assoc: true,
1880            key: KeyKind::Int {
1881                width: 8,
1882                signed: true,
1883            },
1884            obj: false,
1885            nested: Nested::NONE,
1886        };
1887        let mut h = 0;
1888        for k in [5i128, -3, 2] {
1889            h = store(&s, h, Key::Int(k), &[k as u64]);
1890        }
1891        assert_eq!(neighbor(h, None, true), Some(Key::Int(-3)));
1892        assert_eq!(neighbor(h, Some(&Key::Int(2)), true), Some(Key::Int(5)));
1893        assert_eq!(neighbor(h, Some(&Key::Int(5)), true), None);
1894        assert_eq!(key_at(h, 0), Some(Key::Int(-3)));
1895        assert_eq!(key_at(h, 1), Some(Key::Int(2)));
1896        assert_eq!(key_at(h, 2), Some(Key::Int(5)));
1897        assert_eq!(s.key_from_bits(&[0xfd]), Some(Key::Int(-3)));
1898        assert_eq!(s.key_from_bits(&[0x1fd]), None, "an unknown bit");
1899    }
1900
1901    // Classes plan Task 10: collections of class handles own one object
1902    // reference per element. Every test below reads only per-handle
1903    // `objects::refcount` (the heap's `live_count` is process-global and the
1904    // tests run concurrently) and allocates objects of its own template id.
1905
1906    /// A queue / dynamic array of class handles: 64-bit element, two words.
1907    const OQ: Shape = Shape {
1908        elem_words: 2,
1909        assoc: false,
1910        key: KeyKind::Str,
1911        obj: true,
1912        nested: Nested::NONE,
1913    };
1914
1915    fn oa() -> Shape {
1916        Shape {
1917            elem_words: 2,
1918            assoc: true,
1919            key: KeyKind::Int {
1920                width: 8,
1921                signed: true,
1922            },
1923            obj: true,
1924            nested: Nested::NONE,
1925        }
1926    }
1927
1928    /// A fresh object (refcount 1, the caller's reference).
1929    fn obj(template: u32) -> u64 {
1930        crate::objects::alloc(crate::netlist::ClassTemplateId(template), 1)
1931    }
1932
1933    fn rc(h: u64) -> Option<u32> {
1934        crate::objects::refcount(h)
1935    }
1936
1937    #[test]
1938    fn a_pushed_object_element_takes_the_callers_reference() {
1939        let a = obj(1013);
1940        let q = push(&OQ, 0, &[a, 0], false);
1941        assert_eq!(rc(a), Some(1), "the queue owns the one reference");
1942        release(q);
1943        assert_eq!(rc(a), None, "releasing the queue frees what it alone held");
1944    }
1945
1946    #[test]
1947    fn overwriting_deleting_and_clearing_release_the_old_element() {
1948        let (a, b, c) = (obj(1014), obj(1014), obj(1014));
1949        crate::objects::retain(a);
1950        crate::objects::retain(b);
1951        crate::objects::retain(c);
1952        // Each object now has two references: ours and the queue's.
1953        let mut q = push(&OQ, 0, &[a, 0], false);
1954        q = push(&OQ, q, &[b, 0], false);
1955        q = push(&OQ, q, &[c, 0], false);
1956        assert_eq!((rc(a), rc(b), rc(c)), (Some(2), Some(2), Some(2)));
1957        // Overwrite element 0 with `b` (the queue takes another reference we
1958        // acquire for it): `a` loses the queue's reference.
1959        crate::objects::retain(b);
1960        q = set(&OQ, q, 0, &[b, 0], true);
1961        assert_eq!(rc(a), Some(1), "an overwritten element is released");
1962        assert_eq!(rc(b), Some(3));
1963        q = delete_at(&OQ, q, 0);
1964        assert_eq!(rc(b), Some(2), "a deleted element is released");
1965        q = clear(&OQ, q);
1966        assert_eq!(rc(b), Some(1));
1967        assert_eq!(rc(c), Some(1), "clear releases every element");
1968        assert_eq!(size(q), 0);
1969        for h in [a, b, c] {
1970            crate::objects::release(h);
1971            assert_eq!(rc(h), None);
1972        }
1973        release(q);
1974    }
1975
1976    #[test]
1977    fn a_pop_hands_its_reference_to_the_caller() {
1978        let a = obj(1015);
1979        let q = push(&OQ, 0, &[a, 0], false);
1980        let mut out = [0, 0];
1981        let (q, ok) = pop(&OQ, q, &mut out, true);
1982        assert!(ok);
1983        assert_eq!(out[0], a);
1984        assert_eq!(rc(a), Some(1), "the reference moved, it was not dropped");
1985        crate::objects::release(out[0]);
1986        assert_eq!(rc(a), None);
1987        release(q);
1988    }
1989
1990    /// A queue of 128-bit elements holding two class handles (value bits 0
1991    /// and 64 of the 4-state packed `(unk << 128) | val` form).
1992    fn pair_q() -> Shape {
1993        let mut nested = Nested::NONE;
1994        nested.n = 2;
1995        nested.at[1] = 64;
1996        Shape {
1997            elem_words: 4,
1998            assoc: false,
1999            key: KeyKind::Str,
2000            obj: false,
2001            nested,
2002        }
2003    }
2004
2005    #[test]
2006    fn handles_nested_in_an_element_are_owned_and_released_with_it() {
2007        let s = pair_q();
2008        let (a, b, c, d) = (obj(1018), obj(1018), obj(1018), obj(1018));
2009        let mut q = push(&s, 0, &[a, b, 0, 0], false);
2010        q = push(&s, q, &[c, d, 0, 0], false);
2011        assert_eq!(
2012            (rc(a), rc(b), rc(c), rc(d)),
2013            (Some(1), Some(1), Some(1), Some(1))
2014        );
2015        // A copy-on-write clone that changes takes its own references.
2016        let copy = clone(q);
2017        let copy = delete_at(&s, copy, 1);
2018        assert_eq!(
2019            (rc(a), rc(c)),
2020            (Some(2), Some(1)),
2021            "the copy retained element 0"
2022        );
2023        assert_eq!(rc(d), Some(1));
2024        // Overwriting element 0 of `q` (the new value carries references of
2025        // its own) releases both handles it held; the copy still names them.
2026        crate::objects::retain(c);
2027        crate::objects::retain(d);
2028        let q = set(&s, q, 0, &[c, d, 0, 0], true);
2029        assert_eq!((rc(a), rc(b)), (Some(1), Some(1)));
2030        release(copy);
2031        assert_eq!((rc(a), rc(b)), (None, None), "the copy's drop frees them");
2032        // A pop hands both references to the caller.
2033        let mut out = [0; 4];
2034        let (q, ok) = pop(&s, q, &mut out, true);
2035        assert!(ok);
2036        assert_eq!((out[0], out[1]), (c, d));
2037        assert_eq!((rc(c), rc(d)), (Some(2), Some(2)));
2038        release(q);
2039        assert_eq!((rc(c), rc(d)), (Some(1), Some(1)));
2040        crate::objects::release(c);
2041        crate::objects::release(d);
2042        assert_eq!((rc(c), rc(d)), (None, None));
2043    }
2044
2045    #[test]
2046    fn an_ignored_write_gives_back_the_reference_it_was_handed() {
2047        let (a, b, c) = (obj(1016), obj(1016), obj(1016));
2048        let q = push(&OQ, 0, &[c, 0], false);
2049        let q = set(&OQ, q, 5, &[a, 0], true);
2050        assert_eq!(rc(a), None, "an out-of-range set releases the value");
2051        let q = insert(&OQ, q, -1, &[b, 0]);
2052        assert_eq!(rc(b), None, "an out-of-range insert releases the value");
2053        assert_eq!(rc(c), Some(1), "the element already there is untouched");
2054        release(q);
2055        assert_eq!(rc(c), None);
2056    }
2057
2058    #[test]
2059    fn a_copy_on_write_clone_takes_its_own_reference_per_element() {
2060        let a = obj(1017);
2061        let q = push(&OQ, 0, &[a, 0], false);
2062        let copy = clone(q);
2063        assert_eq!(rc(a), Some(1), "an unchanged clone shares the references");
2064        let b = obj(1017);
2065        let copy = push(&OQ, copy, &[b, 0], false);
2066        assert_eq!(rc(a), Some(2), "unsharing the copy retains each element");
2067        release(q);
2068        assert_eq!(rc(a), Some(1), "the original's holder let go of its own");
2069        release(copy);
2070        assert_eq!(rc(a), None);
2071        assert_eq!(rc(b), None);
2072    }
2073
2074    #[test]
2075    fn resized_append_slice_and_from_copies_retain_what_they_copy() {
2076        let (a, b) = (obj(1018), obj(1018));
2077        let q = push(&OQ, push(&OQ, 0, &[a, 0], false), &[b, 0], false);
2078        assert_eq!((rc(a), rc(b)), (Some(1), Some(1)));
2079        let grown = resized(&OQ, q, 3, &[a, 0]);
2080        // `a` once from the copied element 0 and once for the default that
2081        // fills element 2; `b` once for the copied element 1.
2082        assert_eq!((rc(a), rc(b)), (Some(3), Some(2)));
2083        let sl = slice(&OQ, q, 1, 1);
2084        assert_eq!(rc(b), Some(3));
2085        let ap = append(&OQ, 0, q);
2086        assert_eq!((rc(a), rc(b)), (Some(4), Some(4)));
2087        let fc = from_copies(&OQ, &[a, 0, b, 0]);
2088        assert_eq!((rc(a), rc(b)), (Some(5), Some(5)));
2089        for h in [q, grown, sl, ap, fc] {
2090            release(h);
2091        }
2092        assert_eq!(
2093            (rc(a), rc(b)),
2094            (None, None),
2095            "no holder left, nothing leaks"
2096        );
2097    }
2098
2099    #[test]
2100    fn an_associative_array_owns_values_and_its_default() {
2101        let (a, b, d) = (obj(1019), obj(1019), obj(1019));
2102        let s = oa();
2103        let mut h = store(&s, 0, Key::Int(1), &[a, 0]);
2104        crate::objects::retain(a);
2105        h = store(&s, h, Key::Int(2), &[a, 0]);
2106        assert_eq!(rc(a), Some(2), "two keys, two references");
2107        h = store(&s, h, Key::Int(1), &[b, 0]);
2108        assert_eq!(rc(a), Some(1), "replacing a value releases the old one");
2109        h = delete_key(&s, h, &Key::Int(2));
2110        assert_eq!(rc(a), None, "deleting a key releases its value");
2111        h = delete_key(&s, h, &Key::Int(99));
2112        assert_eq!(rc(b), Some(1), "deleting an absent key changes nothing");
2113        h = set_default(&s, h, &[d, 0]);
2114        assert_eq!(rc(d), Some(1));
2115        h = clear(&s, h);
2116        assert_eq!(rc(b), None);
2117        assert_eq!(rc(d), Some(1), "clear keeps the default's reference");
2118        release(h);
2119        assert_eq!(rc(d), None, "freeing the array frees its default");
2120    }
2121
2122    /// A class-handle-keyed map (LRM 7.8.3) owns one reference per key:
2123    /// a new key retains, a re-store of the key does not, delete/clear/free
2124    /// and a copy-on-write unshare release/retain.
2125    #[test]
2126    fn a_handle_keyed_map_owns_its_keys() {
2127        let (k1, k2) = (obj(1021), obj(1021));
2128        let s = Shape {
2129            elem_words: 1,
2130            assoc: true,
2131            key: KeyKind::Obj,
2132            obj: false,
2133            nested: Nested::NONE,
2134        };
2135        let key = |k: u64| Key::Int(i128::from(k));
2136        let mut h = store(&s, 0, key(k1), &[1]);
2137        assert_eq!(rc(k1), Some(2), "a new key takes its own reference");
2138        h = store(&s, h, key(k1), &[2]);
2139        assert_eq!(rc(k1), Some(2), "re-storing an existing key adds none");
2140        h = store(&s, h, key(k2), &[3]);
2141        let c = clone(h);
2142        let c = store(&s, c, key(k2), &[4]);
2143        assert_eq!(rc(k1), Some(3), "an unshared copy retains each key");
2144        h = delete_key(&s, h, &key(k1));
2145        assert_eq!(rc(k1), Some(2), "deleting a key releases it");
2146        h = clear(&s, h);
2147        assert_eq!(rc(k2), Some(2), "clear releases every key");
2148        release(h);
2149        release(c);
2150        assert_eq!(rc(k1), Some(1), "freeing the copy releases its keys");
2151        assert_eq!(rc(k2), Some(1));
2152        crate::objects::release(k1);
2153        crate::objects::release(k2);
2154    }
2155
2156    #[test]
2157    fn a_permutation_moves_references_without_changing_counts() {
2158        let (a, b) = (obj(1020), obj(1020));
2159        let q = push(&OQ, push(&OQ, 0, &[a, 0], false), &[b, 0], false);
2160        let q = permute(&OQ, q, &[1, 0]);
2161        let mut out = [0, 0];
2162        assert!(get(q, 0, &mut out));
2163        assert_eq!(out[0], b);
2164        assert_eq!((rc(a), rc(b)), (Some(1), Some(1)));
2165        release(q);
2166        assert_eq!((rc(a), rc(b)), (None, None));
2167    }
2168
2169    /// `build-aot` refuses, naming the feature `collection`, a design whose process
2170    /// holds a collection that is not a declaration's constant: no `.sv`
2171    /// source reaches that state (every build-time collection is made by
2172    /// `frozen_from_words`/`frozen_map`), so this is the refusal sweep's
2173    /// exercise of that guard (`aot_refusal_sweep.rs` names this test).
2174    #[test]
2175    fn frozen_snapshot_refuses_a_live_collection() {
2176        let live = from_words(&Q, &[1, 2]);
2177        let why = frozen_snapshot().expect_err("a live collection cannot be frozen into an image");
2178        assert!(why.contains("not a constant"), "{why}");
2179        release(live);
2180    }
2181}