format, wasm: Storage::hint, fetched by the lazy reader with a pass's misses

A parser often learns where the next structures are (a node's children,
a structure's body once its prefix gives its length) before it reads
them one at a time. Over openUrl's restartable reader a structure only
reached after a miss costs a pass, and a round trip, of its own.

- clawhdf5-format: `Storage::hint(offset, len)`, "about to be read by
  this operation". Default: nothing (every backend that reads when
  asked); `&T`, `Box`, `Arc` and the facade's `FileData` forward it
  (shifted past a user block, clamped to the file).
- clawhdf5-wasm `LazyStorage` records hinted blocks it lacks. A pass
  that misses nothing ignores them (a hint never adds a round trip); a
  pass that misses also asks for them, in file order, while the pass
  stays within what is left of the operation's `maxFetch` budget (a
  hint never makes a call fail). At most 1 MiB (or a block) per hint
  and 65536 blocks per pass are recorded, whatever a file makes a
  parser hint. `Operation::attempt` follows hints; the plain
  `LazyStorage::attempt` does not. `run_blocking` and the browser's
  driver use the former. `LazyStats::hinted_blocks` counts them.

No parser hints yet: results, passes and requests are unchanged.
Tests: hinted blocks come with a miss and never alone (cached ones
skipped, a plain attempt ignores them), and stay within the fetch
budget, a hint past the end or longer than the file harmless.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-27 22:41:09 -05:00
co-authored by Claude Opus 5.5
parent 425585ee71
commit ca81c3ebfa
4 changed files with 239 additions and 7 deletions
+15
View File
@@ -372,6 +372,21 @@ impl Storage for FileData {
fn as_contiguous(&self) -> Option<&[u8]> {
self.contiguous()
}
fn hint(&self, offset: u64, len: usize) {
if self.contiguous().is_some() {
return;
}
if let Backing::Storage(s) = &self.backing {
// Within the HDF5 data, as a read would be clamped.
let size = Storage::len(self);
let start = offset.min(size);
let len = usize::try_from(size - start).map_or(len, |avail| avail.min(len));
if len > 0 {
s.hint(self.base + start, len);
}
}
}
}
/// `bytes`, a backend's answer to a read of `len` bytes, without anything