fix(format): refuse path-like group and dataset names

FileWriter writes the root group plus one level of groups; it has no way
to create intermediate groups. create_group("a/b") therefore stored a
single link literally named "a/b", which no HDF5 reader can resolve
(h5py: "component not found"). Nesting would mean restructuring the
writer's layout around a group tree, so for now finish() rejects any
group, dataset or external-link name that is empty, "." or contains '/'.
Attribute names may still contain '/'.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-25 21:13:08 -05:00
co-authored by Claude Opus 5.5
parent 14876b8ae5
commit bc820fbd8c
2 changed files with 65 additions and 0 deletions
@@ -524,3 +524,42 @@ fn h5py_reads_all_attributes_next_to_an_empty_string() {
assert_eq!(out, r#"["", "héllo", ["", ""], 3]"#);
h5dump_ok(&path);
}
// ---- 6. path-like names ----
#[test]
fn slash_in_a_group_or_dataset_name_is_an_error() {
// Measured: create_group("a/b") wrote one link literally named "a/b",
// which h5py cannot reach ("component not found"). The writer has no
// nested groups, so such names are refused.
let mut fw = FileWriter::new();
let mut g = fw.create_group("a/b");
g.create_dataset("c").with_f64_data(&[1.0]);
fw.add_group(g.finish());
assert!(fw.finish().is_err());
let mut fw = FileWriter::new();
fw.create_dataset("x/y").with_f64_data(&[1.0]);
assert!(fw.finish().is_err());
let mut fw = FileWriter::new();
let mut g = fw.create_group("g");
g.create_dataset("x/y").with_f64_data(&[1.0]);
fw.add_group(g.finish());
assert!(fw.finish().is_err());
for bad in ["", "."] {
let mut fw = FileWriter::new();
fw.create_dataset(bad).with_f64_data(&[1.0]);
assert!(fw.finish().is_err(), "{bad:?}");
}
// One level of groups still works, and '/' stays legal in attribute names.
let mut fw = FileWriter::new();
let mut g = fw.create_group("g");
g.create_dataset("c").with_f64_data(&[1.0]);
g.set_attr("m/s", AttrValue::I64(1));
fw.add_group(g.finish());
let bytes = fw.finish().unwrap();
header_at(&bytes, "g/c");
}