fix(format): decode the version-1 VDS mapping list HDF5 2.0 writes

With a 2.0 low version bound, libhdf5 stores the VDS mapping list as heap
block version 1: every entry starts with a flags byte (0x04 same file, no
file name; 0x01/0x02 file/dataset name shared with an earlier entry, whose
index is stored in place of the name). The parser treated only a leading
0x04 byte as special, so a 0x00 flags byte read as an empty (same-file)
name and shared names were read as garbage.

Decode it as H5D__virtual_load_layout does, refusing unknown flags,
forward references and block versions above 1.

Test: vds_interop::vds_mapping_block_version1_shared_names (h5py
libver=("v200","v200") with repeated long names; failed before with
"unknown dataspace selection type") plus the exact heap block as a unit
test.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-25 21:59:15 -05:00
co-authored by Claude Opus 5.5
parent 190918a478
commit 2c6c6c176e
4 changed files with 162 additions and 12 deletions
+36
View File
@@ -154,3 +154,39 @@ expect("v1.h5", "grid", "grid")
assert_matches_libhdf5(dir.path(), "v1.h5", "strided", "strided");
assert_matches_libhdf5(dir.path(), "v1.h5", "grid", "grid");
}
// ---------------------------------------------------------------------------
// Mapping list encoding
// ---------------------------------------------------------------------------
/// With a 2.0 low version bound libhdf5 writes the mapping list as block
/// version 1: a flags byte per entry, and repeated names stored as the index
/// of the entry that first spelled them out. The flags byte was mistaken for
/// an empty (same-file) name.
#[test]
fn vds_mapping_block_version1_shared_names() {
skip_if_no_python!();
let dir = tempfile::tempdir().unwrap();
generate(
dir.path(),
r#"
name = "a_rather_long_dataset_name"
with h5py.File("a_rather_long_source_file.h5", "w") as s:
s.create_dataset(name, data=np.arange(12.0) + 100)
with h5py.File("shared.h5", "w", libver=("v200", "v200")) as f:
f.create_dataset(name, data=np.arange(12.0) * -1)
lay = h5py.VirtualLayout(shape=(4, 4), dtype="f8")
for i in range(3):
src = h5py.VirtualSource("a_rather_long_source_file.h5", name, shape=(12,))
lay[i] = src[4 * i:4 * i + 4]
lay[3] = h5py.VirtualSource(".", name, shape=(12,))[0:4]
f.create_virtual_dataset("v", lay)
# the heap block must really be version 1 for this test to mean anything
raw = open("shared.h5", "rb").read()
gcol = raw.index(b"GCOL")
assert raw[gcol + 32] == 1, "expected a version-1 VDS mapping block"
expect("shared.h5", "v", "shared")
"#,
);
assert_matches_libhdf5(dir.path(), "shared.h5", "v", "shared");
}