Parse an `.ixe` buffer into a metadata-light, **zero-copy** index for the anon/lazy check path (see [`LazyIndex`]). Mirrors [`Env::get`]'s section walk and reuses the same battle-tested parsers (so every metadata variant — e.g. `CallSite` — is handled), but: - constants are recorded as `(addr, offset, len)` windows into `data` rather than copied/stored, and their bodies are never parsed here; - t
(data: &[u8])
| 1530 | |
| 1531 | // ============================================================================ |
| 1532 | // Env serialization |
| 1533 | // ============================================================================ |
| 1534 | |
| 1535 | use super::comm::Comm; |
| 1536 | use super::env::{Env, LazyConstSlice, LazyIndex, LazyNamed}; |
| 1537 | #[cfg(not(target_arch = "riscv64"))] |
| 1538 | use super::merkle::merkle_root_canonical_sorted; |
| 1539 | use super::merkle::{merkle_root_canonical, zero_address}; |
| 1540 | |
| 1541 | impl Env { |
| 1542 | /// Tag4 flag for Env (0xE). |
| 1543 | pub const FLAG: u8 = 0xE; |
| 1544 | |
| 1545 | /// `.ixe` format version, carried in the header's Tag4 size field |
| 1546 | /// (versions < 8 cost zero extra bytes). Any change to serialized |
| 1547 | /// bytes bumps this. Readers reject a mismatch; there is no |
| 1548 | /// back-compat reading of old versions — `.ixe` files are |
| 1549 | /// regenerated artifacts. Mirrors `Ixon.Env.VERSION` in |
| 1550 | /// `Ix/Ixon.lean`. |
| 1551 | pub const VERSION: u64 = 1; |
| 1552 | |
| 1553 | /// Serialize an Env to bytes. |
| 1554 | /// |
| 1555 | /// Streaming design: for each section, collect only the *keys* from the |
| 1556 | /// underlying DashMap, sort them (in parallel for the big ones), then |
| 1557 | /// look up each value via `DashMap::get` and serialize it. The `Ref` |
| 1558 | /// guard returned by `get` drops at the end of each loop iteration, so |
| 1559 | /// at most one value is held live beyond the DashMap's own storage — |
| 1560 | /// peak RAM stays close to the steady-state env size instead of 2×. |
| 1561 | /// |
| 1562 | /// Why not just iterate the DashMap directly? Serialization requires a |
| 1563 | /// canonical order (byte-determinism across runs and across different |
| 1564 | /// insertion orders), and DashMap iteration order is shard-dependent. |
| 1565 | /// Sorting the keys is the minimum work to guarantee that. |
| 1566 | pub fn put(&self, buf: &mut Vec<u8>) -> Result<(), String> { |
| 1567 | #[cfg(not(target_arch = "riscv64"))] |
| 1568 | use rayon::slice::ParallelSliceMut; |
| 1569 | |
| 1570 | // Chatty per-section logging, off unless IX_VERBOSE=1 (or its |
| 1571 | // pipeline-wide alias IX_COMPILE_DBG), so we can diagnose |
| 1572 | // serialization stalls on huge envs (Mathlib: ~1M consts). |
| 1573 | let verbose = std::env::var("IX_VERBOSE").is_ok() |
| 1574 | || std::env::var("IX_COMPILE_DBG").is_ok(); |
| 1575 | let overall_start = std::time::Instant::now(); |
| 1576 | |
| 1577 | // Header: Tag4 with flag=0xE, size=VERSION (format version) |
| 1578 | Tag4::new(Self::FLAG, Self::VERSION).put(buf); |
| 1579 | |
| 1580 | // ───────────────────────────────────────────────────────────────────── |
| 1581 | // Canonical merkle root over consts.keys() |
| 1582 | // |
| 1583 | // Hoisted before section 1 so we can sort const_addrs once and reuse |
| 1584 | // it for section 2 below. Always 32 bytes (non-optional) — empty |
| 1585 | // const sets serialize as `zero_address()` (a fixed sentinel that |
| 1586 | // cannot collide with any non-empty canonical root since |
| 1587 | // `merkle_root_canonical` always returns a Blake3 hash for n>=1). |
| 1588 | // Verifiers recompute on deserialize and reject mismatches. |
| 1589 | // ───────────────────────────────────────────────────────────────────── |
nothing calls this directly
no test coverage detected