pub struct FileBuilder { /* private fields */ }Expand description
Builder for creating a new HDF5 file.
§Example
use hdf5_pure::FileBuilder;
use hdf5_pure::AttrValue;
let mut builder = FileBuilder::new();
builder.create_dataset("data").with_f64_data(&[1.0, 2.0, 3.0]);
builder.set_attr("version", AttrValue::I64(1));
builder.write("output.h5").unwrap();Implementations§
Source§impl FileBuilder
impl FileBuilder
Sourcepub fn create_dataset(&mut self, name: &str) -> &mut FormatDatasetBuilder
pub fn create_dataset(&mut self, name: &str) -> &mut FormatDatasetBuilder
Create a dataset at the root level. Returns a mutable reference to
a DatasetBuilder for configuring data, shape, and attributes.
Sourcepub fn create_group(&mut self, name: &str) -> FormatGroupBuilder
pub fn create_group(&mut self, name: &str) -> FormatGroupBuilder
Create a group builder. Call .finish() on the returned builder
to complete it, then pass to add_group().
Sourcepub fn add_group(&mut self, group: FinishedGroup)
pub fn add_group(&mut self, group: FinishedGroup)
Add a finished group to the file.
Sourcepub fn commit_datatype(&mut self, name: &str, datatype: Datatype)
pub fn commit_datatype(&mut self, name: &str, datatype: Datatype)
Commit datatype in the root group under name, the way H5Tcommit
does: the type is written as an object of its own, and datasets and
attributes reference it by path instead of encoding it again.
A committed datatype is what a C-library reader reports by name — h5dump
prints DATATYPE "/mytype" for a dataset using one — and what netCDF-4
writes for every user-defined type. It is also the only way several
objects in a file can be said to share one type rather than to each
declare an identical one.
Name it from a dataset with
DatasetBuilder::with_committed_datatype
or from an attribute with
DatasetBuilder::set_attr_committed
and its group and root counterparts. A name that no committed datatype
matches, or one whose type disagrees with the naming object’s, fails the
write rather than producing a file whose element bytes and declared type
do not match.
use hdf5_pure::{FileBuilder, make_i32_type};
let mut b = FileBuilder::new();
b.commit_datatype("mytype", make_i32_type());
b.create_dataset("d")
.with_i32_data(&[1, 2, 3])
.with_committed_datatype("mytype");
let bytes = b.finish().unwrap();Sourcepub fn set_attr_committed(&mut self, name: &str, value: AttrValue, path: &str)
pub fn set_attr_committed(&mut self, name: &str, value: AttrValue, path: &str)
Attach a root-group attribute whose datatype is the committed one at
path. See commit_datatype and
DatasetBuilder::set_attr_committed.
Sourcepub fn with_create_properties(
&mut self,
properties: FileCreateProperties,
) -> &mut Self
pub fn with_create_properties( &mut self, properties: FileCreateProperties, ) -> &mut Self
Apply every creation property in properties at once — the fcpl
analogue of handing a property list to H5Fcreate.
Each property is applied exactly as the individual setter would, so this
overwrites any value set individually before the call — including the
properties properties leaves unset, which are reset to their defaults
rather than left behind. The two spellings interoperate in the order that
says so: apply a shared FileCreateProperties first, then override one
property for this file.
The reset matters most for the library-version bounds, which select the on-disk format rather than merely validating it: a stale 1.8 bound surviving a property list that names no version would decide the bytes this file is written in.
use hdf5_pure::{FileBuilder, FileCreateProperties, LibVer};
let mut builder = FileBuilder::new();
builder.with_libver_bounds(LibVer::Earliest, LibVer::V18);
// The list names no version, so the bound above is dropped with it.
builder.with_create_properties(FileCreateProperties::new().with_userblock(512));
builder.create_dataset("values").with_f64_data(&[1.0]);
let bytes = builder.finish().unwrap();
assert_eq!(bytes[512 + 8], 3); // the default format, not the 1.8 oneSourcepub fn with_userblock(&mut self, size: u64) -> &mut Self
pub fn with_userblock(&mut self, size: u64) -> &mut Self
Set the userblock size in bytes: zero (no userblock), or a power of two of at least 512. The region is filled with zeros.
Any other size is refused by finish /
finish_to / write with
FormatError::InvalidUserblockSize.
The size is the superblock’s base address, and a reader scans for the
signature at 0, 512, 1024, and so on doubling — so an unaligned size would
hide the superblock where nothing looks for it.
To put something in it, prefer
with_userblock_content, which works on
every output path. Patching the bytes afterwards only works with the
buffered finish; the streaming
finish_to / write have already emitted
the region by the time they return.
Sourcepub fn with_userblock_content(&mut self, content: &[u8]) -> &mut Self
pub fn with_userblock_content(&mut self, content: &[u8]) -> &mut Self
Set the bytes that occupy the head of the userblock region, so the writer
emits them as part of the file. The remainder of the region stays
zero-filled, and content longer than the userblock set by
with_userblock is refused by every output path —
finish, finish_to, and
write — with
FormatError::UserblockContentTooLarge.
Because the userblock leads the file in address order, this is what lets a
wrapper format’s header — MATLAB v7.3’s, for instance — be produced by the
non-seekable finish_to with no second pass.
§Example
use hdf5_pure::FileBuilder;
let mut builder = FileBuilder::new();
builder.with_userblock(512);
builder.with_userblock_content(b"my wrapper format's header");
builder.create_dataset("x").with_f64_data(&[1.0, 2.0]);
let bytes = builder.finish().unwrap();
assert_eq!(&bytes[..26], b"my wrapper format's header");
// The rest of the region is zero-filled, and the HDF5 signature follows it.
assert!(bytes[26..512].iter().all(|&b| b == 0));
assert_eq!(&bytes[512..516], b"\x89HDF");Sourcepub fn with_libver_bounds(&mut self, low: LibVer, high: LibVer) -> &mut Self
pub fn with_libver_bounds(&mut self, low: LibVer, high: LibVer) -> &mut Self
Constrain the on-disk format version of the file, mirroring HDF5’s
H5Pset_libver_bounds. The file is written in the newest format the
bounds allow, between LibVer::WRITER_OLDEST and
LibVer::WRITER_DEFAULT; bounds that leave no such format fail with
Error::Format wrapping
FormatError::LibverBoundsUnsatisfiable.
high selects the format. Earliest..=V18 writes the HDF5 1.8 format —
a version 2 superblock and version 3 data-layout messages — and anything
reaching 1.10 writes the 1.10 one. That is what a file destined for an
older reader wants: MATLAB’s MAT v7.3 loader, for instance, is HDF5
1.8.12 before R2021b, which does not understand a version 3 superblock.
low only rules formats out: as in the C library it licenses newer
encodings without requiring them, so a lower bound of V112, V114 or
LATEST is satisfied by the 1.10 format rather than refused. It does not
license high away — an inverted range such as V114..=V110 is refused
with
FormatError::LibverBoundsUnsatisfiable,
as H5Pset_libver_bounds refuses one.
Content the 1.8 format cannot express is refused rather than silently
upgraded, with
FormatError::LibverTooOldForContent:
a chunked, filtered, or resizable dataset needs the 1.10 chunk indices,
and a file-space setting — a strategy or a page size — needs the 1.10
File Space Info message.
File::open_swmr_writer likewise needs
a version 3 superblock, so a file written to the 1.8 bound cannot host a
SWMR writer.
use hdf5_pure::{FileBuilder, LibVer};
let mut builder = FileBuilder::new();
builder.with_libver_bounds(LibVer::Earliest, LibVer::V18);
builder.create_dataset("values").with_f64_data(&[1.0, 2.0, 3.0]);
let bytes = builder.finish().unwrap();
assert_eq!(bytes[8], 2); // version 2 superblock, readable by HDF5 1.8This differs from the C library, which picks the oldest format the
content needs and reads low as a floor; on Earliest..=Latest
H5Fcreate writes a version 0 superblock where this writes a version 3
one. Leaving the bounds unset is the same as leaving high at Latest.
Sourcepub fn with_file_space_strategy(
&mut self,
strategy: FileSpaceStrategy,
persist: bool,
threshold: u64,
) -> &mut Self
pub fn with_file_space_strategy( &mut self, strategy: FileSpaceStrategy, persist: bool, threshold: u64, ) -> &mut Self
Set the file-space management strategy, mirroring HDF5’s
H5Pset_file_space_strategy. The strategy, persist flag, and free-space
section threshold are recorded in the file’s superblock extension, so
the reference C library and a later reopen observe the choice.
persist = true records that freed space should be tracked on disk across
closes. A brand-new file has nothing to track, so this only records the
intent; freeing space in a later File::open_rw then
writes the on-disk free-space-manager blocks that survive a reopen.
Sourcepub fn with_file_space_page_size(&mut self, page_size: u64) -> &mut Self
pub fn with_file_space_page_size(&mut self, page_size: u64) -> &mut Self
Set the file-space page size, mirroring HDF5’s
H5Pset_file_space_page_size. Recorded in the superblock extension.
Sourcepub fn finish_to<W: Write>(self, w: W) -> Result<(), Error>
pub fn finish_to<W: Write>(self, w: W) -> Result<(), Error>
Serialize the file directly to a Write sink, without first buffering
the whole file in memory.
Produces byte-for-byte the same file as finish, but a
dataset staged for verbatim chunk streaming (repack’s out-of-core path)
has its chunks pulled from the source and written one at a time, so peak
memory stays bounded by a single chunk plus the file metadata rather than
the whole dataset.
The sink is written front-to-back with no seeks, so it can be a socket or a pipe as readily as a file. That is possible because the writer computes every object’s address before it emits a byte, rather than seeking back to patch addresses the way a backpatching writer would.
A failure partway leaves whatever was already written on the sink. With a non-seekable sink there is nothing to roll back, so a caller needing all-or-nothing should write to a temporary path and rename on success.
§Example
use hdf5_pure::FileBuilder;
let build = || {
let mut b = FileBuilder::new();
b.create_dataset("x").with_f64_data(&[1.0, 2.0, 3.0]);
b
};
let mut streamed: Vec<u8> = Vec::new();
build().finish_to(&mut streamed).unwrap();
assert_eq!(build().finish().unwrap(), streamed);Sourcepub fn write<P: AsRef<Path>>(self, path: P) -> Result<(), Error>
pub fn write<P: AsRef<Path>>(self, path: P) -> Result<(), Error>
Serialize and write the file to the given path.
Streams the file to disk (see finish_to), so a repack
staging streamed chunks does not hold the whole output in memory.
The path is created when the first byte is ready, not when the call
starts, so a build refused before any byte is emitted — unsatisfiable or
too-old library-version bounds, an invalid userblock — leaves whatever was
at path untouched. A failure after that (an I/O error, or a refusal
the layout reaches) still leaves a partial file, as
finish_to describes.