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