docs: M1 changelog after review: probe identity, speed, bounded reads

- Per-file probe output is identical for 696 of 697 files, not all:
  cve-2025-2310.h5's error string depends on which parallel chunk decode
  fails first, at f2ff2c4 as on this branch.
- The parser cores are generic (S: Storage + ?Sized); provisional A/B
  numbers against f2ff2c4, including the one bench that still shows
  ObjectHeader::parse slower when old and new are separate binaries.
- Reads sized by untrusted fields are bounded; the harness only accepts
  the known whole-file fallbacks.
- range-reads.md records why M1 went generic rather than &dyn.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-26 14:34:35 -05:00
co-authored by Claude Opus 5.5
parent 76c97f6c94
commit 895c79a2fe
2 changed files with 55 additions and 11 deletions
+12 -2
View File
@@ -261,6 +261,11 @@ every `file_data[a..b]` becomes `file.read_at(a, b - a)?`.
(binary size matters for wasm); `&dyn Storage` costs one indirect call per
structure read, negligible next to parsing. Hot raw-data loops keep their
speed through `as_contiguous()`.
*M1 outcome:* `&dyn` was not negligible for small structures — with it,
`ObjectHeader::parse` was ~25% and a 400-group facade listing ~14% slower
than the slice code (provisional, shared machine). The cores are now
generic (`S: Storage + ?Sized`), so the `&[u8]` wrappers get a `[u8]`
instance and `dyn Storage` is one more instance, not one per backend.
### (b) A page-cache "virtual slice"
@@ -392,7 +397,7 @@ fast path within benchmark noise.
B-tree v1/v2, fractal heap, fixed/extensible array, symbol table, group
v1/v2, shared messages, attributes, fill value, data layout — one commit
each. The old `&[u8]` signature stays as a thin wrapper over the new one
(`fn parse(data: &[u8], ..) { parse_in(data as &dyn Storage, ..) }`), so
(`fn parse(data: &[u8], ..) { parse_in(data, ..) }`, generic core), so
callers and the other crates don't move yet.
- Replace the 5 open-ended slices and 38 `len()` checks with bounded reads.
@@ -433,8 +438,13 @@ Total: roughly 6–10 engineer-weeks for M0–M4 (estimate, not measured).
- `impl Storage for [u8]` returns `Cow::Borrowed` — no copy, no allocation.
- Hot loops (raw-data copies, contiguous typed reads, `read_selection_native`)
branch once on `as_contiguous()` and then run today's code.
- `&dyn` dispatch is per structure, not per byte; parse code keeps working on
- The parser cores are generic over `S: Storage + ?Sized`, so the in-memory
instance has no dispatch at all; a remote backend behind `&dyn Storage`
pays one indirect call per structure read. Parse code keeps working on
the returned slice.
- Reads sized by untrusted fields cover what the parser uses (see
`CHANGELOG.md`, M1), so a hostile size costs no read of the rest of the
file.
- Gate: `crates/clawhdf5/benches/mmap_bench.rs`, the concurrent-read benches in
`clawhdf5-bench`, and the conformance run time, before and after each M1/M2
commit, on an otherwise idle machine. Anything outside noise blocks the