| 2236 | Vec::with_capacity(num_names as usize + 1); |
| 2237 | let anon_addr = Address::from_blake3_hash(*Name::anon().get_hash()); |
| 2238 | names_lookup.insert(anon_addr, Name::anon()); |
| 2239 | for _ in 0..num_names { |
| 2240 | let addr = get_address(&mut buf)?; |
| 2241 | let name = get_name_component(&mut buf, &names_lookup)?; |
| 2242 | name_reverse_index.push(addr.clone()); |
| 2243 | names_lookup.insert(addr, name); |
| 2244 | } |
| 2245 | index.name_reverse_index = name_reverse_index; |
| 2246 | |
| 2247 | // Section 5: Named — keep `name → addr`; the full ConstantMeta |
| 2248 | // (arena, CallSite, ...) is parsed then discarded. The section |
| 2249 | // offset is recorded so a [`NamedMetaCursor`] can re-walk it |
| 2250 | // without re-parsing the earlier sections. |
| 2251 | index.named_section_offset = data.len() - buf.len(); |
| 2252 | let num_named = get_u64(&mut buf)?; |
| 2253 | for _ in 0..num_named { |
| 2254 | let name_idx = get_u64(&mut buf)? as usize; |
| 2255 | let name_addr = |
| 2256 | index.name_reverse_index.get(name_idx).cloned().ok_or_else(|| { |
| 2257 | format!( |
| 2258 | "parse_lazy_index: §5 name index {name_idx} out of range ({} \ |
| 2259 | names){PRE_COMPACT_KEYS}", |
| 2260 | index.name_reverse_index.len() |
| 2261 | ) |
| 2262 | })?; |
| 2263 | // Entry header: const rank + per-name hint + metadata blob |
| 2264 | // length. The blob is SKIPPED — this is the payoff of the |
| 2265 | // length-prefixed layout: the lazy index never parses metadata |
| 2266 | // arenas just to find entry boundaries. A `NamedMetaCursor` |
| 2267 | // re-opened at `named_section_offset` parses them on demand. |
| 2268 | let const_rank = get_u64(&mut buf)? as usize; |
| 2269 | let addr = |
| 2270 | ConstGet::Slices(&index.consts).addr(const_rank, "parse_lazy_index")?; |
| 2271 | let hints = unfuse_opt_hint(get_u64(&mut buf)?) |
| 2272 | .map_err(|e| format!("parse_lazy_index: {e}"))?; |
| 2273 | let meta_len = get_u64(&mut buf)? as usize; |
| 2274 | if buf.len() < meta_len { |
| 2275 | return Err(format!( |
| 2276 | "parse_lazy_index: §5 metadata blob needs {meta_len} bytes, \ |
| 2277 | have {}{PRE_COMPACT_KEYS}", |
| 2278 | buf.len() |
| 2279 | )); |
| 2280 | } |
| 2281 | buf = &buf[meta_len..]; |
| 2282 | let name = names_lookup.get(&name_addr).cloned().ok_or_else(|| { |
| 2283 | format!("parse_lazy_index: missing name for addr {:?}", name_addr) |
| 2284 | })?; |
| 2285 | index.named.push(LazyNamed { name, addr, hints }); |
| 2286 | } |
| 2287 | |
| 2288 | // Section 6: Comms — carried verbatim (tiny; empty for |
| 2289 | // compile-produced envs). The check path ignores them, but |
| 2290 | // `Env::from_lazy_index` consumers (e.g. `ix diff`) want parity |
| 2291 | // with the full reader. |
| 2292 | let num_comms = get_u64(&mut buf)?; |
| 2293 | for _ in 0..num_comms { |
| 2294 | let addr = get_address(&mut buf)?; |
| 2295 | let comm = Comm::get(&mut buf)?; |