Commit Graph
2 Commits
Author SHA1 Message Date
osobhandClaude Fable 5.1 e38c8133bc feat(format): follow soft links; explicit errors for external links and external raw data
- Path resolution follows soft links in both old-style (symbol table, cache
  type 2) and new-style (compact and dense Link message) groups: absolute and
  relative targets, links to groups, links through links, with a depth limit
  so a link cycle is NestingDepthExceeded rather than a hang. A dangling link
  reports the target it could not find. Previously every soft link was
  PathNotFound.
- An external link is FormatError::ExternalLinkUnsupported { filename,
  object_path } instead of a misleading PathNotFound.
- Message 0x0007 (External Data Files) is now a known MessageType, and a
  dataset carrying it is FormatError::ExternalDataFilesUnsupported. Such a
  dataset has no data address in this file, so it would otherwise be read as
  "never written" and answered with fill values — wrong data, no error.
- Dense link iteration is shared between hard-link listing and the new
  symbolic-link lookup; entry listing behaviour is unchanged.
- h5py interop test for both libver settings.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-19 06:39:01 -07:00
osobhandClaude Fable 5.1 12847c6c66 feat(format): apply fill values to unallocated storage on read
HDF5 allocates lazily: a chunk nobody wrote doesn't exist in the file, and a
dataset nobody wrote has no data address. Such regions must read as the
dataset's fill value. There was no Fill Value message parser at all, so:

- a sparse chunked dataset read its holes as zeros — silently wrong whenever
  the fill value isn't zero (h5py `fillvalue=-1` came back as 0);
- a dataset that was created but never written failed with NoDataAllocated /
  "no address for chunked layout" where h5py returns a filled array.

New clawhdf5_format::fill_value: parses Fill Value messages v1-v3 and the old
0x0004 message (validated against HDF5 2.0 output under default and latest
libver), builds a fully filled dataset when there is no storage, and writes the
fill value into exactly the chunk-grid cells absent from the chunk index —
never mistaking a stored zero for a hole, clipping edge chunks, any rank. It is
skipped entirely for the default (zero) fill value. The chunk index dispatch is
extracted from read_chunked_data into a reusable list_chunks.

The reader, lazy and mmap facades apply it on full reads; selection reads go
through a fill-aware full read when the fill value matters. h5py interop test
compares against h5py's own readback, including a sparse 2-D dataset and a
hyperslab straddling allocated and unallocated chunks.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-19 06:35:47 -07:00