diff --git a/CHANGELOG.md b/CHANGELOG.md index bb939de..3092ae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,9 @@ `h5rs check` passes and `h5rs dump` equals h5dump (`crates/clawhdf5/tests/writer_groups_interop.rs`, `crates/clawhdf5-tools/tests/h5rs_interop.rs`). +- **Non-ASCII link names were marked ASCII.** A group or dataset name such as + `größe` was written with the ASCII character set flag (h5py reported + `cset` 0 for it); it is now flagged UTF-8, as h5py writes it. - **libhdf5 could not add links to groups we wrote.** h5py in `"r+"` mode failed with "Unable to create link (message type not found)" on every group `FileWriter` wrote: libhdf5 reads a group's Group Info message before diff --git a/crates/clawhdf5-format/src/file_writer.rs b/crates/clawhdf5-format/src/file_writer.rs index b4a90aa..2d318f5 100644 --- a/crates/clawhdf5-format/src/file_writer.rs +++ b/crates/clawhdf5-format/src/file_writer.rs @@ -220,6 +220,16 @@ fn add_refcount(w: &mut ObjectHeaderWriter, refcount: u32) { } } +/// The character set a link name is written with: UTF-8 when it is not +/// plain ASCII, as h5py writes it. +fn name_charset(name: &str) -> CharacterSet { + if name.is_ascii() { + CharacterSet::Ascii + } else { + CharacterSet::Utf8 + } +} + /// The Link message for `link`, whose group and dataset targets are at the /// given addresses (indexed as in the writer tree). fn link_message(link: &writer_tree::Link, group_addrs: &[u64], ds_addrs: &[u64]) -> LinkMessage { @@ -242,7 +252,7 @@ fn link_message(link: &writer_tree::Link, group_addrs: &[u64], ds_addrs: &[u64]) name: link.name.clone(), link_target, creation_order: link.creation_order, - charset: CharacterSet::Ascii, + charset: name_charset(&link.name), } } diff --git a/crates/clawhdf5/tests/writer_groups_interop.rs b/crates/clawhdf5/tests/writer_groups_interop.rs index 1e8f879..fb7aa23 100644 --- a/crates/clawhdf5/tests/writer_groups_interop.rs +++ b/crates/clawhdf5/tests/writer_groups_interop.rs @@ -592,3 +592,21 @@ fn track_order_lists_members_in_creation_order() { assert_eq!(out, r#"["aaa", ["soft", "aaa"], 21]"#); h5dump_ok(&path); } + +#[test] +fn non_ascii_names_are_utf8() { + skip_if_no_python!(); + let dir = tempfile::tempdir().unwrap(); + let mut b = FileBuilder::new(); + b.create_dataset("größe/wert").with_i32_data(&[1]); + let path = write(&dir, "utf8.h5", b); + let out = h5py( + &path, + "with h5py.File(path, 'r') as f:\n\ + \x20 l = f.id.links.get_info('größe'.encode())\n\ + \x20 print(json.dumps([list(f), list(f['größe']), l.cset], ensure_ascii=False))", + ); + assert_eq!(out, r#"[["größe"], ["wert"], 1]"#); + let f = File::open(&path).unwrap(); + assert_eq!(f.dataset("größe/wert").unwrap().read_i32().unwrap(), [1]); +}