fix(format): parse compound datatype versions 1 and 2 correctly
Compound datasets written with default libver bounds (datatype message version 1, i.e. plain h5py.File(path, 'w')) could not be read: the v1 member layout has 28 bytes of legacy array fields after the byte offset (dimensionality 1, reserved 3, permutation 4, reserved 4, four sizes 16) and the parser skipped 24, so every following member was read 4 bytes off. v2 was also wrong: it keeps the 8-byte name padding and has no array fields. Found by adding a default-libver axis to the h5py-generated-file tests (HDF5 2.0 raised the default low bound to 1.8, so "default" files are a distinct format path from libver='latest'). Adds byte-level v1/v2 regression tests, a truncation test, and fuzz corpus seeds for v1 compound and native complex. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
a8ab9ca054
commit
926dc457e0
@@ -235,17 +235,31 @@ fn h5py_reads_our_array_dataset() {
|
||||
#[test]
|
||||
#[ignore = "requires Python h5py module"]
|
||||
fn read_h5py_generated_compound() {
|
||||
let path = std::env::temp_dir().join("clawhdf5_h5py_compound.h5");
|
||||
check_h5py_generated_compound("latest", ", libver='latest'");
|
||||
}
|
||||
|
||||
/// Same file written with h5py's default format bounds. HDF5 2.0 raised the
|
||||
/// default low bound to 1.8, so "default" files exercise different on-disk
|
||||
/// structures than both `libver='latest'` and pre-2.0 defaults.
|
||||
#[test]
|
||||
#[ignore = "requires Python h5py module"]
|
||||
fn read_h5py_generated_compound_default_libver() {
|
||||
check_h5py_generated_compound("default", "");
|
||||
}
|
||||
|
||||
fn check_h5py_generated_compound(tag: &str, libver_kw: &str) {
|
||||
let path = std::env::temp_dir().join(format!("clawhdf5_h5py_compound_{tag}.h5"));
|
||||
let gen_script = format!(
|
||||
r#"
|
||||
import h5py, numpy as np
|
||||
dt = np.dtype([('x', 'f8'), ('y', 'f8'), ('id', 'i4')])
|
||||
data = np.array([(1.0, 2.0, 10), (3.0, 4.0, 20)], dtype=dt)
|
||||
f = h5py.File('{}', 'w', libver='latest')
|
||||
f = h5py.File('{}', 'w'{})
|
||||
f.create_dataset('particles', data=data)
|
||||
f.close()
|
||||
"#,
|
||||
path.display()
|
||||
path.display(),
|
||||
libver_kw
|
||||
);
|
||||
h5py_read(&path, &gen_script);
|
||||
|
||||
@@ -363,17 +377,31 @@ else:
|
||||
#[test]
|
||||
#[ignore = "requires Python h5py module"]
|
||||
fn read_h5py_generated_enum() {
|
||||
let path = std::env::temp_dir().join("clawhdf5_h5py_enum.h5");
|
||||
check_h5py_generated_enum("latest", ", libver='latest'");
|
||||
}
|
||||
|
||||
/// Same file written with h5py's default format bounds. HDF5 2.0 raised the
|
||||
/// default low bound to 1.8, so "default" files exercise different on-disk
|
||||
/// structures than both `libver='latest'` and pre-2.0 defaults.
|
||||
#[test]
|
||||
#[ignore = "requires Python h5py module"]
|
||||
fn read_h5py_generated_enum_default_libver() {
|
||||
check_h5py_generated_enum("default", "");
|
||||
}
|
||||
|
||||
fn check_h5py_generated_enum(tag: &str, libver_kw: &str) {
|
||||
let path = std::env::temp_dir().join(format!("clawhdf5_h5py_enum_{tag}.h5"));
|
||||
let gen_script = format!(
|
||||
r#"
|
||||
import h5py, numpy as np
|
||||
dt = h5py.enum_dtype({{"RED": 0, "GREEN": 1, "BLUE": 2}}, basetype=np.int32)
|
||||
data = np.array([1, 0, 2, 1], dtype=np.int32)
|
||||
f = h5py.File('{}', 'w', libver='latest')
|
||||
f = h5py.File('{}', 'w'{})
|
||||
f.create_dataset('colors', data=data, dtype=dt)
|
||||
f.close()
|
||||
"#,
|
||||
path.display()
|
||||
path.display(),
|
||||
libver_kw
|
||||
);
|
||||
h5py_read(&path, &gen_script);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user