feat(format): nested groups, soft/hard/external links and creation order in the writer
FileWriter wrote the root group plus one level of groups, and refused
path-like names. The writer now flattens its builders into a group tree
(writer_tree.rs) before layout:
- A name may be a path ("a/b/x", "/a/b/x" at the root); missing
intermediate groups are created as h5py does, and GroupBuilder gains
create_group/add_group so builders nest to any depth. A group added at a
path that already holds a group is merged into it (require_group);
any other repeated name, an empty or "." component, or an absolute path
below the root is an error.
- add_soft_link, add_hard_link and add_external_link on FileWriter,
FileBuilder and GroupBuilder. Hard-link targets are resolved to objects
at finish (through other hard links; a missing target, a soft link on the
way or a cycle of paths is an error). Objects with several hard links get
an Object Reference Count message so libhdf5 can delete one link without
freeing the object.
- track_order(true) per group, or as the file default, tracks and indexes
link creation order: Link Info flags and max order, the order in each
Link message, and a type-6 creation-order B-tree for dense groups.
- A group's link index is one B-tree leaf; more than 65535 links is an
error.
Groups are laid out depth-first from the root, datasets group by group,
and untracked groups keep writing datasets, then groups, then other links:
files with one level of groups are byte-identical to before.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -407,6 +407,30 @@ let values = ds.read_f64()?;
|
||||
assert_eq!(values, vec![22.5, 23.1, 21.8]);
|
||||
```
|
||||
|
||||
### Groups and links
|
||||
|
||||
```rust
|
||||
use clawhdf5::{AttrValue, FileBuilder};
|
||||
|
||||
let mut b = FileBuilder::new();
|
||||
// A path creates its missing intermediate groups, as in h5py.
|
||||
b.create_dataset("run/2026/temps").with_f64_data(&[22.5, 23.1]);
|
||||
// Builders nest; a group added at an existing path is merged into it.
|
||||
let mut run = b.create_group("run");
|
||||
run.set_attr("operator", AttrValue::String("ana".into()));
|
||||
let mut cal = run.create_group("calibration");
|
||||
cal.track_order(true); // h5py lists members in insertion order
|
||||
cal.create_dataset("offset").with_f64_data(&[0.1]);
|
||||
run.add_group(cal.finish());
|
||||
b.add_group(run.finish());
|
||||
b.add_soft_link("latest", "/run/2026"); // h5py.SoftLink
|
||||
b.add_hard_link("temps", "/run/2026/temps"); // f["temps"] = f["run/2026/temps"]
|
||||
b.add_external_link("raw", "raw.h5", "/data");
|
||||
b.write("groups.h5")?;
|
||||
```
|
||||
|
||||
A group holds at most 65 535 links; more is an error.
|
||||
|
||||
### Agent Memory
|
||||
|
||||
```rust
|
||||
|
||||
Reference in New Issue
Block a user