feat: write complex numbers, incl. HDF5 2.0 native complex (class 11)

- Datatype::Complex serializes class 11 version 5 byte-identically to
  libhdf5 2.2.0; containers holding it are written as version 5.
- DatasetBuilder::with_complex_f32/f64_data (h5py's {r, i} compound,
  default) and with_native_complex_f32/f64_data (class 11, opt-in);
  make_(native_)complex_f32/f64_type for attributes.
- Dataset::read_complex_f64/f32 read either form.
- Python create_dataset accepts complex64/complex128 (compound form).
- Parsing unchanged: class 11 still surfaces as {r, i}.
- Tests vs h5py 3.16 / libhdf5 2.0.0 and h5dump 2.2.0; docs.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-28 21:25:03 -05:00
co-authored by Claude Opus 5.5
parent bce07e9cb9
commit d0d5347cd9
21 changed files with 790 additions and 24 deletions
+39
View File
@@ -59,6 +59,45 @@
`'r+'` raises `NotImplementedError` before anything is written; inside a
compound or array type they still raise `TypeError`.
### Writing complex numbers, including HDF5 2.0's native complex type (2026-09-28)
- **h5py's form (default):** `DatasetBuilder::with_complex_f32_data` /
`with_complex_f64_data` take `[re, im]` pairs and write the compound
`{r, i}` h5py writes for numpy `complex64`/`complex128` (h5py 3.16 still
writes this by default); `make_complex_f32_type`/`make_complex_f64_type`
give the datatype for `AttrValue::Raw` attributes.
- **Native complex (opt-in):** `with_native_complex_f32_data` /
`with_native_complex_f64_data` and `make_native_complex_f32_type` /
`make_native_complex_f64_type` write `H5T_COMPLEX_IEEE_F32LE`/`F64LE`
(datatype class 11, version 5), through the new `Datatype::Complex`
variant. The encoding is byte-identical to libhdf5 2.2.0's
(`H5Odtype.c`: homogeneous, rectangular, base type follows), and a
compound, array or variable-length type holding one is written as
version 5, as libhdf5 raises it. No file-level version bound is needed:
the superblock and object headers we write already open in libhdf5 2.0.
Only libhdf5 2.0+ reads class 11 (Debian's h5dump 1.14 fails on the
object), hence opt-in. Checked on tank: h5py 3.16 (libhdf5 2.0.0) reads
datasets (contiguous and chunked+deflate), attributes, a compound member
and an array of them as numpy `complex64`/`complex128` with class 11;
h5dump 2.2.0 prints them as `H5T_COMPLEX_IEEE_F*LE`; `h5rs check --data`
finds no problems.
- **Reading:** `Dataset::read_complex_f64`/`read_complex_f32` return
`[re, im]` pairs from either form (and from h5py's files, native or
not). Parsing is unchanged: class 11 still surfaces as the `{r, i}`
compound (`Datatype::complex_as_compound`), so `Datatype::parse` never
returns `Datatype::Complex` (see `docs/known-issues.md`).
- **Breaking for exhaustive matches:** `Datatype` gained the `Complex`
variant; downstream `match`es over `Datatype` without a wildcard need an
arm (`Datatype::complex_as_compound(size, base)` gives the compound view).
- **Python:** `create_dataset` accepts `complex64` and `complex128` arrays,
written as h5py's compound (native class 11 is Rust-only).
- Tests: `datatype.rs` (byte equality with libhdf5's encoding),
`integration_tests::complex_datasets_and_attributes_round_trip`,
`writer_h5py_tests::h5py_reads_our_complex_datasets_and_attributes`
(runs h5dump 2.x when `CLAWHDF5_H5DUMP2` names one),
`h5py_interop_tests::h5py_complex_datasets_read_as_complex`, and
`test_write_read.py::test_roundtrip_complex` /
`test_read_native_complex_from_h5py`.
### `ObjectHeader::parse` back at its pre-M2/M3 speed (2026-09-27)
- Parsing a version-1 object header was 4% slower than before range-read
M2/M3 (`docs/known-issues.md`). The cause was the call to the per-chunk