fix(format): refuse a dense link or attribute too big for the heap
A message in dense storage is a fractal heap object, and an object must
fit one direct block: 65 515 bytes here, since the writer has no
huge-object path. A bigger one (a soft link with a 80 000-byte target in
a group of more than 8 links) was written without error, cut off at the
end of its block, and libhdf5 could not list the group ("object overruns
end of direct block"). finish() now fails with an error that names the
limit, for links and for dense attributes; a 65 001-byte soft link target
still works and h5py reads it back. The heap packer also skips a child
indirect block whose blocks are all too small for the next object instead
of walking it.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -765,3 +765,45 @@ fn dense_attributes_past_the_direct_blocks_of_the_root() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_link_too_big_for_dense_storage_is_an_error() {
|
||||
// A link message must fit one fractal heap direct block (64 KiB less
|
||||
// its header); the writer has no huge-object path. It used to be
|
||||
// written anyway, cut off, and libhdf5 could not list the group.
|
||||
let mut b = FileBuilder::new();
|
||||
for i in 0..10 {
|
||||
b.create_dataset(&format!("d{i}")).with_i32_data(&[i]);
|
||||
}
|
||||
b.add_soft_link("s", &"/y".repeat(40_000));
|
||||
let err = b.finish().unwrap_err().to_string();
|
||||
assert!(err.contains("fractal heap object holds at most"), "{err}");
|
||||
// The same for a dense attribute.
|
||||
let mut b = FileBuilder::new();
|
||||
let x = b.create_dataset("x");
|
||||
x.with_i32_data(&[1]);
|
||||
for i in 0..9 {
|
||||
x.set_attr(&format!("a{i}"), AttrValue::I64(i));
|
||||
}
|
||||
x.set_attr("big", AttrValue::F64Array(vec![0.5; 9_000]));
|
||||
let err = b.finish().unwrap_err().to_string();
|
||||
assert!(err.contains("fractal heap object holds at most"), "{err}");
|
||||
|
||||
// Just under the limit is fine, and libhdf5 reads it back.
|
||||
skip_if_no_python!();
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut b = FileBuilder::new();
|
||||
for i in 0..10 {
|
||||
b.create_dataset(&format!("d{i}")).with_i32_data(&[i]);
|
||||
}
|
||||
let target = format!("/{}", "y".repeat(65_000));
|
||||
b.add_soft_link("s", &target);
|
||||
let path = write(&dir, "long_soft.h5", b);
|
||||
let out = h5py(
|
||||
&path,
|
||||
"with h5py.File(path, 'r') as f:\n\
|
||||
\x20 print(json.dumps([len(f), len(f.get('s', getlink=True).path)]))",
|
||||
);
|
||||
assert_eq!(out, "[11, 65001]");
|
||||
h5dump_ok(&path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user