Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ jobs:
cargo r --example continuously_rw_1d --features ${{ env.FEATURES }}
cargo r --example committed_datatype --features ${{ env.FEATURES }}
cargo r --example swmr --features ${{ env.FEATURES }}
cargo r --example link_order --features ${{ env.FEATURES }}
if: matrix.rust != 'stable-gnu'
env:
FEATURES: hdf5-sys/static,hdf5-sys/zlib,lzf,blosc-all
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
- Changed `DatasetBuilder::empty_as` and `DatasetBuilder::with_data_as` to accept an existing `Datatype` or `CommittedDatatype` as well as a `TypeDescriptor`, through the new `DatasetType`, so a dataset can be created with a committed datatype.
- Added `FileCreateBuilder::sizes` and `Sizeof` to set the offset and length sizes of a file (breaking change, `SizeofInfo` holds `Sizeof` instead of `usize`)
- Added more variants to `LibraryVersion`. If you specified `Latest` before you may start generating files which are no longer compatible with earlier versions of `hdf5`
- Exported `IterationOrder` and `IndexType`, the arguments of `Group::iter_visit`
- Changed `Group::iter_visit` and `Group::iter_visit_default` to take an `FnMut(&str, LinkInfo) -> Result<()>` closure instead of an accumulator and a `bool` closure, and `LinkInfo::is_utf8` to `LinkInfo::char_encoding` (breaking change). An error returned by the closure is propagated, and `Group::groups`, `Group::datasets`, `Group::committed_datatypes` and `Group::member_names` now fail instead of returning a truncated list when a link cannot be resolved
- Added `Group::member_names_by` and `Group::links` to list the links along an index type in an order, `Group::find_link` to stop a link iteration with a value, and `Group::iter_visit_from` with `LinkCursor` to skip links and resume a stopped iteration
- Added `FileCreateBuilder::link_creation_order`, `GroupCreateBuilder::link_creation_order`, the matching getters and `LinkCreationOrder`, so a group can track and index link creation order
## hdf5-derive unreleased
## hdf5-types unreleased
## hdf5-sys unreleased
Expand Down
86 changes: 86 additions & 0 deletions hdf5/examples/link_order.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//! List the links of a group in the order they were created
//!
//! HDF5 keeps the links of a group in a name index. A group can also record the order in which its
//! links were created, which is what h5py does with `track_order=True` and what netCDF-4 does for
//! every group, so a reader sees the variables in the order the writer added them. Tracking has to
//! be requested when the group is created and cannot be turned on later.

use hdf5::plist::group_create::LinkCreationOrder;
use hdf5::{File, IndexType, IterationOrder, LinkCursor, LinkType, MajorErrorCode, Result};
use hdf5_metno as hdf5;

const FILE_NAME: &str = "link_order.h5";
const VARIABLES: [&str; 5] = ["time", "latitude", "longitude", "temperature", "pressure"];
const PAGE_SIZE: usize = 3;

fn write() -> Result<()> {
let file = File::create(FILE_NAME)?;

let tracked = file
.create_group_builder()
.with_gcpl(|gcpl| gcpl.link_creation_order(LinkCreationOrder::Tracked))
.create("tracked")?;
for name in VARIABLES {
tracked.new_dataset::<f64>().shape(24).create(name)?;
}
tracked.link_soft("temperature", "temp")?;

// A group created with the defaults has only the name index.
let untracked = file.create_group("untracked")?;
for name in VARIABLES {
untracked.new_dataset::<f64>().shape(24).create(name)?;
}
Ok(())
}

fn read() -> Result<()> {
let file = File::open(FILE_NAME)?;
let tracked = file.group("tracked")?;

// member_names() and iter_visit_default() walk the name index.
println!("by name: {:?}", tracked.member_names()?);

// The creation index returns the writer's order, and LinkInfo carries the position in it.
let mut by_creation = vec![];
tracked.iter_visit(IndexType::CreationOrder, IterationOrder::Increasing, |name, info| {
let order = info.creation_order.expect("the group tracks link creation order");
println!("created {order}: {name}");
by_creation.push(name.to_owned());
Ok(())
})?;
assert_eq!(by_creation, ["time", "latitude", "longitude", "temperature", "pressure", "temp"]);

// find_link stops at the first link the closure returns a value for.
let first_soft =
tracked.find_link(IndexType::CreationOrder, IterationOrder::Increasing, |name, info| {
if info.link_type == LinkType::Soft { Ok(Some(name.to_owned())) } else { Ok(None) }
})?;
println!("first soft link: {first_soft:?}");
assert_eq!(first_soft, Some("temp".to_owned()));

// A cursor resumes a stopped iteration, here to read the links in pages.
let mut cursor = LinkCursor::start(IndexType::CreationOrder, IterationOrder::Increasing);
let mut page = vec![];
while let Some(((), next)) = tracked.iter_visit_from(cursor, |name, _| {
page.push(name.to_owned());
if page.len() == PAGE_SIZE { Ok(Some(())) } else { Ok(None) }
})? {
println!("page from {}: {page:?}", cursor.position());
page.clear();
cursor = next;
}

// Creation order is not available in a group that never tracked it.
let err = file
.group("untracked")?
.iter_visit(IndexType::CreationOrder, IterationOrder::Increasing, |_, _| Ok(()))
.expect_err("the group does not track link creation order");
assert!(err.contains_major(MajorErrorCode::SymbolTable));
println!("untracked group: {err}");
Ok(())
}

fn main() -> Result<()> {
write()?;
read()
}
2 changes: 1 addition & 1 deletion hdf5/src/hl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub use self::{
dataspace::Dataspace,
datatype::{Conversion, Datatype},
file::{File, FileBuilder, OpenMode},
group::{Group, GroupBuilder, LinkInfo, LinkType},
group::{Group, GroupBuilder, IndexType, IterationOrder, LinkCursor, LinkInfo, LinkType},
location::{Location, LocationInfo, LocationToken, LocationType},
object::Object,
plist::PropertyList,
Expand Down
Loading
Loading