Skip to main content

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