HDF5 2.0 native complex as a first-class type on read; Python libver=
- Datatype::parse returns Datatype::Complex for class 11 (also inside
compounds, arrays and VL types) instead of the {r, i} compound view.
- Facade: DType::Complex(Box<DType>); read_complex_f32/f64 accept it.
- h5rs dump/ls/diff print native complex as h5dump/h5ls/h5diff 2.2.0 do
(checked against a fixture written by h5py 3.16 / libhdf5 2.0.0);
dump --json keeps the {r, i} compound (hdf5-json has no complex class).
- clawhdf5-wasm reads native complex datasets as [re, im] pairs.
- Python: clawhdf5.File(path, 'w', libver=...) with h5py's values,
mapped to FileBuilder::libver_bounds; 'v108' output opens in HDF5 1.8.23.
- Docs: known-issues entry moved to Fixed (history), CHANGELOG, READMEs.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -145,13 +145,16 @@ pub enum Datatype {
|
||||
/// imaginary, in rectangular form. `size` is twice the base size and the
|
||||
/// base is an IEEE float.
|
||||
///
|
||||
/// This variant exists for **writing** (see
|
||||
/// [`Datatype::parse`] returns this variant for every class-11 message,
|
||||
/// also inside compounds, arrays and variable-length types. Readers that
|
||||
/// want the member view (`{r, i}`, as h5py writes complex numbers by
|
||||
/// default) use [`Datatype::complex_as_compound`]; `read_compound_fields`
|
||||
/// accepts this variant directly.
|
||||
///
|
||||
/// Writing it is opt-in (see
|
||||
/// `type_builders::make_native_complex_f64_type`): only libhdf5 2.0 and
|
||||
/// newer can read class 11, so it is opt-in and h5py's compound `{r, i}`
|
||||
/// stays the default complex encoding. [`Datatype::parse`] still
|
||||
/// surfaces a class-11 message as that equivalent `{r, i}` compound, so
|
||||
/// every compound reader handles both encodings; parsing what this
|
||||
/// variant serializes therefore yields a `Compound`, not a `Complex`.
|
||||
/// newer can read class 11, so h5py's compound `{r, i}` stays the
|
||||
/// default complex encoding.
|
||||
Complex { size: u32, base_type: Box<Datatype> },
|
||||
}
|
||||
|
||||
@@ -857,9 +860,7 @@ impl Datatype {
|
||||
// Complex number (HDF5 2.0, datatype version 5). The properties
|
||||
// are a single base floating-point datatype message; an element
|
||||
// is two consecutive base-type values (real, imaginary). There
|
||||
// is no member list. Surface it as the equivalent two-member
|
||||
// compound `{r, i}` — the same shape h5py writes for numpy
|
||||
// complex dtypes — so downstream compound readers work as-is.
|
||||
// is no member list.
|
||||
if version != 5 {
|
||||
return Err(FormatError::InvalidDatatypeVersion {
|
||||
class: class_id,
|
||||
@@ -875,7 +876,13 @@ impl Datatype {
|
||||
actual: size as usize,
|
||||
});
|
||||
}
|
||||
Ok((Self::complex_as_compound(size, &base_type), pos))
|
||||
Ok((
|
||||
Datatype::Complex {
|
||||
size,
|
||||
base_type: Box::new(base_type),
|
||||
},
|
||||
pos,
|
||||
))
|
||||
}
|
||||
_ => Err(FormatError::InvalidDatatypeClass(class_id)),
|
||||
}
|
||||
@@ -1174,9 +1181,8 @@ impl Datatype {
|
||||
|
||||
/// The `{r, i}` compound equivalent to a native complex type of `size`
|
||||
/// bytes over `base_type`: `r` at offset 0, `i` right after it — the
|
||||
/// shape h5py writes for numpy complex dtypes, and what [`Self::parse`]
|
||||
/// returns for a class-11 message. Readers that meet a
|
||||
/// [`Datatype::Complex`] handle it through this view.
|
||||
/// shape h5py writes for numpy complex dtypes. Readers that want a
|
||||
/// [`Datatype::Complex`] as members handle it through this view.
|
||||
pub fn complex_as_compound(size: u32, base_type: &Datatype) -> Datatype {
|
||||
let base_size = base_type.type_size();
|
||||
Datatype::Compound {
|
||||
@@ -1849,21 +1855,24 @@ mod tests {
|
||||
fn test_complex_v5_from_hdf5_2_0() {
|
||||
let (dt, consumed) = Datatype::parse(&COMPLEX_F64_HDF5_2_0).unwrap();
|
||||
assert_eq!(consumed, COMPLEX_F64_HDF5_2_0.len());
|
||||
match dt {
|
||||
Datatype::Compound { size, members } => {
|
||||
assert_eq!(size, 16);
|
||||
assert_eq!(members.len(), 2);
|
||||
assert_eq!((members[0].name.as_str(), members[0].byte_offset), ("r", 0));
|
||||
assert_eq!((members[1].name.as_str(), members[1].byte_offset), ("i", 8));
|
||||
for m in &members {
|
||||
assert!(matches!(
|
||||
m.datatype,
|
||||
Datatype::FloatingPoint { size: 8, .. }
|
||||
));
|
||||
}
|
||||
match &dt {
|
||||
Datatype::Complex { size, base_type } => {
|
||||
assert_eq!(*size, 16);
|
||||
assert!(matches!(
|
||||
base_type.as_ref(),
|
||||
Datatype::FloatingPoint { size: 8, .. }
|
||||
));
|
||||
}
|
||||
other => panic!("expected Compound, got {other:?}"),
|
||||
other => panic!("expected Complex, got {other:?}"),
|
||||
}
|
||||
// The member view is h5py's `{r, i}` compound.
|
||||
let Datatype::Compound { members, .. } =
|
||||
Datatype::complex_as_compound(16, &crate::type_builders::make_f64_type())
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
assert_eq!((members[0].name.as_str(), members[0].byte_offset), ("r", 0));
|
||||
assert_eq!((members[1].name.as_str(), members[1].byte_offset), ("i", 8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1887,7 +1896,7 @@ mod tests {
|
||||
assert_eq!(members.len(), 2);
|
||||
assert!(matches!(
|
||||
&members[0].datatype,
|
||||
Datatype::Compound { size: 16, members } if members.len() == 2
|
||||
Datatype::Complex { size: 16, .. }
|
||||
));
|
||||
assert_eq!(
|
||||
(members[1].name.as_str(), members[1].byte_offset),
|
||||
@@ -1918,12 +1927,9 @@ mod tests {
|
||||
assert_eq!(dt.serialize(), COMPLEX_F64_HDF5_2_0);
|
||||
assert_eq!(dt.type_size(), 16);
|
||||
dt.check_encodable().unwrap();
|
||||
// Parsing surfaces class 11 as the equivalent `{r, i}` compound.
|
||||
// Parsing returns the native complex type unchanged.
|
||||
let (parsed, _) = Datatype::parse(&dt.serialize()).unwrap();
|
||||
assert_eq!(
|
||||
parsed,
|
||||
Datatype::complex_as_compound(16, &crate::type_builders::make_f64_type())
|
||||
);
|
||||
assert_eq!(parsed, dt);
|
||||
|
||||
let f32c = make_native_complex_f32_type().serialize();
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user