Skip to main content

FileBuilder

Struct FileBuilder 

Source
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

Source

pub fn new() -> Self

Create a new file builder.

Source

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.

Source

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().

Source

pub fn add_group(&mut self, group: FinishedGroup)

Add a finished group to the file.

Source

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();
Source

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.

Source

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 one
Source

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.

Source

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");
Source

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.8

This 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.

Source

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.

Source

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.

Source

pub fn set_attr(&mut self, name: &str, value: AttrValue)

Set an attribute on the root group.

Source

pub fn finish(self) -> Result<Vec<u8>, Error>

Serialize the file to bytes in memory.

Source

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);
Source

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.

Trait Implementations§

Source§

impl Default for FileBuilder

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.