diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f210a85e6..de2e6faf9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,8 +10,9 @@ on: schedule: - cron: '0 18 * * *' +# Ensure this workflow gets cancelled if a newer commit has been pushed on the same branch (unless it's the default branch) concurrency: - group: ${{ github.workflow }}-@{{ github.ref }} + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true env: @@ -23,9 +24,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Check spelling - uses: crate-ci/typos@7bc041cbb7ca9167c9e0e4ccbb26f48eb0f9d4e0 # v1.30.2 + uses: crate-ci/typos@8a48f81b6c64dcfea44b3633223084c4be58ac5f # v1.49.0 lint: name: lint @@ -38,7 +39,7 @@ jobs: - {command: clippy, rust: stable} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install Rust (${{matrix.rust}}) uses: dtolnay/rust-toolchain@stable with: {toolchain: '${{matrix.rust}}', components: 'rustfmt, clippy'} @@ -51,15 +52,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: {submodules: true} - - name: Install Rust (${{matrix.rust}}) + - name: Install Rust uses: dtolnay/rust-toolchain@stable - with: {toolchain: nightly} + with: {toolchain: "nightly-2025-09-01"} - name: Document workspace env: RUSTDOCFLAGS: "--cfg docsrs" - run: cargo doc --features static,zlib,blosc-all,lzf,f16,complex + run: cargo doc --features static,zlib,blosc-all,lzf,f16,complex --no-deps brew: name: brew @@ -68,12 +69,12 @@ jobs: fail-fast: false matrix: include: - - {version: hdf5@1.10} - - {version: hdf5@1.14} + - {version: hdf5@2.1} + - {version: hdf5} - {version: hdf5-mpi, mpi: true} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: {submodules: true} - name: Install Rust (${{matrix.rust}}) uses: dtolnay/rust-toolchain@stable @@ -94,7 +95,6 @@ jobs: include: - {os: ubuntu-latest, version: 1.8.16, channel: conda-forge, rust: stable} - {os: windows-latest, version: 1.8.17, channel: conda-forge, rust: stable} - - {os: macos-13, version: 1.8.18, channel: anaconda, rust: stable} - {os: ubuntu-latest, version: 1.8.20, channel: anaconda, rust: beta} - {os: ubuntu-latest, version: 1.10.1, channel: anaconda, rust: nightly} - {os: windows-latest, version: 1.10.2, channel: anaconda, rust: beta} @@ -102,14 +102,12 @@ jobs: - {os: windows-latest, version: 1.10.4, channel: anaconda, rust: nightly} - {os: ubuntu-latest, version: 1.10.4, mpi: openmpi, channel: conda-forge, rust: stable} - {os: ubuntu-latest, version: 1.10.5, channel: conda-forge, rust: beta} - - {os: macos-13, version: 1.10.5, mpi: openmpi, channel: conda-forge, rust: beta} - {os: ubuntu-latest, version: 1.10.6, channel: anaconda, rust: stable} - {os: ubuntu-latest, version: 1.10.6, mpi: mpich, channel: conda-forge, rust: nightly} - # - {os: ubuntu, version: 1.10.8, channel: conda-forge, rust: stable} - {os: ubuntu-latest, version: 1.12.0, mpi: openmpi, channel: conda-forge, rust: stable} - - {os: macos-latest, version: 1.12.0, channel: conda-forge, rust: stable} - {os: windows-latest, version: 1.12.0, channel: conda-forge, rust: stable} - {os: ubuntu-latest, version: 1.12.1, channel: conda-forge, rust: stable} + - {os: macos-14, version: 1.12.1, mpi: openmpi, channel: conda-forge, rust: beta} - {os: macos-latest, version: 1.14.0, channel: conda-forge, rust: stable} - {os: windows-latest, version: 1.14.0, channel: conda-forge, rust: stable} - {os: ubuntu-latest, version: 1.14.0, channel: conda-forge, rust: stable} @@ -118,13 +116,13 @@ jobs: shell: bash -l {0} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: {submodules: true} - name: Install Rust (${{matrix.rust}}) uses: dtolnay/rust-toolchain@stable with: {toolchain: '${{matrix.rust}}'} - name: Install conda - uses: conda-incubator/setup-miniconda@v3 + uses: conda-incubator/setup-miniconda@v4 with: {auto-update-conda: false, activate-environment: testenv, miniconda-version: latest} - name: Set conda for Arm64 if: runner.arch == 'arm64' && runner.os == 'macOS' @@ -138,6 +136,12 @@ jobs: export HDF5_DIR="$CONDA_PREFIX" [ "${{matrix.mpi}}" != "" ] && FEATURES=mpio [ "${{runner.os}}" != "Windows" ] && export RUSTFLAGS="-C link-args=-Wl,-rpath,$CONDA_PREFIX/lib" + # doctests are linked separately by rustdoc and need the rpath too + [ "${{runner.os}}" != "Windows" ] && export RUSTDOCFLAGS="-C link-args=-Wl,-rpath,$CONDA_PREFIX/lib" + # conda's MPICH uses UCX, which tries to open an RDMA transport on the runner's NIC + # and aborts MPI_Init when it is unsupported. Restrict UCX to TCP and shared memory, + # which is all a single-process test needs. + [ "${{matrix.mpi}}" == "mpich" ] && export UCX_TLS=tcp,self,sm [ "${{matrix.mpi}}" == "mpich" ] && [ "${{runner.os}}" == "Linux" ] && export MPICH_CC=$(which gcc) [ "${{matrix.mpi}}" == "openmpi" ] && [ "${{runner.os}}" == "Linux" ] && export OMPI_CC=$(which gcc) cargo test -vv --features="$FEATURES" @@ -155,7 +159,7 @@ jobs: - {os: macos, rust: stable} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: {submodules: true} - name: Install Rust (${{matrix.rust}}) uses: dtolnay/rust-toolchain@stable @@ -167,13 +171,16 @@ jobs: if: matrix.rust != 'stable-gnu' - name: Run examples run: | - cargo r --example simple --features hdf5-sys/static,hdf5-sys/zlib,lzf,blosc-all - cargo r --example chunking --features hdf5-sys/static,hdf5-sys/zlib,lzf,blosc-all + cargo r --example simple --features ${{ env.FEATURES }} + cargo r --example chunking --features ${{ env.FEATURES }} + cargo r --example continuously_rw_1d --features ${{ env.FEATURES }} if: matrix.rust != 'stable-gnu' + env: + FEATURES: hdf5-sys/static,hdf5-sys/zlib,lzf,blosc-all apt: name: apt - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: @@ -182,7 +189,7 @@ jobs: - {mpi: openmpi, rust: stable} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: {submodules: true} - name: Install Rust (${{matrix.rust}}) uses: dtolnay/rust-toolchain@stable @@ -192,6 +199,7 @@ jobs: [ "${{matrix.mpi}}" == "mpich" ] && PACKAGES="libhdf5-mpich-dev mpich" [ "${{matrix.mpi}}" == "openmpi" ] && PACKAGES="libhdf5-openmpi-dev openmpi-bin" [ "${{matrix.mpi}}" == "serial" ] && PACKAGES="libhdf5-dev" + sudo apt-get update sudo apt-get install $PACKAGES - name: Build and test all crates run: | @@ -214,7 +222,7 @@ jobs: version: ["1.8", "1.10", "1.12", "1.14"] steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: {submodules: true} - name: Install Rust (${{matrix.rust}}) uses: dtolnay/rust-toolchain@stable @@ -260,7 +268,7 @@ jobs: # rust: [stable] # steps: # - name: Checkout repository - # uses: actions/checkout@v4 + # uses: actions/checkout@v7 # with: {submodules: true} # - name: Install Rust (${{matrix.rust}}) # uses: dtolnay/rust-toolchain@stable @@ -279,22 +287,22 @@ jobs: msrv: name: Minimal Supported Rust Version - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: {submodules: true} - name: Install Rust uses: dtolnay/rust-toolchain@stable - with: {toolchain: "1.80"} + with: {toolchain: "1.85.1"} - name: Create lock-file run: cargo update - - name: Override deps - run: - cargo update half@2.5.0 --precise 2.4.1 + # - name: Override deps + # run: | + # cargo update half@2.7.1 --precise 2.4.1 - name: Build and test all crates run: cargo test --locked --workspace -vv --features=hdf5-sys/static,hdf5-sys/zlib --exclude=hdf5-metno-derive @@ -304,7 +312,7 @@ jobs: # runs-on: ubuntu-latest # steps: # - name: Checkout repository - # uses: actions/checkout@v4 + # uses: actions/checkout@v7 # with: {submodules: true} # - name: Install Rust # uses: dtolnay/rust-toolchain@stable @@ -321,7 +329,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: {submodules: true} - name: Install Rust uses: dtolnay/rust-toolchain@stable @@ -329,4 +337,7 @@ jobs: - name: Run test with sanitizer env: RUSTFLAGS: "-Z sanitizer=address" + # Doctests are linked by rustdoc separately, so without this they miss the sanitizer + # runtime and fail to link against the instrumented library + RUSTDOCFLAGS: "-Z sanitizer=address" run: cargo test --features hdf5-sys/static --target x86_64-unknown-linux-gnu --workspace --exclude hdf5-metno-derive diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e800ea45..d81a1dc80 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: submodules: true - name: Install libhdf5 diff --git a/.gitignore b/.gitignore index 2fb61a58a..36ac75c55 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ target/ *.ipynb* .idea/ sweep.timestamp +.cargo/ +.cargo/config.toml \ No newline at end of file diff --git a/.typos.toml b/.typos.toml index 9b4978785..917b922ca 100644 --- a/.typos.toml +++ b/.typos.toml @@ -8,3 +8,8 @@ extend-exclude = [ extend-ignore-identifiers-re = [ "Dout", ] +# Intentional historical HDF5 misspellings, normalized in error.rs scrub() +extend-ignore-re = [ + "accessability", + "accessibilty", +] diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cd5b8f8c..4b2950e17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,163 @@ # Changelog ## hdf5 unreleased -## hdf5-types unreleased ## hdf5-derive unreleased +## hdf5-types unreleased ## hdf5-sys unreleased ## hdf5-src unreleased +## hdf5 0.14.1 +Release date: Aug 12, 2026 +- Line and file_name added to ErrorFrame + +## hdf5-src 0.10.4 +Release date: Aug 11, 2026 +- Build library at a consistent path on all platforms + +## hdf5-sys 0.11.4 +Release date: Aug 10, 2026 +- Backport support for hdf5 2.X series + +## hdf5-sys 0.12.2 +Release date: Aug 04, 2026 +- Add support for hdf5 2.2.0 +- Add symbols introduced in hdf5 2.2.0 + +## hdf5-src 0.10.3 +Release date: Aug 04, 2026 +- Bundling hdf5 2.2.0 + +## hdf5 0.14.0 +Release date: Jul 25, 2026 +- Fixed wrong f64 -> h5 datatype for big endian platforms +- Added `read_raw_into` to read directly into a pre-allocated buffer +- Added `MajorErrorCode` and `MinorErrorCode` for matching on HDF5 error codes +- Added `Error::stack`, `Error::contains_major` and `Error::contains_minor` +- Added `ErrorFrame::major_code` and `ErrorFrame::minor_code` +- Added `ExpandedErrorStack::major_codes`, `minor_codes`, `contains_major` and `contains_minor` +- Renamed error codes map to a single variant, `H5E_BADATOM` and `H5E_BADID` both give `BadId` +- Fixed `append` masking the underlying error with `EEXIST` when the file exists but cannot be opened + +## hdf5-derive 0.10.2 +Release date: Jul 25, 2026 +- Updated syn to version 3 + +## hdf5-sys 0.12.1 +Release date: Jul 25, 2026 +- Added missing H5E error code globals, including `H5E_VOL`, `H5E_CONTEXT` and `H5E_LOGGING` +- Fixed version gates on `H5E_PLUGIN`, `H5E_OPENERROR` and `H5E_SETDISALLOWED` +- Added `H5E_CANTLOCKFILE` and `H5E_CANTUNLOCKFILE` on hdf5 1.10.7 and later + +## hdf5 0.13.0 +Release date: Jul 02, 2026 +- Added `GroupCreate` property list and `GroupBuilder` for configurable group creation +- Added support for setting `obj_track_times` when creating groups +- Restrict enum conversions (breaking change, it is now an error reading incompatible enums) + +## hdf5 0.12.6 +Release date: Jun 25, 2026 +- Use edition 2024 +- Replace dependency paste with pastey + +## hdf5-types 0.11.1 +Release date: Jun 25, 2026 +- Use updated hdf5-sys + +## hdf5-derive 0.10.1 +Release date: Jun 25, 2026 +- Removed dependency proc-macro-error2 + +## hdf5-sys 0.12.0 +Release date: Jun 25, 2026 +- Use edition 2024 +- Added unsafe to all C functions + +## hdf5 0.12.5 +Release date: Jun 04, 2026 +- Added option for setting file locking on access + +## hdf5 0.12.4 +Release date: Mar 23, 2026 +- Support for hdf5 2.1.0 +- MSRV has been increased to 1.85 + +## hdf5-sys 0.11.3 +Release date: Mar 23, 2026 +- Support for hdf5 2.1.0 + +## hdf5-src 0.10.2 +Release date: Mar 23, 2026 +- Update to hdf5 2.1.0 + +## hdf5 v0.12.3 +Release date: Feb 10, 2026 +- Fixed compression of VarLenUnicode resulting in errors + +## hdf5 v0.12.2 +Release date: Feb 09, 2026 +- Fixed MPIO include + +## hdf5-sys v0.11.2 +Release date: Feb 09, 2026 +- Fixed name of MPIO include + +## hdf5-src v0.10.1 +Release date: Feb 09, 2026 +- Fixed name of cmake build option + +## hdf5-sys v0.11.1 +Release date: Jan 20, 2026 +- Fixed an import for windows using prebuilt hdf5 2.0.0 + +## hdf5 v0.12.1 +Release date: Jan 20, 2026 +- No changes + +## hdf5 v0.12.0 +Release date: Jan 19, 2026 +- Add support for hdf5 2.0.0 +- Added support for ZFP compression filters as an optional feature + +## hdf5-types v0.11.0 +Release date: Jan 19, 2026 +- Add support for hdf5 2.0.0 + +## hdf5-derive v0.10.0 +Release date: Jan 19, 2026 +- Add support for hdf5 2.0.0 + +## hdf5-sys v0.11.0 +Release date: Jan 19, 2026 +- Add support for hdf5 2.0.0 +- Remove `H5FD_*_init` functions, these are private to hdf5-c + +## hdf5-src v0.10.0 +Release date: Jan 19, 2026 +- Use hdf5 2.0.0 + +## hdf5 v0.11.0 +Release date: Nov 23, 2025 +- Fixed incorrect retrieved name of attributes +- Added support for Single Writer Multiple Readers (SWMR) (breaking change, OpenMode has extra variant) +- Added support for object copying between Locations +- Improved debug printing for datatype descriptor + +## hdf5-types v0.10.2 +Release date: Nov 23, 2025 +- Added documentation on Typedescriptor::size + +## hdf5-derive v0.9.3 +Release date: Nov 23, 2025 +- Fixed derive macro when inside itself or hdf5_metno package + +## hdf5-src v0.9.5 +Release date: Nov 23, 2025 +- Improved support for static linking with msvc + +## hdf5 v0.10.2 (yanked) +Release date: Oct 16, 2025 +- Upgraded upper bound of ndarray + ## hdf5-types v0.10.1 Release date: Mar 19, 2025 - Fixed deref of null ptr in `VarLenAscii`/`VarLenUnicode` diff --git a/Cargo.toml b/Cargo.toml index ae8717633..48430a373 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [workspace] -resolver = "2" +resolver = "3" members = ["hdf5", "hdf5-types", "hdf5-derive", "hdf5-sys", "hdf5-src"] default-members = ["hdf5", "hdf5-types", "hdf5-derive", "hdf5-sys"] [workspace.package] -rust-version = "1.80.0" +rust-version = "1.85.1" authors = [ "Ivan Smirnov ", "Magnus Ulimoen ", @@ -13,7 +13,7 @@ keywords = ["hdf5"] license = "MIT OR Apache-2.0" repository = "https://github.com/metno/hdf5-rust" homepage = "https://github.com/metno/hdf5-rust" -edition = "2021" +edition = "2024" [workspace.dependencies] # external @@ -24,9 +24,28 @@ libz-sys = { version = "1.1", default-features = false } mpi-sys = "0.2" num-complex = { version = "0.4", default-features = false } regex = "1.10" + # internal -hdf5 = { package = "hdf5-metno", version = "0.9.3", path = "hdf5" } # !V -hdf5-derive = { package = "hdf5-metno-derive", version = "0.9.1", path = "hdf5-derive" } # !V -hdf5-src = { package = "hdf5-metno-src", version = "0.9.3", path = "hdf5-src" } # !V -hdf5-sys = { package = "hdf5-metno-sys", version = "0.10.1", path = "hdf5-sys" } # !V -hdf5-types = { package = "hdf5-metno-types", version = "0.10.0", path = "hdf5-types" } # !V +hdf5 = { package = "hdf5-metno", version = "0.14.0", path = "hdf5" } # !V +hdf5-derive = { package = "hdf5-metno-derive", version = "0.10.0", path = "hdf5-derive" } # !V +hdf5-types = { package = "hdf5-metno-types", version = "0.11.1", path = "hdf5-types" } # !V +hdf5-src = { package = "hdf5-metno-src", version = "0.10.0", path = "hdf5-src" } # !V +hdf5-sys = { package = "hdf5-metno-sys", version = "0.12.1", path = "hdf5-sys" } # !V + + +[profile.dev] +# Fast compile, reasonable runtime +opt-level = 0 # no optimizations = much faster compilation +debug = 1 # keep minimal debug info (0 is smaller, 1 is a nice compromise) +incremental = true # reuse previous compilation work +codegen-units = 16 # more parallel codegen units = faster compiles +lto = "off" # no link-time optimization +panic = "unwind" # default, keeps backtraces usable +overflow-checks = true # keep checks for safety in dev + +[profile.test] +# Inherit dev as a baseline, then tweak for tests +inherits = "dev" +opt-level = 0 # keep tests compiling fast +debug = 1 # enough info for backtraces +overflow-checks = true diff --git a/README.md b/README.md index cc828eda9..60342dd77 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,9 @@ fn main() -> Result<()> { } ``` +You can find this [example][readme-example] as well as other example projects in +the [example directory][examples]. + ## Compatibility ### Platforms @@ -206,3 +209,7 @@ Few things to note when building on Windows: `hdf5` crate is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0). See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. + + +[readme-example]: https://github.com/metno/hdf5-rust/blob/main/hdf5/examples/simple.rs +[examples]: https://github.com/metno/hdf5-rust/tree/main/hdf5/examples \ No newline at end of file diff --git a/hdf5-derive/Cargo.toml b/hdf5-derive/Cargo.toml index 48ea8860d..f050033de 100644 --- a/hdf5-derive/Cargo.toml +++ b/hdf5-derive/Cargo.toml @@ -3,7 +3,7 @@ name = "hdf5-metno-derive" description = "Derive macro for HDF5 structs and enums." categories = ["development-tools::procedural-macro-helpers"] readme = "README.md" -version = "0.9.2" # !V +version = "0.10.2" # !V rust-version.workspace = true authors.workspace = true keywords.workspace = true @@ -17,10 +17,9 @@ proc-macro = true [dependencies] proc-macro-crate = "3.2.0" -proc-macro-error2 = { version = "2.0", default-features = false } proc-macro2 = "1.0" quote = "1.0" -syn = { version = "2.0", features = ["derive", "extra-traits"]} +syn = { version = "3.0", features = ["derive", "extra-traits"]} [dev-dependencies] trybuild = "1.0" diff --git a/hdf5-derive/src/lib.rs b/hdf5-derive/src/lib.rs index 41592f7ef..ad3ab06b3 100644 --- a/hdf5-derive/src/lib.rs +++ b/hdf5-derive/src/lib.rs @@ -5,15 +5,14 @@ use std::mem; use std::str::FromStr; use proc_macro2::{Ident, Span, TokenStream}; -use proc_macro_error2::{abort, proc_macro_error}; -use quote::{quote, ToTokens}; +use quote::{ToTokens, quote}; use syn::{ - parse_macro_input, AttrStyle, Attribute, Data, DeriveInput, Expr, Fields, Index, LitStr, Type, - TypeGenerics, TypePath, + AttrStyle, Attribute, Data, DeriveInput, Expr, Fields, Index, LitStr, Type, TypeGenerics, + TypePath, parse_macro_input, spanned::Spanned, }; +/// Derive macro generating an impl of the trait `H5Type`. #[proc_macro_derive(H5Type, attributes(hdf5))] -#[proc_macro_error] pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); let name = input.ident; @@ -21,13 +20,27 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let body = impl_trait(&name, &input.data, &input.attrs, &ty_generics); // Determine name of parent crate, even if renamed using "package" + // CARGO_CRATE_NAME is the name of the actual crate being compiled (e.g., "simple" for examples) + // If it matches the library name (hdf5_metno), we're compiling the library itself + let is_library = + std::env::var("CARGO_CRATE_NAME").map(|name| name == "hdf5_metno").unwrap_or(false); + let crate_name = match proc_macro_crate::crate_name("hdf5-metno").unwrap() { - proc_macro_crate::FoundCrate::Itself => quote!(::hdf5_metno), + proc_macro_crate::FoundCrate::Itself if is_library => { + // We're in the hdf5-metno library itself, use crate + quote!(crate) + } + proc_macro_crate::FoundCrate::Itself => { + // We're in an example/test of hdf5-metno, use the renamed path + quote!(::hdf5_metno) + } proc_macro_crate::FoundCrate::Name(name) => { + // We're in a different crate that depends on hdf5-metno let ident = Ident::new(&name, Span::call_site()); quote!( ::#ident ) } }; + let expanded = quote! { #[allow(dead_code, unused_variables, unused_attributes)] const _: () = { @@ -107,8 +120,8 @@ fn impl_enum(names: &[String], values: &[Expr], repr: &Ident) -> TokenStream { fn is_phantom_data(ty: &Type) -> bool { match *ty { - Type::Path(TypePath { qself: None, ref path }) => { - path.segments.iter().last().is_some_and(|x| x.ident == "PhantomData") + Type::Path(TypePath { qself: None, ref path, attrs: _ }) => { + path.segments.iter().next_back().is_some_and(|x| x.ident == "PhantomData") } _ => false, } @@ -126,7 +139,7 @@ fn find_repr(attrs: &[Attribute], expected: &[&str]) -> Option { attr.parse_nested_meta(|meta| { if expected.iter().any(|s| meta.path.is_ident(s)) { if repr.is_some() { - abort!(meta.path, "ambiguous repr attribute"); + syn::Error::new(meta.path.span(), "ambiguous repr attribute"); } else { repr = meta.path.get_ident().cloned(); } @@ -165,21 +178,24 @@ fn impl_trait( ) -> TokenStream { match *data { Data::Struct(ref data) => match data.fields { - Fields::Unit => { - abort!(ty, "cannot derive `H5Type` for unit structs"); - } + Fields::Unit => syn::Error::new(ty.span(), "cannot derive `H5Type` for unit structs") + .into_compile_error(), Fields::Named(ref fields) => { let fields: Vec<_> = fields.named.iter().filter(|f| !is_phantom_data(&f.ty)).collect(); if fields.is_empty() { - abort!(ty, "cannot derive `H5Type` for empty structs"); + return syn::Error::new(ty.span(), "cannot derive `H5Type` for empty structs") + .into_compile_error(); } - let repr = - find_repr(attrs, &["C", "packed", "transparent"]).unwrap_or_else(|| { - abort!(ty, - "`H5Type` requires repr(C), repr(packed) or repr(transparent) for structs") - }); + let repr = match find_repr(attrs, &["C", "packed", "transparent"]) { + Some(repr) => repr, + None => return syn::Error::new( + ty.span(), + "`H5Type` requires repr(C), repr(packed) or repr(transparent) for structs", + ) + .into_compile_error(), + }; if repr == "transparent" { assert_eq!(fields.len(), 1); impl_transparent(&fields[0].ty) @@ -202,13 +218,20 @@ fn impl_trait( .map(|(i, f)| (Index::from(i), f)) .unzip(); if fields.is_empty() { - abort!(ty, "cannot derive `H5Type` for empty tuple structs") + return syn::Error::new( + ty.span(), + "cannot derive `H5Type` for empty tuple structs", + ) + .into_compile_error(); } - let repr = find_repr(attrs, &["C", "packed", "transparent"]).unwrap_or_else(|| { - abort!(ty, - "`H5Type` requires repr(C), repr(packed) or repr(transparent) for tuple structs") - }); + let repr = match find_repr(attrs, &["C", "packed", "transparent"]) { + Some(repr) => repr, + None => { + return syn::Error::new(ty.span(), + "`H5Type` requires repr(C), repr(packed) or repr(transparent) for tuple structs").into_compile_error() + } + }; if repr == "transparent" { assert_eq!(fields.len(), 1); impl_transparent(&fields[0].ty) @@ -227,18 +250,30 @@ fn impl_trait( let variants = &data.variants; if variants.iter().any(|v| v.fields != Fields::Unit || v.discriminant.is_none()) { - abort!(ty, "`H5Type` can only be derived for enums with scalar discriminants") + return syn::Error::new( + ty.span(), + "`H5Type` can only be derived for enums with scalar discriminants", + ) + .to_compile_error(); } if variants.is_empty() { - abort!(ty, "cannot derive `H5Type` for empty enums") + return syn::Error::new(ty.span(), "cannot derive `H5Type` for empty enums") + .to_compile_error(); } let enum_reprs = &["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "isize", "usize"]; - let repr = find_repr(attrs, enum_reprs).unwrap_or_else(|| { - abort!(ty, "`H5Type` can only be derived for enums with explicit representation") - }); + let repr = match find_repr(attrs, enum_reprs) { + Some(repr) => repr, + None => { + return syn::Error::new( + ty.span(), + "`H5Type` can only be derived for enums with explicit representation", + ) + .into_compile_error(); + } + }; let names = variants .iter() .map(|v| find_hdf5_rename(&v.attrs).unwrap_or_else(|| v.ident.to_string())) @@ -246,8 +281,7 @@ fn impl_trait( let values = pluck(variants.iter(), |v| v.discriminant.clone().unwrap().1); impl_enum(&names, &values, &repr) } - Data::Union(_) => { - abort!(ty, "cannot derive `H5Type` for tagged unions"); - } + Data::Union(_) => syn::Error::new(ty.span(), "cannot derive `H5Type` for tagged unions") + .to_compile_error(), } } diff --git a/hdf5-derive/tests/compile-fail/pd-empty-struct.rs b/hdf5-derive/tests/compile-fail/pd-empty-struct.rs index 28102f666..00af390f6 100644 --- a/hdf5-derive/tests/compile-fail/pd-empty-struct.rs +++ b/hdf5-derive/tests/compile-fail/pd-empty-struct.rs @@ -5,7 +5,7 @@ use std::marker::PhantomData; #[derive(H5Type)] //~^ ERROR proc-macro derive //~^^ HELP Cannot derive H5Type for empty structs -struct Foo { +struct Foo { t: PhantomData, } diff --git a/hdf5-derive/tests/compile-fail/pd-empty-struct.stderr b/hdf5-derive/tests/compile-fail/pd-empty-struct.stderr index 58900c456..5ba89da5b 100644 --- a/hdf5-derive/tests/compile-fail/pd-empty-struct.stderr +++ b/hdf5-derive/tests/compile-fail/pd-empty-struct.stderr @@ -1,5 +1,5 @@ error: cannot derive `H5Type` for empty structs --> tests/compile-fail/pd-empty-struct.rs:8:8 | -8 | struct Foo { +8 | struct Foo { | ^^^ diff --git a/hdf5-derive/tests/compile-fail/pd-empty-tuple-struct.rs b/hdf5-derive/tests/compile-fail/pd-empty-tuple-struct.rs index 4b4188007..d296dfc03 100644 --- a/hdf5-derive/tests/compile-fail/pd-empty-tuple-struct.rs +++ b/hdf5-derive/tests/compile-fail/pd-empty-tuple-struct.rs @@ -5,6 +5,6 @@ use std::marker::PhantomData; #[derive(H5Type)] //~^ ERROR proc-macro derive //~^^ HELP Cannot derive H5Type for empty tuple structs -struct Foo(PhantomData); +struct Foo(PhantomData); fn main() {} diff --git a/hdf5-derive/tests/compile-fail/pd-empty-tuple-struct.stderr b/hdf5-derive/tests/compile-fail/pd-empty-tuple-struct.stderr index 3adb0a954..1f4f3dbf3 100644 --- a/hdf5-derive/tests/compile-fail/pd-empty-tuple-struct.stderr +++ b/hdf5-derive/tests/compile-fail/pd-empty-tuple-struct.stderr @@ -1,5 +1,5 @@ error: cannot derive `H5Type` for empty tuple structs --> tests/compile-fail/pd-empty-tuple-struct.rs:8:8 | -8 | struct Foo(PhantomData); +8 | struct Foo(PhantomData); | ^^^ diff --git a/hdf5-src/Cargo.toml b/hdf5-src/Cargo.toml index c863daef9..62e5543d4 100644 --- a/hdf5-src/Cargo.toml +++ b/hdf5-src/Cargo.toml @@ -1,28 +1,54 @@ [package] name = "hdf5-metno-src" -license-file = "ext/hdf5/COPYING" +license-file = "ext/hdf5/LICENSE" build = "build.rs" description = "Build script for compiling HDF5 C library from source." links = "hdf5src" readme = "README.md" categories = ["development-tools::ffi"] -exclude = [ - "ext/hdf5/bin/**", - "ext/hdf5/c++/**", - "ext/hdf5/examples/**", - "ext/hdf5/fortran/**", - "ext/hdf5/java/**", - "ext/hdf5/release_docs/**", - "ext/hdf5/test/**", - "ext/hdf5/testpar/**", - "ext/hdf5/tools/**", - "ext/hdf5/hl/test/**", - "ext/hdf5/hl/tools/**", - "ext/hdf5/hl/examples/**", - "ext/hdf5/HDF5Examples/**", - "ext/hdf5/doxygen/**", +include = [ + "Cargo.toml", + "build.rs", + "README.md", + "src/**/*.rs", + "ext/hdf5/**/CMakeLists.txt", + "ext/hdf5/LICENSE", + "ext/hdf5/**/*.h", + "ext/hdf5/**/*.c", + "ext/hdf5/**/*.am", + "ext/hdf5/**/*.cmake", + "ext/hdf5/**/*.in", + "ext/hdf5/config/*-warnings", + "ext/hdf5/config/*flags", + "ext/hdf5/config/apple", + "ext/hdf5/config/cygwin", + "ext/hdf5/config/freebsd", + "ext/hdf5/config/ibm-aix", + "ext/hdf5/config/linux-gnu", + "ext/hdf5/config/netbsd", + "ext/hdf5/config/solaris", + "ext/hdf5/release_docs/USING_HDF5_CMake.txt", + "ext/hdf5/release_docs/RELEASE.txt", + "!ext/hdf5/bin/**", + "!ext/hdf5/c++/**", + "!ext/hdf5/examples/**", + "!ext/hdf5/fortran/**", + "!ext/hdf5/java/**", + "!ext/hdf5/release_docs/**", + "!ext/hdf5/test/**", + "!ext/hdf5/testpar/**", + "!ext/hdf5/tools/**", + "!ext/hdf5/hl/test/**", + "!ext/hdf5/hl/tools/**", + "!ext/hdf5/hl/fortran/**", + "!ext/hdf5/hl/c++/**", + "!ext/hdf5/hl/examples/**", + "!ext/hdf5/HDF5Examples/**", + "!ext/hdf5/doxygen/**", + "!ext/hdf5/docs/**", + "!ext/hdf5/docs", ] -version = "0.9.4" # !V +version = "0.10.4" # !V rust-version.workspace = true authors.workspace = true keywords.workspace = true diff --git a/hdf5-src/build.rs b/hdf5-src/build.rs index 699adaaf9..189c28f8a 100644 --- a/hdf5-src/build.rs +++ b/hdf5-src/build.rs @@ -33,6 +33,15 @@ fn main() { println!("cargo::rerun-if-changed=build.rs"); let mut cfg = cmake::Config::new("ext/hdf5"); + if cfg!(target_env = "msvc") { + cfg.define("CMAKE_POLICY_DEFAULT_CMP0091", "NEW"); + if let Ok(var) = env::var("CMAKE_MSVC_RUNTIME_LIBRARY") { + cfg.define("CMAKE_MSVC_RUNTIME_LIBRARY", var); + } + } + + cfg.define("CMAKE_INSTALL_LIBDIR", "lib"); + // only build the static c library, disable everything else cfg.define("HDF5_NO_PACKAGES", "ON"); for option in &[ @@ -54,7 +63,7 @@ fn main() { for option in &[ "HDF5_ENABLE_DEPRECATED_SYMBOLS", "HDF5_ENABLE_THREADSAFE", - "ALLOW_UNSUPPORTED", + "HDF5_ALLOW_UNSUPPORTED", "HDF5_BUILD_HL_LIB", "HDF5_ENABLE_NONSTANDARD_FEATURE_FLOAT16", "HDF5_ENABLE_SZIP_SUPPORT", @@ -67,7 +76,7 @@ fn main() { let mut zlib_header = env::split_paths(&zlib_include_dir).next().unwrap(); zlib_header.push("zlib.h"); let zlib_lib = "z"; - cfg.define("HDF5_ENABLE_Z_LIB_SUPPORT", "ON") + cfg.define("HDF5_ENABLE_ZLIB_SUPPORT", "ON") .define("H5_ZLIB_HEADER", &zlib_header) .define("ZLIB_STATIC_LIBRARY", zlib_lib); println!("cargo::metadata=zlib_header={}", zlib_header.to_str().unwrap()); @@ -84,22 +93,16 @@ fn main() { cfg.define("HDF5_ENABLE_THREADSAFE", "ON"); if feature_enabled("HL") { println!("cargo::warning=Unsupported HDF5 options: hl with threadsafe."); - cfg.define("ALLOW_UNSUPPORTED", "ON"); + cfg.define("HDF5_ALLOW_UNSUPPORTED", "ON"); } } let targeting_windows = env::var("CARGO_CFG_TARGET_OS").unwrap() == "windows"; - let debug_postfix = if targeting_windows { "_D" } else { "_debug" }; if feature_enabled("HL") { cfg.define("HDF5_BUILD_HL_LIB", "ON"); - let mut hdf5_hl_lib = + let hdf5_hl_lib = if cfg!(target_env = "msvc") { "libhdf5_hl" } else { "hdf5_hl" }.to_owned(); - if let Ok(opt_level) = env::var("OPT_LEVEL") { - if opt_level == "0" { - hdf5_hl_lib.push_str(debug_postfix); - } - } println!("cargo::metadata=hl_library={}", hdf5_hl_lib); } @@ -117,12 +120,7 @@ fn main() { let hdf5_incdir = format!("{}/include", dst.display()); println!("cargo::metadata=include={}", hdf5_incdir); - let mut hdf5_lib = if cfg!(target_env = "msvc") { "libhdf5" } else { "hdf5" }.to_owned(); - if let Ok(opt_level) = env::var("OPT_LEVEL") { - if opt_level == "0" { - hdf5_lib.push_str(debug_postfix); - } - } + let hdf5_lib = if cfg!(target_env = "msvc") { "libhdf5" } else { "hdf5" }.to_owned(); println!("cargo::metadata=library={}", hdf5_lib); } diff --git a/hdf5-src/ext/hdf5 b/hdf5-src/ext/hdf5 index 0fe0459fc..49df1b4e5 160000 --- a/hdf5-src/ext/hdf5 +++ b/hdf5-src/ext/hdf5 @@ -1 +1 @@ -Subproject commit 0fe0459fc24d71be13d5f266513c2832b525671b +Subproject commit 49df1b4e5ca4108d56e80f06321a749da1535268 diff --git a/hdf5-sys/Cargo.toml b/hdf5-sys/Cargo.toml index 6813b0f6e..1c6f0023f 100644 --- a/hdf5-sys/Cargo.toml +++ b/hdf5-sys/Cargo.toml @@ -5,7 +5,7 @@ description = "Native bindings to the HDF5 library." links = "hdf5" readme = "README.md" categories = ["development-tools::ffi", "filesystem", "science"] -version = "0.10.1" # !V +version = "0.12.2" # !V rust-version.workspace = true authors.workspace = true keywords.workspace = true @@ -41,7 +41,7 @@ pkg-config = "0.3" [target.'cfg(windows)'.build-dependencies] serde = "1.0" serde_derive = "1.0" -winreg = { version = "0.55", features = ["serialization-serde"] } +winreg = { version = "0.56", features = ["serialization-serde"] } [package.metadata.docs.rs] features = ["static", "zlib"] diff --git a/hdf5-sys/README.md b/hdf5-sys/README.md index 228e12215..611504f3e 100644 --- a/hdf5-sys/README.md +++ b/hdf5-sys/README.md @@ -9,7 +9,7 @@ This crate supports linking to a static build of HDF5. The HDF5 C library is bui via the `hdf5-src` crate which is then linked in when the `static` feature is set. See below for a list of supported options for static builds. -As of the time of writing, the version of the HDF5 library that is built is 1.10.5, +As of the time of writing, the version of the HDF5 library that is built is 2.0.0, but it may be incremented later. ## Crate features diff --git a/hdf5-sys/build.rs b/hdf5-sys/build.rs index 142b1df9a..a2cf85313 100644 --- a/hdf5-sys/build.rs +++ b/hdf5-sys/build.rs @@ -14,7 +14,7 @@ use std::process::Command; use regex::Regex; fn feature_enabled(feature: &str) -> bool { - env::var(format!("CARGO_FEATURE_{}", feature)).is_ok() + env::var(format!("CARGO_FEATURE_{feature}")).is_ok() } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] @@ -30,12 +30,21 @@ impl Version { } pub fn parse(s: &str) -> Option { - let re = Regex::new(r"^(1)\.(8|10|12|14)\.(\d\d?)(_|.\d+)?((-|.)(patch)?\d+)?$").ok()?; - let captures = re.captures(s)?; + let re_v2 = Regex::new(r"^2\.(\d+)\.(\d+)(?:.+)?$").expect("Invalid regex"); + if let Some(captures) = re_v2.captures(s) { + return Some(Self { + major: 2, + minor: captures.get(1).and_then(|c| c.as_str().parse::().ok())?, + micro: captures.get(2).and_then(|c| c.as_str().parse::().ok())?, + }); + }; + let re_v1 = Regex::new(r"^1\.(8|10|12|14)\.(\d\d?)(_|.\d+)?((-|.)(patch)?\d+)?$") + .expect("Invalid regex"); + let captures = re_v1.captures(s)?; Some(Self { - major: captures.get(1).and_then(|c| c.as_str().parse::().ok())?, - minor: captures.get(2).and_then(|c| c.as_str().parse::().ok())?, - micro: captures.get(3).and_then(|c| c.as_str().parse::().ok())?, + major: 1, + minor: captures.get(1).and_then(|c| c.as_str().parse::().ok())?, + micro: captures.get(2).and_then(|c| c.as_str().parse::().ok())?, }) } @@ -53,10 +62,13 @@ impl Debug for Version { fn known_hdf5_versions() -> Vec { // Keep up to date with known_hdf5_versions in hdf5 let mut vs = Vec::new(); + vs.extend((0..=0).map(|v| Version::new(2, 2, v))); // 2.2.[0] + vs.extend((0..=1).map(|v| Version::new(2, 1, v))); // 2.1.[0-1] + vs.extend((0..=0).map(|v| Version::new(2, 0, v))); // 2.0.[0] vs.extend((5..=21).map(|v| Version::new(1, 8, v))); // 1.8.[5-23] vs.extend((0..=8).map(|v| Version::new(1, 10, v))); // 1.10.[0-10] vs.extend((0..=2).map(|v| Version::new(1, 12, v))); // 1.12.[0-2] - vs.extend((0..=5).map(|v| Version::new(1, 14, v))); // 1.14.[0-5] + vs.extend((0..=6).map(|v| Version::new(1, 14, v))); // 1.14.[0-6] vs } @@ -130,7 +142,7 @@ fn validate_runtime_version(config: &Config) { println!("Adding extra link paths (ld)..."); for caps in re.captures_iter(&ldv) { let path = &caps["path"]; - println!(" {}", path); + println!(" {path}"); link_paths.push(path.into()); } } else { @@ -144,10 +156,10 @@ fn validate_runtime_version(config: &Config) { if let Some(filename) = path.file_name() { let filename = filename.to_str().unwrap_or(""); if path.is_file() && libfiles.contains(&filename) { - println!("Attempting to load: {:?}", path); + println!("Attempting to load: {path:?}"); match get_runtime_version_single(&path) { Ok(version) => { - println!(" => runtime version = {:?}", version); + println!(" => runtime version = {version:?}"); if version == config.header.version { println!("HDF5 library runtime version matches headers."); return; @@ -158,7 +170,7 @@ fn validate_runtime_version(config: &Config) { ); } Err(err) => { - println!(" => {}", err); + println!(" => {err}"); } } } @@ -185,7 +197,7 @@ impl Header { let inc_dir = inc_dir.as_ref(); let header = get_conf_header(inc_dir); - println!("Parsing HDF5 config from:\n {:?}", header); + println!("Parsing HDF5 config from:\n {header:?}"); let contents = fs::read_to_string(header).unwrap(); let mut hdr = Self::default(); @@ -217,7 +229,7 @@ impl Header { if let Some(version) = Version::parse(value) { hdr.version = version; } else { - panic!("Invalid H5_VERSION: {:?}", value); + panic!("Invalid H5_VERSION: {value:?}"); } }; } @@ -251,7 +263,7 @@ pub struct LibrarySearcher { #[cfg(any(all(unix, not(target_os = "macos")), windows))] mod pkgconf { - use super::{is_inc_dir, LibrarySearcher}; + use super::{LibrarySearcher, is_inc_dir}; pub fn find_hdf5_via_pkg_config(config: &mut LibrarySearcher) { if config.inc_dir.is_some() { @@ -261,7 +273,10 @@ mod pkgconf { // If we're going to windows-gnu we can use pkg-config, but only so long as // we're coming from a windows host. if cfg!(windows) { - std::env::set_var("PKG_CONFIG_ALLOW_CROSS", "1"); + // Safety: build.rs is run single-threaded + unsafe { + std::env::set_var("PKG_CONFIG_ALLOW_CROSS", "1"); + } } // Try pkg-config. Note that HDF5 only ships pkg-config metadata @@ -274,11 +289,11 @@ mod pkgconf { println!("Found HDF5 pkg-config entry"); println!(" Include paths:"); for dir in &library.include_paths { - println!(" - {:?}", dir); + println!(" - {dir:?}"); } println!(" Link paths:"); for dir in &library.link_paths { - println!(" - {:?}", dir); + println!(" - {dir:?}"); } for dir in &library.include_paths { if is_inc_dir(dir) { @@ -289,7 +304,7 @@ mod pkgconf { } if let Some(ref inc_dir) = config.inc_dir { println!("Located HDF5 headers at:"); - println!(" {:?}", inc_dir); + println!(" {inc_dir:?}"); } else { println!("Unable to locate HDF5 headers from pkg-config info."); } @@ -302,7 +317,7 @@ mod pkgconf { #[cfg(all(unix, not(target_os = "macos")))] mod unix { pub use super::pkgconf::find_hdf5_via_pkg_config; - use super::{is_inc_dir, LibrarySearcher}; + use super::{LibrarySearcher, is_inc_dir}; pub fn find_hdf5_in_default_location(config: &mut LibrarySearcher) { if config.inc_dir.is_some() { @@ -314,8 +329,8 @@ mod unix { ("/usr/include", "/usr/lib64"), ] { if is_inc_dir(inc_dir) { - println!("Found HDF5 headers at:\n {:?}", inc_dir); - println!("Adding to link path:\n {:?}", lib_dir); + println!("Found HDF5 headers at:\n {inc_dir:?}"); + println!("Adding to link path:\n {lib_dir:?}"); config.inc_dir = Some(inc_dir.into()); config.link_paths.push(lib_dir.into()); break; @@ -334,19 +349,25 @@ mod macos { } // We have to explicitly support homebrew since the HDF5 bottle isn't // packaged with pkg-config metadata. - let (v18, v110, v112, v114) = if let Some(version) = config.version { + let (v21, v20, v18, v110, v112, v114) = if let Some(version) = config.version { ( + version.major == 2 && version.minor == 1, + version.major == 2 && version.minor == 0, version.major == 1 && version.minor == 8, version.major == 1 && version.minor == 10, version.major == 1 && version.minor == 12, version.major == 1 && version.minor == 14, ) } else { - (false, false, false, false) + (false, false, false, false, false, false) }; println!( "Attempting to find HDF5 via Homebrew ({})...", - if v18 { + if v21 { + "2.1.*" + } else if v20 { + "2.0.*" + } else if v18 { "1.8.*" } else if v110 { "1.10.*" @@ -358,6 +379,20 @@ mod macos { "any version" } ); + if !(v18 || v110 || v112 || v114 || v20) { + if let Some(out) = run_command("brew", &["--prefix", "hdf5@2.1"]) { + if is_root_dir(&out) { + config.inc_dir = Some(PathBuf::from(out).join("include")); + } + } + } + if !(v18 || v110 || v112 || v114) { + if let Some(out) = run_command("brew", &["--prefix", "hdf5@2.0"]) { + if is_root_dir(&out) { + config.inc_dir = Some(PathBuf::from(out).join("include")); + } + } + } if !(v18 || v110 || v112) { if let Some(out) = run_command("brew", &["--prefix", "hdf5@1.14"]) { if is_root_dir(&out) { @@ -395,7 +430,7 @@ mod macos { } if let Some(ref inc_dir) = config.inc_dir { println!("Found Homebrew HDF5 headers at:"); - println!(" {:?}", inc_dir); + println!(" {inc_dir:?}"); } } } @@ -410,8 +445,8 @@ mod windows { use serde::de::Error; use serde::{Deserialize, Deserializer}; use serde_derive::Deserialize as DeriveDeserialize; - use winreg::enums::HKEY_LOCAL_MACHINE; use winreg::RegKey; + use winreg::enums::HKEY_LOCAL_MACHINE; impl<'de> Deserialize<'de> for Version { fn deserialize(deserializer: D) -> Result @@ -462,7 +497,7 @@ mod windows { fn get_hdf5_app(version: Option) -> Option { if let Some(version) = version { - println!("Searching for installed HDF5 with version {:?}...", version); + println!("Searching for installed HDF5 with version {version:?}..."); } else { println!("Searching for installed HDF5 (any version)...") } @@ -484,7 +519,7 @@ mod windows { } if apps.len() > 1 { println!("Selecting the latest version ({:?}):", latest.version); - println!("- {:?}", latest); + println!("- {latest:?}"); } Some(latest.clone()) } @@ -510,7 +545,7 @@ mod windows { for path in env::split_paths(&var_path) { if let Ok(path) = path.canonicalize() { if path == bin_dir { - println!("Found in PATH: {:?}", path); + println!("Found in PATH: {path:?}"); return; } } @@ -525,7 +560,7 @@ impl LibrarySearcher { let mut config = Self::default(); if let Some(var) = env::var_os("HDF5_DIR") { println!("Setting HDF5 root from environment variable:"); - println!(" HDF5_DIR = {:?}", var); + println!(" HDF5_DIR = {var:?}"); let root = PathBuf::from(var); assert!(!root.is_relative(), "HDF5_DIR cannot be relative."); @@ -542,7 +577,7 @@ impl LibrarySearcher { let alt_inc_dir = root_dir.join("Library").join("include"); if !is_inc_dir(inc_dir) && is_inc_dir(&alt_inc_dir) { println!("Detected MSVC conda environment, changing headers dir to:"); - println!(" {:?}", alt_inc_dir); + println!(" {alt_inc_dir:?}"); config.inc_dir = Some(alt_inc_dir); } } @@ -550,11 +585,11 @@ impl LibrarySearcher { } if let Ok(var) = env::var("HDF5_VERSION") { println!("Setting HDF5 version from environment variable:"); - println!(" HDF5_VERSION = {:?}", var); + println!(" HDF5_VERSION = {var:?}"); if let Some(v) = Version::parse(&var) { config.version = Some(v); } else { - panic!("Invalid HDF5 version: {}", var); + panic!("Invalid HDF5 version: {var}"); } } config @@ -583,10 +618,10 @@ impl LibrarySearcher { if self.user_provided_dir { let lib_dir = format!("{}/lib", envdir.to_string_lossy()); println!("Custom HDF5_DIR provided; rpath can be set via:"); - println!(" RUSTFLAGS=\"-C link-args=-Wl,-rpath,{}\"", lib_dir); + println!(" RUSTFLAGS=\"-C link-args=-Wl,-rpath,{lib_dir}\""); if cfg!(target_os = "macos") { println!("On some OS X installations, you may also need to set:"); - println!(" DYLD_FALLBACK_LIBRARY_PATH=\"{}\"", lib_dir); + println!(" DYLD_FALLBACK_LIBRARY_PATH=\"{lib_dir}\""); } } } @@ -598,7 +633,7 @@ impl LibrarySearcher { pub fn finalize(self) -> Config { if let Some(ref inc_dir) = self.inc_dir { - assert!(is_inc_dir(inc_dir), "Invalid HDF5 headers directory: {:?}", inc_dir); + assert!(is_inc_dir(inc_dir), "Invalid HDF5 headers directory: {inc_dir:?}"); let mut link_paths = self.link_paths; if link_paths.is_empty() { if let Some(root_dir) = inc_dir.parent() { @@ -704,7 +739,7 @@ impl Config { for (flag, feature, native) in [ (!h.have_no_deprecated, "deprecated", "HDF5_ENABLE_DEPRECATED_SYMBOLS"), (h.have_threadsafe, "threadsafe", "HDF5_ENABLE_THREADSAFE"), - (h.have_filter_deflate, "zlib", "HDF5_ENABLE_Z_LIB_SUPPORT"), + (h.have_filter_deflate, "zlib", "HDF5_ENABLE_ZLIB_SUPPORT"), ] { if feature_enabled(&feature.to_ascii_uppercase()) { assert!( @@ -723,7 +758,7 @@ fn main() { let mut searcher = LibrarySearcher::new_from_env(); searcher.try_locate_hdf5_library(); let config = searcher.finalize(); - println!("{:#?}", config); + println!("{config:#?}"); config.emit_link_flags(); config.emit_cfg_flags(); write_hdf5_version(config.header.version); @@ -735,24 +770,24 @@ fn get_build_and_emit() { if feature_enabled("ZLIB") { let zlib_lib = env::var("DEP_HDF5SRC_ZLIB").unwrap(); - println!("cargo::metadata=zlib={}", &zlib_lib); + println!("cargo::metadata=zlib={}", zlib_lib); } if feature_enabled("HL") { let hdf5_hl_lib = env::var("DEP_HDF5SRC_HL_LIBRARY").unwrap(); - println!("cargo::rustc-link-lib=static={}", &hdf5_hl_lib); - println!("cargo::metadata=hl_library={}", &hdf5_hl_lib); + println!("cargo::rustc-link-lib=static={}", hdf5_hl_lib); + println!("cargo::metadata=hl_library={}", hdf5_hl_lib); } let hdf5_root = env::var("DEP_HDF5SRC_ROOT").unwrap(); - println!("cargo::metadata=root={}", &hdf5_root); + println!("cargo::metadata=root={}", hdf5_root); let hdf5_incdir = env::var("DEP_HDF5SRC_INCLUDE").unwrap(); - println!("cargo::metadata=include={}", &hdf5_incdir); + println!("cargo::metadata=include={}", hdf5_incdir); let hdf5_lib = env::var("DEP_HDF5SRC_LIBRARY").unwrap(); - println!("cargo::metadata=library={}", &hdf5_lib); + println!("cargo::metadata=library={}", hdf5_lib); - println!("cargo::rustc-link-search=native={}/lib", &hdf5_root); - println!("cargo::rustc-link-lib=static={}", &hdf5_lib); + println!("cargo::rustc-link-search=native={}/lib", hdf5_root); + println!("cargo::rustc-link-lib=static={}", hdf5_lib); let header = Header::parse(&hdf5_incdir); let config = Config { header, inc_dir: "".into(), link_paths: Vec::new() }; diff --git a/hdf5-sys/src/h5.rs b/hdf5-sys/src/h5.rs index 72ddd670e..989038201 100644 --- a/hdf5-sys/src/h5.rs +++ b/hdf5-sys/src/h5.rs @@ -65,35 +65,35 @@ impl Default for H5_ih_info_t { } } -extern "C" { - pub fn H5open() -> herr_t; - pub fn H5close() -> herr_t; - pub fn H5dont_atexit() -> herr_t; - pub fn H5garbage_collect() -> herr_t; - pub fn H5set_free_list_limits( +unsafe extern "C" { + pub unsafe fn H5open() -> herr_t; + pub unsafe fn H5close() -> herr_t; + pub unsafe fn H5dont_atexit() -> herr_t; + pub unsafe fn H5garbage_collect() -> herr_t; + pub unsafe fn H5set_free_list_limits( reg_global_lim: c_int, reg_list_lim: c_int, arr_global_lim: c_int, arr_list_lim: c_int, blk_global_lim: c_int, blk_list_lim: c_int, ) -> herr_t; - pub fn H5get_libversion( + pub unsafe fn H5get_libversion( majnum: *mut c_uint, minnum: *mut c_uint, relnum: *mut c_uint, ) -> herr_t; - pub fn H5check_version(majnum: c_uint, minnum: c_uint, relnum: c_uint) -> herr_t; + pub unsafe fn H5check_version(majnum: c_uint, minnum: c_uint, relnum: c_uint) -> herr_t; } #[cfg(feature = "1.8.13")] -extern "C" { - pub fn H5free_memory(mem: *mut c_void) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5free_memory(mem: *mut c_void) -> herr_t; } #[cfg(feature = "1.8.15")] -extern "C" { - pub fn H5allocate_memory(size: size_t, clear: hbool_t) -> *mut c_void; - pub fn H5resize_memory(mem: *mut c_void, size: size_t) -> *mut c_void; +unsafe extern "C" { + pub unsafe fn H5allocate_memory(size: size_t, clear: hbool_t) -> *mut c_void; + pub unsafe fn H5resize_memory(mem: *mut c_void, size: size_t) -> *mut c_void; } #[cfg(feature = "1.8.16")] -extern "C" { - pub fn H5is_library_threadsafe(is_ts: *mut hbool_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5is_library_threadsafe(is_ts: *mut hbool_t) -> herr_t; } #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] @@ -109,9 +109,9 @@ pub struct H5_alloc_stats_t { } #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] -extern "C" { - pub fn H5get_alloc_stats(stats: *mut H5_alloc_stats_t) -> herr_t; - pub fn H5get_free_list_sizes( +unsafe extern "C" { + pub unsafe fn H5get_alloc_stats(stats: *mut H5_alloc_stats_t) -> herr_t; + pub unsafe fn H5get_free_list_sizes( reg_size: *mut size_t, arr_size: *mut size_t, blk_size: *mut size_t, fac_size: *mut size_t, ) -> herr_t; } @@ -120,7 +120,7 @@ extern "C" { type H5_atclose_func_t = Option; #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5atclose(func: H5_atclose_func_t, ctx: *mut c_void) -> herr_t; - pub fn H5is_library_terminating(is_terminating: *mut hbool_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5atclose(func: H5_atclose_func_t, ctx: *mut c_void) -> herr_t; + pub unsafe fn H5is_library_terminating(is_terminating: *mut hbool_t) -> herr_t; } diff --git a/hdf5-sys/src/h5a.rs b/hdf5-sys/src/h5a.rs index d1c7f8f92..ffca5b341 100644 --- a/hdf5-sys/src/h5a.rs +++ b/hdf5-sys/src/h5a.rs @@ -42,140 +42,142 @@ pub type H5A_operator2_t = Option< ) -> herr_t, >; -extern "C" { - pub fn H5Acreate2( +unsafe extern "C" { + pub unsafe fn H5Acreate2( loc_id: hid_t, attr_name: *const c_char, type_id: hid_t, space_id: hid_t, acpl_id: hid_t, aapl_id: hid_t, ) -> hid_t; - pub fn H5Acreate_by_name( + pub unsafe fn H5Acreate_by_name( loc_id: hid_t, obj_name: *const c_char, attr_name: *const c_char, type_id: hid_t, space_id: hid_t, acpl_id: hid_t, aapl_id: hid_t, lapl_id: hid_t, ) -> hid_t; - pub fn H5Aopen(obj_id: hid_t, attr_name: *const c_char, aapl_id: hid_t) -> hid_t; - pub fn H5Aopen_by_name( + pub unsafe fn H5Aopen(obj_id: hid_t, attr_name: *const c_char, aapl_id: hid_t) -> hid_t; + pub unsafe fn H5Aopen_by_name( loc_id: hid_t, obj_name: *const c_char, attr_name: *const c_char, aapl_id: hid_t, lapl_id: hid_t, ) -> hid_t; - pub fn H5Aopen_by_idx( + pub unsafe fn H5Aopen_by_idx( loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, aapl_id: hid_t, lapl_id: hid_t, ) -> hid_t; - pub fn H5Awrite(attr_id: hid_t, type_id: hid_t, buf: *const c_void) -> herr_t; - pub fn H5Aread(attr_id: hid_t, type_id: hid_t, buf: *mut c_void) -> herr_t; - pub fn H5Aclose(attr_id: hid_t) -> herr_t; - pub fn H5Aget_space(attr_id: hid_t) -> hid_t; - pub fn H5Aget_type(attr_id: hid_t) -> hid_t; - pub fn H5Aget_create_plist(attr_id: hid_t) -> hid_t; - pub fn H5Aget_name(attr_id: hid_t, buf_size: size_t, buf: *mut c_char) -> ssize_t; - pub fn H5Aget_name_by_idx( + pub unsafe fn H5Awrite(attr_id: hid_t, type_id: hid_t, buf: *const c_void) -> herr_t; + pub unsafe fn H5Aread(attr_id: hid_t, type_id: hid_t, buf: *mut c_void) -> herr_t; + pub unsafe fn H5Aclose(attr_id: hid_t) -> herr_t; + pub unsafe fn H5Aget_space(attr_id: hid_t) -> hid_t; + pub unsafe fn H5Aget_type(attr_id: hid_t) -> hid_t; + pub unsafe fn H5Aget_create_plist(attr_id: hid_t) -> hid_t; + pub unsafe fn H5Aget_name(attr_id: hid_t, buf_size: size_t, buf: *mut c_char) -> ssize_t; + pub unsafe fn H5Aget_name_by_idx( loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, name: *mut c_char, size: size_t, lapl_id: hid_t, ) -> ssize_t; - pub fn H5Aget_storage_size(attr_id: hid_t) -> hsize_t; - pub fn H5Aget_info(attr_id: hid_t, ainfo: *mut H5A_info_t) -> herr_t; - pub fn H5Aget_info_by_name( + pub unsafe fn H5Aget_storage_size(attr_id: hid_t) -> hsize_t; + pub unsafe fn H5Aget_info(attr_id: hid_t, ainfo: *mut H5A_info_t) -> herr_t; + pub unsafe fn H5Aget_info_by_name( loc_id: hid_t, obj_name: *const c_char, attr_name: *const c_char, ainfo: *mut H5A_info_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Aget_info_by_idx( + pub unsafe fn H5Aget_info_by_idx( loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, ainfo: *mut H5A_info_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Arename(loc_id: hid_t, old_name: *const c_char, new_name: *const c_char) -> herr_t; - pub fn H5Arename_by_name( + pub unsafe fn H5Arename( + loc_id: hid_t, old_name: *const c_char, new_name: *const c_char, + ) -> herr_t; + pub unsafe fn H5Arename_by_name( loc_id: hid_t, obj_name: *const c_char, old_attr_name: *const c_char, new_attr_name: *const c_char, lapl_id: hid_t, ) -> herr_t; - pub fn H5Aiterate2( + pub unsafe fn H5Aiterate2( loc_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, idx: *mut hsize_t, op: H5A_operator2_t, op_data: *mut c_void, ) -> herr_t; - pub fn H5Aiterate_by_name( + pub unsafe fn H5Aiterate_by_name( loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, idx: *mut hsize_t, op: H5A_operator2_t, op_data: *mut c_void, lapd_id: hid_t, ) -> herr_t; - pub fn H5Adelete(loc_id: hid_t, name: *const c_char) -> herr_t; - pub fn H5Adelete_by_name( + pub unsafe fn H5Adelete(loc_id: hid_t, name: *const c_char) -> herr_t; + pub unsafe fn H5Adelete_by_name( loc_id: hid_t, obj_name: *const c_char, attr_name: *const c_char, lapl_id: hid_t, ) -> herr_t; - pub fn H5Adelete_by_idx( + pub unsafe fn H5Adelete_by_idx( loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Aexists(obj_id: hid_t, attr_name: *const c_char) -> htri_t; - pub fn H5Aexists_by_name( + pub unsafe fn H5Aexists(obj_id: hid_t, attr_name: *const c_char) -> htri_t; + pub unsafe fn H5Aexists_by_name( obj_id: hid_t, obj_name: *const c_char, attr_name: *const c_char, lapl_id: hid_t, ) -> htri_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Aget_info")] - pub fn H5Aget_num_attrs(loc_id: hid_t) -> c_int; + pub unsafe fn H5Aget_num_attrs(loc_id: hid_t) -> c_int; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Aopen_by_idx")] - pub fn H5Aopen_idx(loc_id: hid_t, idx: c_uint) -> hid_t; + pub unsafe fn H5Aopen_idx(loc_id: hid_t, idx: c_uint) -> hid_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Aopen_by_name")] - pub fn H5Aopen_name(loc_id: hid_t, name: *const c_char) -> hid_t; + pub unsafe fn H5Aopen_name(loc_id: hid_t, name: *const c_char) -> hid_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Acreate2")] - pub fn H5Acreate1( + pub unsafe fn H5Acreate1( loc_id: hid_t, name: *const c_char, type_id: hid_t, space_id: hid_t, acpl_id: hid_t, ) -> hid_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Aiterate2")] - pub fn H5Aiterate1( + pub unsafe fn H5Aiterate1( loc_id: hid_t, attr_num: *mut c_uint, op: H5A_operator1_t, op_data: *mut c_void, ) -> herr_t; } #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5Aclose_async( +unsafe extern "C" { + pub unsafe fn H5Aclose_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, attr_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Acreate_async( + pub unsafe fn H5Acreate_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, attr_name: *const c_char, type_id: hid_t, space_id: hid_t, acpl_id: hid_t, aapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Acreate_by_name_async( + pub unsafe fn H5Acreate_by_name_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, obj_name: *const c_char, attr_name: *const c_char, type_id: hid_t, space_id: hid_t, acpl_id: hid_t, aapl_id: hid_t, lapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Aexists_async( + pub unsafe fn H5Aexists_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, obj_id: hid_t, attr_name: *const c_char, exists: *mut hbool_t, es_id: hid_t, ) -> herr_t; - pub fn H5Aexists_by_name_async( + pub unsafe fn H5Aexists_by_name_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, obj_name: *const c_char, attr_name: *const c_char, exists: *mut hbool_t, lapl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Aopen_async( + pub unsafe fn H5Aopen_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, obj_id: hid_t, attr_name: *const c_char, aapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Aopen_by_idx_async( + pub unsafe fn H5Aopen_by_idx_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: c_ulong, aapl_id: hid_t, lapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Aopen_by_name_async( + pub unsafe fn H5Aopen_by_name_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, obj_name: *const c_char, attr_name: *const c_char, aapl_id: hid_t, lapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Aread_async( + pub unsafe fn H5Aread_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, attr_id: hid_t, dtype_id: hid_t, buf: *mut c_void, es_id: hid_t, ) -> herr_t; - pub fn H5Arename_async( + pub unsafe fn H5Arename_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, old_name: *const c_char, new_name: *const c_char, es_id: hid_t, ) -> herr_t; - pub fn H5Arename_by_name_async( + pub unsafe fn H5Arename_by_name_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, obj_name: *const c_char, old_attr_name: *const c_char, new_attr_name: *const c_char, lapl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Awrite_async( + pub unsafe fn H5Awrite_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, attr_id: hid_t, type_id: hid_t, buf: *const c_void, es_id: hid_t, ) -> herr_t; diff --git a/hdf5-sys/src/h5d.rs b/hdf5-sys/src/h5d.rs index 2a83e4149..6f1c8a95b 100644 --- a/hdf5-sys/src/h5d.rs +++ b/hdf5-sys/src/h5d.rs @@ -27,33 +27,22 @@ pub enum H5D_layout_t { H5D_NLAYOUTS = 3, } -impl Default for H5D_layout_t { - fn default() -> Self { - Self::H5D_CONTIGUOUS - } -} - pub type H5D_chunk_index_t = c_uint; pub const H5D_CHUNK_BTREE: H5D_chunk_index_t = 0; pub const H5D_CHUNK_IDX_BTREE: H5D_chunk_index_t = H5D_CHUNK_BTREE; #[repr(C)] -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Debug)] pub enum H5D_alloc_time_t { H5D_ALLOC_TIME_ERROR = -1, + #[default] H5D_ALLOC_TIME_DEFAULT = 0, H5D_ALLOC_TIME_EARLY = 1, H5D_ALLOC_TIME_LATE = 2, H5D_ALLOC_TIME_INCR = 3, } -impl Default for H5D_alloc_time_t { - fn default() -> Self { - Self::H5D_ALLOC_TIME_DEFAULT - } -} - #[repr(C)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] pub enum H5D_space_status_t { @@ -64,35 +53,25 @@ pub enum H5D_space_status_t { } #[repr(C)] -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Debug)] pub enum H5D_fill_time_t { H5D_FILL_TIME_ERROR = -1, H5D_FILL_TIME_ALLOC = 0, H5D_FILL_TIME_NEVER = 1, + #[default] H5D_FILL_TIME_IFSET = 2, } -impl Default for H5D_fill_time_t { - fn default() -> Self { - Self::H5D_FILL_TIME_IFSET - } -} - #[repr(C)] -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Debug)] pub enum H5D_fill_value_t { H5D_FILL_VALUE_ERROR = -1, H5D_FILL_VALUE_UNDEFINED = 0, + #[default] H5D_FILL_VALUE_DEFAULT = 1, H5D_FILL_VALUE_USER_DEFINED = 2, } -impl Default for H5D_fill_value_t { - fn default() -> Self { - Self::H5D_FILL_VALUE_DEFAULT - } -} - #[repr(C)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] pub enum H5D_mpio_actual_chunk_opt_mode_t { @@ -151,63 +130,65 @@ pub type H5D_gather_func_t = Option< ) -> herr_t, >; -extern "C" { - pub fn H5Dcreate2( +unsafe extern "C" { + pub unsafe fn H5Dcreate2( loc_id: hid_t, name: *const c_char, type_id: hid_t, space_id: hid_t, lcpl_id: hid_t, dcpl_id: hid_t, dapl_id: hid_t, ) -> hid_t; - pub fn H5Dcreate_anon( + pub unsafe fn H5Dcreate_anon( file_id: hid_t, type_id: hid_t, space_id: hid_t, plist_id: hid_t, dapl_id: hid_t, ) -> hid_t; - pub fn H5Dopen2(file_id: hid_t, name: *const c_char, dapl_id: hid_t) -> hid_t; - pub fn H5Dclose(dset_id: hid_t) -> herr_t; - pub fn H5Dget_space(dset_id: hid_t) -> hid_t; - pub fn H5Dget_space_status(dset_id: hid_t, allocation: *mut H5D_space_status_t) -> herr_t; - pub fn H5Dget_type(dset_id: hid_t) -> hid_t; - pub fn H5Dget_create_plist(dset_id: hid_t) -> hid_t; - pub fn H5Dget_access_plist(dset_id: hid_t) -> hid_t; - pub fn H5Dget_storage_size(dset_id: hid_t) -> hsize_t; - pub fn H5Dget_offset(dset_id: hid_t) -> haddr_t; - pub fn H5Dread( + pub unsafe fn H5Dopen2(file_id: hid_t, name: *const c_char, dapl_id: hid_t) -> hid_t; + pub unsafe fn H5Dclose(dset_id: hid_t) -> herr_t; + pub unsafe fn H5Dget_space(dset_id: hid_t) -> hid_t; + pub unsafe fn H5Dget_space_status( + dset_id: hid_t, allocation: *mut H5D_space_status_t, + ) -> herr_t; + pub unsafe fn H5Dget_type(dset_id: hid_t) -> hid_t; + pub unsafe fn H5Dget_create_plist(dset_id: hid_t) -> hid_t; + pub unsafe fn H5Dget_access_plist(dset_id: hid_t) -> hid_t; + pub unsafe fn H5Dget_storage_size(dset_id: hid_t) -> hsize_t; + pub unsafe fn H5Dget_offset(dset_id: hid_t) -> haddr_t; + pub unsafe fn H5Dread( dset_id: hid_t, mem_type_id: hid_t, mem_space_id: hid_t, file_space_id: hid_t, plist_id: hid_t, buf: *mut c_void, ) -> herr_t; - pub fn H5Dwrite( + pub unsafe fn H5Dwrite( dset_id: hid_t, mem_type_id: hid_t, mem_space_id: hid_t, file_space_id: hid_t, plist_id: hid_t, buf: *const c_void, ) -> herr_t; - pub fn H5Diterate( + pub unsafe fn H5Diterate( buf: *mut c_void, type_id: hid_t, space_id: hid_t, op: H5D_operator_t, operator_data: *mut c_void, ) -> herr_t; #[cfg_attr(feature = "1.12.0", deprecated(note = "deprecated in HDF5 1.12.0, use H5Treclaim"))] - pub fn H5Dvlen_reclaim( + pub unsafe fn H5Dvlen_reclaim( type_id: hid_t, space_id: hid_t, plist_id: hid_t, buf: *mut c_void, ) -> herr_t; - pub fn H5Dvlen_get_buf_size( + pub unsafe fn H5Dvlen_get_buf_size( dataset_id: hid_t, type_id: hid_t, space_id: hid_t, size: *mut hsize_t, ) -> herr_t; - pub fn H5Dfill( + pub unsafe fn H5Dfill( fill: *const c_void, fill_type: hid_t, buf: *mut c_void, buf_type: hid_t, space: hid_t, ) -> herr_t; - pub fn H5Dset_extent(dset_id: hid_t, size: *const hsize_t) -> herr_t; - pub fn H5Ddebug(dset_id: hid_t) -> herr_t; + pub unsafe fn H5Dset_extent(dset_id: hid_t, size: *const hsize_t) -> herr_t; + pub unsafe fn H5Ddebug(dset_id: hid_t) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Dcreate2")] - pub fn H5Dcreate1( + pub unsafe fn H5Dcreate1( file_id: hid_t, name: *const c_char, type_id: hid_t, space_id: hid_t, dcpl_id: hid_t, ) -> hid_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Dopen2")] - pub fn H5Dopen1(file_id: hid_t, name: *const c_char) -> hid_t; + pub unsafe fn H5Dopen1(file_id: hid_t, name: *const c_char) -> hid_t; } #[cfg(feature = "1.8.11")] -extern "C" { - pub fn H5Dscatter( +unsafe extern "C" { + pub unsafe fn H5Dscatter( op: H5D_scatter_func_t, op_data: *mut c_void, type_id: hid_t, dst_space_id: hid_t, dst_buf: *mut c_void, ) -> herr_t; - pub fn H5Dgather( + pub unsafe fn H5Dgather( src_space_id: hid_t, src_buf: *const c_void, type_id: hid_t, dst_buf_size: size_t, dst_buf: *mut c_void, op: H5D_gather_func_t, op_data: *mut c_void, ) -> herr_t; @@ -218,10 +199,11 @@ mod hdf5_1_10_0 { use super::*; #[repr(C)] - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] + #[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Debug)] pub enum H5D_layout_t { H5D_LAYOUT_ERROR = -1, H5D_COMPACT = 0, + #[default] H5D_CONTIGUOUS = 1, H5D_CHUNKED = 2, H5D_VIRTUAL = 3, @@ -229,19 +211,14 @@ mod hdf5_1_10_0 { } #[repr(C)] - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] + #[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Debug)] pub enum H5D_vds_view_t { H5D_VDS_ERROR = -1, H5D_VDS_FIRST_MISSING = 0, + #[default] H5D_VDS_LAST_AVAILABLE = 1, } - impl Default for H5D_vds_view_t { - fn default() -> Self { - Self::H5D_VDS_LAST_AVAILABLE - } - } - pub const H5D_CHUNK_DONT_FILTER_PARTIAL_CHUNKS: c_uint = 0x0002; pub type H5D_append_cb_t = Option< @@ -252,11 +229,13 @@ mod hdf5_1_10_0 { ) -> herr_t, >; - extern "C" { - pub fn H5Dflush(dset_id: hid_t) -> herr_t; - pub fn H5Drefresh(dset_id: hid_t) -> herr_t; - pub fn H5Dformat_convert(dset_id: hid_t) -> herr_t; - pub fn H5Dget_chunk_index_type(did: hid_t, idx_type: *mut H5D_chunk_index_t) -> herr_t; + unsafe extern "C" { + pub unsafe fn H5Dflush(dset_id: hid_t) -> herr_t; + pub unsafe fn H5Drefresh(dset_id: hid_t) -> herr_t; + pub unsafe fn H5Dformat_convert(dset_id: hid_t) -> herr_t; + pub unsafe fn H5Dget_chunk_index_type( + did: hid_t, idx_type: *mut H5D_chunk_index_t, + ) -> herr_t; } } @@ -264,27 +243,39 @@ mod hdf5_1_10_0 { pub use self::hdf5_1_10_0::*; #[cfg(feature = "1.10.3")] -extern "C" { - pub fn H5Dread_chunk( +unsafe extern "C" { + #[cfg_attr(not(feature = "2.0.0"), link_name = "H5Dread_chunk")] + pub unsafe fn H5Dread_chunk1( dset_id: hid_t, dxpl_id: hid_t, offset: *const hsize_t, filters: *mut u32, buf: *mut c_void, ) -> herr_t; - pub fn H5Dwrite_chunk( + #[cfg(feature = "2.0.0")] + pub unsafe fn H5Dread_chunk2( + dset_id: hid_t, dxpl_id: hid_t, offset: *const hsize_t, filters: *mut u32, + buf: *mut c_void, buf_size: *mut size_t, + ) -> herr_t; + pub unsafe fn H5Dwrite_chunk( dset_id: hid_t, dxpl_id: hid_t, filters: u32, offset: *const hsize_t, data_size: size_t, buf: *const c_void, ) -> herr_t; } +#[cfg(all(feature = "1.10.3", not(feature = "2.0.0")))] +pub use self::H5Dread_chunk1 as H5Dread_chunk; +#[cfg(feature = "2.0.0")] +pub use self::H5Dread_chunk2 as H5Dread_chunk; #[cfg(feature = "1.10.5")] -extern "C" { - pub fn H5Dget_chunk_info( +unsafe extern "C" { + pub unsafe fn H5Dget_chunk_info( dset_id: hid_t, fspace_id: hid_t, index: hsize_t, offset: *mut hsize_t, filter_mask: *mut c_uint, addr: *mut haddr_t, size: *mut hsize_t, ) -> herr_t; - pub fn H5Dget_chunk_info_by_coord( + pub unsafe fn H5Dget_chunk_info_by_coord( dset_id: hid_t, offset: *const hsize_t, filter_mask: *mut c_uint, addr: *mut haddr_t, size: *mut hsize_t, ) -> herr_t; - pub fn H5Dget_num_chunks(dset_id: hid_t, fspace_id: hid_t, nchunks: *mut hsize_t) -> herr_t; + pub unsafe fn H5Dget_num_chunks( + dset_id: hid_t, fspace_id: hid_t, nchunks: *mut hsize_t, + ) -> herr_t; } #[cfg(feature = "1.14.0")] @@ -299,37 +290,37 @@ pub type H5D_chunk_iter_op_t = Option< >; #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5Dchunk_iter( +unsafe extern "C" { + pub unsafe fn H5Dchunk_iter( dset_id: hid_t, dxpl: hid_t, cb: H5D_chunk_iter_op_t, op_data: *mut c_void, ) -> herr_t; - pub fn H5Dclose_async( + pub unsafe fn H5Dclose_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, dset_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Dcreate_async( + pub unsafe fn H5Dcreate_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, type_id: hid_t, space_id: hid_t, lcpl_id: hid_t, dcpl_id: hid_t, dapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Dget_space_async( + pub unsafe fn H5Dget_space_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, dset_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Dopen_async( + pub unsafe fn H5Dopen_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, dapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Dread_async( + pub unsafe fn H5Dread_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, dset_id: hid_t, mem_type_id: hid_t, mem_space_id: hid_t, file_space_id: hid_t, dxpl_id: hid_t, buf: *mut c_void, es_id: hid_t, ) -> herr_t; - pub fn H5Dset_extent_async( + pub unsafe fn H5Dset_extent_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, dset_id: hid_t, size: *mut c_ulong, es_id: hid_t, ) -> herr_t; - pub fn H5Dwrite_async( + pub unsafe fn H5Dwrite_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, dset_id: hid_t, mem_type_id: hid_t, mem_space_id: hid_t, file_space_id: hid_t, dxpl_id: hid_t, buf: *const c_void, es_id: hid_t, diff --git a/hdf5-sys/src/h5e.rs b/hdf5-sys/src/h5e.rs index 8e9399d32..d59be354e 100644 --- a/hdf5-sys/src/h5e.rs +++ b/hdf5-sys/src/h5e.rs @@ -77,69 +77,71 @@ pub type H5E_walk2_t = Option< pub type H5E_auto2_t = Option herr_t>; -extern "C" { - pub fn H5Eregister_class( +unsafe extern "C" { + pub unsafe fn H5Eregister_class( cls_name: *const c_char, lib_name: *const c_char, version: *const c_char, ) -> hid_t; - pub fn H5Eunregister_class(class_id: hid_t) -> herr_t; - pub fn H5Eclose_msg(err_id: hid_t) -> herr_t; - pub fn H5Ecreate_msg(cls: hid_t, msg_type: H5E_type_t, msg: *const c_char) -> hid_t; - pub fn H5Ecreate_stack() -> hid_t; - pub fn H5Eget_current_stack() -> hid_t; + pub unsafe fn H5Eunregister_class(class_id: hid_t) -> herr_t; + pub unsafe fn H5Eclose_msg(err_id: hid_t) -> herr_t; + pub unsafe fn H5Ecreate_msg(cls: hid_t, msg_type: H5E_type_t, msg: *const c_char) -> hid_t; + pub unsafe fn H5Ecreate_stack() -> hid_t; + pub unsafe fn H5Eget_current_stack() -> hid_t; #[cfg(feature = "1.14.0")] - pub fn H5Eappend_stack( + pub unsafe fn H5Eappend_stack( dst_stack_id: hid_t, src_stack_id: hid_t, close_source_stack: hbool_t, ) -> herr_t; #[cfg(feature = "1.14.5")] - pub fn H5Eis_paused(stack_id: hid_t, is_paused: *mut hbool_t) -> herr_t; + pub unsafe fn H5Eis_paused(stack_id: hid_t, is_paused: *mut hbool_t) -> herr_t; #[cfg(feature = "1.14.5")] - pub fn H5Epause_stack(stack_id: hid_t) -> herr_t; + pub unsafe fn H5Epause_stack(stack_id: hid_t) -> herr_t; #[cfg(feature = "1.14.5")] - pub fn H5Eresume_stack(stack_id: hid_t) -> herr_t; - pub fn H5Eclose_stack(stack_id: hid_t) -> herr_t; - pub fn H5Eget_class_name(class_id: hid_t, name: *mut c_char, size: size_t) -> ssize_t; - pub fn H5Eset_current_stack(err_stack_id: hid_t) -> herr_t; - pub fn H5Epush2( + pub unsafe fn H5Eresume_stack(stack_id: hid_t) -> herr_t; + pub unsafe fn H5Eclose_stack(stack_id: hid_t) -> herr_t; + pub unsafe fn H5Eget_class_name(class_id: hid_t, name: *mut c_char, size: size_t) -> ssize_t; + pub unsafe fn H5Eset_current_stack(err_stack_id: hid_t) -> herr_t; + pub unsafe fn H5Epush2( err_stack: hid_t, file: *const c_char, func: *const c_char, line: c_uint, cls_id: hid_t, maj_id: hid_t, min_id: hid_t, msg: *const c_char, ... ) -> herr_t; - pub fn H5Epop(err_stack: hid_t, count: size_t) -> herr_t; - pub fn H5Eprint2(err_stack: hid_t, stream: *mut FILE) -> herr_t; - pub fn H5Ewalk2( + pub unsafe fn H5Epop(err_stack: hid_t, count: size_t) -> herr_t; + pub unsafe fn H5Eprint2(err_stack: hid_t, stream: *mut FILE) -> herr_t; + pub unsafe fn H5Ewalk2( err_stack: hid_t, direction: H5E_direction_t, func: H5E_walk2_t, client_data: *mut c_void, ) -> herr_t; - pub fn H5Eget_auto2( + pub unsafe fn H5Eget_auto2( estack_id: hid_t, func: *mut H5E_auto2_t, client_data: *mut *mut c_void, ) -> herr_t; - pub fn H5Eset_auto2(estack_id: hid_t, func: H5E_auto2_t, client_data: *mut c_void) -> herr_t; - pub fn H5Eclear2(err_stack: hid_t) -> herr_t; - pub fn H5Eauto_is_v2(err_stack: hid_t, is_stack: *mut c_uint) -> herr_t; - pub fn H5Eget_msg( + pub unsafe fn H5Eset_auto2( + estack_id: hid_t, func: H5E_auto2_t, client_data: *mut c_void, + ) -> herr_t; + pub unsafe fn H5Eclear2(err_stack: hid_t) -> herr_t; + pub unsafe fn H5Eauto_is_v2(err_stack: hid_t, is_stack: *mut c_uint) -> herr_t; + pub unsafe fn H5Eget_msg( msg_id: hid_t, type_: *mut H5E_type_t, msg: *mut c_char, size: size_t, ) -> ssize_t; - pub fn H5Eget_num(error_stack_id: hid_t) -> ssize_t; + pub unsafe fn H5Eget_num(error_stack_id: hid_t) -> ssize_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Epush2")] - pub fn H5Epush1( + pub unsafe fn H5Epush1( file: *const c_char, func: *const c_char, line: c_uint, maj: H5E_major_t, min: H5E_minor_t, str_: *const c_char, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Eprint2")] - pub fn H5Eprint1(stream: *mut FILE) -> herr_t; + pub unsafe fn H5Eprint1(stream: *mut FILE) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Ewalk2")] - pub fn H5Ewalk1( + pub unsafe fn H5Ewalk1( direction: H5E_direction_t, func: H5E_walk1_t, client_data: *mut c_void, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Eget_auto2")] - pub fn H5Eget_auto1(func: *mut H5E_auto1_t, client_data: *mut *mut c_void) -> herr_t; + pub unsafe fn H5Eget_auto1(func: *mut H5E_auto1_t, client_data: *mut *mut c_void) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Eset_auto2")] - pub fn H5Eset_auto1(func: H5E_auto1_t, client_data: *mut c_void) -> herr_t; + pub unsafe fn H5Eset_auto1(func: H5E_auto1_t, client_data: *mut c_void) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Eclear2")] - pub fn H5Eclear1() -> herr_t; + pub unsafe fn H5Eclear1() -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Eget_msg")] - pub fn H5Eget_major(maj: H5E_major_t) -> *mut c_char; + pub unsafe fn H5Eget_major(maj: H5E_major_t) -> *mut c_char; #[deprecated(note = "deprecated in HDF5 1.8.0")] - pub fn H5Eget_minor(min: H5E_minor_t) -> *mut c_char; + pub unsafe fn H5Eget_minor(min: H5E_minor_t) -> *mut c_char; } pub use self::globals::*; @@ -158,6 +160,7 @@ mod globals { extern_static!(H5E_FILE, H5E_FILE_g); extern_static!(H5E_SOHM, H5E_SOHM_g); extern_static!(H5E_SYM, H5E_SYM_g); + #[cfg(feature = "1.8.11")] extern_static!(H5E_PLUGIN, H5E_PLUGIN_g); extern_static!(H5E_VFL, H5E_VFL_g); extern_static!(H5E_INTERNAL, H5E_INTERNAL_g); @@ -212,6 +215,7 @@ mod globals { extern_static!(H5E_CANTGET, H5E_CANTGET_g); extern_static!(H5E_CANTSET, H5E_CANTSET_g); extern_static!(H5E_DUPCLASS, H5E_DUPCLASS_g); + #[cfg(feature = "1.8.9")] extern_static!(H5E_SETDISALLOWED, H5E_SETDISALLOWED_g); extern_static!(H5E_CANTMERGE, H5E_CANTMERGE_g); extern_static!(H5E_CANTREVIVE, H5E_CANTREVIVE_g); @@ -237,6 +241,7 @@ mod globals { extern_static!(H5E_COMPLEN, H5E_COMPLEN_g); extern_static!(H5E_PATH, H5E_PATH_g); extern_static!(H5E_NONE_MINOR, H5E_NONE_MINOR_g); + #[cfg(feature = "1.8.11")] extern_static!(H5E_OPENERROR, H5E_OPENERROR_g); extern_static!(H5E_FILEEXISTS, H5E_FILEEXISTS_g); extern_static!(H5E_FILEOPEN, H5E_FILEOPEN_g); @@ -301,9 +306,9 @@ mod globals { extern_static!(H5E_CANTREMOVE, H5E_CANTREMOVE_g); extern_static!(H5E_CANTCONVERT, H5E_CANTCONVERT_g); extern_static!(H5E_BADSIZE, H5E_BADSIZE_g); - #[cfg(feature = "1.12.1")] + #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] extern_static!(H5E_CANTLOCKFILE, H5E_CANTLOCKFILE_g); - #[cfg(feature = "1.12.1")] + #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] extern_static!(H5E_CANTUNLOCKFILE, H5E_CANTUNLOCKFILE_g); #[cfg(feature = "1.12.1")] extern_static!(H5E_LIB, H5E_LIB_g); @@ -323,6 +328,58 @@ mod globals { extern_static!(H5E_ID, H5E_ID_g); #[cfg(feature = "1.14.0")] extern_static!(H5E_UNMOUNT, H5E_UNMOUNT_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5E_RTREE, H5E_RTREE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5E_THREADSAFE, H5E_THREADSAFE_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_EARRAY, H5E_EARRAY_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_FARRAY, H5E_FARRAY_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_PAGEBUF, H5E_PAGEBUF_g); + #[cfg(feature = "1.10.3")] + extern_static!(H5E_CONTEXT, H5E_CONTEXT_g); + #[cfg(feature = "1.12.0")] + extern_static!(H5E_MAP, H5E_MAP_g); + #[cfg(feature = "1.12.0")] + extern_static!(H5E_VOL, H5E_VOL_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTAPPEND, H5E_CANTAPPEND_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTCORK, H5E_CANTCORK_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTDEPEND, H5E_CANTDEPEND_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTNOTIFY, H5E_CANTNOTIFY_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTTAG, H5E_CANTTAG_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTUNCORK, H5E_CANTUNCORK_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTUNDEPEND, H5E_CANTUNDEPEND_g); + #[cfg(all(feature = "1.10.0", not(feature = "1.12.0")))] + extern_static!(H5E_LOGFAIL, H5E_LOGFAIL_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_CANTCLEAN, H5E_CANTCLEAN_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_CANTMARKCLEAN, H5E_CANTMARKCLEAN_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_CANTMARKSERIALIZED, H5E_CANTMARKSERIALIZED_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_CANTMARKUNSERIALIZED, H5E_CANTMARKUNSERIALIZED_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_CANTUNSERIALIZE, H5E_CANTUNSERIALIZE_g); + #[cfg(feature = "1.12.0")] + extern_static!(H5E_CANTDELETEFILE, H5E_CANTDELETEFILE_g); + #[cfg(feature = "1.10.2")] + extern_static!(H5E_CANTGATHER, H5E_CANTGATHER_g); + #[cfg(feature = "1.10.7")] + extern_static!(H5E_INCONSISTENTSTATE, H5E_INCONSISTENTSTATE_g); + #[cfg(feature = "1.10.5")] + extern_static!(H5E_LOGGING, H5E_LOGGING_g); + #[cfg(feature = "1.10.2")] + extern_static!(H5E_NO_INDEPENDENT, H5E_NO_INDEPENDENT_g); } #[cfg(all(target_env = "msvc", not(feature = "static")))] @@ -340,6 +397,7 @@ mod globals { extern_static!(H5E_FILE, __imp_H5E_FILE_g); extern_static!(H5E_SOHM, __imp_H5E_SOHM_g); extern_static!(H5E_SYM, __imp_H5E_SYM_g); + #[cfg(feature = "1.8.11")] extern_static!(H5E_PLUGIN, __imp_H5E_PLUGIN_g); extern_static!(H5E_VFL, __imp_H5E_VFL_g); extern_static!(H5E_INTERNAL, __imp_H5E_INTERNAL_g); @@ -394,6 +452,7 @@ mod globals { extern_static!(H5E_CANTGET, __imp_H5E_CANTGET_g); extern_static!(H5E_CANTSET, __imp_H5E_CANTSET_g); extern_static!(H5E_DUPCLASS, __imp_H5E_DUPCLASS_g); + #[cfg(feature = "1.8.9")] extern_static!(H5E_SETDISALLOWED, __imp_H5E_SETDISALLOWED_g); extern_static!(H5E_CANTMERGE, __imp_H5E_CANTMERGE_g); extern_static!(H5E_CANTREVIVE, __imp_H5E_CANTREVIVE_g); @@ -419,6 +478,7 @@ mod globals { extern_static!(H5E_COMPLEN, __imp_H5E_COMPLEN_g); extern_static!(H5E_PATH, __imp_H5E_PATH_g); extern_static!(H5E_NONE_MINOR, __imp_H5E_NONE_MINOR_g); + #[cfg(feature = "1.8.11")] extern_static!(H5E_OPENERROR, __imp_H5E_OPENERROR_g); extern_static!(H5E_FILEEXISTS, __imp_H5E_FILEEXISTS_g); extern_static!(H5E_FILEOPEN, __imp_H5E_FILEOPEN_g); @@ -483,9 +543,9 @@ mod globals { extern_static!(H5E_CANTREMOVE, __imp_H5E_CANTREMOVE_g); extern_static!(H5E_CANTCONVERT, __imp_H5E_CANTCONVERT_g); extern_static!(H5E_BADSIZE, __imp_H5E_BADSIZE_g); - #[cfg(feature = "1.12.1")] + #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] extern_static!(H5E_CANTLOCKFILE, __imp_H5E_CANTLOCKFILE_g); - #[cfg(feature = "1.12.1")] + #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] extern_static!(H5E_CANTUNLOCKFILE, __imp_H5E_CANTUNLOCKFILE_g); #[cfg(feature = "1.12.1")] extern_static!(H5E_LIB, __imp_H5E_LIB_g); @@ -505,4 +565,56 @@ mod globals { extern_static!(H5E_ID, __imp_H5E_ID_g); #[cfg(feature = "1.14.0")] extern_static!(H5E_UNMOUNT, __imp_H5E_UNMOUNT_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5E_RTREE, __imp_H5E_RTREE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5E_THREADSAFE, __imp_H5E_THREADSAFE_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_EARRAY, __imp_H5E_EARRAY_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_FARRAY, __imp_H5E_FARRAY_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_PAGEBUF, __imp_H5E_PAGEBUF_g); + #[cfg(feature = "1.10.3")] + extern_static!(H5E_CONTEXT, __imp_H5E_CONTEXT_g); + #[cfg(feature = "1.12.0")] + extern_static!(H5E_MAP, __imp_H5E_MAP_g); + #[cfg(feature = "1.12.0")] + extern_static!(H5E_VOL, __imp_H5E_VOL_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTAPPEND, __imp_H5E_CANTAPPEND_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTCORK, __imp_H5E_CANTCORK_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTDEPEND, __imp_H5E_CANTDEPEND_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTNOTIFY, __imp_H5E_CANTNOTIFY_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTTAG, __imp_H5E_CANTTAG_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTUNCORK, __imp_H5E_CANTUNCORK_g); + #[cfg(feature = "1.10.0")] + extern_static!(H5E_CANTUNDEPEND, __imp_H5E_CANTUNDEPEND_g); + #[cfg(all(feature = "1.10.0", not(feature = "1.12.0")))] + extern_static!(H5E_LOGFAIL, __imp_H5E_LOGFAIL_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_CANTCLEAN, __imp_H5E_CANTCLEAN_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_CANTMARKCLEAN, __imp_H5E_CANTMARKCLEAN_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_CANTMARKSERIALIZED, __imp_H5E_CANTMARKSERIALIZED_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_CANTMARKUNSERIALIZED, __imp_H5E_CANTMARKUNSERIALIZED_g); + #[cfg(feature = "1.10.1")] + extern_static!(H5E_CANTUNSERIALIZE, __imp_H5E_CANTUNSERIALIZE_g); + #[cfg(feature = "1.12.0")] + extern_static!(H5E_CANTDELETEFILE, __imp_H5E_CANTDELETEFILE_g); + #[cfg(feature = "1.10.2")] + extern_static!(H5E_CANTGATHER, __imp_H5E_CANTGATHER_g); + #[cfg(feature = "1.10.7")] + extern_static!(H5E_INCONSISTENTSTATE, __imp_H5E_INCONSISTENTSTATE_g); + #[cfg(feature = "1.10.5")] + extern_static!(H5E_LOGGING, __imp_H5E_LOGGING_g); + #[cfg(feature = "1.10.2")] + extern_static!(H5E_NO_INDEPENDENT, __imp_H5E_NO_INDEPENDENT_g); } diff --git a/hdf5-sys/src/h5es.rs b/hdf5-sys/src/h5es.rs index 66a41e662..31a9ad797 100644 --- a/hdf5-sys/src/h5es.rs +++ b/hdf5-sys/src/h5es.rs @@ -56,32 +56,35 @@ pub type H5ES_event_complete_func_t = Option< pub type H5ES_event_insert_func_t = Option c_int>; -extern "C" { - pub fn H5ESinsert_request(es_id: hid_t, connector_id: hid_t, request: *mut c_void) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5ESinsert_request( + es_id: hid_t, connector_id: hid_t, request: *mut c_void, + ) -> herr_t; } -extern "C" { - pub fn H5EScancel( +unsafe extern "C" { + pub unsafe fn H5EScancel( es_id: hid_t, num_not_canceled: *mut size_t, err_occured: *mut hbool_t, ) -> herr_t; - pub fn H5ESclose(es_id: hid_t) -> herr_t; - pub fn H5EScreate() -> hid_t; - pub fn H5ESfree_err_info(num_err_info: size_t, err_info: *mut H5ES_err_info_t) -> herr_t; - pub fn H5ESget_count(es_id: hid_t, count: *mut size_t) -> herr_t; - pub fn H5ESget_err_count(es_id: hid_t, num_errs: *mut size_t) -> herr_t; - pub fn H5ESget_err_info( + pub unsafe fn H5ESclose(es_id: hid_t) -> herr_t; + pub unsafe fn H5EScreate() -> hid_t; + pub unsafe fn H5ESfree_err_info(num_err_info: size_t, err_info: *mut H5ES_err_info_t) + -> herr_t; + pub unsafe fn H5ESget_count(es_id: hid_t, count: *mut size_t) -> herr_t; + pub unsafe fn H5ESget_err_count(es_id: hid_t, num_errs: *mut size_t) -> herr_t; + pub unsafe fn H5ESget_err_info( es_id: hid_t, num_err_info: size_t, err_info: *mut H5ES_err_info_t, err_cleared: *mut size_t, ) -> herr_t; - pub fn H5ESget_err_status(es_id: hid_t, err_occured: *mut hbool_t) -> herr_t; - pub fn H5ESget_op_counter(es_id: hid_t, counter: *mut u64) -> herr_t; - pub fn H5ESregister_complete_func( + pub unsafe fn H5ESget_err_status(es_id: hid_t, err_occured: *mut hbool_t) -> herr_t; + pub unsafe fn H5ESget_op_counter(es_id: hid_t, counter: *mut u64) -> herr_t; + pub unsafe fn H5ESregister_complete_func( es_id: hid_t, func: H5ES_event_complete_func_t, ctx: *mut c_void, ) -> herr_t; - pub fn H5ESregister_insert_func( + pub unsafe fn H5ESregister_insert_func( es_id: hid_t, func: H5ES_event_insert_func_t, ctx: *mut c_void, ) -> herr_t; - pub fn H5ESwait( + pub unsafe fn H5ESwait( es_id: hid_t, timeout: u64, num_in_progress: *mut size_t, err_occured: *mut hbool_t, ) -> herr_t; } diff --git a/hdf5-sys/src/h5f.rs b/hdf5-sys/src/h5f.rs index 092651d5c..05c5954ad 100644 --- a/hdf5-sys/src/h5f.rs +++ b/hdf5-sys/src/h5f.rs @@ -54,20 +54,15 @@ pub enum H5F_scope_t { } #[repr(C)] -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Debug)] pub enum H5F_close_degree_t { + #[default] H5F_CLOSE_DEFAULT = 0, H5F_CLOSE_WEAK = 1, H5F_CLOSE_SEMI = 2, H5F_CLOSE_STRONG = 3, } -impl Default for H5F_close_degree_t { - fn default() -> Self { - Self::H5F_CLOSE_DEFAULT - } -} - #[cfg_attr(feature = "1.10.0", deprecated(note = "deprecated in HDF5 1.10.0, use H5F_info2_t"))] #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -130,11 +125,19 @@ pub enum H5F_libver_t { H5F_LIBVER_V112 = 3, #[cfg(feature = "1.14.0")] H5F_LIBVER_V114 = 4, + #[cfg(feature = "2.0.0")] + H5F_LIBVER_V200 = 5, H5F_LIBVER_NBOUNDS, } -#[cfg(feature = "1.10.2")] +#[cfg(all(feature = "1.10.2", not(feature = "1.12.0")))] pub const H5F_LIBVER_LATEST: H5F_libver_t = H5F_LIBVER_V110; +#[cfg(all(feature = "1.12.0", not(feature = "1.14.0")))] +pub const H5F_LIBVER_LATEST: H5F_libver_t = H5F_LIBVER_V112; +#[cfg(all(feature = "1.14.0", not(feature = "2.0.0")))] +pub const H5F_LIBVER_LATEST: H5F_libver_t = H5F_LIBVER_V114; +#[cfg(feature = "2.0.0")] +pub const H5F_LIBVER_LATEST: H5F_libver_t = H5F_LIBVER_V200; impl Default for H5F_libver_t { fn default() -> Self { @@ -142,66 +145,74 @@ impl Default for H5F_libver_t { } } -extern "C" { +unsafe extern "C" { #[cfg_attr( feature = "1.10.2", deprecated(note = "deprecated in HDF5 1.10.2, use H5Fset_libver_bounds()") )] - pub fn H5Fset_latest_format(file_id: hid_t, latest_format: hbool_t) -> herr_t; - pub fn H5Fis_hdf5(filename: *const c_char) -> htri_t; + pub unsafe fn H5Fset_latest_format(file_id: hid_t, latest_format: hbool_t) -> herr_t; + pub unsafe fn H5Fis_hdf5(filename: *const c_char) -> htri_t; #[cfg(feature = "1.12.0")] - pub fn H5Fis_accessible(container_name: *const c_char, fapl_id: hid_t) -> htri_t; - pub fn H5Fcreate( + pub unsafe fn H5Fis_accessible(container_name: *const c_char, fapl_id: hid_t) -> htri_t; + pub unsafe fn H5Fcreate( filename: *const c_char, flags: c_uint, create_plist: hid_t, access_plist: hid_t, ) -> hid_t; - pub fn H5Fopen(filename: *const c_char, flags: c_uint, access_plist: hid_t) -> hid_t; - pub fn H5Freopen(file_id: hid_t) -> hid_t; - pub fn H5Fflush(object_id: hid_t, scope: H5F_scope_t) -> herr_t; - pub fn H5Fclose(file_id: hid_t) -> herr_t; + pub unsafe fn H5Fopen(filename: *const c_char, flags: c_uint, access_plist: hid_t) -> hid_t; + pub unsafe fn H5Freopen(file_id: hid_t) -> hid_t; + pub unsafe fn H5Fflush(object_id: hid_t, scope: H5F_scope_t) -> herr_t; + pub unsafe fn H5Fclose(file_id: hid_t) -> herr_t; #[cfg(feature = "1.12.0")] - pub fn H5Fdelete(filename: *const c_char, fapl_id: hid_t) -> herr_t; - pub fn H5Fget_create_plist(file_id: hid_t) -> hid_t; - pub fn H5Fget_access_plist(file_id: hid_t) -> hid_t; - pub fn H5Fget_intent(file_id: hid_t, intent: *mut c_uint) -> herr_t; + pub unsafe fn H5Fdelete(filename: *const c_char, fapl_id: hid_t) -> herr_t; + pub unsafe fn H5Fget_create_plist(file_id: hid_t) -> hid_t; + pub unsafe fn H5Fget_access_plist(file_id: hid_t) -> hid_t; + pub unsafe fn H5Fget_intent(file_id: hid_t, intent: *mut c_uint) -> herr_t; #[cfg(feature = "1.12.0")] - pub fn H5Fget_fileno(file_id: hid_t, fileno: *mut c_ulong) -> herr_t; - pub fn H5Fget_obj_count(file_id: hid_t, types: c_uint) -> ssize_t; - pub fn H5Fget_obj_ids( + pub unsafe fn H5Fget_fileno(file_id: hid_t, fileno: *mut c_ulong) -> herr_t; + pub unsafe fn H5Fget_obj_count(file_id: hid_t, types: c_uint) -> ssize_t; + pub unsafe fn H5Fget_obj_ids( file_id: hid_t, types: c_uint, max_objs: size_t, obj_id_list: *mut hid_t, ) -> ssize_t; - pub fn H5Fget_vfd_handle(file_id: hid_t, fapl: hid_t, file_handle: *mut *mut c_void) -> herr_t; - pub fn H5Fmount(loc: hid_t, name: *const c_char, child: hid_t, plist: hid_t) -> herr_t; - pub fn H5Funmount(loc: hid_t, name: *const c_char) -> herr_t; - pub fn H5Fget_freespace(file_id: hid_t) -> hssize_t; - pub fn H5Fget_filesize(file_id: hid_t, size: *mut hsize_t) -> herr_t; - pub fn H5Fget_mdc_config(file_id: hid_t, config_ptr: *mut H5AC_cache_config_t) -> herr_t; + pub unsafe fn H5Fget_vfd_handle( + file_id: hid_t, fapl: hid_t, file_handle: *mut *mut c_void, + ) -> herr_t; + pub unsafe fn H5Fmount(loc: hid_t, name: *const c_char, child: hid_t, plist: hid_t) -> herr_t; + pub unsafe fn H5Funmount(loc: hid_t, name: *const c_char) -> herr_t; + pub unsafe fn H5Fget_freespace(file_id: hid_t) -> hssize_t; + pub unsafe fn H5Fget_filesize(file_id: hid_t, size: *mut hsize_t) -> herr_t; + pub unsafe fn H5Fget_mdc_config(file_id: hid_t, config_ptr: *mut H5AC_cache_config_t) + -> herr_t; #[cfg(not(feature = "1.14.0"))] - pub fn H5Fset_mdc_config(file_id: hid_t, config_ptr: *mut H5AC_cache_config_t) -> herr_t; + pub unsafe fn H5Fset_mdc_config(file_id: hid_t, config_ptr: *mut H5AC_cache_config_t) + -> herr_t; #[cfg(feature = "1.14.0")] - pub fn H5Fset_mdc_config(file_id: hid_t, config_ptr: *const H5AC_cache_config_t) -> herr_t; - pub fn H5Fget_mdc_hit_rate(file_id: hid_t, hit_rate_ptr: *mut c_double) -> herr_t; - pub fn H5Fget_mdc_size( + pub unsafe fn H5Fset_mdc_config( + file_id: hid_t, config_ptr: *const H5AC_cache_config_t, + ) -> herr_t; + pub unsafe fn H5Fget_mdc_hit_rate(file_id: hid_t, hit_rate_ptr: *mut c_double) -> herr_t; + pub unsafe fn H5Fget_mdc_size( file_id: hid_t, max_size_ptr: *mut size_t, min_clean_size_ptr: *mut size_t, cur_size_ptr: *mut size_t, cur_num_entries_ptr: *mut c_int, ) -> herr_t; - pub fn H5Freset_mdc_hit_rate_stats(file_id: hid_t) -> herr_t; - pub fn H5Fget_name(obj_id: hid_t, name: *mut c_char, size: size_t) -> ssize_t; + pub unsafe fn H5Freset_mdc_hit_rate_stats(file_id: hid_t) -> herr_t; + pub unsafe fn H5Fget_name(obj_id: hid_t, name: *mut c_char, size: size_t) -> ssize_t; } #[cfg(feature = "1.8.7")] -extern "C" { - pub fn H5Fclear_elink_file_cache(file_id: hid_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Fclear_elink_file_cache(file_id: hid_t) -> herr_t; } #[cfg(feature = "1.8.9")] -extern "C" { - pub fn H5Fget_file_image(file_id: hid_t, buf_ptr: *mut c_void, buf_len: size_t) -> ssize_t; +unsafe extern "C" { + pub unsafe fn H5Fget_file_image( + file_id: hid_t, buf_ptr: *mut c_void, buf_len: size_t, + ) -> ssize_t; } #[cfg(all(feature = "1.8.9", feature = "have-parallel"))] -extern "C" { - pub fn H5Fset_mpi_atomicity(file_id: hid_t, flag: hbool_t) -> herr_t; - pub fn H5Fget_mpi_atomicity(file_id: hid_t, flag: *mut hbool_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Fset_mpi_atomicity(file_id: hid_t, flag: hbool_t) -> herr_t; + pub unsafe fn H5Fget_mpi_atomicity(file_id: hid_t, flag: *mut hbool_t) -> herr_t; } #[cfg(feature = "1.10.0")] @@ -309,28 +320,28 @@ mod hdf5_1_10_0 { pub use self::H5F_file_space_type_t::*; - extern "C" { - pub fn H5Fstart_swmr_write(file_id: hid_t) -> herr_t; - pub fn H5Fget_metadata_read_retry_info( + unsafe extern "C" { + pub unsafe fn H5Fstart_swmr_write(file_id: hid_t) -> herr_t; + pub unsafe fn H5Fget_metadata_read_retry_info( file_id: hid_t, info: *mut H5F_retry_info_t, ) -> herr_t; - pub fn H5Fstart_mdc_logging(file_id: hid_t) -> herr_t; - pub fn H5Fstop_mdc_logging(file_id: hid_t) -> herr_t; - pub fn H5Fget_free_sections( + pub unsafe fn H5Fstart_mdc_logging(file_id: hid_t) -> herr_t; + pub unsafe fn H5Fstop_mdc_logging(file_id: hid_t) -> herr_t; + pub unsafe fn H5Fget_free_sections( file_id: hid_t, type_: H5F_mem_t, nsects: size_t, sect_info: *mut H5F_sect_info_t, ) -> ssize_t; - pub fn H5Fformat_convert(fid: hid_t) -> herr_t; - pub fn H5Fget_info2(obj_id: hid_t, finfo: *mut H5F_info2_t) -> herr_t; + pub unsafe fn H5Fformat_convert(fid: hid_t) -> herr_t; + pub unsafe fn H5Fget_info2(obj_id: hid_t, finfo: *mut H5F_info2_t) -> herr_t; } } -extern "C" { +unsafe extern "C" { #[cfg_attr( feature = "1.10.0", deprecated(note = "deprecated in HDF5 1.10.0, use H5Fget_info2") )] #[cfg_attr(not(feature = "1.10.0"), link_name = "H5Fget_info")] - pub fn H5Fget_info1(obj_id: hid_t, finfo: *mut H5F_info1_t) -> herr_t; + pub unsafe fn H5Fget_info1(obj_id: hid_t, finfo: *mut H5F_info1_t) -> herr_t; } #[cfg(feature = "1.10.0")] @@ -353,12 +364,12 @@ mod hdf5_1_10_1 { pub use self::H5F_fspace_strategy_t::*; - extern "C" { - pub fn H5Fget_mdc_image_info( + unsafe extern "C" { + pub unsafe fn H5Fget_mdc_image_info( file_id: hid_t, image_addr: *mut haddr_t, image_size: *mut hsize_t, ) -> herr_t; - pub fn H5Freset_page_buffering_stats(file_id: hid_t) -> herr_t; - pub fn H5Fget_page_buffering_stats( + pub unsafe fn H5Freset_page_buffering_stats(file_id: hid_t) -> herr_t; + pub unsafe fn H5Fget_page_buffering_stats( file_id: hid_t, accesses: *mut c_uint, hits: *mut c_uint, misses: *mut c_uint, evictions: *mut c_uint, bypasses: *mut c_uint, ) -> herr_t; @@ -369,30 +380,30 @@ mod hdf5_1_10_1 { pub use self::hdf5_1_10_1::*; #[cfg(feature = "1.10.5")] -extern "C" { - pub fn H5Fget_dset_no_attrs_hint(file_id: hid_t, minimize: *mut hbool_t) -> herr_t; - pub fn H5Fset_dset_no_attrs_hint(file_id: hid_t, minimize: hbool_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Fget_dset_no_attrs_hint(file_id: hid_t, minimize: *mut hbool_t) -> herr_t; + pub unsafe fn H5Fset_dset_no_attrs_hint(file_id: hid_t, minimize: hbool_t) -> herr_t; } #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5Fclose_async( +unsafe extern "C" { + pub unsafe fn H5Fclose_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, file_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Fcreate_async( + pub unsafe fn H5Fcreate_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, filename: *const c_char, flags: c_uint, fcpl_id: hid_t, fapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Fflush_async( + pub unsafe fn H5Fflush_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, object_id: hid_t, scope: H5F_scope_t, es_id: hid_t, ) -> herr_t; - pub fn H5Fopen_async( + pub unsafe fn H5Fopen_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, filename: *const c_char, flags: c_uint, access_plit: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5reopen_async( + pub unsafe fn H5reopen_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, file_id: hid_t, es_id: hid_t, ) -> hid_t; diff --git a/hdf5-sys/src/h5fd.rs b/hdf5-sys/src/h5fd.rs index 157d247ae..c0135d098 100644 --- a/hdf5-sys/src/h5fd.rs +++ b/hdf5-sys/src/h5fd.rs @@ -331,63 +331,48 @@ impl Default for H5FD_file_image_callbacks_t { } } -extern "C" { - pub fn H5FDregister(cls: *const H5FD_class_t) -> hid_t; - pub fn H5FDunregister(driver_id: hid_t) -> herr_t; - pub fn H5FDopen( +unsafe extern "C" { + pub unsafe fn H5FDregister(cls: *const H5FD_class_t) -> hid_t; + pub unsafe fn H5FDunregister(driver_id: hid_t) -> herr_t; + pub unsafe fn H5FDopen( name: *const c_char, flags: c_uint, fapl_id: hid_t, maxaddr: haddr_t, ) -> *mut H5FD_t; - pub fn H5FDclose(file: *mut H5FD_t) -> herr_t; - pub fn H5FDcmp(f1: *const H5FD_t, f2: *const H5FD_t) -> c_int; - pub fn H5FDquery(f: *const H5FD_t, flags: *mut c_ulong) -> c_int; - pub fn H5FDalloc( + pub unsafe fn H5FDclose(file: *mut H5FD_t) -> herr_t; + pub unsafe fn H5FDcmp(f1: *const H5FD_t, f2: *const H5FD_t) -> c_int; + pub unsafe fn H5FDquery(f: *const H5FD_t, flags: *mut c_ulong) -> c_int; + pub unsafe fn H5FDalloc( file: *mut H5FD_t, type_: H5FD_mem_t, dxpl_id: hid_t, size: hsize_t, ) -> haddr_t; - pub fn H5FDfree( + pub unsafe fn H5FDfree( file: *mut H5FD_t, type_: H5FD_mem_t, dxpl_id: hid_t, addr: haddr_t, size: hsize_t, ) -> herr_t; - pub fn H5FDget_eoa(file: *mut H5FD_t, type_: H5FD_mem_t) -> haddr_t; - pub fn H5FDset_eoa(file: *mut H5FD_t, type_: H5FD_mem_t, eoa: haddr_t) -> herr_t; - pub fn H5FDget_eof(file: *mut H5FD_t) -> haddr_t; - pub fn H5FDget_vfd_handle( + pub unsafe fn H5FDget_eoa(file: *mut H5FD_t, type_: H5FD_mem_t) -> haddr_t; + pub unsafe fn H5FDset_eoa(file: *mut H5FD_t, type_: H5FD_mem_t, eoa: haddr_t) -> herr_t; + pub unsafe fn H5FDget_eof(file: *mut H5FD_t) -> haddr_t; + pub unsafe fn H5FDget_vfd_handle( file: *mut H5FD_t, fapl: hid_t, file_handle: *mut *mut c_void, ) -> herr_t; - pub fn H5FDread( + pub unsafe fn H5FDread( file: *mut H5FD_t, type_: H5FD_mem_t, dxpl_id: hid_t, addr: haddr_t, size: size_t, buf: *mut c_void, ) -> herr_t; - pub fn H5FDwrite( + pub unsafe fn H5FDwrite( file: *mut H5FD_t, type_: H5FD_mem_t, dxpl_id: hid_t, addr: haddr_t, size: size_t, buf: *const c_void, ) -> herr_t; - pub fn H5FDflush(file: *mut H5FD_t, dxpl_id: hid_t, closing: c_uint) -> herr_t; - pub fn H5FDtruncate(file: *mut H5FD_t, dxpl_id: hid_t, closing: hbool_t) -> herr_t; + pub unsafe fn H5FDflush(file: *mut H5FD_t, dxpl_id: hid_t, closing: c_uint) -> herr_t; + pub unsafe fn H5FDtruncate(file: *mut H5FD_t, dxpl_id: hid_t, closing: hbool_t) -> herr_t; } -// drivers -extern "C" { - pub fn H5FD_sec2_init() -> hid_t; - pub fn H5FD_core_init() -> hid_t; - pub fn H5FD_stdio_init() -> hid_t; - pub fn H5FD_family_init() -> hid_t; - pub fn H5FD_log_init() -> hid_t; - pub fn H5FD_multi_init() -> hid_t; -} - -#[cfg(feature = "have-parallel")] -extern "C" { - pub fn H5FD_mpio_init() -> hid_t; -} - -#[cfg(feature = "have-direct")] -extern "C" { - pub fn H5FD_direct_init() -> hid_t; +#[cfg(all(not(feature = "2.0.0"), feature = "have-parallel"))] +unsafe extern "C" { + pub unsafe fn H5FD_mpio_init() -> hid_t; } #[cfg(feature = "1.10.0")] -extern "C" { - pub fn H5FDlock(file: *mut H5FD_t, rw: hbool_t) -> herr_t; - pub fn H5FDunlock(file: *mut H5FD_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5FDlock(file: *mut H5FD_t, rw: hbool_t) -> herr_t; + pub unsafe fn H5FDunlock(file: *mut H5FD_t) -> herr_t; } #[cfg(all(feature = "1.10.6", not(feature = "1.14.0")))] @@ -408,10 +393,10 @@ pub mod hdfs { stream_buffer_size: i32, } - extern "C" { - pub fn H5FD_hdfs_init() -> hid_t; - pub fn H5Pget_fapl_hdfs(fapl_id: hid_t, fa: *mut H5FD_hdfs_fapl_t) -> herr_t; - pub fn H5Pset_fapl_hdfs(fapl_id: hid_t, fa: *mut H5FD_hdfs_fapl_t) -> herr_t; + unsafe extern "C" { + pub unsafe fn H5FD_hdfs_init() -> hid_t; + pub unsafe fn H5Pget_fapl_hdfs(fapl_id: hid_t, fa: *mut H5FD_hdfs_fapl_t) -> herr_t; + pub unsafe fn H5Pset_fapl_hdfs(fapl_id: hid_t, fa: *mut H5FD_hdfs_fapl_t) -> herr_t; } } @@ -432,10 +417,10 @@ pub mod ros3 { secret_key: [c_char; H5FD_ROS3_MAX_SECRET_KEY_LEN as usize + 1], } - extern "C" { - pub fn H5FD_ros3_init() -> hid_t; - pub fn H5Pget_fapl_ros3(fapl_id: hid_t, fa: *mut H5FD_ros3_fapl_t) -> herr_t; - pub fn H5Pset_fapl_ros3(fapl_id: hid_t, fa: *mut H5FD_ros3_fapl_t) -> herr_t; + unsafe extern "C" { + pub unsafe fn H5FD_ros3_init() -> hid_t; + pub unsafe fn H5Pget_fapl_ros3(fapl_id: hid_t, fa: *mut H5FD_ros3_fapl_t) -> herr_t; + pub unsafe fn H5Pset_fapl_ros3(fapl_id: hid_t, fa: *mut H5FD_ros3_fapl_t) -> herr_t; } } @@ -458,32 +443,67 @@ pub mod splitter { ignore_wo_errs: hbool_t, } - extern "C" { - pub fn H5FD_splitter_init() -> hid_t; - pub fn H5Pget_fapl_splitter( + unsafe extern "C" { + pub unsafe fn H5FD_splitter_init() -> hid_t; + pub unsafe fn H5Pget_fapl_splitter( fapl_id: hid_t, config_ptr: *mut H5FD_splitter_vfg_config_t, ) -> herr_t; - pub fn H5Pset_fapl_splitter( + pub unsafe fn H5Pset_fapl_splitter( fapl_id: hid_t, config_ptr: *mut H5FD_splitter_vfg_config_t, ) -> herr_t; } } #[cfg(feature = "1.10.2")] -extern "C" { - pub fn H5FDdriver_query(driver_id: hid_t, flags: *mut c_ulong) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5FDdriver_query(driver_id: hid_t, flags: *mut c_ulong) -> herr_t; } #[cfg(feature = "1.14.0")] type H5FD_perform_init_func_t = Option hid_t>; #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5FDctl( +unsafe extern "C" { + pub unsafe fn H5FDctl( file: *mut H5FD_t, op_cod: u64, flags: u64, input: *const c_void, output: *mut *mut c_void, ) -> herr_t; - pub fn H5FDdelete(name: *const c_char, fapl_id: hid_t) -> herr_t; - pub fn H5FDis_driver_registered_by_name(driver_name: *const c_char) -> htri_t; - pub fn H5FDis_driver_registered_by_value(driver_value: H5FD_class_value_t) -> htri_t; - pub fn H5FDperform_init(p: H5FD_perform_init_func_t) -> hid_t; + pub unsafe fn H5FDdelete(name: *const c_char, fapl_id: hid_t) -> herr_t; + pub unsafe fn H5FDis_driver_registered_by_name(driver_name: *const c_char) -> htri_t; + pub unsafe fn H5FDis_driver_registered_by_value(driver_value: H5FD_class_value_t) -> htri_t; + #[cfg(not(feature = "2.0.0"))] + pub unsafe fn H5FDperform_init(p: H5FD_perform_init_func_t) -> hid_t; } + +#[cfg(all(feature = "2.0.0", not(all(target_env = "msvc", not(feature = "static")))))] +mod globals_2_0_0 { + pub use crate::h5i::hid_t as id_t; + extern_static!(H5FD_CORE, H5FD_CORE_id_g); + extern_static!(H5FD_FAMILY, H5FD_FAMILY_id_g); + extern_static!(H5FD_LOG, H5FD_LOG_id_g); + extern_static!(H5FD_MULTI, H5FD_MULTI_id_g); + extern_static!(H5FD_ONION, H5FD_ONION_id_g); + extern_static!(H5FD_SEC2, H5FD_SEC2_id_g); + extern_static!(H5FD_SPLITTER, H5FD_SPLITTER_id_g); + extern_static!(H5FD_STDIO, H5FD_STDIO_id_g); + #[cfg(feature = "have-parallel")] + extern_static!(H5FD_MPIO, H5FD_MPIO_id_g); +} + +#[cfg(all(feature = "2.0.0", all(target_env = "msvc", not(feature = "static"))))] +mod globals_2_0_0 { + // dllimport hack + pub type id_t = usize; + extern_static!(H5FD_CORE, __imp_H5FD_CORE_id_g); + extern_static!(H5FD_FAMILY, __imp_H5FD_FAMILY_id_g); + extern_static!(H5FD_LOG, __imp_H5FD_LOG_id_g); + extern_static!(H5FD_MULTI, __imp_H5FD_MULTI_id_g); + extern_static!(H5FD_ONION, __imp_H5FD_ONION_id_g); + extern_static!(H5FD_SEC2, __imp_H5FD_SEC2_id_g); + extern_static!(H5FD_SPLITTER, __imp_H5FD_SPLITTER_id_g); + extern_static!(H5FD_STDIO, __imp_H5FD_STDIO_id_g); + #[cfg(feature = "have-parallel")] + extern_static!(H5FD_MPIO, __imp_H5FD_MPIO_id_g); +} + +#[cfg(feature = "2.0.0")] +pub use globals_2_0_0::*; diff --git a/hdf5-sys/src/h5g.rs b/hdf5-sys/src/h5g.rs index 22aaa4d00..17713ecb2 100644 --- a/hdf5-sys/src/h5g.rs +++ b/hdf5-sys/src/h5g.rs @@ -6,7 +6,7 @@ pub use {H5Gcreate2 as H5Gcreate, H5Gopen2 as H5Gopen}; use crate::internal_prelude::*; -use crate::h5l::{H5L_type_t, H5L_SAME_LOC, H5L_TYPE_ERROR, H5L_TYPE_HARD, H5L_TYPE_SOFT}; +use crate::h5l::{H5L_SAME_LOC, H5L_TYPE_ERROR, H5L_TYPE_HARD, H5L_TYPE_SOFT, H5L_type_t}; use crate::h5o::H5O_stat_t; pub const H5G_SAME_LOC: hid_t = H5L_SAME_LOC; @@ -49,67 +49,71 @@ impl Default for H5G_info_t { } } -extern "C" { - pub fn H5Gcreate2( +unsafe extern "C" { + pub unsafe fn H5Gcreate2( loc_id: hid_t, name: *const c_char, lcpl_id: hid_t, gcpl_id: hid_t, gapl_id: hid_t, ) -> hid_t; - pub fn H5Gcreate_anon(loc_id: hid_t, gcpl_id: hid_t, gapl_id: hid_t) -> hid_t; - pub fn H5Gopen2(loc_id: hid_t, name: *const c_char, gapl_id: hid_t) -> hid_t; - pub fn H5Gget_create_plist(group_id: hid_t) -> hid_t; - pub fn H5Gget_info(loc_id: hid_t, ginfo: *mut H5G_info_t) -> herr_t; - pub fn H5Gget_info_by_name( + pub unsafe fn H5Gcreate_anon(loc_id: hid_t, gcpl_id: hid_t, gapl_id: hid_t) -> hid_t; + pub unsafe fn H5Gopen2(loc_id: hid_t, name: *const c_char, gapl_id: hid_t) -> hid_t; + pub unsafe fn H5Gget_create_plist(group_id: hid_t) -> hid_t; + pub unsafe fn H5Gget_info(loc_id: hid_t, ginfo: *mut H5G_info_t) -> herr_t; + pub unsafe fn H5Gget_info_by_name( loc_id: hid_t, name: *const c_char, ginfo: *mut H5G_info_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Gget_info_by_idx( + pub unsafe fn H5Gget_info_by_idx( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, ginfo: *mut H5G_info_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Gclose(group_id: hid_t) -> herr_t; + pub unsafe fn H5Gclose(group_id: hid_t) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Gcreate2")] - pub fn H5Gcreate1(loc_id: hid_t, name: *const c_char, size_hint: size_t) -> hid_t; + pub unsafe fn H5Gcreate1(loc_id: hid_t, name: *const c_char, size_hint: size_t) -> hid_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Oget_comment")] - pub fn H5Gget_comment( + pub unsafe fn H5Gget_comment( loc_id: hid_t, name: *const c_char, bufsize: size_t, buf: *mut c_char, ) -> c_int; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Lget_val")] - pub fn H5Gget_linkval(loc_id: hid_t, name: *const c_char, comment: *const c_char) -> herr_t; + pub unsafe fn H5Gget_linkval( + loc_id: hid_t, name: *const c_char, comment: *const c_char, + ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Gget_info")] - pub fn H5Gget_num_objs(loc_id: hid_t, num_objs: *mut hsize_t) -> herr_t; + pub unsafe fn H5Gget_num_objs(loc_id: hid_t, num_objs: *mut hsize_t) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Oget_info or H5Lget_info")] - pub fn H5Gget_objinfo( + pub unsafe fn H5Gget_objinfo( loc_id: hid_t, name: *const c_char, follow_link: hbool_t, statubuf: *mut H5G_stat_t, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Lget_name_by_idx")] - pub fn H5Gget_objname_by_idx( + pub unsafe fn H5Gget_objname_by_idx( loc_id: hid_t, idx: hsize_t, name: *mut c_char, size: size_t, ) -> ssize_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Oget_info")] - pub fn H5Gget_objtype_by_idx(loc_id: hid_t, idx: hsize_t) -> H5G_obj_t; + pub unsafe fn H5Gget_objtype_by_idx(loc_id: hid_t, idx: hsize_t) -> H5G_obj_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Lcreate_hard or H5Lcreate_soft")] - pub fn H5Glink( + pub unsafe fn H5Glink( cur_loc_id: hid_t, type_: H5G_link_t, cur_name: *const c_char, new_name: *const c_char, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Lcreate_hard or H5Lcreate_soft")] - pub fn H5Glink2( + pub unsafe fn H5Glink2( cur_loc_id: hid_t, cur_name: *const c_char, type_: H5G_link_t, new_loc_id: hid_t, new_name: *const c_char, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Lmove")] - pub fn H5Gmove(src_loc_id: hid_t, src_name: *const c_char, dst_name: *const c_char) -> herr_t; + pub unsafe fn H5Gmove( + src_loc_id: hid_t, src_name: *const c_char, dst_name: *const c_char, + ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Lmove")] - pub fn H5Gmove2( + pub unsafe fn H5Gmove2( src_loc_id: hid_t, src_name: *const c_char, dst_loc_id: hid_t, dst_name: *const c_char, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Gopen2")] - pub fn H5Gopen1(loc_id: hid_t, name: *const c_char) -> hid_t; + pub unsafe fn H5Gopen1(loc_id: hid_t, name: *const c_char) -> hid_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Ldelete")] - pub fn H5Gunlink(loc_id: hid_t, name: *const c_char) -> herr_t; + pub unsafe fn H5Gunlink(loc_id: hid_t, name: *const c_char) -> herr_t; } #[cfg(feature = "1.10.0")] -extern "C" { - pub fn H5Gflush(group_id: hid_t) -> herr_t; - pub fn H5Grefresh(group_id: hid_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Gflush(group_id: hid_t) -> herr_t; + pub unsafe fn H5Grefresh(group_id: hid_t) -> herr_t; } #[repr(C)] @@ -139,29 +143,29 @@ pub struct H5G_stat_t { } #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5Gclose_async( +unsafe extern "C" { + pub unsafe fn H5Gclose_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, group_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Gcreate_async( + pub unsafe fn H5Gcreate_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, lcpl_id: hid_t, gcpl_id: hid_t, gapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Gget_info_async( + pub unsafe fn H5Gget_info_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, ginfo: *mut H5G_info_t, es_id: hid_t, ) -> herr_t; - pub fn H5Gget_info_by_idx_async( + pub unsafe fn H5Gget_info_by_idx_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: c_ulong, ginfo: *mut H5G_info_t, lapl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Gget_info_by_name_async( + pub unsafe fn H5Gget_info_by_name_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, ginfo: *mut H5G_info_t, lapl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Gopen_async( + pub unsafe fn H5Gopen_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, gapl_id: hid_t, es_id: hid_t, ) -> hid_t; diff --git a/hdf5-sys/src/h5i.rs b/hdf5-sys/src/h5i.rs index 9fb1578a8..f3c151a1e 100644 --- a/hdf5-sys/src/h5i.rs +++ b/hdf5-sys/src/h5i.rs @@ -52,32 +52,44 @@ pub type H5I_search_func_t = #[cfg(feature = "1.12.0")] pub type H5I_iterate_func_t = Option herr_t>; -extern "C" { - pub fn H5Iregister(type_: H5I_type_t, object: *const c_void) -> hid_t; - pub fn H5Iobject_verify(id: hid_t, id_type: H5I_type_t) -> *mut c_void; - pub fn H5Iremove_verify(id: hid_t, id_type: H5I_type_t) -> *mut c_void; - pub fn H5Iget_type(id: hid_t) -> H5I_type_t; - pub fn H5Iget_file_id(id: hid_t) -> hid_t; - pub fn H5Iget_name(id: hid_t, name: *mut c_char, size: size_t) -> ssize_t; - pub fn H5Iinc_ref(id: hid_t) -> c_int; - pub fn H5Idec_ref(id: hid_t) -> c_int; - pub fn H5Iget_ref(id: hid_t) -> c_int; - pub fn H5Iregister_type( +unsafe extern "C" { + pub unsafe fn H5Iregister(type_: H5I_type_t, object: *const c_void) -> hid_t; + pub unsafe fn H5Iobject_verify(id: hid_t, id_type: H5I_type_t) -> *mut c_void; + pub unsafe fn H5Iremove_verify(id: hid_t, id_type: H5I_type_t) -> *mut c_void; + pub unsafe fn H5Iget_type(id: hid_t) -> H5I_type_t; + pub unsafe fn H5Iget_file_id(id: hid_t) -> hid_t; + pub unsafe fn H5Iget_name(id: hid_t, name: *mut c_char, size: size_t) -> ssize_t; + pub unsafe fn H5Iinc_ref(id: hid_t) -> c_int; + pub unsafe fn H5Idec_ref(id: hid_t) -> c_int; + pub unsafe fn H5Iget_ref(id: hid_t) -> c_int; + #[cfg_attr(not(feature = "2.0.0"), link_name = "H5Iregister_type")] + pub unsafe fn H5Iregister_type1( hash_size: size_t, reserved: c_uint, free_func: H5I_free_t, ) -> H5I_type_t; - pub fn H5Iclear_type(type_: H5I_type_t, force: hbool_t) -> herr_t; - pub fn H5Idestroy_type(type_: H5I_type_t) -> herr_t; - pub fn H5Iinc_type_ref(type_: H5I_type_t) -> c_int; - pub fn H5Idec_type_ref(type_: H5I_type_t) -> c_int; - pub fn H5Iget_type_ref(type_: H5I_type_t) -> c_int; - pub fn H5Isearch(type_: H5I_type_t, func: H5I_search_func_t, key: *mut c_void) -> *mut c_void; + #[cfg(feature = "2.0.0")] + pub unsafe fn H5Iregister_type2(reserved: c_uint, free_func: H5I_free_t) -> H5I_type_t; + pub unsafe fn H5Iclear_type(type_: H5I_type_t, force: hbool_t) -> herr_t; + pub unsafe fn H5Idestroy_type(type_: H5I_type_t) -> herr_t; + pub unsafe fn H5Iinc_type_ref(type_: H5I_type_t) -> c_int; + pub unsafe fn H5Idec_type_ref(type_: H5I_type_t) -> c_int; + pub unsafe fn H5Iget_type_ref(type_: H5I_type_t) -> c_int; + pub unsafe fn H5Isearch( + type_: H5I_type_t, func: H5I_search_func_t, key: *mut c_void, + ) -> *mut c_void; #[cfg(feature = "1.12.0")] - pub fn H5Iiterate(type_: H5I_type_t, op: H5I_iterate_func_t, op_data: *mut c_void) -> herr_t; - pub fn H5Inmembers(type_: H5I_type_t, num_members: *mut hsize_t) -> herr_t; - pub fn H5Itype_exists(type_: H5I_type_t) -> htri_t; - pub fn H5Iis_valid(id: hid_t) -> htri_t; + pub unsafe fn H5Iiterate( + type_: H5I_type_t, op: H5I_iterate_func_t, op_data: *mut c_void, + ) -> herr_t; + pub unsafe fn H5Inmembers(type_: H5I_type_t, num_members: *mut hsize_t) -> herr_t; + pub unsafe fn H5Itype_exists(type_: H5I_type_t) -> htri_t; + pub unsafe fn H5Iis_valid(id: hid_t) -> htri_t; } +#[cfg(not(feature = "2.0.0"))] +pub use self::H5Iregister_type1 as H5Iregister_type; +#[cfg(feature = "2.0.0")] +pub use self::H5Iregister_type2 as H5Iregister_type; + #[cfg(feature = "1.14.0")] pub type H5I_future_realize_func_t = Option< unsafe extern "C" fn(future_object: *mut c_void, actual_object_id: *mut hid_t) -> herr_t, @@ -88,8 +100,8 @@ pub type H5I_future_discard_func_t = Option herr_t>; #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5Iregister_future( +unsafe extern "C" { + pub unsafe fn H5Iregister_future( type_: H5I_type_t, object: *const c_void, realize_cb: H5I_future_realize_func_t, discard_cb: H5I_future_discard_func_t, ) -> hid_t; diff --git a/hdf5-sys/src/h5l.rs b/hdf5-sys/src/h5l.rs index 5677c20e0..54cb18cc9 100644 --- a/hdf5-sys/src/h5l.rs +++ b/hdf5-sys/src/h5l.rs @@ -124,10 +124,10 @@ impl Default for H5L_info2_t__u { #[cfg(feature = "1.12.0")] impl H5L_info2_t__u { pub unsafe fn token(&mut self) -> *mut H5O_token_t { - &mut self.token as *mut H5O_token_t + unsafe { &mut self.token as *mut H5O_token_t } } pub unsafe fn val_size(&mut self) -> *mut size_t { - &mut self.val_size as *mut size_t + unsafe { &mut self.val_size as *mut size_t } } } @@ -232,46 +232,46 @@ pub type H5L_elink_traverse_t = Option< ) -> herr_t, >; -extern "C" { - pub fn H5Lmove( +unsafe extern "C" { + pub unsafe fn H5Lmove( src_loc: hid_t, src_name: *const c_char, dst_loc: hid_t, dst_name: *const c_char, lcpl_id: hid_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Lcopy( + pub unsafe fn H5Lcopy( src_loc: hid_t, src_name: *const c_char, dst_loc: hid_t, dst_name: *const c_char, lcpl_id: hid_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Lcreate_hard( + pub unsafe fn H5Lcreate_hard( cur_loc: hid_t, cur_name: *const c_char, dst_loc: hid_t, dst_name: *const c_char, lcpl_id: hid_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Lcreate_soft( + pub unsafe fn H5Lcreate_soft( link_target: *const c_char, link_loc_id: hid_t, link_name: *const c_char, lcpl_id: hid_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Ldelete(loc_id: hid_t, name: *const c_char, lapl_id: hid_t) -> herr_t; - pub fn H5Ldelete_by_idx( + pub unsafe fn H5Ldelete(loc_id: hid_t, name: *const c_char, lapl_id: hid_t) -> herr_t; + pub unsafe fn H5Ldelete_by_idx( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Lget_val( + pub unsafe fn H5Lget_val( loc_id: hid_t, name: *const c_char, buf: *mut c_void, size: size_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Lget_val_by_idx( + pub unsafe fn H5Lget_val_by_idx( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, buf: *mut c_void, size: size_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Lexists(loc_id: hid_t, name: *const c_char, lapl_id: hid_t) -> htri_t; + pub unsafe fn H5Lexists(loc_id: hid_t, name: *const c_char, lapl_id: hid_t) -> htri_t; #[cfg_attr( feature = "1.12.0", deprecated(note = "deprecated in HDF5 1.12.0, use H5Lget_info2()") )] #[cfg_attr(not(feature = "1.12.0"), link_name = "H5Lget_info")] - pub fn H5Lget_info1( + pub unsafe fn H5Lget_info1( loc_id: hid_t, name: *const c_char, linfo: *mut H5L_info1_t, lapl_id: hid_t, ) -> herr_t; #[cfg(feature = "1.12.0")] - pub fn H5Lget_info2( + pub unsafe fn H5Lget_info2( loc_id: hid_t, name: *const c_char, linfo: *mut H5L_info2_t, lapl_id: hid_t, ) -> herr_t; #[cfg_attr( @@ -279,16 +279,16 @@ extern "C" { deprecated(note = "deprecated in HDF5 1.12.0, use H5Lget_info_by_idx2()") )] #[cfg_attr(not(feature = "1.12.0"), link_name = "H5Lget_info_by_idx")] - pub fn H5Lget_info_by_idx( + pub unsafe fn H5Lget_info_by_idx( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, linfo: *mut H5L_info1_t, lapl_id: hid_t, ) -> herr_t; #[cfg(feature = "1.12.0")] - pub fn H5Lget_info_by_idx2( + pub unsafe fn H5Lget_info_by_idx2( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, linfo: *mut H5L_info2_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Lget_name_by_idx( + pub unsafe fn H5Lget_name_by_idx( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, name: *mut c_char, size: size_t, lapl_id: hid_t, ) -> ssize_t; @@ -297,12 +297,12 @@ extern "C" { deprecated(note = "deprecated in HDF5 1.12.0, use H5Literate2()") )] #[cfg_attr(not(feature = "1.12.0"), link_name = "H5Literate")] - pub fn H5Literate1( + pub unsafe fn H5Literate1( grp_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, idx: *mut hsize_t, op: H5L_iterate1_t, op_data: *mut c_void, ) -> herr_t; #[cfg(feature = "1.12.0")] - pub fn H5Literate2( + pub unsafe fn H5Literate2( grp_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, idx: *mut hsize_t, op: H5L_iterate2_t, op_data: *mut c_void, ) -> herr_t; @@ -311,23 +311,23 @@ extern "C" { deprecated(note = "deprecated in HDF5 1.12.0, use H5Literate_by_name2()") )] #[cfg_attr(not(feature = "1.12.0"), link_name = "H5Literate_by_name")] - pub fn H5Literate_by_name1( + pub unsafe fn H5Literate_by_name1( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, idx: *mut hsize_t, op: H5L_iterate1_t, op_data: *mut c_void, lapl_id: hid_t, ) -> herr_t; #[cfg(feature = "1.12.0")] - pub fn H5Literate_by_name2( + pub unsafe fn H5Literate_by_name2( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, idx: *mut hsize_t, op: H5L_iterate2_t, op_data: *mut c_void, lapl_id: hid_t, ) -> herr_t; #[cfg_attr(feature = "1.12.0", deprecated(note = "deprecated in HDF5 1.12.0, use H5Lvisit2()"))] #[cfg_attr(not(feature = "1.12.0"), link_name = "H5Lvisit")] - pub fn H5Lvisit1( + pub unsafe fn H5Lvisit1( grp_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, op: H5L_iterate1_t, op_data: *mut c_void, ) -> herr_t; #[cfg(feature = "1.12.0")] - pub fn H5Lvisit2( + pub unsafe fn H5Lvisit2( grp_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, op: H5L_iterate2_t, op_data: *mut c_void, ) -> herr_t; @@ -336,27 +336,27 @@ extern "C" { deprecated(note = "deprecated in HDF5 1.12.0, use H5Lvisit_by_name2()") )] #[cfg_attr(not(feature = "1.12.0"), link_name = "H5Lvisit_by_name")] - pub fn H5Lvisit_by_name1( + pub unsafe fn H5Lvisit_by_name1( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, op: H5L_iterate1_t, op_data: *mut c_void, lapl_id: hid_t, ) -> herr_t; #[cfg(feature = "1.12.0")] - pub fn H5Lvisit_by_name2( + pub unsafe fn H5Lvisit_by_name2( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, op: H5L_iterate2_t, op_data: *mut c_void, lapl_id: hid_t, ) -> herr_t; - pub fn H5Lcreate_ud( + pub unsafe fn H5Lcreate_ud( link_loc_id: hid_t, link_name: *const c_char, link_type: H5L_type_t, udata: *const c_void, udata_size: size_t, lcpl_id: hid_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Lregister(cls: *const H5L_class_t) -> herr_t; - pub fn H5Lunregister(id: H5L_type_t) -> herr_t; - pub fn H5Lis_registered(id: H5L_type_t) -> htri_t; - pub fn H5Lunpack_elink_val( + pub unsafe fn H5Lregister(cls: *const H5L_class_t) -> herr_t; + pub unsafe fn H5Lunregister(id: H5L_type_t) -> herr_t; + pub unsafe fn H5Lis_registered(id: H5L_type_t) -> htri_t; + pub unsafe fn H5Lunpack_elink_val( ext_linkval: *const c_void, link_size: size_t, flags: *mut c_uint, filename: *mut *const c_char, obj_path: *mut *const c_char, ) -> herr_t; - pub fn H5Lcreate_external( + pub unsafe fn H5Lcreate_external( file_name: *const c_char, obj_name: *const c_char, link_loc_id: hid_t, link_name: *const c_char, lcpl_id: hid_t, lapl_id: hid_t, ) -> herr_t; @@ -372,31 +372,31 @@ pub use self::{ }; #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5Lcreate_hard_async( +unsafe extern "C" { + pub unsafe fn H5Lcreate_hard_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, cur_loc_id: hid_t, cur_name: *const c_char, new_loc_id: hid_t, new_name: *const c_char, lcpl_id: hid_t, lapl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Lcreate_soft_async( + pub unsafe fn H5Lcreate_soft_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, link_target: *const c_char, link_loc_id: hid_t, link_name: *const c_char, lcpl_id: hid_t, lapl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Ldelete_async( + pub unsafe fn H5Ldelete_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, lapl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Ldelete_by_idx_async( + pub unsafe fn H5Ldelete_by_idx_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: c_ulong, lapl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Lexists_async( + pub unsafe fn H5Lexists_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, exists: *mut hbool_t, lapl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Literate_async( + pub unsafe fn H5Literate_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, group_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, idx_p: *mut c_ulong, op: H5L_iterate2_t, op_data: *mut c_void, es_id: hid_t, diff --git a/hdf5-sys/src/h5o.rs b/hdf5-sys/src/h5o.rs index 603fd9f53..c1776223b 100644 --- a/hdf5-sys/src/h5o.rs +++ b/hdf5-sys/src/h5o.rs @@ -12,9 +12,8 @@ pub use { pub use {H5O_info2_t as H5O_info_t, H5O_iterate2_t as H5O_iterate_t}; #[cfg(not(feature = "1.10.3"))] pub use { - H5Oget_info1 as H5Oget_info, H5Oget_info_by_idx1 as H5Oget_info_by_idx, - H5Oget_info_by_name1 as H5Oget_info_by_name, H5Ovisit1 as H5Ovisit, - H5Ovisit_by_name1 as H5Ovisit_by_name, + H5Oget_info_by_idx1 as H5Oget_info_by_idx, H5Oget_info_by_name1 as H5Oget_info_by_name, + H5Oget_info1 as H5Oget_info, H5Ovisit_by_name1 as H5Ovisit_by_name, H5Ovisit1 as H5Ovisit, }; use crate::internal_prelude::*; @@ -207,116 +206,120 @@ pub type H5O_mcdt_search_cb_t = Option H5O_mcdt_search_ret_t>; #[cfg(not(feature = "1.10.3"))] -extern "C" { +unsafe extern "C" { #[link_name = "H5Oget_info"] - pub fn H5Oget_info1(loc_id: hid_t, oinfo: *mut H5O_info1_t) -> herr_t; + pub unsafe fn H5Oget_info1(loc_id: hid_t, oinfo: *mut H5O_info1_t) -> herr_t; #[link_name = "H5Oget_info_by_name"] - pub fn H5Oget_info_by_name1( + pub unsafe fn H5Oget_info_by_name1( loc_id: hid_t, name: *const c_char, oinfo: *mut H5O_info1_t, lapl_id: hid_t, ) -> herr_t; #[link_name = "H5Oget_info_by_idx"] - pub fn H5Oget_info_by_idx1( + pub unsafe fn H5Oget_info_by_idx1( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, oinfo: *mut H5O_info1_t, lapl_id: hid_t, ) -> herr_t; #[link_name = "H5Ovisit"] - pub fn H5Ovisit1( + pub unsafe fn H5Ovisit1( obj_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, op: H5O_iterate1_t, op_data: *mut c_void, ) -> herr_t; #[link_name = "H5Ovisit_by_name"] - pub fn H5Ovisit_by_name1( + pub unsafe fn H5Ovisit_by_name1( loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, op: H5O_iterate1_t, op_data: *mut c_void, lapl_id: hid_t, ) -> herr_t; } -extern "C" { - pub fn H5Oopen(loc_id: hid_t, name: *const c_char, lapl_id: hid_t) -> hid_t; - pub fn H5Oopen_by_addr(loc_id: hid_t, addr: haddr_t) -> hid_t; - pub fn H5Oopen_by_idx( +unsafe extern "C" { + pub unsafe fn H5Oopen(loc_id: hid_t, name: *const c_char, lapl_id: hid_t) -> hid_t; + pub unsafe fn H5Oopen_by_addr(loc_id: hid_t, addr: haddr_t) -> hid_t; + pub unsafe fn H5Oopen_by_idx( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, lapl_id: hid_t, ) -> hid_t; - pub fn H5Olink( + pub unsafe fn H5Olink( obj_id: hid_t, new_loc_id: hid_t, new_name: *const c_char, lcpl_id: hid_t, lapl_id: hid_t, ) -> herr_t; - pub fn H5Oincr_refcount(object_id: hid_t) -> herr_t; - pub fn H5Odecr_refcount(object_id: hid_t) -> herr_t; - pub fn H5Ocopy( + pub unsafe fn H5Oincr_refcount(object_id: hid_t) -> herr_t; + pub unsafe fn H5Odecr_refcount(object_id: hid_t) -> herr_t; + pub unsafe fn H5Ocopy( src_loc_id: hid_t, src_name: *const c_char, dst_loc_id: hid_t, dst_name: *const c_char, ocpypl_id: hid_t, lcpl_id: hid_t, ) -> herr_t; #[deprecated(note = "function is deprecated in favor of object attributes")] - pub fn H5Oset_comment(obj_id: hid_t, comment: *const c_char) -> herr_t; + pub unsafe fn H5Oset_comment(obj_id: hid_t, comment: *const c_char) -> herr_t; #[deprecated(note = "function is deprecated in favor of object attributes")] - pub fn H5Oset_comment_by_name( + pub unsafe fn H5Oset_comment_by_name( loc_id: hid_t, name: *const c_char, comment: *const c_char, lapl_id: hid_t, ) -> herr_t; - pub fn H5Oget_comment(obj_id: hid_t, comment: *mut c_char, bufsize: size_t) -> ssize_t; - pub fn H5Oget_comment_by_name( + pub unsafe fn H5Oget_comment(obj_id: hid_t, comment: *mut c_char, bufsize: size_t) -> ssize_t; + pub unsafe fn H5Oget_comment_by_name( loc_id: hid_t, name: *const c_char, comment: *mut c_char, bufsize: size_t, lapl_id: hid_t, ) -> ssize_t; - pub fn H5Oclose(object_id: hid_t) -> herr_t; + pub unsafe fn H5Oclose(object_id: hid_t) -> herr_t; } #[cfg(feature = "1.8.5")] use crate::h5::htri_t; #[cfg(feature = "1.8.5")] -extern "C" { - pub fn H5Oexists_by_name(loc_id: hid_t, name: *const c_char, lapl_id: hid_t) -> htri_t; +unsafe extern "C" { + pub unsafe fn H5Oexists_by_name(loc_id: hid_t, name: *const c_char, lapl_id: hid_t) -> htri_t; } #[cfg(feature = "1.10.0")] -extern "C" { - pub fn H5Odisable_mdc_flushes(object_id: hid_t) -> herr_t; - pub fn H5Oenable_mdc_flushes(object_id: hid_t) -> herr_t; - pub fn H5Oare_mdc_flushes_disabled(object_id: hid_t, are_disabled: *mut hbool_t) -> herr_t; - pub fn H5Oflush(obj_id: hid_t) -> herr_t; - pub fn H5Orefresh(oid: hid_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Odisable_mdc_flushes(object_id: hid_t) -> herr_t; + pub unsafe fn H5Oenable_mdc_flushes(object_id: hid_t) -> herr_t; + pub unsafe fn H5Oare_mdc_flushes_disabled( + object_id: hid_t, are_disabled: *mut hbool_t, + ) -> herr_t; + pub unsafe fn H5Oflush(obj_id: hid_t) -> herr_t; + pub unsafe fn H5Orefresh(oid: hid_t) -> herr_t; } #[cfg(feature = "1.10.3")] mod hdf5_1_10_3 { use super::*; - extern "C" { - pub fn H5Oget_info2(loc_id: hid_t, oinfo: *mut H5O_info1_t, fields: c_uint) -> herr_t; - pub fn H5Oget_info_by_name2( + unsafe extern "C" { + pub unsafe fn H5Oget_info2( + loc_id: hid_t, oinfo: *mut H5O_info1_t, fields: c_uint, + ) -> herr_t; + pub unsafe fn H5Oget_info_by_name2( loc_id: hid_t, name: *const c_char, oinfo: *mut H5O_info1_t, fields: c_uint, lapl_id: hid_t, ) -> herr_t; - pub fn H5Oget_info_by_idx2( + pub unsafe fn H5Oget_info_by_idx2( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, oinfo: *mut H5O_info1_t, fields: c_uint, lapl_id: hid_t, ) -> herr_t; - pub fn H5Ovisit2( + pub unsafe fn H5Ovisit2( obj_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, op: H5O_iterate1_t, op_data: *mut c_void, fields: c_uint, ) -> herr_t; - pub fn H5Ovisit_by_name2( + pub unsafe fn H5Ovisit_by_name2( loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, op: H5O_iterate1_t, op_data: *mut c_void, fields: c_uint, lapl_id: hid_t, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.10.3, use H5Oget_info2")] - pub fn H5Oget_info1(loc_id: hid_t, oinfo: *mut H5O_info1_t) -> herr_t; + pub unsafe fn H5Oget_info1(loc_id: hid_t, oinfo: *mut H5O_info1_t) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.10.3, use H5Oget_info_by_name2")] - pub fn H5Oget_info_by_name1( + pub unsafe fn H5Oget_info_by_name1( loc_id: hid_t, name: *const c_char, oinfo: *mut H5O_info1_t, lapl_id: hid_t, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.10.3, use H5Oget_info_by_idx2")] - pub fn H5Oget_info_by_idx1( + pub unsafe fn H5Oget_info_by_idx1( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, oinfo: *mut H5O_info1_t, lapl_id: hid_t, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.10.3, use H5Ovisit2")] - pub fn H5Ovisit1( + pub unsafe fn H5Ovisit1( obj_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, op: H5O_iterate1_t, op_data: *mut c_void, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.10.3, use H5Ovisit_by_name2")] - pub fn H5Ovisit_by_name1( + pub unsafe fn H5Ovisit_by_name1( loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, op: H5O_iterate1_t, op_data: *mut c_void, lapl_id: hid_t, ) -> herr_t; @@ -324,9 +327,8 @@ mod hdf5_1_10_3 { #[cfg(not(feature = "1.10.5"))] pub use self::{ - H5Oget_info1 as H5Oget_info, H5Oget_info_by_idx1 as H5Oget_info_by_idx, - H5Oget_info_by_name1 as H5Oget_info_by_name, H5Ovisit1 as H5Ovisit, - H5Ovisit_by_name1 as H5Ovisit_by_name, + H5Oget_info_by_idx1 as H5Oget_info_by_idx, H5Oget_info_by_name1 as H5Oget_info_by_name, + H5Oget_info1 as H5Oget_info, H5Ovisit_by_name1 as H5Ovisit_by_name, H5Ovisit1 as H5Ovisit, }; } @@ -334,28 +336,28 @@ mod hdf5_1_10_3 { pub use self::hdf5_1_10_3::*; #[cfg(feature = "1.10.5")] -extern "C" { +unsafe extern "C" { // They've messed up when introducing compatibility macros which broke ABI compatibility; // in 1.10.5 those APIs were copied over to old names in order to be compatible with // older library versions - so we can link to them directly again. #[deprecated(note = "deprecated in HDF5 1.10.3, use H5Oget_info2")] - pub fn H5Oget_info(loc_id: hid_t, oinfo: *mut H5O_info1_t) -> herr_t; + pub unsafe fn H5Oget_info(loc_id: hid_t, oinfo: *mut H5O_info1_t) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.10.3, use H5Oget_info_by_name2")] - pub fn H5Oget_info_by_name( + pub unsafe fn H5Oget_info_by_name( loc_id: hid_t, name: *const c_char, oinfo: *mut H5O_info1_t, lapl_id: hid_t, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.10.3, use H5Oget_info_by_idx2")] - pub fn H5Oget_info_by_idx( + pub unsafe fn H5Oget_info_by_idx( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, oinfo: *mut H5O_info1_t, lapl_id: hid_t, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.10.3, use H5Ovisit2")] - pub fn H5Ovisit( + pub unsafe fn H5Ovisit( obj_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, op: H5O_iterate1_t, op_data: *mut c_void, ) -> herr_t; #[deprecated(note = "deprecated in HDF5 1.10.3, use H5Ovisit_by_name2")] - pub fn H5Ovisit_by_name( + pub unsafe fn H5Ovisit_by_name( loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, op: H5O_iterate1_t, op_data: *mut c_void, lapl_id: hid_t, ) -> herr_t; @@ -419,42 +421,42 @@ pub struct H5O_stat_t { } #[cfg(feature = "1.12.0")] -extern "C" { - pub fn H5Oget_info3(loc_id: hid_t, oinfo: *mut H5O_info2_t, fields: c_uint) -> herr_t; - pub fn H5Oget_info_by_idx3( +unsafe extern "C" { + pub unsafe fn H5Oget_info3(loc_id: hid_t, oinfo: *mut H5O_info2_t, fields: c_uint) -> herr_t; + pub unsafe fn H5Oget_info_by_idx3( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, oinfo: *mut H5O_info2_t, fields: c_uint, lapl_id: hid_t, ) -> herr_t; - pub fn H5Oget_info_by_name3( + pub unsafe fn H5Oget_info_by_name3( loc_id: hid_t, name: *const c_char, oinfo: *mut H5O_info2_t, fields: c_uint, lapl_id: hid_t, ) -> herr_t; - pub fn H5Oget_native_info( + pub unsafe fn H5Oget_native_info( loc_id: hid_t, oinfor: *mut H5O_native_info_t, fields: c_uint, ) -> herr_t; - pub fn H5Oget_native_info_by_idx( + pub unsafe fn H5Oget_native_info_by_idx( loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: hsize_t, oinfo: *mut H5O_native_info_t, fields: c_uint, lapl_id: hid_t, ) -> herr_t; - pub fn H5Oget_native_info_by_name( + pub unsafe fn H5Oget_native_info_by_name( loc_id: hid_t, name: *const c_char, oinfo: *mut H5O_native_info_t, fields: c_uint, lapl_id: hid_t, ) -> herr_t; - pub fn H5Oopen_by_token(loc_id: hid_t, token: H5O_token_t) -> hid_t; - pub fn H5Otoken_cmp( + pub unsafe fn H5Oopen_by_token(loc_id: hid_t, token: H5O_token_t) -> hid_t; + pub unsafe fn H5Otoken_cmp( loc_id: hid_t, token1: *const H5O_token_t, token2: *const H5O_token_t, cmp_value: *mut c_int, ) -> herr_t; - pub fn H5Otoken_from_str( + pub unsafe fn H5Otoken_from_str( loc_id: hid_t, token_str: *const c_char, token: *mut H5O_token_t, ) -> herr_t; - pub fn H5Otoken_to_str( + pub unsafe fn H5Otoken_to_str( loc_id: hid_t, token: *const H5O_token_t, token_str: *mut *mut c_char, ) -> herr_t; - pub fn H5Ovisit3( + pub unsafe fn H5Ovisit3( obj_id: hid_t, idx_type: H5_index_t, order: H5_iter_order_t, op: H5O_iterate2_t, op_data: *mut c_void, fields: c_uint, ) -> herr_t; - pub fn H5Ovisit_by_name3( + pub unsafe fn H5Ovisit_by_name3( loc_id: hid_t, obj_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, op: H5O_iterate2_t, op_data: *mut c_void, fields: c_uint, lapl_id: hid_t, ) -> herr_t; @@ -477,34 +479,34 @@ mod globals { } #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5Oclose_async( +unsafe extern "C" { + pub unsafe fn H5Oclose_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, object_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Ocopy_async( + pub unsafe fn H5Ocopy_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, src_loc_id: hid_t, src_name: *const c_char, dst_loc_id: hid_t, dst_name: *const c_char, ocpypl_id: hid_t, lcpl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Oflush_async( + pub unsafe fn H5Oflush_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, obj_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Oget_info_by_name_async( + pub unsafe fn H5Oget_info_by_name_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, oinfo: *mut H5O_info2_t, fields: c_uint, lapl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Oopen_async( + pub unsafe fn H5Oopen_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, lapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Oopen_by_idx_async( + pub unsafe fn H5Oopen_by_idx_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, group_name: *const c_char, idx_type: H5_index_t, order: H5_iter_order_t, n: c_ulong, lapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Orefresh_async( + pub unsafe fn H5Orefresh_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, oid: hid_t, es_id: hid_t, ) -> herr_t; diff --git a/hdf5-sys/src/h5p.rs b/hdf5-sys/src/h5p.rs index b6e46744e..37f9eefa3 100644 --- a/hdf5-sys/src/h5p.rs +++ b/hdf5-sys/src/h5p.rs @@ -245,303 +245,332 @@ mod globals { pub use globals::*; } -extern "C" { - pub fn H5Pcreate_class( +unsafe extern "C" { + pub unsafe fn H5Pcreate_class( parent: hid_t, name: *const c_char, cls_create: H5P_cls_create_func_t, create_data: *mut c_void, cls_copy: H5P_cls_copy_func_t, copy_data: *mut c_void, cls_close: H5P_cls_close_func_t, close_data: *mut c_void, ) -> hid_t; - pub fn H5Pget_class_name(pclass_id: hid_t) -> *mut c_char; - pub fn H5Pcreate(cls_id: hid_t) -> hid_t; - pub fn H5Pregister2( + pub unsafe fn H5Pget_class_name(pclass_id: hid_t) -> *mut c_char; + pub unsafe fn H5Pcreate(cls_id: hid_t) -> hid_t; + pub unsafe fn H5Pregister2( cls_id: hid_t, name: *const c_char, size: size_t, def_value: *mut c_void, prp_create: H5P_prp_create_func_t, prp_set: H5P_prp_set_func_t, prp_get: H5P_prp_get_func_t, prp_del: H5P_prp_delete_func_t, prp_copy: H5P_prp_copy_func_t, prp_cmp: H5P_prp_compare_func_t, prp_close: H5P_prp_close_func_t, ) -> herr_t; - pub fn H5Pinsert2( + pub unsafe fn H5Pinsert2( plist_id: hid_t, name: *const c_char, size: size_t, value: *mut c_void, prp_set: H5P_prp_set_func_t, prp_get: H5P_prp_get_func_t, prp_delete: H5P_prp_delete_func_t, prp_copy: H5P_prp_copy_func_t, prp_cmp: H5P_prp_compare_func_t, prp_close: H5P_prp_close_func_t, ) -> herr_t; - pub fn H5Pset(plist_id: hid_t, name: *const c_char, value: *mut c_void) -> herr_t; - pub fn H5Pexist(plist_id: hid_t, name: *const c_char) -> htri_t; - pub fn H5Pget_size(id: hid_t, name: *const c_char, size: *mut size_t) -> herr_t; - pub fn H5Pget_nprops(id: hid_t, nprops: *mut size_t) -> herr_t; - pub fn H5Pget_class(plist_id: hid_t) -> hid_t; - pub fn H5Pget_class_parent(pclass_id: hid_t) -> hid_t; - pub fn H5Pget(plist_id: hid_t, name: *const c_char, value: *mut c_void) -> herr_t; - pub fn H5Pequal(id1: hid_t, id2: hid_t) -> htri_t; - pub fn H5Pisa_class(plist_id: hid_t, pclass_id: hid_t) -> htri_t; - pub fn H5Piterate( + pub unsafe fn H5Pset(plist_id: hid_t, name: *const c_char, value: *mut c_void) -> herr_t; + pub unsafe fn H5Pexist(plist_id: hid_t, name: *const c_char) -> htri_t; + pub unsafe fn H5Pget_size(id: hid_t, name: *const c_char, size: *mut size_t) -> herr_t; + pub unsafe fn H5Pget_nprops(id: hid_t, nprops: *mut size_t) -> herr_t; + pub unsafe fn H5Pget_class(plist_id: hid_t) -> hid_t; + pub unsafe fn H5Pget_class_parent(pclass_id: hid_t) -> hid_t; + pub unsafe fn H5Pget(plist_id: hid_t, name: *const c_char, value: *mut c_void) -> herr_t; + pub unsafe fn H5Pequal(id1: hid_t, id2: hid_t) -> htri_t; + pub unsafe fn H5Pisa_class(plist_id: hid_t, pclass_id: hid_t) -> htri_t; + pub unsafe fn H5Piterate( id: hid_t, idx: *mut c_int, iter_func: H5P_iterate_t, iter_data: *mut c_void, ) -> c_int; - pub fn H5Pcopy_prop(dst_id: hid_t, src_id: hid_t, name: *const c_char) -> herr_t; - pub fn H5Premove(plist_id: hid_t, name: *const c_char) -> herr_t; - pub fn H5Punregister(pclass_id: hid_t, name: *const c_char) -> herr_t; - pub fn H5Pclose_class(plist_id: hid_t) -> herr_t; - pub fn H5Pclose(plist_id: hid_t) -> herr_t; - pub fn H5Pcopy(plist_id: hid_t) -> hid_t; - pub fn H5Pset_attr_phase_change( + pub unsafe fn H5Pcopy_prop(dst_id: hid_t, src_id: hid_t, name: *const c_char) -> herr_t; + pub unsafe fn H5Premove(plist_id: hid_t, name: *const c_char) -> herr_t; + pub unsafe fn H5Punregister(pclass_id: hid_t, name: *const c_char) -> herr_t; + pub unsafe fn H5Pclose_class(plist_id: hid_t) -> herr_t; + pub unsafe fn H5Pclose(plist_id: hid_t) -> herr_t; + pub unsafe fn H5Pcopy(plist_id: hid_t) -> hid_t; + pub unsafe fn H5Pset_attr_phase_change( plist_id: hid_t, max_compact: c_uint, min_dense: c_uint, ) -> herr_t; - pub fn H5Pget_attr_phase_change( + pub unsafe fn H5Pget_attr_phase_change( plist_id: hid_t, max_compact: *mut c_uint, min_dense: *mut c_uint, ) -> herr_t; - pub fn H5Pset_attr_creation_order(plist_id: hid_t, crt_order_flags: c_uint) -> herr_t; - pub fn H5Pget_attr_creation_order(plist_id: hid_t, crt_order_flags: *mut c_uint) -> herr_t; - pub fn H5Pset_obj_track_times(plist_id: hid_t, track_times: hbool_t) -> herr_t; - pub fn H5Pget_obj_track_times(plist_id: hid_t, track_times: *mut hbool_t) -> herr_t; - pub fn H5Pmodify_filter( + pub unsafe fn H5Pset_attr_creation_order(plist_id: hid_t, crt_order_flags: c_uint) -> herr_t; + pub unsafe fn H5Pget_attr_creation_order( + plist_id: hid_t, crt_order_flags: *mut c_uint, + ) -> herr_t; + pub unsafe fn H5Pset_obj_track_times(plist_id: hid_t, track_times: hbool_t) -> herr_t; + pub unsafe fn H5Pget_obj_track_times(plist_id: hid_t, track_times: *mut hbool_t) -> herr_t; + pub unsafe fn H5Pmodify_filter( plist_id: hid_t, filter: H5Z_filter_t, flags: c_uint, cd_nelmts: size_t, cd_values: *const c_uint, ) -> herr_t; - pub fn H5Pset_filter( + pub unsafe fn H5Pset_filter( plist_id: hid_t, filter: H5Z_filter_t, flags: c_uint, cd_nelmts: size_t, c_values: *const c_uint, ) -> herr_t; - pub fn H5Pget_nfilters(plist_id: hid_t) -> c_int; - pub fn H5Pget_filter2( + pub unsafe fn H5Pget_nfilters(plist_id: hid_t) -> c_int; + pub unsafe fn H5Pget_filter2( plist_id: hid_t, filter: c_uint, flags: *mut c_uint, cd_nelmts: *mut size_t, cd_values: *mut c_uint, namelen: size_t, name: *mut c_char, filter_config: *mut c_uint, ) -> H5Z_filter_t; - pub fn H5Pget_filter_by_id2( + pub unsafe fn H5Pget_filter_by_id2( plist_id: hid_t, id: H5Z_filter_t, flags: *mut c_uint, cd_nelmts: *mut size_t, cd_values: *mut c_uint, namelen: size_t, name: *mut c_char, filter_config: *mut c_uint, ) -> herr_t; - pub fn H5Pall_filters_avail(plist_id: hid_t) -> htri_t; - pub fn H5Premove_filter(plist_id: hid_t, filter: H5Z_filter_t) -> herr_t; - pub fn H5Pset_deflate(plist_id: hid_t, aggression: c_uint) -> herr_t; - pub fn H5Pset_fletcher32(plist_id: hid_t) -> herr_t; - pub fn H5Pset_userblock(plist_id: hid_t, size: hsize_t) -> herr_t; - pub fn H5Pget_userblock(plist_id: hid_t, size: *mut hsize_t) -> herr_t; - pub fn H5Pset_sizes(plist_id: hid_t, sizeof_addr: size_t, sizeof_size: size_t) -> herr_t; - pub fn H5Pget_sizes( + pub unsafe fn H5Pall_filters_avail(plist_id: hid_t) -> htri_t; + pub unsafe fn H5Premove_filter(plist_id: hid_t, filter: H5Z_filter_t) -> herr_t; + pub unsafe fn H5Pset_deflate(plist_id: hid_t, aggression: c_uint) -> herr_t; + pub unsafe fn H5Pset_fletcher32(plist_id: hid_t) -> herr_t; + pub unsafe fn H5Pset_userblock(plist_id: hid_t, size: hsize_t) -> herr_t; + pub unsafe fn H5Pget_userblock(plist_id: hid_t, size: *mut hsize_t) -> herr_t; + pub unsafe fn H5Pset_sizes(plist_id: hid_t, sizeof_addr: size_t, sizeof_size: size_t) + -> herr_t; + pub unsafe fn H5Pget_sizes( plist_id: hid_t, sizeof_addr: *mut size_t, sizeof_size: *mut size_t, ) -> herr_t; - pub fn H5Pset_sym_k(plist_id: hid_t, ik: c_uint, lk: c_uint) -> herr_t; - pub fn H5Pget_sym_k(plist_id: hid_t, ik: *mut c_uint, lk: *mut c_uint) -> herr_t; - pub fn H5Pset_istore_k(plist_id: hid_t, ik: c_uint) -> herr_t; - pub fn H5Pget_istore_k(plist_id: hid_t, ik: *mut c_uint) -> herr_t; - pub fn H5Pset_shared_mesg_nindexes(plist_id: hid_t, nindexes: c_uint) -> herr_t; - pub fn H5Pget_shared_mesg_nindexes(plist_id: hid_t, nindexes: *mut c_uint) -> herr_t; - pub fn H5Pset_shared_mesg_index( + pub unsafe fn H5Pset_sym_k(plist_id: hid_t, ik: c_uint, lk: c_uint) -> herr_t; + pub unsafe fn H5Pget_sym_k(plist_id: hid_t, ik: *mut c_uint, lk: *mut c_uint) -> herr_t; + pub unsafe fn H5Pset_istore_k(plist_id: hid_t, ik: c_uint) -> herr_t; + pub unsafe fn H5Pget_istore_k(plist_id: hid_t, ik: *mut c_uint) -> herr_t; + pub unsafe fn H5Pset_shared_mesg_nindexes(plist_id: hid_t, nindexes: c_uint) -> herr_t; + pub unsafe fn H5Pget_shared_mesg_nindexes(plist_id: hid_t, nindexes: *mut c_uint) -> herr_t; + pub unsafe fn H5Pset_shared_mesg_index( plist_id: hid_t, index_num: c_uint, mesg_type_flags: c_uint, min_mesg_size: c_uint, ) -> herr_t; - pub fn H5Pget_shared_mesg_index( + pub unsafe fn H5Pget_shared_mesg_index( plist_id: hid_t, index_num: c_uint, mesg_type_flags: *mut c_uint, min_mesg_size: *mut c_uint, ) -> herr_t; - pub fn H5Pset_shared_mesg_phase_change( + pub unsafe fn H5Pset_shared_mesg_phase_change( plist_id: hid_t, max_list: c_uint, min_btree: c_uint, ) -> herr_t; - pub fn H5Pget_shared_mesg_phase_change( + pub unsafe fn H5Pget_shared_mesg_phase_change( plist_id: hid_t, max_list: *mut c_uint, min_btree: *mut c_uint, ) -> herr_t; - pub fn H5Pset_alignment(fapl_id: hid_t, threshold: hsize_t, alignment: hsize_t) -> herr_t; - pub fn H5Pget_alignment( + pub unsafe fn H5Pset_alignment( + fapl_id: hid_t, threshold: hsize_t, alignment: hsize_t, + ) -> herr_t; + pub unsafe fn H5Pget_alignment( fapl_id: hid_t, threshold: *mut hsize_t, alignment: *mut hsize_t, ) -> herr_t; - pub fn H5Pset_driver(plist_id: hid_t, driver_id: hid_t, driver_info: *const c_void) -> herr_t; - pub fn H5Pget_driver(plist_id: hid_t) -> hid_t; - pub fn H5Pget_driver_info(plist_id: hid_t) -> *mut c_void; - pub fn H5Pset_cache( + pub unsafe fn H5Pset_driver( + plist_id: hid_t, driver_id: hid_t, driver_info: *const c_void, + ) -> herr_t; + pub unsafe fn H5Pget_driver(plist_id: hid_t) -> hid_t; + pub unsafe fn H5Pget_driver_info(plist_id: hid_t) -> *mut c_void; + pub unsafe fn H5Pset_cache( plist_id: hid_t, mdc_nelmts: c_int, rdcc_nslots: size_t, rdcc_nbytes: size_t, rdcc_w0: c_double, ) -> herr_t; - pub fn H5Pget_cache( + pub unsafe fn H5Pget_cache( plist_id: hid_t, mdc_nelmts: *mut c_int, rdcc_nslots: *mut size_t, rdcc_nbytes: *mut size_t, rdcc_w0: *mut c_double, ) -> herr_t; - pub fn H5Pset_mdc_config(plist_id: hid_t, config_ptr: *const H5AC_cache_config_t) -> herr_t; - pub fn H5Pget_mdc_config(plist_id: hid_t, config_ptr: *mut H5AC_cache_config_t) -> herr_t; - pub fn H5Pset_gc_references(fapl_id: hid_t, gc_ref: c_uint) -> herr_t; - pub fn H5Pget_gc_references(fapl_id: hid_t, gc_ref: *mut c_uint) -> herr_t; - pub fn H5Pset_fclose_degree(fapl_id: hid_t, degree: H5F_close_degree_t) -> herr_t; - pub fn H5Pget_fclose_degree(fapl_id: hid_t, degree: *mut H5F_close_degree_t) -> herr_t; - pub fn H5Pset_meta_block_size(fapl_id: hid_t, size: hsize_t) -> herr_t; - pub fn H5Pget_meta_block_size(fapl_id: hid_t, size: *mut hsize_t) -> herr_t; - pub fn H5Pset_sieve_buf_size(fapl_id: hid_t, size: size_t) -> herr_t; - pub fn H5Pget_sieve_buf_size(fapl_id: hid_t, size: *mut size_t) -> herr_t; - pub fn H5Pset_small_data_block_size(fapl_id: hid_t, size: hsize_t) -> herr_t; - pub fn H5Pget_small_data_block_size(fapl_id: hid_t, size: *mut hsize_t) -> herr_t; - pub fn H5Pset_libver_bounds(plist_id: hid_t, low: H5F_libver_t, high: H5F_libver_t) -> herr_t; - pub fn H5Pget_libver_bounds( + pub unsafe fn H5Pset_mdc_config( + plist_id: hid_t, config_ptr: *const H5AC_cache_config_t, + ) -> herr_t; + pub unsafe fn H5Pget_mdc_config( + plist_id: hid_t, config_ptr: *mut H5AC_cache_config_t, + ) -> herr_t; + pub unsafe fn H5Pset_gc_references(fapl_id: hid_t, gc_ref: c_uint) -> herr_t; + pub unsafe fn H5Pget_gc_references(fapl_id: hid_t, gc_ref: *mut c_uint) -> herr_t; + pub unsafe fn H5Pset_fclose_degree(fapl_id: hid_t, degree: H5F_close_degree_t) -> herr_t; + pub unsafe fn H5Pget_fclose_degree(fapl_id: hid_t, degree: *mut H5F_close_degree_t) -> herr_t; + pub unsafe fn H5Pset_meta_block_size(fapl_id: hid_t, size: hsize_t) -> herr_t; + pub unsafe fn H5Pget_meta_block_size(fapl_id: hid_t, size: *mut hsize_t) -> herr_t; + pub unsafe fn H5Pset_sieve_buf_size(fapl_id: hid_t, size: size_t) -> herr_t; + pub unsafe fn H5Pget_sieve_buf_size(fapl_id: hid_t, size: *mut size_t) -> herr_t; + pub unsafe fn H5Pset_small_data_block_size(fapl_id: hid_t, size: hsize_t) -> herr_t; + pub unsafe fn H5Pget_small_data_block_size(fapl_id: hid_t, size: *mut hsize_t) -> herr_t; + pub unsafe fn H5Pset_libver_bounds( + plist_id: hid_t, low: H5F_libver_t, high: H5F_libver_t, + ) -> herr_t; + pub unsafe fn H5Pget_libver_bounds( plist_id: hid_t, low: *mut H5F_libver_t, high: *mut H5F_libver_t, ) -> herr_t; - pub fn H5Pset_layout(plist_id: hid_t, layout: H5D_layout_t) -> herr_t; - pub fn H5Pget_layout(plist_id: hid_t) -> H5D_layout_t; - pub fn H5Pset_chunk(plist_id: hid_t, ndims: c_int, dim: *const hsize_t) -> herr_t; - pub fn H5Pget_chunk(plist_id: hid_t, max_ndims: c_int, dim: *mut hsize_t) -> c_int; - pub fn H5Pset_external( + pub unsafe fn H5Pset_layout(plist_id: hid_t, layout: H5D_layout_t) -> herr_t; + pub unsafe fn H5Pget_layout(plist_id: hid_t) -> H5D_layout_t; + pub unsafe fn H5Pset_chunk(plist_id: hid_t, ndims: c_int, dim: *const hsize_t) -> herr_t; + pub unsafe fn H5Pget_chunk(plist_id: hid_t, max_ndims: c_int, dim: *mut hsize_t) -> c_int; + pub unsafe fn H5Pset_external( plist_id: hid_t, name: *const c_char, offset: off_t, size: hsize_t, ) -> herr_t; - pub fn H5Pget_external_count(plist_id: hid_t) -> c_int; - pub fn H5Pget_external( + pub unsafe fn H5Pget_external_count(plist_id: hid_t) -> c_int; + pub unsafe fn H5Pget_external( plist_id: hid_t, idx: c_uint, name_size: size_t, name: *mut c_char, offset: *mut off_t, size: *mut hsize_t, ) -> herr_t; - pub fn H5Pset_szip(plist_id: hid_t, options_mask: c_uint, pixels_per_block: c_uint) -> herr_t; - pub fn H5Pset_shuffle(plist_id: hid_t) -> herr_t; - pub fn H5Pset_nbit(plist_id: hid_t) -> herr_t; - pub fn H5Pset_scaleoffset( + pub unsafe fn H5Pset_szip( + plist_id: hid_t, options_mask: c_uint, pixels_per_block: c_uint, + ) -> herr_t; + pub unsafe fn H5Pset_shuffle(plist_id: hid_t) -> herr_t; + pub unsafe fn H5Pset_nbit(plist_id: hid_t) -> herr_t; + pub unsafe fn H5Pset_scaleoffset( plist_id: hid_t, scale_type: H5Z_SO_scale_type_t, scale_factor: c_int, ) -> herr_t; - pub fn H5Pset_fill_value(plist_id: hid_t, type_id: hid_t, value: *const c_void) -> herr_t; - pub fn H5Pget_fill_value(plist_id: hid_t, type_id: hid_t, value: *mut c_void) -> herr_t; - pub fn H5Pfill_value_defined(plist: hid_t, status: *mut H5D_fill_value_t) -> herr_t; - pub fn H5Pset_alloc_time(plist_id: hid_t, alloc_time: H5D_alloc_time_t) -> herr_t; - pub fn H5Pget_alloc_time(plist_id: hid_t, alloc_time: *mut H5D_alloc_time_t) -> herr_t; - pub fn H5Pset_fill_time(plist_id: hid_t, fill_time: H5D_fill_time_t) -> herr_t; - pub fn H5Pget_fill_time(plist_id: hid_t, fill_time: *mut H5D_fill_time_t) -> herr_t; - pub fn H5Pset_chunk_cache( + pub unsafe fn H5Pset_fill_value( + plist_id: hid_t, type_id: hid_t, value: *const c_void, + ) -> herr_t; + pub unsafe fn H5Pget_fill_value(plist_id: hid_t, type_id: hid_t, value: *mut c_void) -> herr_t; + pub unsafe fn H5Pfill_value_defined(plist: hid_t, status: *mut H5D_fill_value_t) -> herr_t; + pub unsafe fn H5Pset_alloc_time(plist_id: hid_t, alloc_time: H5D_alloc_time_t) -> herr_t; + pub unsafe fn H5Pget_alloc_time(plist_id: hid_t, alloc_time: *mut H5D_alloc_time_t) -> herr_t; + pub unsafe fn H5Pset_fill_time(plist_id: hid_t, fill_time: H5D_fill_time_t) -> herr_t; + pub unsafe fn H5Pget_fill_time(plist_id: hid_t, fill_time: *mut H5D_fill_time_t) -> herr_t; + pub unsafe fn H5Pset_chunk_cache( dapl_id: hid_t, rdcc_nslots: size_t, rdcc_nbytes: size_t, rdcc_w0: c_double, ) -> herr_t; - pub fn H5Pget_chunk_cache( + pub unsafe fn H5Pget_chunk_cache( dapl_id: hid_t, rdcc_nslots: *mut size_t, rdcc_nbytes: *mut size_t, rdcc_w0: *mut c_double, ) -> herr_t; - pub fn H5Pset_data_transform(plist_id: hid_t, expression: *const c_char) -> herr_t; - pub fn H5Pget_data_transform(plist_id: hid_t, expression: *mut c_char, size: size_t) - -> ssize_t; - pub fn H5Pset_buffer( + pub unsafe fn H5Pset_data_transform(plist_id: hid_t, expression: *const c_char) -> herr_t; + pub unsafe fn H5Pget_data_transform( + plist_id: hid_t, expression: *mut c_char, size: size_t, + ) -> ssize_t; + pub unsafe fn H5Pset_buffer( plist_id: hid_t, size: size_t, tconv: *mut c_void, bkg: *mut c_void, ) -> herr_t; - pub fn H5Pget_buffer(plist_id: hid_t, tconv: *mut *mut c_void, bkg: *mut *mut c_void) - -> size_t; - pub fn H5Pset_edc_check(plist_id: hid_t, check: H5Z_EDC_t) -> herr_t; - pub fn H5Pget_edc_check(plist_id: hid_t) -> H5Z_EDC_t; - pub fn H5Pset_filter_callback( + pub unsafe fn H5Pget_buffer( + plist_id: hid_t, tconv: *mut *mut c_void, bkg: *mut *mut c_void, + ) -> size_t; + pub unsafe fn H5Pset_edc_check(plist_id: hid_t, check: H5Z_EDC_t) -> herr_t; + pub unsafe fn H5Pget_edc_check(plist_id: hid_t) -> H5Z_EDC_t; + pub unsafe fn H5Pset_filter_callback( plist_id: hid_t, func: H5Z_filter_func_t, op_data: *mut c_void, ) -> herr_t; - pub fn H5Pset_btree_ratios( + pub unsafe fn H5Pset_btree_ratios( plist_id: hid_t, left: c_double, middle: c_double, right: c_double, ) -> herr_t; - pub fn H5Pget_btree_ratios( + pub unsafe fn H5Pget_btree_ratios( plist_id: hid_t, left: *mut c_double, middle: *mut c_double, right: *mut c_double, ) -> herr_t; - pub fn H5Pset_vlen_mem_manager( + pub unsafe fn H5Pset_vlen_mem_manager( plist_id: hid_t, alloc_func: H5MM_allocate_t, alloc_info: *mut c_void, free_func: H5MM_free_t, free_info: *mut c_void, ) -> herr_t; - pub fn H5Pget_vlen_mem_manager( + pub unsafe fn H5Pget_vlen_mem_manager( plist_id: hid_t, alloc_func: *mut H5MM_allocate_t, alloc_info: *mut *mut c_void, free_func: *mut H5MM_free_t, free_info: *mut *mut c_void, ) -> herr_t; - pub fn H5Pset_hyper_vector_size(fapl_id: hid_t, size: size_t) -> herr_t; - pub fn H5Pget_hyper_vector_size(fapl_id: hid_t, size: *mut size_t) -> herr_t; - pub fn H5Pset_type_conv_cb( + pub unsafe fn H5Pset_hyper_vector_size(fapl_id: hid_t, size: size_t) -> herr_t; + pub unsafe fn H5Pget_hyper_vector_size(fapl_id: hid_t, size: *mut size_t) -> herr_t; + pub unsafe fn H5Pset_type_conv_cb( dxpl_id: hid_t, op: H5T_conv_except_func_t, operate_data: *mut c_void, ) -> herr_t; - pub fn H5Pget_type_conv_cb( + pub unsafe fn H5Pget_type_conv_cb( dxpl_id: hid_t, op: *mut H5T_conv_except_func_t, operate_data: *mut *mut c_void, ) -> herr_t; - pub fn H5Pset_create_intermediate_group(plist_id: hid_t, crt_intmd: c_uint) -> herr_t; - pub fn H5Pget_create_intermediate_group(plist_id: hid_t, crt_intmd: *mut c_uint) -> herr_t; - pub fn H5Pset_local_heap_size_hint(plist_id: hid_t, size_hint: size_t) -> herr_t; - pub fn H5Pget_local_heap_size_hint(plist_id: hid_t, size_hint: *mut size_t) -> herr_t; - pub fn H5Pset_link_phase_change( + pub unsafe fn H5Pset_create_intermediate_group(plist_id: hid_t, crt_intmd: c_uint) -> herr_t; + pub unsafe fn H5Pget_create_intermediate_group( + plist_id: hid_t, crt_intmd: *mut c_uint, + ) -> herr_t; + pub unsafe fn H5Pset_local_heap_size_hint(plist_id: hid_t, size_hint: size_t) -> herr_t; + pub unsafe fn H5Pget_local_heap_size_hint(plist_id: hid_t, size_hint: *mut size_t) -> herr_t; + pub unsafe fn H5Pset_link_phase_change( plist_id: hid_t, max_compact: c_uint, min_dense: c_uint, ) -> herr_t; - pub fn H5Pget_link_phase_change( + pub unsafe fn H5Pget_link_phase_change( plist_id: hid_t, max_compact: *mut c_uint, min_dense: *mut c_uint, ) -> herr_t; - pub fn H5Pset_est_link_info( + pub unsafe fn H5Pset_est_link_info( plist_id: hid_t, est_num_entries: c_uint, est_name_len: c_uint, ) -> herr_t; - pub fn H5Pget_est_link_info( + pub unsafe fn H5Pget_est_link_info( plist_id: hid_t, est_num_entries: *mut c_uint, est_name_len: *mut c_uint, ) -> herr_t; - pub fn H5Pset_link_creation_order(plist_id: hid_t, crt_order_flags: c_uint) -> herr_t; - pub fn H5Pget_link_creation_order(plist_id: hid_t, crt_order_flags: *mut c_uint) -> herr_t; - pub fn H5Pset_char_encoding(plist_id: hid_t, encoding: H5T_cset_t) -> herr_t; - pub fn H5Pget_char_encoding(plist_id: hid_t, encoding: *mut H5T_cset_t) -> herr_t; - pub fn H5Pset_nlinks(plist_id: hid_t, nlinks: size_t) -> herr_t; - pub fn H5Pget_nlinks(plist_id: hid_t, nlinks: *mut size_t) -> herr_t; - pub fn H5Pset_elink_prefix(plist_id: hid_t, prefix: *const c_char) -> herr_t; - pub fn H5Pget_elink_prefix(plist_id: hid_t, prefix: *mut c_char, size: size_t) -> ssize_t; - pub fn H5Pget_elink_fapl(lapl_id: hid_t) -> hid_t; - pub fn H5Pset_elink_fapl(lapl_id: hid_t, fapl_id: hid_t) -> herr_t; - pub fn H5Pset_elink_acc_flags(lapl_id: hid_t, flags: c_uint) -> herr_t; - pub fn H5Pget_elink_acc_flags(lapl_id: hid_t, flags: *mut c_uint) -> herr_t; - pub fn H5Pset_elink_cb( + pub unsafe fn H5Pset_link_creation_order(plist_id: hid_t, crt_order_flags: c_uint) -> herr_t; + pub unsafe fn H5Pget_link_creation_order( + plist_id: hid_t, crt_order_flags: *mut c_uint, + ) -> herr_t; + pub unsafe fn H5Pset_char_encoding(plist_id: hid_t, encoding: H5T_cset_t) -> herr_t; + pub unsafe fn H5Pget_char_encoding(plist_id: hid_t, encoding: *mut H5T_cset_t) -> herr_t; + pub unsafe fn H5Pset_nlinks(plist_id: hid_t, nlinks: size_t) -> herr_t; + pub unsafe fn H5Pget_nlinks(plist_id: hid_t, nlinks: *mut size_t) -> herr_t; + pub unsafe fn H5Pset_elink_prefix(plist_id: hid_t, prefix: *const c_char) -> herr_t; + pub unsafe fn H5Pget_elink_prefix( + plist_id: hid_t, prefix: *mut c_char, size: size_t, + ) -> ssize_t; + pub unsafe fn H5Pget_elink_fapl(lapl_id: hid_t) -> hid_t; + pub unsafe fn H5Pset_elink_fapl(lapl_id: hid_t, fapl_id: hid_t) -> herr_t; + pub unsafe fn H5Pset_elink_acc_flags(lapl_id: hid_t, flags: c_uint) -> herr_t; + pub unsafe fn H5Pget_elink_acc_flags(lapl_id: hid_t, flags: *mut c_uint) -> herr_t; + pub unsafe fn H5Pset_elink_cb( lapl_id: hid_t, func: H5L_elink_traverse_t, op_data: *mut c_void, ) -> herr_t; - pub fn H5Pget_elink_cb( + pub unsafe fn H5Pget_elink_cb( lapl_id: hid_t, func: *mut H5L_elink_traverse_t, op_data: *mut *mut c_void, ) -> herr_t; - pub fn H5Pset_copy_object(plist_id: hid_t, crt_intmd: c_uint) -> herr_t; - pub fn H5Pget_copy_object(plist_id: hid_t, crt_intmd: *mut c_uint) -> herr_t; + pub unsafe fn H5Pset_copy_object(plist_id: hid_t, crt_intmd: c_uint) -> herr_t; + pub unsafe fn H5Pget_copy_object(plist_id: hid_t, crt_intmd: *mut c_uint) -> herr_t; #[cfg_attr( feature = "1.10.0", deprecated(note = "deprecated in HDF5 1.10.0, use H5Fget_info2()") )] - pub fn H5Pget_version( + pub unsafe fn H5Pget_version( plist_id: hid_t, boot: *mut c_uint, freelist: *mut c_uint, stab: *mut c_uint, shhdr: *mut c_uint, ) -> herr_t; } // drivers -extern "C" { +unsafe extern "C" { // sec2 - pub fn H5Pset_fapl_sec2(fapl_id: hid_t) -> herr_t; + pub unsafe fn H5Pset_fapl_sec2(fapl_id: hid_t) -> herr_t; // core - pub fn H5Pset_fapl_core(fapl_id: hid_t, increment: size_t, backing_store: hbool_t) -> herr_t; - pub fn H5Pget_fapl_core( + pub unsafe fn H5Pset_fapl_core( + fapl_id: hid_t, increment: size_t, backing_store: hbool_t, + ) -> herr_t; + pub unsafe fn H5Pget_fapl_core( fapl_id: hid_t, increment: *mut size_t, backing_store: *mut hbool_t, ) -> herr_t; // stdio - pub fn H5Pset_fapl_stdio(fapl_id: hid_t) -> herr_t; + pub unsafe fn H5Pset_fapl_stdio(fapl_id: hid_t) -> herr_t; // family - pub fn H5Pset_fapl_family(fapl_id: hid_t, memb_size: hsize_t, memb_fapl_id: hid_t) -> herr_t; - pub fn H5Pget_fapl_family( + pub unsafe fn H5Pset_fapl_family( + fapl_id: hid_t, memb_size: hsize_t, memb_fapl_id: hid_t, + ) -> herr_t; + pub unsafe fn H5Pget_fapl_family( fapl_id: hid_t, memb_size: *mut hsize_t, memb_fapl_id: *mut hid_t, ) -> herr_t; - pub fn H5Pset_family_offset(fapl_id: hid_t, offset: hsize_t) -> herr_t; - pub fn H5Pget_family_offset(fapl_id: hid_t, offset: *mut hsize_t) -> herr_t; + pub unsafe fn H5Pset_family_offset(fapl_id: hid_t, offset: hsize_t) -> herr_t; + pub unsafe fn H5Pget_family_offset(fapl_id: hid_t, offset: *mut hsize_t) -> herr_t; // multi/split - pub fn H5Pset_fapl_multi( + pub unsafe fn H5Pset_fapl_multi( fapl_id: hid_t, memb_map: *const H5FD_mem_t, memb_fapl: *const hid_t, memb_name: *const *const c_char, memb_addr: *const haddr_t, relax: hbool_t, ) -> herr_t; - pub fn H5Pget_fapl_multi( + pub unsafe fn H5Pget_fapl_multi( fapl_id: hid_t, memb_map: *mut H5FD_mem_t, memb_fapl: *mut hid_t, memb_name: *mut *const c_char, memb_addr: *mut haddr_t, relax: *mut hbool_t, ) -> herr_t; - pub fn H5Pset_multi_type(fapl_id: hid_t, type_: H5FD_mem_t) -> herr_t; - pub fn H5Pget_multi_type(fapl_id: hid_t, type_: *mut H5FD_mem_t) -> herr_t; - pub fn H5Pset_fapl_split( + pub unsafe fn H5Pset_multi_type(fapl_id: hid_t, type_: H5FD_mem_t) -> herr_t; + pub unsafe fn H5Pget_multi_type(fapl_id: hid_t, type_: *mut H5FD_mem_t) -> herr_t; + pub unsafe fn H5Pset_fapl_split( fapl_id: hid_t, meta_ext: *const c_char, meta_plist_id: hid_t, raw_ext: *const c_char, raw_plist_id: hid_t, ) -> herr_t; // log - pub fn H5Pset_fapl_log( + pub unsafe fn H5Pset_fapl_log( fapl_id: hid_t, logfile: *const c_char, flags: c_ulonglong, buf_size: size_t, ) -> herr_t; // mpi-io - #[cfg(feature = "mpio")] - pub fn H5Pset_fapl_mpio( + #[cfg(all(feature = "have-parallel", feature = "mpio"))] + pub unsafe fn H5Pset_fapl_mpio( fapl_id: hid_t, comm: mpi_sys::MPI_Comm, info: mpi_sys::MPI_Info, ) -> herr_t; - #[cfg(feature = "mpio")] - pub fn H5Pget_fapl_mpio( + #[cfg(all(feature = "have-parallel", feature = "mpio"))] + pub unsafe fn H5Pget_fapl_mpio( fapl_id: hid_t, comm: *mut mpi_sys::MPI_Comm, info: *mut mpi_sys::MPI_Info, ) -> herr_t; // direct #[cfg(feature = "have-direct")] - pub fn H5Pset_fapl_direct( + pub unsafe fn H5Pset_fapl_direct( fapl_id: hid_t, alignment: size_t, block_size: size_t, cbuf_size: size_t, ) -> herr_t; #[cfg(feature = "have-direct")] - pub fn H5Pget_fapl_direct( + pub unsafe fn H5Pget_fapl_direct( fapl_id: hid_t, alignment: *mut size_t, block_size: *mut size_t, cbuf_size: *mut size_t, ) -> herr_t; } @@ -575,18 +604,19 @@ mod mpio { H5FD_MPIO_INDIVIDUAL_IO = 1, } - extern "C" { - pub fn H5Pset_dxpl_mpio(dxpl_id: hid_t, xfer_mode: H5FD_mpio_xfer_t) -> herr_t; - pub fn H5Pget_dxpl_mpio(dxpl_id: hid_t, xfer_mode: *mut H5FD_mpio_xfer_t) -> herr_t; - pub fn H5Pset_dxpl_mpio_collective_opt( + unsafe extern "C" { + pub unsafe fn H5Pset_dxpl_mpio(dxpl_id: hid_t, xfer_mode: H5FD_mpio_xfer_t) -> herr_t; + pub unsafe fn H5Pget_dxpl_mpio(dxpl_id: hid_t, xfer_mode: *mut H5FD_mpio_xfer_t) -> herr_t; + pub unsafe fn H5Pset_dxpl_mpio_collective_opt( dxpl_id: hid_t, opt_mode: H5FD_mpio_collective_opt_t, ) -> herr_t; - pub fn H5Pset_dxpl_mpio_chunk_opt( + pub unsafe fn H5Pset_dxpl_mpio_chunk_opt( dxpl_id: hid_t, opt_mode: H5FD_mpio_chunk_opt_t, ) -> herr_t; - pub fn H5Pset_dxpl_mpio_chunk_opt_num(dxpl_id: hid_t, num_chunk_per_proc: c_uint) - -> herr_t; - pub fn H5Pset_dxpl_mpio_chunk_opt_ratio( + pub unsafe fn H5Pset_dxpl_mpio_chunk_opt_num( + dxpl_id: hid_t, num_chunk_per_proc: c_uint, + ) -> herr_t; + pub unsafe fn H5Pset_dxpl_mpio_chunk_opt_ratio( dxpl_id: hid_t, percent_num_proc_per_chunk: c_uint, ) -> herr_t; } @@ -596,52 +626,57 @@ mod mpio { pub use self::mpio::*; #[cfg(target_os = "windows")] -extern "C" { - pub fn H5Pset_fapl_windows(fapl_id: hid_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Pset_fapl_windows(fapl_id: hid_t) -> herr_t; } #[cfg(feature = "1.8.7")] -extern "C" { - pub fn H5Pset_elink_file_cache_size(plist_id: hid_t, efc_size: c_uint) -> herr_t; - pub fn H5Pget_elink_file_cache_size(plist_id: hid_t, efc_size: *mut c_uint) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Pset_elink_file_cache_size(plist_id: hid_t, efc_size: c_uint) -> herr_t; + pub unsafe fn H5Pget_elink_file_cache_size(plist_id: hid_t, efc_size: *mut c_uint) -> herr_t; } #[cfg(feature = "1.8.9")] -extern "C" { - pub fn H5Pset_file_image(fapl_id: hid_t, buf_ptr: *mut c_void, buf_len: size_t) -> herr_t; - pub fn H5Pget_file_image( +unsafe extern "C" { + pub unsafe fn H5Pset_file_image( + fapl_id: hid_t, buf_ptr: *mut c_void, buf_len: size_t, + ) -> herr_t; + pub unsafe fn H5Pget_file_image( fapl_id: hid_t, buf_ptr_ptr: *mut *mut c_void, buf_len_ptr: *mut size_t, ) -> herr_t; - pub fn H5Pset_file_image_callbacks( + pub unsafe fn H5Pset_file_image_callbacks( fapl_id: hid_t, callbacks_ptr: *mut H5FD_file_image_callbacks_t, ) -> herr_t; - pub fn H5Pget_file_image_callbacks( + pub unsafe fn H5Pget_file_image_callbacks( fapl_id: hid_t, callbacks_ptr: *mut H5FD_file_image_callbacks_t, ) -> herr_t; - pub fn H5Pset_mcdt_search_cb( + pub unsafe fn H5Pset_mcdt_search_cb( plist_id: hid_t, func: H5O_mcdt_search_cb_t, op_data: *mut c_void, ) -> herr_t; - pub fn H5Pget_mcdt_search_cb( + pub unsafe fn H5Pget_mcdt_search_cb( plist_id: hid_t, func: *mut H5O_mcdt_search_cb_t, op_data: *mut *mut c_void, ) -> herr_t; - pub fn H5Padd_merge_committed_dtype_path(plist_id: hid_t, path: *const c_char) -> herr_t; - pub fn H5Pfree_merge_committed_dtype_paths(plist_id: hid_t) -> herr_t; + pub unsafe fn H5Padd_merge_committed_dtype_path(plist_id: hid_t, path: *const c_char) + -> herr_t; + pub unsafe fn H5Pfree_merge_committed_dtype_paths(plist_id: hid_t) -> herr_t; } #[cfg(feature = "1.8.13")] -extern "C" { - pub fn H5Pset_core_write_tracking( +unsafe extern "C" { + pub unsafe fn H5Pset_core_write_tracking( fapl_id: hid_t, is_enabled: hbool_t, page_size: size_t, ) -> herr_t; - pub fn H5Pget_core_write_tracking( + pub unsafe fn H5Pget_core_write_tracking( fapl_id: hid_t, is_enabled: *mut hbool_t, page_size: *mut size_t, ) -> herr_t; } #[cfg(feature = "1.8.17")] -extern "C" { - pub fn H5Pset_efile_prefix(dapl_id: hid_t, prefix: *const c_char) -> herr_t; - pub fn H5Pget_efile_prefix(dapl_id: hid_t, prefix: *const c_char, size: size_t) -> ssize_t; +unsafe extern "C" { + pub unsafe fn H5Pset_efile_prefix(dapl_id: hid_t, prefix: *const c_char) -> herr_t; + pub unsafe fn H5Pget_efile_prefix( + dapl_id: hid_t, prefix: *const c_char, size: size_t, + ) -> ssize_t; } #[cfg(feature = "1.10.0")] @@ -651,75 +686,75 @@ use crate::{ }; #[cfg(feature = "1.10.0")] -extern "C" { - pub fn H5Pset_append_flush( +unsafe extern "C" { + pub unsafe fn H5Pset_append_flush( plist_id: hid_t, ndims: c_uint, boundary: *const hsize_t, func: H5D_append_cb_t, udata: *mut c_void, ) -> herr_t; - pub fn H5Pget_append_flush( + pub unsafe fn H5Pget_append_flush( plist_id: hid_t, dims: c_uint, boundary: *mut hsize_t, func: *mut H5D_append_cb_t, udata: *mut *mut c_void, ) -> herr_t; - pub fn H5Pset_object_flush_cb( + pub unsafe fn H5Pset_object_flush_cb( plist_id: hid_t, func: H5F_flush_cb_t, udata: *mut c_void, ) -> herr_t; - pub fn H5Pget_object_flush_cb( + pub unsafe fn H5Pget_object_flush_cb( plist_id: hid_t, func: *mut H5F_flush_cb_t, udata: *mut *mut c_void, ) -> herr_t; - pub fn H5Pget_metadata_read_attempts(plist_id: hid_t, attempts: *mut c_uint) -> herr_t; - pub fn H5Pset_metadata_read_attempts(plist_id: hid_t, attempts: c_uint) -> herr_t; - pub fn H5Pset_mdc_log_options( + pub unsafe fn H5Pget_metadata_read_attempts(plist_id: hid_t, attempts: *mut c_uint) -> herr_t; + pub unsafe fn H5Pset_metadata_read_attempts(plist_id: hid_t, attempts: c_uint) -> herr_t; + pub unsafe fn H5Pset_mdc_log_options( plist_id: hid_t, is_enabled: hbool_t, location: *const c_char, start_on_access: hbool_t, ) -> herr_t; - pub fn H5Pget_mdc_log_options( + pub unsafe fn H5Pget_mdc_log_options( plist_id: hid_t, is_enabled: *mut hbool_t, location: *mut c_char, location_size: *mut size_t, start_on_access: *mut hbool_t, ) -> herr_t; - pub fn H5Fget_mdc_logging_status( + pub unsafe fn H5Fget_mdc_logging_status( file_id: hid_t, is_enabled: *mut hbool_t, is_currently_logging: *mut hbool_t, ) -> herr_t; - pub fn H5Pset_virtual( + pub unsafe fn H5Pset_virtual( dcpl_id: hid_t, vspace_id: hid_t, src_file_name: *const c_char, src_dset_name: *const c_char, src_space_id: hid_t, ) -> herr_t; - pub fn H5Pget_virtual_count(dcpl_id: hid_t, count: *mut size_t) -> herr_t; - pub fn H5Pget_virtual_vspace(dcpl_id: hid_t, index: size_t) -> hid_t; - pub fn H5Pget_virtual_srcspace(dcpl_id: hid_t, index: size_t) -> hid_t; - pub fn H5Pget_virtual_dsetname( + pub unsafe fn H5Pget_virtual_count(dcpl_id: hid_t, count: *mut size_t) -> herr_t; + pub unsafe fn H5Pget_virtual_vspace(dcpl_id: hid_t, index: size_t) -> hid_t; + pub unsafe fn H5Pget_virtual_srcspace(dcpl_id: hid_t, index: size_t) -> hid_t; + pub unsafe fn H5Pget_virtual_dsetname( dcpl_id: hid_t, index: size_t, name: *mut c_char, size: size_t, ) -> ssize_t; - pub fn H5Pget_virtual_filename( + pub unsafe fn H5Pget_virtual_filename( dcpl_id: hid_t, index: size_t, name: *mut c_char, size: size_t, ) -> ssize_t; - pub fn H5Pget_virtual_printf_gap(plist_id: hid_t, gap_size: *mut hsize_t) -> herr_t; - pub fn H5Pset_virtual_printf_gap(plist_id: hid_t, gap_size: hsize_t) -> herr_t; - pub fn H5Pget_virtual_view(plist_id: hid_t, view: *mut H5D_vds_view_t) -> herr_t; - pub fn H5Pset_virtual_view(plist_id: hid_t, view: H5D_vds_view_t) -> herr_t; - pub fn H5Pget_chunk_opts(plist_id: hid_t, opts: *mut c_uint) -> herr_t; - pub fn H5Pset_chunk_opts(plist_id: hid_t, opts: c_uint) -> herr_t; + pub unsafe fn H5Pget_virtual_printf_gap(plist_id: hid_t, gap_size: *mut hsize_t) -> herr_t; + pub unsafe fn H5Pset_virtual_printf_gap(plist_id: hid_t, gap_size: hsize_t) -> herr_t; + pub unsafe fn H5Pget_virtual_view(plist_id: hid_t, view: *mut H5D_vds_view_t) -> herr_t; + pub unsafe fn H5Pset_virtual_view(plist_id: hid_t, view: H5D_vds_view_t) -> herr_t; + pub unsafe fn H5Pget_chunk_opts(plist_id: hid_t, opts: *mut c_uint) -> herr_t; + pub unsafe fn H5Pset_chunk_opts(plist_id: hid_t, opts: c_uint) -> herr_t; #[cfg_attr( feature = "1.12.0", deprecated(note = "deprecated in HDF5 1.12.0, use H5Pencode2()") )] #[cfg_attr(not(feature = "1.12.0"), link_name = "H5Pencode")] - pub fn H5Pencode1(plist_id: hid_t, buf: *mut c_void, nalloc: *mut size_t) -> herr_t; + pub unsafe fn H5Pencode1(plist_id: hid_t, buf: *mut c_void, nalloc: *mut size_t) -> herr_t; #[cfg(feature = "1.12.0")] - pub fn H5Pencode2( + pub unsafe fn H5Pencode2( plist_id: hid_t, buf: *mut c_void, nalloc: *mut size_t, fapl_id: hid_t, ) -> herr_t; - pub fn H5Pdecode(buf: *const c_void) -> hid_t; + pub unsafe fn H5Pdecode(buf: *const c_void) -> hid_t; #[cfg_attr( feature = "1.10.1", deprecated(note = "deprecated in HDF5 1.10.1, use H5Pset_file_space_strategy()") )] - pub fn H5Pset_file_space( + pub unsafe fn H5Pset_file_space( plist_id: hid_t, strategy: H5F_file_space_type_t, threshold: hsize_t, ) -> herr_t; #[cfg_attr( feature = "1.10.1", deprecated(note = "deprecated in HDF5 1.10.1, use H5Pget_file_space_strategy()") )] - pub fn H5Pget_file_space( + pub unsafe fn H5Pget_file_space( plist_id: hid_t, strategy: *mut H5F_file_space_type_t, threshold: *mut hsize_t, ) -> herr_t; } @@ -730,84 +765,108 @@ pub use self::H5Pencode1 as H5Pencode; pub use self::H5Pencode2 as H5Pencode; #[cfg(all(feature = "1.10.0", feature = "have-parallel"))] -extern "C" { - pub fn H5Pset_coll_metadata_write(fapl_id: hid_t, is_collective: hbool_t) -> herr_t; - pub fn H5Pget_coll_metadata_write(fapl_id: hid_t, is_collective: *mut hbool_t) -> herr_t; - pub fn H5Pset_all_coll_metadata_ops(accpl_id: hid_t, is_collective: hbool_t) -> herr_t; - pub fn H5Pget_all_coll_metadata_ops(accpl_id: hid_t, is_collective: *mut hbool_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Pset_coll_metadata_write(fapl_id: hid_t, is_collective: hbool_t) -> herr_t; + pub unsafe fn H5Pget_coll_metadata_write(fapl_id: hid_t, is_collective: *mut hbool_t) + -> herr_t; + pub unsafe fn H5Pset_all_coll_metadata_ops(accpl_id: hid_t, is_collective: hbool_t) -> herr_t; + pub unsafe fn H5Pget_all_coll_metadata_ops( + accpl_id: hid_t, is_collective: *mut hbool_t, + ) -> herr_t; } #[cfg(feature = "1.10.1")] -extern "C" { - pub fn H5Pset_evict_on_close(fapl_id: hid_t, evict_on_close: hbool_t) -> herr_t; - pub fn H5Pget_evict_on_close(fapl_id: hid_t, evict_on_close: *mut hbool_t) -> herr_t; - pub fn H5Pset_mdc_image_config( +unsafe extern "C" { + pub unsafe fn H5Pset_evict_on_close(fapl_id: hid_t, evict_on_close: hbool_t) -> herr_t; + pub unsafe fn H5Pget_evict_on_close(fapl_id: hid_t, evict_on_close: *mut hbool_t) -> herr_t; + pub unsafe fn H5Pset_mdc_image_config( plist_id: hid_t, config_ptr: *const H5AC_cache_image_config_t, ) -> herr_t; - pub fn H5Pget_mdc_image_config( + pub unsafe fn H5Pget_mdc_image_config( plist_id: hid_t, config_ptr: *mut H5AC_cache_image_config_t, ) -> herr_t; - pub fn H5Pset_page_buffer_size( + pub unsafe fn H5Pset_page_buffer_size( plist_id: hid_t, buf_size: size_t, min_meta_per: c_uint, min_raw_per: c_uint, ) -> herr_t; - pub fn H5Pget_page_buffer_size( + pub unsafe fn H5Pget_page_buffer_size( plist_id: hid_t, buf_size: *mut size_t, min_meta_per: *mut c_uint, min_raw_per: *mut c_uint, ) -> herr_t; - pub fn H5Pset_file_space_strategy( + pub unsafe fn H5Pset_file_space_strategy( plist_id: hid_t, strategy: H5F_fspace_strategy_t, persist: hbool_t, threshold: hsize_t, ) -> herr_t; - pub fn H5Pget_file_space_strategy( + pub unsafe fn H5Pget_file_space_strategy( plist_id: hid_t, strategy: *mut H5F_fspace_strategy_t, persist: *mut hbool_t, threshold: *mut hsize_t, ) -> herr_t; - pub fn H5Pset_file_space_page_size(plist_id: hid_t, fsp_size: hsize_t) -> herr_t; - pub fn H5Pget_file_space_page_size(plist_id: hid_t, fsp_size: *mut hsize_t) -> herr_t; + pub unsafe fn H5Pset_file_space_page_size(plist_id: hid_t, fsp_size: hsize_t) -> herr_t; + pub unsafe fn H5Pget_file_space_page_size(plist_id: hid_t, fsp_size: *mut hsize_t) -> herr_t; } #[cfg(feature = "1.10.2")] -extern "C" { - pub fn H5Pset_virtual_prefix(dapl_id: hid_t, prefix: *const c_char) -> herr_t; - pub fn H5Pget_virtual_prefix(dapl_id: hid_t, prefix: *mut c_char, size: size_t) -> ssize_t; +unsafe extern "C" { + pub unsafe fn H5Pset_virtual_prefix(dapl_id: hid_t, prefix: *const c_char) -> herr_t; + pub unsafe fn H5Pget_virtual_prefix( + dapl_id: hid_t, prefix: *mut c_char, size: size_t, + ) -> ssize_t; } #[cfg(feature = "1.10.5")] -extern "C" { - pub fn H5Pget_dset_no_attrs_hint(dcpl_id: hid_t, minimize: *mut hbool_t) -> herr_t; - pub fn H5Pset_dset_no_attrs_hint(dcpl_id: hid_t, minimize: hbool_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Pget_dset_no_attrs_hint(dcpl_id: hid_t, minimize: *mut hbool_t) -> herr_t; + pub unsafe fn H5Pset_dset_no_attrs_hint(dcpl_id: hid_t, minimize: hbool_t) -> herr_t; } #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] -extern "C" { - pub fn H5Pget_file_locking( +unsafe extern "C" { + pub unsafe fn H5Pget_file_locking( fapl_id: hid_t, use_file_locking: *mut hbool_t, ignore_when_disable: *mut hbool_t, ) -> herr_t; - pub fn H5Pset_file_locking( + pub unsafe fn H5Pset_file_locking( fapl_id: hid_t, use_file_locking: hbool_t, ignore_when_disable: hbool_t, ) -> herr_t; } #[cfg(feature = "1.12.0")] -extern "C" { - pub fn H5Pget_vol_id(plist_id: hid_t, vol_id: *mut hid_t) -> herr_t; - pub fn H5Pget_vol_info(plist_id: hid_t, vol_info: *mut *mut c_void) -> herr_t; - pub fn H5Pset_vol(plist_id: hid_t, new_vol_id: hid_t, new_vol_id: *const c_void) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Pget_vol_id(plist_id: hid_t, vol_id: *mut hid_t) -> herr_t; + pub unsafe fn H5Pget_vol_info(plist_id: hid_t, vol_info: *mut *mut c_void) -> herr_t; + pub unsafe fn H5Pset_vol( + plist_id: hid_t, new_vol_id: hid_t, new_vol_id: *const c_void, + ) -> herr_t; } #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5Pget_driver_config_str( +unsafe extern "C" { + pub unsafe fn H5Pget_driver_config_str( fapl_id: hid_t, config_buf: *mut c_char, buf_size: size_t, ) -> ssize_t; - pub fn H5Pget_vol_cap_flags(plist_id: hid_t, cap_flags: *mut c_uint) -> herr_t; - pub fn H5Pset_dataset_io_hyperslab_selection( + pub unsafe fn H5Pget_vol_cap_flags(plist_id: hid_t, cap_flags: *mut c_uint) -> herr_t; + pub unsafe fn H5Pset_dataset_io_hyperslab_selection( plist_id: hid_t, rank: c_uint, op: H5S_seloper_t, start: *const hsize_t, stride: *const hsize_t, count: *const hsize_t, block: *const hsize_t, ) -> herr_t; - pub fn H5Pset_driver_by_name( + pub unsafe fn H5Pset_driver_by_name( plist_id: hid_t, driver_name: *const c_char, driver_config: *const c_char, ) -> herr_t; - pub fn H5Pset_driver_by_value( + pub unsafe fn H5Pset_driver_by_value( plist_id: hid_t, driver_value: H5FD_class_value_t, driver_config: *const c_char, ) -> herr_t; } + +#[cfg(feature = "2.0.0")] +unsafe extern "C" { + pub unsafe fn H5Pget_virtual_spatial_tree(dcpl_id: hid_t, use_tree: *mut hbool_t) -> herr_t; + pub unsafe fn H5Pset_virtual_spatial_tree(dapl_id: hid_t, use_tree: hbool_t) -> herr_t; +} + +#[cfg(feature = "2.2.0")] +unsafe extern "C" { + pub unsafe fn H5Pget_fapl_ros3_block_caching( + fapl_id: hid_t, block_size: *mut size_t, bloc_cache_size: *mut size_t, + lock_superblock: *mut hbool_t, + ) -> herr_t; + pub unsafe fn H5Pset_fapl_ros3_block_caching( + fapl_id: hid_t, block_size: size_t, bloc_cache_size: size_t, lock_superblock: hbool_t, + ) -> herr_t; +} diff --git a/hdf5-sys/src/h5pl.rs b/hdf5-sys/src/h5pl.rs index fec2b4413..0935f8259 100644 --- a/hdf5-sys/src/h5pl.rs +++ b/hdf5-sys/src/h5pl.rs @@ -23,9 +23,9 @@ mod hdf5_1_8_15 { pub const H5PL_FILTER_PLUGIN: c_uint = 0x0001; pub const H5PL_ALL_PLUGIN: c_uint = 0xffff; - extern "C" { - pub fn H5PLget_loading_state(plugin_flags: *mut c_int) -> herr_t; - pub fn H5PLset_loading_state(plugin_flags: *mut c_int) -> herr_t; + unsafe extern "C" { + pub unsafe fn H5PLget_loading_state(plugin_flags: *mut c_int) -> herr_t; + pub unsafe fn H5PLset_loading_state(plugin_flags: *mut c_int) -> herr_t; } } @@ -33,12 +33,12 @@ mod hdf5_1_8_15 { pub use self::hdf5_1_8_15::*; #[cfg(feature = "1.10.1")] -extern "C" { - pub fn H5PLappend(search_path: *const c_char) -> herr_t; - pub fn H5PLprepend(search_path: *const c_char) -> herr_t; - pub fn H5PLreplace(search_path: *const c_char, index: c_uint) -> herr_t; - pub fn H5PLinsert(search_path: *const c_char, index: c_uint) -> herr_t; - pub fn H5PLremove(index: c_uint) -> herr_t; - pub fn H5PLget(index: c_uint, path_buf: *mut c_char, buf_size: size_t) -> ssize_t; - pub fn H5PLsize(num_paths: *mut c_uint) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5PLappend(search_path: *const c_char) -> herr_t; + pub unsafe fn H5PLprepend(search_path: *const c_char) -> herr_t; + pub unsafe fn H5PLreplace(search_path: *const c_char, index: c_uint) -> herr_t; + pub unsafe fn H5PLinsert(search_path: *const c_char, index: c_uint) -> herr_t; + pub unsafe fn H5PLremove(index: c_uint) -> herr_t; + pub unsafe fn H5PLget(index: c_uint, path_buf: *mut c_char, buf_size: size_t) -> ssize_t; + pub unsafe fn H5PLsize(num_paths: *mut c_uint) -> herr_t; } diff --git a/hdf5-sys/src/h5r.rs b/hdf5-sys/src/h5r.rs index c19054628..cc2ab8fb4 100644 --- a/hdf5-sys/src/h5r.rs +++ b/hdf5-sys/src/h5r.rs @@ -36,31 +36,36 @@ pub enum H5R_type_t { pub type hobj_ref_t = haddr_t; pub type hdset_reg_ref_t = [c_uchar; 12usize]; -extern "C" { - pub fn H5Rcreate( +unsafe extern "C" { + pub unsafe fn H5Rcreate( ref_: *mut c_void, loc_id: hid_t, name: *const c_char, ref_type: H5R_type_t, space_id: hid_t, ) -> herr_t; - pub fn H5Rget_region(dataset: hid_t, ref_type: H5R_type_t, ref_: *const c_void) -> hid_t; + pub unsafe fn H5Rget_region(dataset: hid_t, ref_type: H5R_type_t, ref_: *const c_void) + -> hid_t; #[deprecated(note = "deprecated in HDF5 1.8.0, use H5Rget_obj_type2")] - pub fn H5Rget_obj_type1(id: hid_t, ref_type: H5R_type_t, ref_: *const c_void) -> H5G_obj_t; - pub fn H5Rget_obj_type2( + pub unsafe fn H5Rget_obj_type1( + id: hid_t, ref_type: H5R_type_t, ref_: *const c_void, + ) -> H5G_obj_t; + pub unsafe fn H5Rget_obj_type2( id: hid_t, ref_type: H5R_type_t, ref_: *const c_void, obj_type: *mut H5O_type_t, ) -> herr_t; - pub fn H5Rget_name( + pub unsafe fn H5Rget_name( loc_id: hid_t, ref_type: H5R_type_t, ref_: *const c_void, name: *mut c_char, size: size_t, ) -> ssize_t; } -extern "C" { +unsafe extern "C" { #[cfg_attr( feature = "1.10.0", deprecated(note = "deprecated in HDF5 1.10.0, use H5Rdereference2") )] #[cfg_attr(not(feature = "1.10.0"), link_name = "H5Rdereference")] - pub fn H5Rdereference1(obj_id: hid_t, ref_type: H5R_type_t, ref_: *const c_void) -> hid_t; + pub unsafe fn H5Rdereference1( + obj_id: hid_t, ref_type: H5R_type_t, ref_: *const c_void, + ) -> hid_t; #[cfg(feature = "1.10.0")] - pub fn H5Rdereference2( + pub unsafe fn H5Rdereference2( obj_id: hid_t, oapl_id: hid_t, ref_type: H5R_type_t, ref_: *const c_void, ) -> hid_t; } @@ -91,46 +96,54 @@ pub struct H5R_ref_t { } #[cfg(feature = "1.12.0")] -extern "C" { - pub fn H5Rcopy(src_ref_ptr: *const H5R_ref_t, dst_ref_ptr: *mut H5R_ref_t) -> herr_t; - pub fn H5Rcreate_attr( +unsafe extern "C" { + pub unsafe fn H5Rcopy(src_ref_ptr: *const H5R_ref_t, dst_ref_ptr: *mut H5R_ref_t) -> herr_t; + pub unsafe fn H5Rcreate_attr( loc_id: hid_t, name: *const c_char, attr_name: *const c_char, oapl_id: hid_t, ref_ptr: *mut H5R_ref_t, ) -> herr_t; - pub fn H5Rcreate_object( + pub unsafe fn H5Rcreate_object( loc_id: hid_t, name: *const c_char, oapl_id: hid_t, ref_ptr: *mut H5R_ref_t, ) -> herr_t; - pub fn H5Rcreate_region( + pub unsafe fn H5Rcreate_region( loc_id: hid_t, name: *const c_char, space_id: hid_t, oapl_id: hid_t, ref_ptr: *mut H5R_ref_t, ) -> herr_t; - pub fn H5Rdestroy(ref_ptr: *mut H5R_ref_t) -> herr_t; - pub fn H5Requal(ref1_ptr: *const H5R_ref_t, ref2_ptr: *const H5R_ref_t) -> htri_t; - pub fn H5Rget_attr_name(ref_ptr: *const H5R_ref_t, name: *mut c_char, size: size_t) -> ssize_t; - pub fn H5Rget_file_name(ref_ptr: *const H5R_ref_t, name: *mut c_char, size: size_t) -> ssize_t; - pub fn H5Rget_obj_name( + pub unsafe fn H5Rdestroy(ref_ptr: *mut H5R_ref_t) -> herr_t; + pub unsafe fn H5Requal(ref1_ptr: *const H5R_ref_t, ref2_ptr: *const H5R_ref_t) -> htri_t; + pub unsafe fn H5Rget_attr_name( + ref_ptr: *const H5R_ref_t, name: *mut c_char, size: size_t, + ) -> ssize_t; + pub unsafe fn H5Rget_file_name( + ref_ptr: *const H5R_ref_t, name: *mut c_char, size: size_t, + ) -> ssize_t; + pub unsafe fn H5Rget_obj_name( ref_ptr: *const H5R_ref_t, rapl_id: hid_t, name: *mut c_char, size: size_t, ) -> ssize_t; - pub fn H5Rget_obj_type3( + pub unsafe fn H5Rget_obj_type3( ref_ptr: *const H5R_ref_t, rapl_id: hid_t, obj_type: *mut H5O_type_t, ) -> herr_t; - pub fn H5Rget_type(ref_ptr: *const H5R_ref_t) -> H5R_type_t; - pub fn H5Ropen_attr(ref_ptr: *const H5R_ref_t, rapl_id: hid_t, aapl_id: hid_t) -> hid_t; - pub fn H5Ropen_object(ref_ptr: *const H5R_ref_t, rapl_id: hid_t, oapl_id: hid_t) -> hid_t; - pub fn H5Ropen_region(ref_ptr: *const H5R_ref_t, rapl_id: hid_t, oapl_id: hid_t) -> hid_t; + pub unsafe fn H5Rget_type(ref_ptr: *const H5R_ref_t) -> H5R_type_t; + pub unsafe fn H5Ropen_attr(ref_ptr: *const H5R_ref_t, rapl_id: hid_t, aapl_id: hid_t) -> hid_t; + pub unsafe fn H5Ropen_object( + ref_ptr: *const H5R_ref_t, rapl_id: hid_t, oapl_id: hid_t, + ) -> hid_t; + pub unsafe fn H5Ropen_region( + ref_ptr: *const H5R_ref_t, rapl_id: hid_t, oapl_id: hid_t, + ) -> hid_t; } #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5Ropen_attr_async( +unsafe extern "C" { + pub unsafe fn H5Ropen_attr_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, ref_ptr: *mut H5R_ref_t, rapl_id: hid_t, aapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Ropen_object_async( + pub unsafe fn H5Ropen_object_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, ref_ptr: *mut H5R_ref_t, rapl_id: hid_t, oapl_id: hid_t, es_id: hid_t, ) -> hid_t; - pub fn H5Ropen_region_async( + pub unsafe fn H5Ropen_region_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, ref_ptr: *mut H5R_ref_t, rapl_id: hid_t, oapl_id: hid_t, es_id: hid_t, ) -> hid_t; diff --git a/hdf5-sys/src/h5s.rs b/hdf5-sys/src/h5s.rs index 5247f53a4..702b57282 100644 --- a/hdf5-sys/src/h5s.rs +++ b/hdf5-sys/src/h5s.rs @@ -53,98 +53,104 @@ pub enum H5S_sel_type { H5S_SEL_N = 4, } -extern "C" { - pub fn H5Screate(type_: H5S_class_t) -> hid_t; - pub fn H5Screate_simple(rank: c_int, dims: *const hsize_t, maxdims: *const hsize_t) -> hid_t; - pub fn H5Sset_extent_simple( +unsafe extern "C" { + pub unsafe fn H5Screate(type_: H5S_class_t) -> hid_t; + pub unsafe fn H5Screate_simple( + rank: c_int, dims: *const hsize_t, maxdims: *const hsize_t, + ) -> hid_t; + pub unsafe fn H5Sset_extent_simple( space_id: hid_t, rank: c_int, dims: *const hsize_t, max: *const hsize_t, ) -> herr_t; - pub fn H5Scopy(space_id: hid_t) -> hid_t; - pub fn H5Sclose(space_id: hid_t) -> herr_t; + pub unsafe fn H5Scopy(space_id: hid_t) -> hid_t; + pub unsafe fn H5Sclose(space_id: hid_t) -> herr_t; #[cfg_attr( feature = "1.12.0", deprecated(note = "deprecated in HDF5 1.12.0, use H5Sencode2()") )] #[cfg_attr(not(feature = "1.12.0"), link_name = "H5Sencode")] - pub fn H5Sencode1(obj_id: hid_t, buf: *mut c_void, nalloc: *mut size_t) -> herr_t; + pub unsafe fn H5Sencode1(obj_id: hid_t, buf: *mut c_void, nalloc: *mut size_t) -> herr_t; #[cfg(feature = "1.12.0")] - pub fn H5Sencode2( + pub unsafe fn H5Sencode2( obj_id: hid_t, buf: *mut c_void, nalloc: *mut size_t, fapl_id: hid_t, ) -> herr_t; - pub fn H5Sdecode(buf: *const c_void) -> hid_t; - pub fn H5Sget_simple_extent_npoints(space_id: hid_t) -> hssize_t; - pub fn H5Sget_simple_extent_ndims(space_id: hid_t) -> c_int; - pub fn H5Sget_simple_extent_dims( + pub unsafe fn H5Sdecode(buf: *const c_void) -> hid_t; + pub unsafe fn H5Sget_simple_extent_npoints(space_id: hid_t) -> hssize_t; + pub unsafe fn H5Sget_simple_extent_ndims(space_id: hid_t) -> c_int; + pub unsafe fn H5Sget_simple_extent_dims( space_id: hid_t, dims: *mut hsize_t, maxdims: *mut hsize_t, ) -> c_int; - pub fn H5Sis_simple(space_id: hid_t) -> htri_t; - pub fn H5Sget_select_npoints(spaceid: hid_t) -> hssize_t; - pub fn H5Sselect_hyperslab( + pub unsafe fn H5Sis_simple(space_id: hid_t) -> htri_t; + pub unsafe fn H5Sget_select_npoints(spaceid: hid_t) -> hssize_t; + pub unsafe fn H5Sselect_hyperslab( space_id: hid_t, op: H5S_seloper_t, start: *const hsize_t, _stride: *const hsize_t, count: *const hsize_t, _block: *const hsize_t, ) -> herr_t; - pub fn H5Sselect_elements( + pub unsafe fn H5Sselect_elements( space_id: hid_t, op: H5S_seloper_t, num_elem: size_t, coord: *const hsize_t, ) -> herr_t; - pub fn H5Sget_simple_extent_type(space_id: hid_t) -> H5S_class_t; - pub fn H5Sset_extent_none(space_id: hid_t) -> herr_t; - pub fn H5Sextent_copy(dst_id: hid_t, src_id: hid_t) -> herr_t; - pub fn H5Sextent_equal(sid1: hid_t, sid2: hid_t) -> htri_t; - pub fn H5Sselect_all(spaceid: hid_t) -> herr_t; - pub fn H5Sselect_none(spaceid: hid_t) -> herr_t; - pub fn H5Soffset_simple(space_id: hid_t, offset: *const hssize_t) -> herr_t; - pub fn H5Sselect_valid(spaceid: hid_t) -> htri_t; - pub fn H5Sget_select_hyper_nblocks(spaceid: hid_t) -> hssize_t; - pub fn H5Sget_select_elem_npoints(spaceid: hid_t) -> hssize_t; - pub fn H5Sget_select_hyper_blocklist( + pub unsafe fn H5Sget_simple_extent_type(space_id: hid_t) -> H5S_class_t; + pub unsafe fn H5Sset_extent_none(space_id: hid_t) -> herr_t; + pub unsafe fn H5Sextent_copy(dst_id: hid_t, src_id: hid_t) -> herr_t; + pub unsafe fn H5Sextent_equal(sid1: hid_t, sid2: hid_t) -> htri_t; + pub unsafe fn H5Sselect_all(spaceid: hid_t) -> herr_t; + pub unsafe fn H5Sselect_none(spaceid: hid_t) -> herr_t; + pub unsafe fn H5Soffset_simple(space_id: hid_t, offset: *const hssize_t) -> herr_t; + pub unsafe fn H5Sselect_valid(spaceid: hid_t) -> htri_t; + pub unsafe fn H5Sget_select_hyper_nblocks(spaceid: hid_t) -> hssize_t; + pub unsafe fn H5Sget_select_elem_npoints(spaceid: hid_t) -> hssize_t; + pub unsafe fn H5Sget_select_hyper_blocklist( spaceid: hid_t, startblock: hsize_t, numblocks: hsize_t, buf: *mut hsize_t, ) -> herr_t; - pub fn H5Sget_select_elem_pointlist( + pub unsafe fn H5Sget_select_elem_pointlist( spaceid: hid_t, startpoint: hsize_t, numpoints: hsize_t, buf: *mut hsize_t, ) -> herr_t; - pub fn H5Sget_select_bounds(spaceid: hid_t, start: *mut hsize_t, end: *mut hsize_t) -> herr_t; - pub fn H5Sget_select_type(spaceid: hid_t) -> H5S_sel_type; + pub unsafe fn H5Sget_select_bounds( + spaceid: hid_t, start: *mut hsize_t, end: *mut hsize_t, + ) -> herr_t; + pub unsafe fn H5Sget_select_type(spaceid: hid_t) -> H5S_sel_type; } #[cfg(feature = "1.10.0")] -extern "C" { - pub fn H5Sis_regular_hyperslab(spaceid: hid_t) -> htri_t; - pub fn H5Sget_regular_hyperslab( +unsafe extern "C" { + pub unsafe fn H5Sis_regular_hyperslab(spaceid: hid_t) -> htri_t; + pub unsafe fn H5Sget_regular_hyperslab( spaceid: hid_t, start: *mut hsize_t, stride: *mut hsize_t, count: *mut hsize_t, block: *mut hsize_t, ) -> htri_t; } #[cfg(any(feature = "1.12.0", feature = "1.10.7"))] -extern "C" { - pub fn H5Scombine_hyperslab( +unsafe extern "C" { + pub unsafe fn H5Scombine_hyperslab( space_id: hid_t, op: H5S_seloper_t, start: *const hsize_t, stride: *const hsize_t, count: *const hsize_t, block: *const hsize_t, ) -> hid_t; - pub fn H5Scombine_select(space1_id: hid_t, op: H5S_seloper_t, space2_id: hid_t) -> hid_t; - pub fn H5Smodify_select(space1_id: hid_t, op: H5S_seloper_t, space2_id: hid_t) -> herr_t; - pub fn H5Sselect_adjust(space_id: hid_t, offset: *const hssize_t) -> herr_t; - pub fn H5Sselect_copy(dst_id: hid_t, src_id: hid_t) -> herr_t; - pub fn H5Sselect_intersect_block( + pub unsafe fn H5Scombine_select(space1_id: hid_t, op: H5S_seloper_t, space2_id: hid_t) + -> hid_t; + pub unsafe fn H5Smodify_select(space1_id: hid_t, op: H5S_seloper_t, space2_id: hid_t) + -> herr_t; + pub unsafe fn H5Sselect_adjust(space_id: hid_t, offset: *const hssize_t) -> herr_t; + pub unsafe fn H5Sselect_copy(dst_id: hid_t, src_id: hid_t) -> herr_t; + pub unsafe fn H5Sselect_intersect_block( space_id: hid_t, start: *const hsize_t, end: *const hsize_t, ) -> htri_t; - pub fn H5Sselect_project_intersection( + pub unsafe fn H5Sselect_project_intersection( src_space_id: hid_t, dst_space_id: hid_t, src_intersect_space_id: hid_t, ) -> hid_t; - pub fn H5Sselect_shape_same(space1_id: hid_t, space2_id: hid_t) -> htri_t; + pub unsafe fn H5Sselect_shape_same(space1_id: hid_t, space2_id: hid_t) -> htri_t; } #[cfg(feature = "1.12.0")] -extern "C" { - pub fn H5Ssel_iter_close(sel_iter_id: hid_t) -> herr_t; - pub fn H5Ssel_iter_create(space_id: hid_t, elmt_size: size_t, flags: c_uint) -> hid_t; - pub fn H5Ssel_iter_get_seq_list( +unsafe extern "C" { + pub unsafe fn H5Ssel_iter_close(sel_iter_id: hid_t) -> herr_t; + pub unsafe fn H5Ssel_iter_create(space_id: hid_t, elmt_size: size_t, flags: c_uint) -> hid_t; + pub unsafe fn H5Ssel_iter_get_seq_list( sel_iter_id: hid_t, maxseq: size_t, maxbytes: size_t, nseq: *mut size_t, nbytes: *mut size_t, off: *mut hsize_t, len: *mut size_t, ) -> herr_t; } #[cfg(feature = "1.12.1")] -extern "C" { - pub fn H5Ssel_iter_reset(sel_iter_id: hid_t, space_id: hid_t) -> hid_t; +unsafe extern "C" { + pub unsafe fn H5Ssel_iter_reset(sel_iter_id: hid_t, space_id: hid_t) -> hid_t; } diff --git a/hdf5-sys/src/h5t.rs b/hdf5-sys/src/h5t.rs index eabad5b3b..bd465e9ad 100644 --- a/hdf5-sys/src/h5t.rs +++ b/hdf5-sys/src/h5t.rs @@ -36,7 +36,14 @@ pub enum H5T_class_t { H5T_ENUM = 8, H5T_VLEN = 9, H5T_ARRAY = 10, + + #[cfg(not(feature = "2.0.0"))] H5T_NCLASSES = 11, + + #[cfg(feature = "2.0.0")] + H5T_COMPLEX = 11, + #[cfg(feature = "2.0.0")] + H5T_NCLASSES = 12, } #[cfg(feature = "1.8.6")] @@ -81,9 +88,10 @@ pub enum H5T_norm_t { } #[repr(C)] -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Debug)] pub enum H5T_cset_t { H5T_CSET_ERROR = -1, + #[default] H5T_CSET_ASCII = 0, H5T_CSET_UTF8 = 1, H5T_CSET_RESERVED_2 = 2, @@ -102,12 +110,6 @@ pub enum H5T_cset_t { H5T_CSET_RESERVED_15 = 15, } -impl Default for H5T_cset_t { - fn default() -> Self { - Self::H5T_CSET_ASCII - } -} - pub const H5T_NCSET: H5T_cset_t = H5T_CSET_RESERVED_2; #[repr(C)] @@ -252,103 +254,118 @@ pub type H5T_conv_except_func_t = Option< ) -> H5T_conv_ret_t, >; -extern "C" { - pub fn H5Tcreate(type_: H5T_class_t, size: size_t) -> hid_t; - pub fn H5Tcopy(type_id: hid_t) -> hid_t; - pub fn H5Tclose(type_id: hid_t) -> herr_t; - pub fn H5Tequal(type1_id: hid_t, type2_id: hid_t) -> htri_t; - pub fn H5Tlock(type_id: hid_t) -> herr_t; - pub fn H5Tcommit2( +unsafe extern "C" { + pub unsafe fn H5Tcreate(type_: H5T_class_t, size: size_t) -> hid_t; + pub unsafe fn H5Tcopy(type_id: hid_t) -> hid_t; + pub unsafe fn H5Tclose(type_id: hid_t) -> herr_t; + pub unsafe fn H5Tequal(type1_id: hid_t, type2_id: hid_t) -> htri_t; + pub unsafe fn H5Tlock(type_id: hid_t) -> herr_t; + pub unsafe fn H5Tcommit2( loc_id: hid_t, name: *const c_char, type_id: hid_t, lcpl_id: hid_t, tcpl_id: hid_t, tapl_id: hid_t, ) -> herr_t; - pub fn H5Topen2(loc_id: hid_t, name: *const c_char, tapl_id: hid_t) -> hid_t; - pub fn H5Tcommit_anon(loc_id: hid_t, type_id: hid_t, tcpl_id: hid_t, tapl_id: hid_t) -> herr_t; - pub fn H5Tget_create_plist(type_id: hid_t) -> hid_t; - pub fn H5Tcommitted(type_id: hid_t) -> htri_t; - pub fn H5Tencode(obj_id: hid_t, buf: *mut c_void, nalloc: *mut size_t) -> herr_t; - pub fn H5Tdecode(buf: *const c_void) -> hid_t; - pub fn H5Tinsert( + pub unsafe fn H5Topen2(loc_id: hid_t, name: *const c_char, tapl_id: hid_t) -> hid_t; + pub unsafe fn H5Tcommit_anon( + loc_id: hid_t, type_id: hid_t, tcpl_id: hid_t, tapl_id: hid_t, + ) -> herr_t; + pub unsafe fn H5Tget_create_plist(type_id: hid_t) -> hid_t; + pub unsafe fn H5Tcommitted(type_id: hid_t) -> htri_t; + pub unsafe fn H5Tencode(obj_id: hid_t, buf: *mut c_void, nalloc: *mut size_t) -> herr_t; + #[cfg_attr(not(feature = "2.0.0"), link_name = "H5Tdecode")] + pub unsafe fn H5Tdecode1(buf: *const c_void) -> hid_t; + #[cfg(feature = "2.0.0")] + pub unsafe fn H5Tdecode2(buf: *const c_void, buf_size: size_t) -> hid_t; + pub unsafe fn H5Tinsert( parent_id: hid_t, name: *const c_char, offset: size_t, member_id: hid_t, ) -> herr_t; - pub fn H5Tpack(type_id: hid_t) -> herr_t; - pub fn H5Tenum_create(base_id: hid_t) -> hid_t; - pub fn H5Tenum_insert(type_: hid_t, name: *const c_char, value: *const c_void) -> herr_t; - pub fn H5Tenum_nameof( + pub unsafe fn H5Tpack(type_id: hid_t) -> herr_t; + pub unsafe fn H5Tenum_create(base_id: hid_t) -> hid_t; + pub unsafe fn H5Tenum_insert(type_: hid_t, name: *const c_char, value: *const c_void) + -> herr_t; + pub unsafe fn H5Tenum_nameof( type_: hid_t, value: *const c_void, name: *mut c_char, size: size_t, ) -> herr_t; - pub fn H5Tenum_valueof(type_: hid_t, name: *const c_char, value: *mut c_void) -> herr_t; - pub fn H5Tvlen_create(base_id: hid_t) -> hid_t; - pub fn H5Tarray_create2(base_id: hid_t, ndims: c_uint, dim: *const hsize_t) -> hid_t; - pub fn H5Tget_array_ndims(type_id: hid_t) -> c_int; - pub fn H5Tget_array_dims2(type_id: hid_t, dims: *mut hsize_t) -> c_int; - pub fn H5Tset_tag(type_: hid_t, tag: *const c_char) -> herr_t; - pub fn H5Tget_tag(type_: hid_t) -> *mut c_char; - pub fn H5Tget_super(type_: hid_t) -> hid_t; - pub fn H5Tget_class(type_id: hid_t) -> H5T_class_t; - pub fn H5Tdetect_class(type_id: hid_t, cls: H5T_class_t) -> htri_t; - pub fn H5Tget_size(type_id: hid_t) -> size_t; - pub fn H5Tget_order(type_id: hid_t) -> H5T_order_t; - pub fn H5Tget_precision(type_id: hid_t) -> size_t; - pub fn H5Tget_offset(type_id: hid_t) -> c_int; - pub fn H5Tget_pad(type_id: hid_t, lsb: *mut H5T_pad_t, msb: *mut H5T_pad_t) -> herr_t; - pub fn H5Tget_sign(type_id: hid_t) -> H5T_sign_t; - pub fn H5Tget_fields( + pub unsafe fn H5Tenum_valueof(type_: hid_t, name: *const c_char, value: *mut c_void) -> herr_t; + pub unsafe fn H5Tvlen_create(base_id: hid_t) -> hid_t; + pub unsafe fn H5Tarray_create2(base_id: hid_t, ndims: c_uint, dim: *const hsize_t) -> hid_t; + pub unsafe fn H5Tget_array_ndims(type_id: hid_t) -> c_int; + pub unsafe fn H5Tget_array_dims2(type_id: hid_t, dims: *mut hsize_t) -> c_int; + pub unsafe fn H5Tset_tag(type_: hid_t, tag: *const c_char) -> herr_t; + pub unsafe fn H5Tget_tag(type_: hid_t) -> *mut c_char; + pub unsafe fn H5Tget_super(type_: hid_t) -> hid_t; + pub unsafe fn H5Tget_class(type_id: hid_t) -> H5T_class_t; + pub unsafe fn H5Tdetect_class(type_id: hid_t, cls: H5T_class_t) -> htri_t; + pub unsafe fn H5Tget_size(type_id: hid_t) -> size_t; + pub unsafe fn H5Tget_order(type_id: hid_t) -> H5T_order_t; + pub unsafe fn H5Tget_precision(type_id: hid_t) -> size_t; + pub unsafe fn H5Tget_offset(type_id: hid_t) -> c_int; + pub unsafe fn H5Tget_pad(type_id: hid_t, lsb: *mut H5T_pad_t, msb: *mut H5T_pad_t) -> herr_t; + pub unsafe fn H5Tget_sign(type_id: hid_t) -> H5T_sign_t; + pub unsafe fn H5Tget_fields( type_id: hid_t, spos: *mut size_t, epos: *mut size_t, esize: *mut size_t, mpos: *mut size_t, msize: *mut size_t, ) -> herr_t; - pub fn H5Tget_ebias(type_id: hid_t) -> size_t; - pub fn H5Tget_norm(type_id: hid_t) -> H5T_norm_t; - pub fn H5Tget_inpad(type_id: hid_t) -> H5T_pad_t; - pub fn H5Tget_strpad(type_id: hid_t) -> H5T_str_t; - pub fn H5Tget_nmembers(type_id: hid_t) -> c_int; - pub fn H5Tget_member_name(type_id: hid_t, membno: c_uint) -> *mut c_char; - pub fn H5Tget_member_index(type_id: hid_t, name: *const c_char) -> c_int; - pub fn H5Tget_member_offset(type_id: hid_t, membno: c_uint) -> size_t; - pub fn H5Tget_member_class(type_id: hid_t, membno: c_uint) -> H5T_class_t; - pub fn H5Tget_member_type(type_id: hid_t, membno: c_uint) -> hid_t; - pub fn H5Tget_member_value(type_id: hid_t, membno: c_uint, value: *mut c_void) -> herr_t; - pub fn H5Tget_cset(type_id: hid_t) -> H5T_cset_t; - pub fn H5Tis_variable_str(type_id: hid_t) -> htri_t; - pub fn H5Tget_native_type(type_id: hid_t, direction: H5T_direction_t) -> hid_t; - pub fn H5Tset_size(type_id: hid_t, size: size_t) -> herr_t; - pub fn H5Tset_order(type_id: hid_t, order: H5T_order_t) -> herr_t; - pub fn H5Tset_precision(type_id: hid_t, prec: size_t) -> herr_t; - pub fn H5Tset_offset(type_id: hid_t, offset: size_t) -> herr_t; - pub fn H5Tset_pad(type_id: hid_t, lsb: H5T_pad_t, msb: H5T_pad_t) -> herr_t; - pub fn H5Tset_sign(type_id: hid_t, sign: H5T_sign_t) -> herr_t; - pub fn H5Tset_fields( + pub unsafe fn H5Tget_ebias(type_id: hid_t) -> size_t; + pub unsafe fn H5Tget_norm(type_id: hid_t) -> H5T_norm_t; + pub unsafe fn H5Tget_inpad(type_id: hid_t) -> H5T_pad_t; + pub unsafe fn H5Tget_strpad(type_id: hid_t) -> H5T_str_t; + pub unsafe fn H5Tget_nmembers(type_id: hid_t) -> c_int; + pub unsafe fn H5Tget_member_name(type_id: hid_t, membno: c_uint) -> *mut c_char; + pub unsafe fn H5Tget_member_index(type_id: hid_t, name: *const c_char) -> c_int; + pub unsafe fn H5Tget_member_offset(type_id: hid_t, membno: c_uint) -> size_t; + pub unsafe fn H5Tget_member_class(type_id: hid_t, membno: c_uint) -> H5T_class_t; + pub unsafe fn H5Tget_member_type(type_id: hid_t, membno: c_uint) -> hid_t; + pub unsafe fn H5Tget_member_value(type_id: hid_t, membno: c_uint, value: *mut c_void) + -> herr_t; + pub unsafe fn H5Tget_cset(type_id: hid_t) -> H5T_cset_t; + pub unsafe fn H5Tis_variable_str(type_id: hid_t) -> htri_t; + pub unsafe fn H5Tget_native_type(type_id: hid_t, direction: H5T_direction_t) -> hid_t; + pub unsafe fn H5Tset_size(type_id: hid_t, size: size_t) -> herr_t; + pub unsafe fn H5Tset_order(type_id: hid_t, order: H5T_order_t) -> herr_t; + pub unsafe fn H5Tset_precision(type_id: hid_t, prec: size_t) -> herr_t; + pub unsafe fn H5Tset_offset(type_id: hid_t, offset: size_t) -> herr_t; + pub unsafe fn H5Tset_pad(type_id: hid_t, lsb: H5T_pad_t, msb: H5T_pad_t) -> herr_t; + pub unsafe fn H5Tset_sign(type_id: hid_t, sign: H5T_sign_t) -> herr_t; + pub unsafe fn H5Tset_fields( type_id: hid_t, spos: size_t, epos: size_t, esize: size_t, mpos: size_t, msize: size_t, ) -> herr_t; - pub fn H5Tset_ebias(type_id: hid_t, ebias: size_t) -> herr_t; - pub fn H5Tset_norm(type_id: hid_t, norm: H5T_norm_t) -> herr_t; - pub fn H5Tset_inpad(type_id: hid_t, pad: H5T_pad_t) -> herr_t; - pub fn H5Tset_cset(type_id: hid_t, cset: H5T_cset_t) -> herr_t; - pub fn H5Tset_strpad(type_id: hid_t, strpad: H5T_str_t) -> herr_t; - pub fn H5Tregister( + pub unsafe fn H5Tset_ebias(type_id: hid_t, ebias: size_t) -> herr_t; + pub unsafe fn H5Tset_norm(type_id: hid_t, norm: H5T_norm_t) -> herr_t; + pub unsafe fn H5Tset_inpad(type_id: hid_t, pad: H5T_pad_t) -> herr_t; + pub unsafe fn H5Tset_cset(type_id: hid_t, cset: H5T_cset_t) -> herr_t; + pub unsafe fn H5Tset_strpad(type_id: hid_t, strpad: H5T_str_t) -> herr_t; + pub unsafe fn H5Tregister( pers: H5T_pers_t, name: *const c_char, src_id: hid_t, dst_id: hid_t, func: H5T_conv_t, ) -> herr_t; - pub fn H5Tunregister( + pub unsafe fn H5Tunregister( pers: H5T_pers_t, name: *const c_char, src_id: hid_t, dst_id: hid_t, func: H5T_conv_t, ) -> herr_t; - pub fn H5Tfind(src_id: hid_t, dst_id: hid_t, pcdata: *mut *mut H5T_cdata_t) -> H5T_conv_t; - pub fn H5Tcompiler_conv(src_id: hid_t, dst_id: hid_t) -> htri_t; - pub fn H5Tconvert( + pub unsafe fn H5Tfind( + src_id: hid_t, dst_id: hid_t, pcdata: *mut *mut H5T_cdata_t, + ) -> H5T_conv_t; + pub unsafe fn H5Tcompiler_conv(src_id: hid_t, dst_id: hid_t) -> htri_t; + pub unsafe fn H5Tconvert( src_id: hid_t, dst_id: hid_t, nelmts: size_t, buf: *mut c_void, background: *mut c_void, plist_id: hid_t, ) -> herr_t; #[deprecated(note = "deprecated since HDF5 1.8.0, use H5Tcommit2")] - pub fn H5Tcommit1(loc_id: hid_t, name: *const c_char, type_id: hid_t) -> herr_t; + pub unsafe fn H5Tcommit1(loc_id: hid_t, name: *const c_char, type_id: hid_t) -> herr_t; #[deprecated(note = "deprecated since HDF5 1.8.0, use H5Tcommit2")] - pub fn H5Topen1(loc_id: hid_t, name: *const c_char) -> hid_t; + pub unsafe fn H5Topen1(loc_id: hid_t, name: *const c_char) -> hid_t; #[deprecated(note = "deprecated since HDF5 1.8.0, use H5Tarray_create2")] - pub fn H5Tarray_create1( + pub unsafe fn H5Tarray_create1( base_id: hid_t, ndims: c_int, dim: *const hsize_t, perm: *const c_int, ) -> hid_t; #[deprecated(note = "deprecated since HDF5 1.8.0, use H5Tget_array_dims2")] - pub fn H5Tget_array_dims1(type_id: hid_t, dims: *mut hsize_t, perm: *mut c_int) -> c_int; + pub unsafe fn H5Tget_array_dims1(type_id: hid_t, dims: *mut hsize_t, perm: *mut c_int) + -> c_int; } +#[cfg(not(feature = "2.0.0"))] +pub use self::H5Tdecode1 as H5Tdecode; +#[cfg(feature = "2.0.0")] +pub use self::H5Tdecode2 as H5Tdecode; + pub use self::globals::*; #[cfg(not(all(target_env = "msvc", not(feature = "static"))))] @@ -443,6 +460,32 @@ mod globals { extern_static!(H5T_NATIVE_UINT_FAST64, H5T_NATIVE_UINT_FAST64_g); #[cfg(feature = "1.12.0")] extern_static!(H5T_STD_REF, H5T_STD_REF_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F16BE, H5T_COMPLEX_IEEE_F16BE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F16LE, H5T_COMPLEX_IEEE_F16LE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F32BE, H5T_COMPLEX_IEEE_F32BE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F32LE, H5T_COMPLEX_IEEE_F32LE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F64BE, H5T_COMPLEX_IEEE_F64BE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F64LE, H5T_COMPLEX_IEEE_F64LE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_FLOAT_BFLOAT16BE, H5T_FLOAT_BFLOAT16BE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_FLOAT_BFLOAT16LE, H5T_FLOAT_BFLOAT16LE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_FLOAT_F8E4M3, H5T_FLOAT_F8E4M3_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_FLOAT_F8E5M2, H5T_FLOAT_F8E5M2_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_NATIVE_DOUBLE_COMPLEX, H5T_NATIVE_DOUBLE_COMPLEX_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_NATIVE_FLOAT_COMPLEX, H5T_NATIVE_FLOAT_COMPLEX_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_NATIVE_LDOUBLE_COMPLEX, H5T_NATIVE_LDOUBLE_COMPLEX_g); } #[cfg(all(target_env = "msvc", not(feature = "static")))] @@ -538,32 +581,65 @@ mod globals { extern_static!(H5T_NATIVE_UINT_FAST64, __imp_H5T_NATIVE_UINT_FAST64_g); #[cfg(feature = "1.12.0")] extern_static!(H5T_STD_REF, __imp_H5T_STD_REF_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F16BE, __imp_H5T_COMPLEX_IEEE_F16BE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F16LE, __imp_H5T_COMPLEX_IEEE_F16LE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F32BE, __imp_H5T_COMPLEX_IEEE_F32BE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F32LE, __imp_H5T_COMPLEX_IEEE_F32LE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F64BE, __imp_H5T_COMPLEX_IEEE_F64BE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_COMPLEX_IEEE_F64LE, __imp_H5T_COMPLEX_IEEE_F64LE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_FLOAT_BFLOAT16BE, __imp_H5T_FLOAT_BFLOAT16BE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_FLOAT_BFLOAT16LE, __imp_H5T_FLOAT_BFLOAT16LE_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_FLOAT_F8E4M3, __imp_H5T_FLOAT_F8E4M3_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_FLOAT_F8E5M2, __imp_H5T_FLOAT_F8E5M2_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_NATIVE_DOUBLE_COMPLEX, __imp_H5T_NATIVE_DOUBLE_COMPLEX_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_NATIVE_FLOAT_COMPLEX, __imp_H5T_NATIVE_FLOAT_COMPLEX_g); + #[cfg(feature = "2.0.0")] + extern_static!(H5T_NATIVE_LDOUBLE_COMPLEX, __imp_H5T_NATIVE_LDOUBLE_COMPLEX_g); } #[cfg(feature = "1.10.0")] -extern "C" { - pub fn H5Tflush(type_id: hid_t) -> herr_t; - pub fn H5Trefresh(type_id: hid_t) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Tflush(type_id: hid_t) -> herr_t; + pub unsafe fn H5Trefresh(type_id: hid_t) -> herr_t; } #[cfg(feature = "1.12.0")] -extern "C" { - pub fn H5Treclaim(type_id: hid_t, space_id: hid_t, dxpl_id: hid_t, buf: *mut c_void) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Treclaim( + type_id: hid_t, space_id: hid_t, dxpl_id: hid_t, buf: *mut c_void, + ) -> herr_t; } #[cfg(feature = "1.14.0")] -extern "C" { - pub fn H5Tclose_async( +unsafe extern "C" { + pub unsafe fn H5Tclose_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, type_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Tcommit_async( + pub unsafe fn H5Tcommit_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, type_id: hid_t, lcpl_id: hid_t, tcpl_id: hid_t, tapl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5Topen_async( + pub unsafe fn H5Topen_async( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, tapl_id: hid_t, es_id: hid_t, ) -> hid_t; } + +#[cfg(feature = "2.0.0")] +unsafe extern "C" { + pub unsafe fn H5Tcomplex_create(base_type_id: hid_t) -> hid_t; +} diff --git a/hdf5-sys/src/h5vl.rs b/hdf5-sys/src/h5vl.rs index 56446df7d..f724dc340 100644 --- a/hdf5-sys/src/h5vl.rs +++ b/hdf5-sys/src/h5vl.rs @@ -47,20 +47,22 @@ pub struct H5VL_class_t { >, } -extern "C" { - pub fn H5VLclose(connector_id: hid_t) -> herr_t; - pub fn H5VLget_connector_id(obj_id: hid_t) -> hid_t; - pub fn H5VLget_connector_id_by_name(name: *const c_char) -> hid_t; - pub fn H5VLget_connector_id_by_value(connector_value: H5VL_class_value_t) -> hid_t; - pub fn H5VLget_connector_name(id: hid_t, name: *mut c_char, size: size_t) -> ssize_t; - pub fn H5VLis_connector_registered_by_name(name: *const c_char) -> htri_t; - pub fn H5VLis_connector_registered_by_value(connector_value: H5VL_class_value_t) -> htri_t; - pub fn H5VLregister_connector(cls: *const H5VL_class_t, vipl_id: hid_t) -> hid_t; - pub fn H5VLregister_connector_by_name(name: *const c_char, vipl_id: hid_t) -> hid_t; - pub fn H5VLregister_connector_by_value( +unsafe extern "C" { + pub unsafe fn H5VLclose(connector_id: hid_t) -> herr_t; + pub unsafe fn H5VLget_connector_id(obj_id: hid_t) -> hid_t; + pub unsafe fn H5VLget_connector_id_by_name(name: *const c_char) -> hid_t; + pub unsafe fn H5VLget_connector_id_by_value(connector_value: H5VL_class_value_t) -> hid_t; + pub unsafe fn H5VLget_connector_name(id: hid_t, name: *mut c_char, size: size_t) -> ssize_t; + pub unsafe fn H5VLis_connector_registered_by_name(name: *const c_char) -> htri_t; + pub unsafe fn H5VLis_connector_registered_by_value( + connector_value: H5VL_class_value_t, + ) -> htri_t; + pub unsafe fn H5VLregister_connector(cls: *const H5VL_class_t, vipl_id: hid_t) -> hid_t; + pub unsafe fn H5VLregister_connector_by_name(name: *const c_char, vipl_id: hid_t) -> hid_t; + pub unsafe fn H5VLregister_connector_by_value( connector_value: H5VL_class_value_t, vipl_id: hid_t, ) -> hid_t; - pub fn H5VLunregister_connector(vol_id: hid_t) -> herr_t; + pub unsafe fn H5VLunregister_connector(vol_id: hid_t) -> herr_t; } #[cfg(feature = "1.12.1")] @@ -83,8 +85,8 @@ pub enum H5VL_subclass_t { } #[cfg(feature = "1.12.1")] -extern "C" { - pub fn H5VLquery_optional( +unsafe extern "C" { + pub unsafe fn H5VLquery_optional( obj_id: hid_t, subcls: H5VL_subclass_t, opt_type: c_int, supported: *mut hbool_t, ) -> herr_t; } @@ -1877,60 +1879,88 @@ mod v1_14_0 { pub args: *mut c_void, } - extern "C" { - pub fn H5VLattr_optional_op( + unsafe extern "C" { + pub unsafe fn H5VLattr_optional_op( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, attr_id: hid_t, args: *mut H5VL_optional_args_t, dxpl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5VLdataset_optional_op( + pub unsafe fn H5VLdataset_optional_op( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, dset_id: hid_t, args: *mut H5VL_optional_args_t, dxpl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5VLdatatype_optional_op( + pub unsafe fn H5VLdatatype_optional_op( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, type_id: hid_t, args: *mut H5VL_optional_args_t, dxpl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5VLfile_optional_op( + pub unsafe fn H5VLfile_optional_op( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, file_id: hid_t, args: *mut H5VL_optional_args_t, dxpl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5VLfind_opt_operation( + pub unsafe fn H5VLfind_opt_operation( subcls: H5VL_subclass_t, op_name: *const c_char, op_val: *mut c_int, ) -> herr_t; - pub fn H5VLgroup_optional_op( + pub unsafe fn H5VLgroup_optional_op( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, group_id: hid_t, args: *mut H5VL_optional_args_t, dxpl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5VLlink_optional_op( + pub unsafe fn H5VLlink_optional_op( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, lapl_id: hid_t, args: *mut H5VL_optional_args_t, dxpl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5VLobject_optional_op( + pub unsafe fn H5VLobject_optional_op( app_file: *const c_char, app_func: *const c_char, app_line: c_uint, loc_id: hid_t, name: *const c_char, lapl_id: hid_t, args: *mut H5VL_optional_args_t, dxpl_id: hid_t, es_id: hid_t, ) -> herr_t; - pub fn H5VLregister_opt_operation( + pub unsafe fn H5VLregister_opt_operation( subcls: H5VL_subclass_t, op_name: *const c_char, op_val: *mut c_int, ) -> herr_t; - pub fn H5VLrequest_optional_op( + pub unsafe fn H5VLrequest_optional_op( req: *mut c_void, connector_id: hid_t, args: *mut H5VL_optional_args_t, ) -> herr_t; - pub fn H5VLunregister_opt_operation( + pub unsafe fn H5VLunregister_opt_operation( subcls: H5VL_subclass_t, op_name: *const c_char, ) -> herr_t; } - extern "C" { - pub fn H5VLfinish_lib_state() -> herr_t; - pub fn H5VLintrospect_get_cap_flags( + unsafe extern "C" { + #[cfg(not(feature = "2.0.0"))] + pub unsafe fn H5VLstart_lib_state() -> herr_t; + #[cfg(not(feature = "2.0.0"))] + pub unsafe fn H5VLfinish_lib_state() -> herr_t; + pub unsafe fn H5VLintrospect_get_cap_flags( info: *const c_void, connector_id: hid_t, cap_flags: *mut c_uint, ) -> herr_t; - pub fn H5VLstart_lib_state() -> herr_t; } - extern "C" { - pub fn H5VLobject_is_native(obj_id: hid_t, is_native: *mut hbool_t) -> herr_t; + unsafe extern "C" { + pub unsafe fn H5VLobject_is_native(obj_id: hid_t, is_native: *mut hbool_t) -> herr_t; } } + +#[cfg(feature = "2.0.0")] +unsafe extern "C" { + pub unsafe fn H5VLclose_lib_context(context: *mut c_void) -> herr_t; + pub unsafe fn H5VLopen_lib_context(context: *mut *mut c_void) -> herr_t; +} + +#[cfg(not(all(target_env = "msvc", not(feature = "static"))))] +#[cfg(feature = "2.0.0")] +mod globals { + pub use crate::h5i::hid_t as id_t; + extern_static!(H5VL_NATIVE, H5VL_NATIVE_g); + extern_static!(H5VL_PASSTHRU, H5VL_PASSTHRU_g); +} + +#[cfg(all(target_env = "msvc", not(feature = "static")))] +#[cfg(feature = "2.0.0")] +mod globals { + // dllimport hack + pub type id_t = usize; + extern_static!(H5VL_NATIVE, __imp_H5VL_NATIVE_g); + extern_static!(H5VL_PASSTHRU, __imp_H5VL_PASSTHRU_g); +} + +#[cfg(feature = "2.0.0")] +pub use globals::*; diff --git a/hdf5-sys/src/h5z.rs b/hdf5-sys/src/h5z.rs index 740569140..2f51fe6f8 100644 --- a/hdf5-sys/src/h5z.rs +++ b/hdf5-sys/src/h5z.rs @@ -143,11 +143,13 @@ impl Default for H5Z_class2_t { } } -extern "C" { - pub fn H5Zregister(cls: *const c_void) -> herr_t; - pub fn H5Zunregister(id: H5Z_filter_t) -> herr_t; - pub fn H5Zfilter_avail(id: H5Z_filter_t) -> htri_t; - pub fn H5Zget_filter_info(filter: H5Z_filter_t, filter_config_flags: *mut c_uint) -> herr_t; +unsafe extern "C" { + pub unsafe fn H5Zregister(cls: *const c_void) -> herr_t; + pub unsafe fn H5Zunregister(id: H5Z_filter_t) -> herr_t; + pub unsafe fn H5Zfilter_avail(id: H5Z_filter_t) -> htri_t; + pub unsafe fn H5Zget_filter_info( + filter: H5Z_filter_t, filter_config_flags: *mut c_uint, + ) -> herr_t; } #[repr(C)] diff --git a/hdf5-sys/src/lib.rs b/hdf5-sys/src/lib.rs index e02192b74..7f667ea1a 100644 --- a/hdf5-sys/src/lib.rs +++ b/hdf5-sys/src/lib.rs @@ -17,7 +17,7 @@ extern crate mpi_sys; macro_rules! extern_static { ($dest:ident, $src:ident) => { - extern "C" { + unsafe extern "C" { static $src: id_t; } pub static $dest: &'static id_t = unsafe { &$src }; @@ -59,12 +59,12 @@ pub mod h5es; #[allow(non_camel_case_types)] mod internal_prelude { pub use crate::h5::{ - haddr_t, hbool_t, herr_t, hsize_t, hssize_t, htri_t, H5_ih_info_t, H5_index_t, - H5_iter_order_t, + H5_ih_info_t, H5_index_t, H5_iter_order_t, haddr_t, hbool_t, herr_t, hsize_t, hssize_t, + htri_t, }; pub use crate::h5i::hid_t; pub use crate::h5t::H5T_cset_t; - pub use libc::{int64_t, off_t, size_t, ssize_t, time_t, uint32_t, uint64_t, FILE}; + pub use libc::{FILE, int64_t, off_t, size_t, ssize_t, time_t, uint32_t, uint64_t}; #[allow(unused_imports)] pub use std::os::raw::{ c_char, c_double, c_float, c_int, c_long, c_longlong, c_uchar, c_uint, c_ulong, @@ -83,7 +83,7 @@ mod tests { use super::h5::H5get_libversion; use super::h5::H5open; use super::h5p::H5P_CLS_ROOT; - use super::{Version, HDF5_VERSION, LOCK}; + use super::{HDF5_VERSION, LOCK, Version}; #[test] fn version_test() { diff --git a/hdf5-types/Cargo.toml b/hdf5-types/Cargo.toml index 8d489835b..e4399414a 100644 --- a/hdf5-types/Cargo.toml +++ b/hdf5-types/Cargo.toml @@ -4,7 +4,7 @@ description = "Native Rust equivalents of HDF5 types." readme = "README.md" build = "build.rs" categories = ["encoding"] -version = "0.10.1" # !V +version = "0.11.1" # !V rust-version.workspace = true authors.workspace = true keywords.workspace = true diff --git a/hdf5-types/src/array.rs b/hdf5-types/src/array.rs index c6438e2b9..1345bd1b4 100644 --- a/hdf5-types/src/array.rs +++ b/hdf5-types/src/array.rs @@ -5,6 +5,7 @@ use std::ops::Deref; use std::ptr; use std::slice; +/// A variable-length array. #[repr(C)] pub struct VarLenArray { len: usize, @@ -13,6 +14,14 @@ pub struct VarLenArray { } impl VarLenArray { + /// Creates a `VarLenArray` by copying the first `len` elements stored at `p`. + /// + /// Returns an empty array if `p` is null. + /// + /// # Safety + /// + /// - `p` must be valid for reads of `len * size_of::()` bytes. + /// - `p` must point to `len` consecutive properly initialized and aligned values of type `T`. pub unsafe fn from_parts(p: *const T, len: usize) -> Self { let (len, ptr) = if !p.is_null() && len != 0 { let dst = crate::malloc(len * mem::size_of::()); @@ -24,26 +33,31 @@ impl VarLenArray { Self { len, ptr: ptr as *const _, tag: PhantomData } } + /// Creates a `VarLenArray` from a slice by copying its elements. #[inline] pub fn from_slice(arr: &[T]) -> Self { unsafe { Self::from_parts(arr.as_ptr(), arr.len()) } } + /// Returns a raw pointer to the array's buffer. #[inline] pub fn as_ptr(&self) -> *const T { self.ptr } + /// Returns the number of elements in the array. #[inline] pub fn len(&self) -> usize { self.len as _ } + /// Returns `true` if the array has a length of zero. #[inline] pub fn is_empty(&self) -> bool { self.len == 0 } + /// Returns a slice containing the entire array. #[inline] pub fn as_slice(&self) -> &[T] { self diff --git a/hdf5-types/src/dyn_value.rs b/hdf5-types/src/dyn_value.rs index 23a09357a..1a9a2dfe7 100644 --- a/hdf5-types/src/dyn_value.rs +++ b/hdf5-types/src/dyn_value.rs @@ -1,9 +1,11 @@ +//! Dynamically-typed values. + use std::fmt::{self, Debug, Display}; use std::mem; use std::ptr; use std::slice; -use crate::h5type::{hvl_t, CompoundType, EnumType, FloatSize, H5Type, IntSize, TypeDescriptor}; +use crate::h5type::{CompoundType, EnumType, FloatSize, H5Type, IntSize, TypeDescriptor, hvl_t}; use crate::string::{VarLenAscii, VarLenUnicode}; fn read_raw(buf: &[u8]) -> T { @@ -26,6 +28,7 @@ unsafe trait DynClone { fn dyn_clone(&mut self, out: &mut [u8]); } +/// A dynamically-typed integer. #[derive(Copy, Clone, PartialEq, Eq)] pub enum DynInteger { Int8(i8), @@ -114,6 +117,7 @@ impl From for DynValue<'_> { } } +/// A dynamically-typed floating-point value. #[derive(Copy, Clone, PartialEq)] pub enum DynFloat { #[cfg(feature = "f16")] @@ -173,6 +177,7 @@ impl From for DynValue<'_> { } } +/// A dynamically-typed scalar value. #[derive(Copy, Clone, PartialEq)] pub enum DynScalar { Integer(DynInteger), @@ -212,6 +217,7 @@ impl From for DynValue<'static> { } } +/// A dynamically-typed enumeration value. #[derive(Copy, Clone)] pub struct DynEnum<'a> { tp: &'a EnumType, @@ -269,6 +275,7 @@ impl<'a> From> for DynValue<'a> { } } +/// A dynamically-typed compound value. pub struct DynCompound<'a> { tp: &'a CompoundType, buf: &'a [u8], @@ -279,7 +286,7 @@ impl<'a> DynCompound<'a> { Self { tp, buf } } - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator)> { self.tp.fields.iter().map(move |field| { ( field.name.as_ref(), @@ -354,6 +361,7 @@ impl<'a> From> for DynValue<'a> { } } +/// A dynamically-typed array. pub struct DynArray<'a> { tp: &'a TypeDescriptor, buf: &'a [u8], @@ -379,7 +387,7 @@ impl<'a> DynArray<'a> { } } - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator> { let ptr = self.get_ptr(); let len = self.get_len(); let size = self.tp.size(); @@ -470,6 +478,7 @@ impl<'a> From> for DynValue<'a> { } } +/// A fixed-length string with a dynamic encoding. pub struct DynFixedString<'a> { buf: &'a [u8], unicode: bool, @@ -529,6 +538,7 @@ impl<'a> From> for DynValue<'a> { } } +/// A variable-length string with a dynamic encoding. pub struct DynVarLenString<'a> { buf: &'a [u8], unicode: bool, @@ -540,11 +550,7 @@ impl<'a> DynVarLenString<'a> { } fn get_ptr(&self) -> *const u8 { - if self.unicode { - self.as_unicode().as_ptr() - } else { - self.as_ascii().as_ptr() - } + if self.unicode { self.as_unicode().as_ptr() } else { self.as_ascii().as_ptr() } } fn raw_len(&self) -> usize { @@ -633,6 +639,7 @@ impl<'a> From> for DynValue<'a> { } } +/// A dynamically-typed string. #[derive(PartialEq, Eq)] pub enum DynString<'a> { Fixed(DynFixedString<'a>), @@ -677,6 +684,7 @@ impl<'a> From> for DynValue<'a> { } } +/// A borrowed value with dynamic type. #[derive(PartialEq)] pub enum DynValue<'a> { Scalar(DynScalar), @@ -687,6 +695,7 @@ pub enum DynValue<'a> { } impl<'a> DynValue<'a> { + /// Constructs a new `DynValue` from a `TypeDescriptor` and a byte slice. pub fn new(tp: &'a TypeDescriptor, buf: &'a [u8]) -> Self { use TypeDescriptor::*; debug_assert_eq!(tp.size(), buf.len()); @@ -695,10 +704,10 @@ impl<'a> DynValue<'a> { Integer(size) | Unsigned(size) => DynInteger::read(buf, true, *size).into(), Float(size) => DynFloat::read(buf, *size).into(), Boolean => DynScalar::Boolean(read_raw(buf)).into(), - Enum(ref tp) => DynEnum::new(tp, DynInteger::read(buf, tp.signed, tp.size)).into(), - Compound(ref tp) => DynCompound::new(tp, buf).into(), - FixedArray(ref tp, n) => DynArray::new(tp, buf, Some(*n)).into(), - VarLenArray(ref tp) => DynArray::new(tp, buf, None).into(), + Enum(tp) => DynEnum::new(tp, DynInteger::read(buf, tp.signed, tp.size)).into(), + Compound(tp) => DynCompound::new(tp, buf).into(), + FixedArray(tp, n) => DynArray::new(tp, buf, Some(*n)).into(), + VarLenArray(tp) => DynArray::new(tp, buf, None).into(), FixedAscii(_) => DynFixedString::new(buf, false).into(), FixedUnicode(_) => DynFixedString::new(buf, true).into(), VarLenAscii => DynVarLenString::new(buf, false).into(), @@ -749,12 +758,14 @@ impl Display for DynValue<'_> { } } +/// An owned value with dynamic type. pub struct OwnedDynValue { tp: TypeDescriptor, buf: Box<[u8]>, } impl OwnedDynValue { + /// Constructs a new `OwnedDynValue` from the given value. pub fn new(value: T) -> Self { let ptr = (&value as *const T).cast::(); let len = mem::size_of_val(&value); @@ -763,10 +774,12 @@ impl OwnedDynValue { Self { tp: T::type_descriptor(), buf: buf.to_owned().into_boxed_slice() } } - pub fn get(&self) -> DynValue { + /// Returns a borrowed version of the contained value. + pub fn get(&self) -> DynValue<'_> { DynValue::new(&self.tp, &self.buf) } + /// Returns the value's type descriptor. pub fn type_descriptor(&self) -> &TypeDescriptor { &self.tp } @@ -781,9 +794,12 @@ impl OwnedDynValue { Self { tp, buf } } - /// Cast to the concrete type + /// Tries to downcast the value to a concrete type. + /// + /// # Errors /// - /// Will fail if the type-descriptors are not equal + /// If the type descriptors of `self` and `T` are not equal, this will fail and return a + /// `Result::Err` containing the original value. pub fn cast(mut self) -> Result { use mem::MaybeUninit; if self.tp != T::type_descriptor() { diff --git a/hdf5-types/src/h5type.rs b/hdf5-types/src/h5type.rs index ad18b7e42..00aa2f1b0 100644 --- a/hdf5-types/src/h5type.rs +++ b/hdf5-types/src/h5type.rs @@ -14,15 +14,21 @@ pub(crate) struct hvl_t { pub ptr: *mut c_void, } +/// A valid integer size. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum IntSize { + /// 1 byte. U1 = 1, + /// 2 bytes. U2 = 2, + /// 4 bytes. U4 = 4, + /// 8 bytes. U8 = 8, } impl IntSize { + /// Returns an `IntSize` of `size` bytes, or `None` if `size` is invalid. pub const fn from_int(size: usize) -> Option { if size == 1 { Some(Self::U1) @@ -38,15 +44,20 @@ impl IntSize { } } +/// A valid floating-point number size. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum FloatSize { + /// 2 bytes. #[cfg(feature = "f16")] U2 = 2, + /// 4 bytes. U4 = 4, + /// 8 bytes. U8 = 8, } impl FloatSize { + /// Returns a `FloatSize` of `size` bytes, or `None` if `size` is invalid. pub const fn from_int(size: usize) -> Option { #[cfg(feature = "f16")] { @@ -62,20 +73,28 @@ impl FloatSize { } } +/// A descriptor for an enumeration datatype member. #[derive(Clone, Debug, PartialEq, Eq)] pub struct EnumMember { + /// The name of the member. pub name: String, + /// The value of the member. pub value: u64, } +/// A descriptor for an enumeration datatype. #[derive(Clone, Debug, PartialEq, Eq)] pub struct EnumType { + /// The size of the underlying integer type. pub size: IntSize, + /// Whether to use a signed integer. pub signed: bool, + /// The enumeration datatype members. pub members: Vec, } impl EnumType { + /// Returns the type descriptor of the underlying integer datatype. #[inline] pub fn base_type(&self) -> TypeDescriptor { if self.signed { @@ -86,31 +105,42 @@ impl EnumType { } } +/// A descriptor for a compound datatype field. #[derive(Clone, Debug, PartialEq, Eq)] pub struct CompoundField { + /// The name of the field. pub name: String, + /// The type of the field. pub ty: TypeDescriptor, + /// The byte offset of the field. pub offset: usize, + /// The ordering of the field within the compound type. pub index: usize, } impl CompoundField { + /// Creates a new `CompoundField`. pub fn new(name: &str, ty: TypeDescriptor, offset: usize, index: usize) -> Self { Self { name: name.to_owned(), ty, offset, index } } + /// Creates a new `CompoundField` for a concrete type. pub fn typed(name: &str, offset: usize, index: usize) -> Self { Self::new(name, T::type_descriptor(), offset, index) } } +/// A descriptor for a compound datatype. #[derive(Clone, Debug, PartialEq, Eq)] pub struct CompoundType { + /// The fields of the datatype. pub fields: Vec, + /// The size in bytes of the datatype. pub size: usize, } impl CompoundType { + /// Converts `self` to a C struct representation. pub fn to_c_repr(&self) -> Self { let mut layout = self.clone(); layout.fields.sort_by_key(|f| f.index); @@ -133,6 +163,7 @@ impl CompoundType { layout } + /// Converts `self` to a packed representation. pub fn to_packed_repr(&self) -> Self { let mut layout = self.clone(); layout.fields.sort_by_key(|f| f.index); @@ -146,19 +177,32 @@ impl CompoundType { } } +/// A descriptor for an HDF5 datatype. #[derive(Clone, Debug, PartialEq, Eq)] pub enum TypeDescriptor { + /// A signed integer. Integer(IntSize), + /// An unsigned integer. Unsigned(IntSize), + /// A floating-point number. Float(FloatSize), + /// A boolean value. Boolean, + /// An enumeration datatype. Enum(EnumType), + /// A compound datatype. Compound(CompoundType), + /// A fixed-length array. FixedArray(Box, usize), + /// A fixed-length ASCII string. FixedAscii(usize), + /// A fixed-length UTF-8 string. FixedUnicode(usize), + /// A variable-length array. VarLenArray(Box), + /// A variable-length ASCII string. VarLenAscii, + /// A variable-length UTF-8 string. VarLenUnicode, Reference(Reference), } @@ -179,12 +223,12 @@ impl Display for TypeDescriptor { TypeDescriptor::Float(FloatSize::U4) => write!(f, "float32"), TypeDescriptor::Float(FloatSize::U8) => write!(f, "float64"), TypeDescriptor::Boolean => write!(f, "bool"), - TypeDescriptor::Enum(ref tp) => write!(f, "enum ({})", tp.base_type()), - TypeDescriptor::Compound(ref tp) => write!(f, "compound ({} fields)", tp.fields.len()), - TypeDescriptor::FixedArray(ref tp, n) => write!(f, "[{}; {}]", tp, n), - TypeDescriptor::FixedAscii(n) => write!(f, "string (len {})", n), - TypeDescriptor::FixedUnicode(n) => write!(f, "unicode (len {})", n), - TypeDescriptor::VarLenArray(ref tp) => write!(f, "[{}] (var len)", tp), + TypeDescriptor::Enum(tp) => write!(f, "enum ({})", tp.base_type()), + TypeDescriptor::Compound(tp) => write!(f, "compound ({} fields)", tp.fields.len()), + TypeDescriptor::FixedArray(tp, n) => write!(f, "[{tp}; {n}]"), + TypeDescriptor::FixedAscii(n) => write!(f, "string (len {n})"), + TypeDescriptor::FixedUnicode(n) => write!(f, "unicode (len {n})"), + TypeDescriptor::VarLenArray(tp) => write!(f, "[{tp}] (var len)"), TypeDescriptor::VarLenAscii => write!(f, "string (var len)"), TypeDescriptor::VarLenUnicode => write!(f, "unicode (var len)"), TypeDescriptor::Reference(Reference::Object) => write!(f, "reference (object)"), @@ -196,6 +240,7 @@ impl Display for TypeDescriptor { } impl TypeDescriptor { + /// Returns the size of the [`TypeDescriptor`] variant in bytes pub fn size(&self) -> usize { match *self { Self::Integer(size) | Self::Unsigned(size) => size as _, @@ -223,6 +268,7 @@ impl TypeDescriptor { } } + /// Converts `self` to a C-compatible representation. pub fn to_c_repr(&self) -> Self { match *self { Self::Compound(ref compound) => Self::Compound(compound.to_c_repr()), @@ -232,6 +278,7 @@ impl TypeDescriptor { } } + /// Converts `self` to a packed representation. pub fn to_packed_repr(&self) -> Self { match *self { Self::Compound(ref compound) => Self::Compound(compound.to_packed_repr()), @@ -242,7 +289,18 @@ impl TypeDescriptor { } } +/// A type that can be represented as an HDF5 datatype. +/// +/// # Derivable +/// This trait can be used with `#[derive]`. +/// +/// To `derive` on structs, they must be one of: `repr(C)`, `repr(packed)`, or `repr(transparent)`, +/// and have at least one field, all of which must implement `H5Type`. +/// +/// To `derive` on enums, they must have an explicit `repr` and at least one variant, all of which +/// must be unit variants with explicit discriminants. pub unsafe trait H5Type: 'static { + /// Returns a descriptor for an equivalent HDF5 datatype. fn type_descriptor() -> TypeDescriptor; } @@ -384,7 +442,7 @@ unsafe impl H5Type for VarLenUnicode { #[cfg(test)] pub mod tests { use super::TypeDescriptor as TD; - use super::{hvl_t, FloatSize, H5Type, IntSize}; + use super::{FloatSize, H5Type, IntSize, hvl_t}; use crate::array::VarLenArray; use crate::string::{FixedAscii, FixedUnicode, VarLenAscii, VarLenUnicode}; use std::mem; diff --git a/hdf5-types/src/lib.rs b/hdf5-types/src/lib.rs index a1daa8337..acd414748 100644 --- a/hdf5-types/src/lib.rs +++ b/hdf5-types/src/lib.rs @@ -3,15 +3,16 @@ #![allow(clippy::missing_const_for_fn)] #![allow(clippy::redundant_pub_crate)] #![allow(clippy::must_use_candidate)] +#![allow(unsafe_op_in_unsafe_fn)] //! Types that can be stored and retrieved from a `HDF5` dataset //! //! Crate features: //! * `h5-alloc`: Use the `hdf5` allocator for varlen types and dynamic values. -//! This is necessary on platforms which uses different allocators -//! in different libraries (e.g. dynamic libraries on windows), -//! or if `hdf5-c` is compiled with the MEMCHECKER option. -//! This option is forced on in the case of using a `windows` DLL. +//! This is necessary on platforms which uses different allocators +//! in different libraries (e.g. dynamic libraries on windows), +//! or if `hdf5-c` is compiled with the MEMCHECKER option. +//! This option is forced on in the case of using a `windows` DLL. #[cfg(test)] #[macro_use] @@ -54,6 +55,7 @@ pub(crate) unsafe fn free(ptr: *mut core::ffi::c_void) { } } +/// Whether this crate is using the HDF5 library for allocations instead of `libc`. pub const USING_H5_ALLOCATOR: bool = { cfg_if::cfg_if! { if #[cfg(any(feature = "h5-alloc", windows_dll))] { diff --git a/hdf5-types/src/string.rs b/hdf5-types/src/string.rs index 0efc278f3..42377237f 100644 --- a/hdf5-types/src/string.rs +++ b/hdf5-types/src/string.rs @@ -11,6 +11,7 @@ use std::str::{self, FromStr}; use ascii::{AsAsciiStr, AsAsciiStrError, AsciiStr}; +/// Errors that can occur when attempting to interpret a byte sequence as a string. #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[non_exhaustive] pub enum StringError { @@ -36,7 +37,7 @@ impl fmt::Display for StringError { StringError::InsufficientCapacity => { write!(f, "string error: insufficient capacity for fixed sized string") } - StringError::AsciiError(err) => write!(f, "string error: {}", err), + StringError::AsciiError(err) => write!(f, "string error: {err}"), } } } @@ -181,6 +182,7 @@ impl_string_traits!(VarLenUnicode); // ================================================================================ +/// A variable-length ASCII string. #[repr(C)] pub struct VarLenAscii { ptr: *mut u8, @@ -203,6 +205,7 @@ impl Clone for VarLenAscii { } impl VarLenAscii { + /// Creates a new empty `VarLenAscii` string. #[inline] pub fn new() -> Self { unsafe { @@ -220,6 +223,7 @@ impl VarLenAscii { Self { ptr } } + /// Returns the length of `self`. #[inline] pub fn len(&self) -> usize { if self.ptr.is_null() { @@ -230,16 +234,19 @@ impl VarLenAscii { unsafe { libc::strlen(self.ptr as *const _) } } + /// Returns `true` if `self` has a length of zero bytes. #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } + /// Returns a raw pointer to the string's buffer. #[inline] pub fn as_ptr(&self) -> *const u8 { self.ptr } + /// Returns the contents of the string as a byte slice. #[inline] pub fn as_bytes(&self) -> &[u8] { // Treat null as empty. This could be changed to option @@ -250,16 +257,28 @@ impl VarLenAscii { unsafe { slice::from_raw_parts(self.ptr as *const _, self.len()) } } + /// Returns the contents of the string as a string slice. #[inline] pub fn as_str(&self) -> &str { unsafe { str::from_utf8_unchecked(self.as_bytes()) } } + /// Converts a byte slice into a `VarLenAscii` without checking that the string contains only + /// non-zero ASCII bytes. + /// + /// # Safety + /// + /// The bytes must be valid ASCII and non-zero. #[inline] pub unsafe fn from_ascii_unchecked>(bytes: &B) -> Self { Self::from_bytes(bytes.as_ref()) } + /// Converts a byte slice into a `VarLenAscii`. + /// + /// # Errors + /// + /// Returns `Err` if the bytes are not valid ASCII, or if any byte is zero. pub fn from_ascii>(bytes: &B) -> Result { let bytes = bytes.as_ref(); if !bytes.iter().all(|&c| c != 0) { @@ -299,6 +318,7 @@ unsafe impl Sync for VarLenAscii {} // ================================================================================ +/// A variable-length UTF-8 string. #[repr(C)] pub struct VarLenUnicode { ptr: *mut u8, @@ -321,6 +341,7 @@ impl Clone for VarLenUnicode { } impl VarLenUnicode { + /// Creates a new empty `VarLenUnicode` string. #[inline] pub fn new() -> Self { unsafe { @@ -348,21 +369,25 @@ impl VarLenUnicode { libc::strlen(self.ptr as *const _) } + /// Returns the length of `self`. #[inline] pub fn len(&self) -> usize { self.as_str().len() } + /// Returns `true` if `self` has a length of zero bytes. #[inline] pub fn is_empty(&self) -> bool { unsafe { self.raw_len() == 0 } } + /// Returns a raw pointer to the string's buffer. #[inline] pub fn as_ptr(&self) -> *const u8 { self.ptr } + /// Returns the contents of the string as a byte slice. #[inline] pub fn as_bytes(&self) -> &[u8] { // Treat null as empty. This could be changed to option @@ -373,11 +398,18 @@ impl VarLenUnicode { unsafe { slice::from_raw_parts(self.ptr as *const _, self.raw_len()) } } + /// Returns the contents of the string as a string slice. #[inline] pub fn as_str(&self) -> &str { unsafe { str::from_utf8_unchecked(self.as_bytes()) } } + /// Converts a byte slice into a `VarLenUnicode` without checking that the string contains only + /// non-zero UTF-8 bytes. + /// + /// # Safety + /// + /// The bytes must be valid UTF-8 and non-zero. #[inline] pub unsafe fn from_str_unchecked>(s: S) -> Self { Self::from_bytes(s.borrow().as_bytes()) @@ -403,6 +435,7 @@ unsafe impl Sync for VarLenUnicode {} // ================================================================================ +/// A fixed-length ASCII string. #[repr(C)] #[derive(Copy, Clone)] pub struct FixedAscii { @@ -410,6 +443,7 @@ pub struct FixedAscii { } impl FixedAscii { + /// Creates a new empty `FixedAscii` string. #[inline] pub fn new() -> Self { unsafe { Self { buf: mem::zeroed() } } @@ -428,41 +462,59 @@ impl FixedAscii { unsafe { slice::from_raw_parts(self.buf.as_ptr(), N) } } + /// Returns the string's capacity in bytes. #[inline] pub const fn capacity() -> usize { N } + /// Returns the length of `self`. #[inline] pub fn len(&self) -> usize { self.as_raw_slice().iter().rev().skip_while(|&c| *c == 0).count() } + /// Returns `true` if `self` has a length of zero bytes. #[inline] pub fn is_empty(&self) -> bool { self.as_raw_slice().iter().all(|&c| c == 0) } + /// Returns a raw pointer to the string's buffer. #[inline] pub fn as_ptr(&self) -> *const u8 { self.buf.as_ptr() } + /// Returns the contents of the string as a byte slice. #[inline] pub fn as_bytes(&self) -> &[u8] { &self.as_raw_slice()[..self.len()] } + /// Returns the contents of the string as a string slice. #[inline] pub fn as_str(&self) -> &str { unsafe { str::from_utf8_unchecked(self.as_bytes()) } } + /// Converts a byte slice into a `FixedAscii` without checking that the string is valid ASCII, + /// and truncating at the type's capacity. + /// + /// # Safety + /// + /// The bytes must be valid ASCII. #[inline] pub unsafe fn from_ascii_unchecked>(bytes: &B) -> Self { Self::from_bytes(bytes.as_ref()) } + /// Converts a byte slice into a `FixedAscii`. + /// + /// # Errors + /// + /// Returns `Err` if the bytes are not valid ASCII or if the slice length is greater than the + /// type's capacity. pub fn from_ascii>(bytes: &B) -> Result { let bytes = bytes.as_ref(); if bytes.len() > N { @@ -497,6 +549,7 @@ impl AsAsciiStr for FixedAscii { // ================================================================================ +/// A fixed-length UTF-8 string. #[repr(C)] #[derive(Copy, Clone)] pub struct FixedUnicode { @@ -504,6 +557,7 @@ pub struct FixedUnicode { } impl FixedUnicode { + /// Creates a new empty `FixedUnicode` string. #[inline] pub fn new() -> Self { unsafe { Self { buf: mem::zeroed() } } @@ -527,36 +581,48 @@ impl FixedUnicode { self.as_raw_slice().iter().rev().skip_while(|&c| *c == 0).count() } + /// Returns the string's capacity in bytes. #[inline] pub const fn capacity() -> usize { N } + /// Returns the length of `self`. #[inline] pub fn len(&self) -> usize { self.as_str().len() } + /// Returns `true` if `self` has a length of zero bytes. #[inline] pub fn is_empty(&self) -> bool { self.raw_len() == 0 } + /// Returns a raw pointer to the string's buffer. #[inline] pub fn as_ptr(&self) -> *const u8 { self.buf.as_ptr() } + /// Returns the contents of the string as a byte slice. #[inline] pub fn as_bytes(&self) -> &[u8] { &self.as_raw_slice()[..self.raw_len()] } + /// Returns the contents of the string as a string slice. #[inline] pub fn as_str(&self) -> &str { unsafe { str::from_utf8_unchecked(self.as_bytes()) } } + /// Converts a byte slice into a `FixedUnicode` without checking that the string is valid UTF-8, + /// and truncating at the type's capacity. + /// + /// # Safety + /// + /// The bytes must be valid UTF-8. #[inline] pub unsafe fn from_str_unchecked>(s: S) -> Self { Self::from_bytes(s.borrow().as_bytes()) diff --git a/hdf5/Cargo.toml b/hdf5/Cargo.toml index 6733e9ff3..9d88eb0de 100644 --- a/hdf5/Cargo.toml +++ b/hdf5/Cargo.toml @@ -4,7 +4,7 @@ readme = "../README.md" description = "Thread-safe Rust bindings for the HDF5 library." build = "build.rs" categories = ["science", "filesystem"] -version = "0.10.1" # !V +version = "0.14.1" # !V rust-version.workspace = true authors.workspace = true keywords.workspace = true @@ -39,6 +39,8 @@ mpio = ["dep:mpi-sys", "hdf5-sys/mpio"] complex = ["hdf5-types/complex"] # Enable float16 type support. f16 = ["hdf5-types/f16"] +# Enable ZFP Support +zfp = ["dep:zfp-sys"] # The features with version numbers such as 1.10.3, 1.12.0 are metafeatures # and is only available when the HDF5 library is at least this version. @@ -51,12 +53,12 @@ bitflags = "2.4" blosc-sys = { version = "0.3", package = "blosc-src", optional = true } cfg-if = { workspace = true } errno = { version = "0.3", optional = true } -lazy_static = "1.4" libc = { workspace = true } lzf-sys = { version = "0.1", optional = true } mpi-sys = { workspace = true, optional = true } -ndarray = ">=0.15, <=0.16" -paste = "1.0" +ndarray = ">=0.15, <=0.17" +pastey = "0.2.3" +zfp-sys = {version= "0.4.2", optional= true } # internal hdf5-derive = { workspace = true } hdf5-sys = { workspace = true } @@ -66,13 +68,11 @@ hdf5-types = { workspace = true } half = { workspace = true } num-complex = { workspace = true } parking_lot = "0.12.3" -paste = "1.0" pretty_assertions = "1.4" -rand = { version = "0.9", features = ["small_rng"] } +rand = { version = "0.10" } regex = { workspace = true } scopeguard = "1.2" tempfile = "3.9" [package.metadata.docs.rs] -features = ["static", "zlib", "blosc", "lzf", "f16", "complex"] -rustdoc-args = ["--cfg", "docsrs"] +features = ["static", "zlib", "blosc", "lzf", "f16", "complex","zfp"] diff --git a/hdf5/build.rs b/hdf5/build.rs index eea9ddc04..f4ea42a52 100644 --- a/hdf5/build.rs +++ b/hdf5/build.rs @@ -16,10 +16,13 @@ impl Version { fn known_hdf5_versions() -> Vec { // Keep up to date with known_hdf5_versions in hdf5-sys let mut vs = Vec::new(); + vs.extend((0..=0).map(|v| Version::new(2, 2, v))); // 2.2.[0] + vs.extend((0..=1).map(|v| Version::new(2, 1, v))); // 2.1.[0-1] + vs.extend((0..=0).map(|v| Version::new(2, 0, v))); // 2.0.[0] vs.extend((5..=21).map(|v| Version::new(1, 8, v))); // 1.8.[5-23] vs.extend((0..=8).map(|v| Version::new(1, 10, v))); // 1.10.[0-10] vs.extend((0..=2).map(|v| Version::new(1, 12, v))); // 1.12.[0-2] - vs.extend((0..=5).map(|v| Version::new(1, 14, v))); // 1.14.[0-5] + vs.extend((0..=6).map(|v| Version::new(1, 14, v))); // 1.14.[0-6] vs } @@ -35,8 +38,8 @@ fn main() { } println!("cargo::rustc-check-cfg=cfg(msvc_dll_indirection)"); - let print_feature = |key: &str| println!("cargo::rustc-cfg=feature=\"{}\"", key); - let print_cfg = |key: &str| println!("cargo::rustc-cfg={}", key); + let print_feature = |key: &str| println!("cargo::rustc-cfg=feature=\"{key}\""); + let print_cfg = |key: &str| println!("cargo::rustc-cfg={key}"); for (key, _) in env::vars() { match key.as_str() { // public features diff --git a/hdf5/examples/continuously_rw_1d.rs b/hdf5/examples/continuously_rw_1d.rs new file mode 100644 index 000000000..8c68b8f50 --- /dev/null +++ b/hdf5/examples/continuously_rw_1d.rs @@ -0,0 +1,90 @@ +//! Create, and continuously write several chunks to a chunked 1D dataset +//! +//! Finally read it, get the chunking metadata, and use it to read slices aligned to the chunk size + +#[cfg(feature = "1.10.5")] +fn main() -> hdf5_metno::Result<()> { + example::write_hdf5()?; + example::read_hdf5()?; + Ok(()) +} + +#[cfg(not(feature = "1.10.5"))] +fn main() { + println!( + "needs version 1.10.5 or later for querying the number of chunks with H5Dget_num_chunks" + ); +} + +#[cfg(feature = "1.10.5")] +mod example { + use hdf5::{File, Result}; + use hdf5_metno::{self as hdf5, Extent}; + use ndarray::Array1; + + // Shared between read_hdf5() and write_hdf5(), otherwise they only rely on file metadata + const FILE_NAME: &str = "continuous_chunks.h5"; + const DATASET_NAME: &str = "count"; + + const CHUNK_SIZE: usize = 5; + const NUM_CHUNKS: usize = 3; // total chunks to write + + pub fn write_hdf5() -> Result<()> { + println!("Creating file '{FILE_NAME}' with 1D resizable dataset"); + let file = File::create(FILE_NAME)?; + let shape = Extent::resizable(0); // 1D resizable + let ds = + file.new_dataset::().chunk((CHUNK_SIZE,)).shape(shape).create(DATASET_NAME)?; + + // Simulate continuously accumulating data in a buffer + // and writing it to the dataset anytime there's enough to fill a chunk + let mut buf = Vec::with_capacity(CHUNK_SIZE); + for i in 0..NUM_CHUNKS * CHUNK_SIZE { + buf.push(i); + if buf.len() == CHUNK_SIZE { + let current_size = ds.size(); + println!("[{i}] writing new chunk, current size = {current_size}"); + ds.resize((current_size + CHUNK_SIZE,))?; + ds.write_slice(&buf, current_size..current_size + CHUNK_SIZE)?; + buf.clear(); + } + } + + Ok(()) + } + + pub fn read_hdf5() -> Result<()> { + println!("Reading file '{FILE_NAME}'"); + let file = File::open(FILE_NAME)?; + let ds = file.dataset(DATASET_NAME)?; + + // Check shape + let shape = ds.shape(); + println!("Dataset shape: {:?}", shape); + assert_eq!(shape, &[CHUNK_SIZE * NUM_CHUNKS]); + + // Get chunking metadata + let chunk_size = ds.chunk().unwrap()[0]; + println!("Chunk size: {chunk_size}"); + assert_eq!(chunk_size, CHUNK_SIZE); + + let num_chunks = ds.num_chunks().unwrap(); + println!("Number of chunks: {num_chunks}"); + assert_eq!(num_chunks, NUM_CHUNKS); + + // Read the dataset chunk for chunk + for chunk_idx in 0..num_chunks { + let chunk_start = chunk_idx * chunk_size; + let arr: Array1 = ds.read_slice(chunk_start..chunk_start + chunk_size)?; + println!("Dataset Chunk #{chunk_idx}: {arr:?}"); + assert_eq!(arr, Array1::from_iter(chunk_start..chunk_start + chunk_size)); + } + + // Read the full dataset in one go + let arr: Array1 = ds.read_1d()?; + println!("Full dataset: {arr:?}"); + assert_eq!(arr, Array1::from_iter(0..CHUNK_SIZE * NUM_CHUNKS)); + + Ok(()) + } +} diff --git a/hdf5/examples/swmr.rs b/hdf5/examples/swmr.rs new file mode 100644 index 000000000..b2912f6e7 --- /dev/null +++ b/hdf5/examples/swmr.rs @@ -0,0 +1,49 @@ +#[cfg(feature = "1.10.2")] +use hdf5_metno as hdf5; + +#[cfg(feature = "1.10.2")] +fn reader() { + let file = hdf5::File::open_as("swmr.h5", hdf5::OpenMode::ReadSWMR).unwrap(); + println!("Reader: Opened file"); + let var = file.dataset("foo").unwrap(); + + for _ in 0..5 { + var.refresh().unwrap(); + let shape = var.shape(); + println!("Reader: Got shape: {shape:?}"); + // If reading one should use the shape directly without + // using the convenience read_2d etc. functions which + // might get confused if the shape is changed during reading + std::thread::sleep(std::time::Duration::from_secs(5)); + } +} + +fn main() { + #[cfg(not(feature = "1.10.2"))] + println!("This examples requires hdf5 >= 1.10.2 to enable SWMR and set libver_bounds"); + + #[cfg(feature = "1.10.2")] + { + let file = hdf5::File::with_options() + .with_fapl(|fapl| fapl.libver_v110()) + .create("swmr.h5") + .unwrap(); + + let var = file.new_dataset::().shape((0.., 5)).create("foo").unwrap(); + + file.start_swmr().unwrap(); + println!("Writer: Wrote file"); + + let thread = std::thread::spawn(|| reader()); + + for i in 0..5 { + var.resize((i + 1, 5)).unwrap(); + var.write_slice(&[i, i, i, i, i], (i, 0..)).unwrap(); + var.flush().unwrap(); + println!("Writer: Wrote {i}"); + std::thread::sleep(std::time::Duration::from_secs(5)); + } + + thread.join().unwrap(); + } +} diff --git a/hdf5/src/class.rs b/hdf5/src/class.rs index 66a2c5314..2fcabdf07 100644 --- a/hdf5/src/class.rs +++ b/hdf5/src/class.rs @@ -81,6 +81,20 @@ pub trait ObjectClass: Sized { } } +/// Takes ownership of an object via its identifier. +/// +/// # Errors +/// +/// Returns an error if `id` does not refer to an object of type `T`. +/// +/// # Safety +/// +/// This should only be called with an identifier obtained from an object constructor in the HDF5 C +/// library. The reference count of a newly created object is 1, so this function creates a handle +/// for the object without incrementing its reference count. +/// +/// This function is unsafe because improper use may lead to the object being closed before all its +/// handles are dropped. pub unsafe fn from_id(id: hid_t) -> Result { T::from_id(id) } diff --git a/hdf5/src/dim.rs b/hdf5/src/dim.rs index 9b1bf82b1..c4b550f2d 100644 --- a/hdf5/src/dim.rs +++ b/hdf5/src/dim.rs @@ -11,11 +11,7 @@ pub trait Dimension { fn size(&self) -> Ix { let dims = self.dims(); - if dims.is_empty() { - 1 - } else { - dims.iter().product() - } + if dims.is_empty() { 1 } else { dims.iter().product() } } } diff --git a/hdf5/src/error.rs b/hdf5/src/error.rs index e350a7294..44cb7a76e 100644 --- a/hdf5/src/error.rs +++ b/hdf5/src/error.rs @@ -8,11 +8,13 @@ use std::ptr::{self, addr_of_mut}; use ndarray::ShapeError; +use crate::error_codes::{MajorErrorCode, MinorErrorCode}; + #[cfg(not(feature = "1.10.0"))] use hdf5_sys::h5::hssize_t; use hdf5_sys::h5e::{ - H5E_auto2_t, H5E_error2_t, H5Eget_current_stack, H5Eget_msg, H5Eprint2, H5Eset_auto2, H5Ewalk2, - H5E_DEFAULT, H5E_WALK_DOWNWARD, + H5E_DEFAULT, H5E_WALK_DOWNWARD, H5E_auto2_t, H5E_error2_t, H5Eget_current_stack, H5Eget_msg, + H5Eprint2, H5Eset_auto2, H5Ewalk2, }; use crate::internal_prelude::*; @@ -35,6 +37,7 @@ pub fn silence_errors(silence: bool) { h5lock!(silence_errors_no_sync(silence)); } +/// A stack of error records from an HDF5 library call. #[repr(transparent)] #[derive(Clone)] pub struct ErrorStack(Handle); @@ -80,7 +83,13 @@ impl ErrorStack { let (desc, func) = (string_from_cstr(e.desc), string_from_cstr(e.func_name)); let major = get_h5_str(|m, s| H5Eget_msg(e.maj_num, ptr::null_mut(), m, s))?; let minor = get_h5_str(|m, s| H5Eget_msg(e.min_num, ptr::null_mut(), m, s))?; - Ok(ErrorFrame::new(&desc, &func, &major, &minor)) + let major_code = MajorErrorCode::from_id(e.maj_num); + let minor_code = MinorErrorCode::from_id(e.min_num); + let line = e.line.try_into().unwrap(); + let file_name = string_from_cstr(e.file_name); + Ok(ErrorFrame::new( + &desc, &func, &major, &minor, major_code, minor_code, line, file_name, + )) }; match closure(*err_desc) { Ok(frame) => { @@ -107,6 +116,7 @@ impl ErrorStack { } } +/// An error record for an HDF5 library call. #[derive(Clone, Debug)] pub struct ErrorFrame { desc: String, @@ -114,32 +124,72 @@ pub struct ErrorFrame { major: String, minor: String, description: String, + major_code: MajorErrorCode, + minor_code: MinorErrorCode, + line: usize, + file_name: String, } impl ErrorFrame { - pub(crate) fn new(desc: &str, func: &str, major: &str, minor: &str) -> Self { + pub(crate) fn new( + desc: &str, func: &str, major: &str, minor: &str, major_code: MajorErrorCode, + minor_code: MinorErrorCode, line: usize, file_name: String, + ) -> Self { Self { desc: desc.into(), func: func.into(), major: major.into(), minor: minor.into(), description: format!("{func}(): {desc}"), + major_code, + minor_code, + line, + file_name: file_name.into(), } } + /// Returns the error description. pub fn desc(&self) -> &str { self.desc.as_ref() } + /// Returns the function name. + pub fn func(&self) -> &str { + self.func.as_ref() + } + + /// Returns the major code: which part of the library failed. + pub fn major_code(&self) -> MajorErrorCode { + self.major_code + } + + /// Returns the minor code: how the operation failed. + pub fn minor_code(&self) -> MinorErrorCode { + self.minor_code + } + + /// Returns a message with the error description and the relevant function name. pub fn description(&self) -> &str { self.description.as_ref() } + /// Returns the line in the source which triggered the error. + pub fn line(&self) -> usize { + self.line + } + + /// Returns the file name in the source which triggered the error. + pub fn file_name(&self) -> &str { + self.file_name.as_ref() + } + + /// Returns a message with the error description and the relevant function name. pub fn detail(&self) -> Option { Some(format!("Error in {}(): {} [{}: {}]", self.func, self.desc, self.major, self.minor)) } } +/// A converted [`ErrorStack`] with methods to access [`ErrorFrame`] data. #[derive(Clone, Debug)] pub struct ExpandedErrorStack { frames: Vec, @@ -167,21 +217,43 @@ impl ExpandedErrorStack { pub(crate) fn push(&mut self, frame: ErrorFrame) { self.frames.push(frame); - if !self.is_empty() { - let top_desc = self.frames[0].description().to_owned(); - if self.len() == 1 { - self.description = Some(top_desc); - } else { - self.description = - Some(format!("{}: {}", top_desc, self.frames[self.len() - 1].desc())); - } + + self.description = match self.frames[..] { + [] => unreachable!(), + [ref first] => Some(first.description().to_owned()), + [ref first, .., ref last] => Some(format!("{}: {}", first.description(), last.desc())), } } + /// Returns the top [`ErrorFrame`] of the stack, or `None` if it is empty. pub fn top(&self) -> Option<&ErrorFrame> { self.first() } + /// Returns the major code of every frame, outermost first. + pub fn major_codes(&self) -> impl DoubleEndedIterator + '_ { + self.frames.iter().map(ErrorFrame::major_code) + } + + /// Returns the minor code of every frame, outermost first. + pub fn minor_codes(&self) -> impl DoubleEndedIterator + '_ { + self.frames.iter().map(ErrorFrame::minor_code) + } + + /// Returns whether any frame in the stack carries the given major code. + pub fn contains_major(&self, code: MajorErrorCode) -> bool { + self.major_codes().any(|c| c == code) + } + + /// Returns whether any frame in the stack carries the given minor code. + /// + /// The cause is usually on an inner frame, so prefer this over [`top`](Self::top): opening + /// a corrupt file reports `CantOpenFile` on the outermost frame and `NotHdf5` further down. + pub fn contains_minor(&self, code: MinorErrorCode) -> bool { + self.minor_codes().any(|c| c == code) + } + + /// Returns the description of the error on top of the stack. pub fn description(&self) -> &str { match self.description { None => "unknown library error", @@ -189,6 +261,7 @@ impl ExpandedErrorStack { } } + /// Returns a detailed message for the error on top of the stack, or `None` if it is empty. pub fn detail(&self) -> Option { self.top().and_then(ErrorFrame::detail) } @@ -217,6 +290,37 @@ impl Error { Err(Self::Internal("Could not get errorstack".to_owned())) } } + + /// Returns the expanded error stack, or `None` if there is none to expand. + pub fn stack(&self) -> Option { + match *self { + Self::Internal(_) => None, + Self::HDF5(ref stack) => stack.clone().expand().ok(), + } + } + + /// Returns whether any frame carries the given major code, `false` for + /// [`Internal`](Self::Internal) errors. + pub fn contains_major(&self, code: MajorErrorCode) -> bool { + self.stack().is_some_and(|s| s.contains_major(code)) + } + + /// Returns whether any frame carries the given minor code, `false` for + /// [`Internal`](Self::Internal) errors. + /// + /// ```no_run + /// use hdf5_metno as hdf5; + /// use hdf5::MinorErrorCode; + /// + /// # let dir = tempfile::tempdir().unwrap(); + /// # let path = dir.path().join("corrupt.h5"); + /// # std::fs::write(&path, b"not an HDF5 file").unwrap(); + /// let err = hdf5::File::open(&path).unwrap_err(); + /// assert!(err.contains_minor(MinorErrorCode::NotHdf5)); + /// ``` + pub fn contains_minor(&self, code: MinorErrorCode) -> bool { + self.stack().is_some_and(|s| s.contains_minor(code)) + } } impl From<&str> for Error { @@ -288,11 +392,7 @@ pub trait H5ErrorCode: Copy { fn is_err_code(value: Self) -> bool; fn h5check(value: Self) -> Result { - if Self::is_err_code(value) { - Err(Error::query().unwrap_or_else(|e| e)) - } else { - Ok(value) - } + if Self::is_err_code(value) { Err(Error::query().unwrap_or_else(|e| e)) } else { Ok(value) } } } @@ -332,11 +432,194 @@ impl H5ErrorCode for libc::ssize_t { pub mod tests { use hdf5_sys::h5p::{H5Pclose, H5Pcreate}; - use crate::globals::H5P_ROOT; + use crate::error_codes::{MAJOR_CODES, MINOR_CODES}; + use crate::globals::{H5E_CANTOPENFILE, H5E_FILE, H5P_ROOT}; use crate::internal_prelude::*; + use crate::test::with_tmp_path; + use crate::{MajorErrorCode, MinorErrorCode}; + use hdf5_sys::h5e::H5Eget_msg; + use std::fs; + use std::ptr; use super::ExpandedErrorStack; + /// Checks every code this crate declares against the linked HDF5 itself: the description + /// must be byte-identical to what `H5Eget_msg` returns for that id. This is what validates + /// the transcribed table, and it fails if a variant is wired to the wrong global, if a + /// description literal is wrong, or if a renamed code's `meta` gates overlap so that the + /// wrong arm is selected. + #[test] + pub fn test_descriptions_match_the_linked_library() { + assert!(!MAJOR_CODES.is_empty() && !MINOR_CODES.is_empty()); + for (&id, &code) in MAJOR_CODES.iter() { + assert_eq!(scrub(code.description().unwrap()), scrub(&msg_of(id)), "{code:?}"); + } + for (&id, &code) in MINOR_CODES.iter() { + // H5E_LOGFAIL is upstream's binary-compatibility alias for H5E_LOGGING and carries + // its own message. Both ids resolve to Logging, so only the canonical one can + // match, therefor we pin the alias's message (instead of skipping it). + #[cfg(all(feature = "1.10.5", not(feature = "1.12.0")))] + if id == *crate::globals::H5E_LOGFAIL { + assert_eq!(code, MinorErrorCode::Logging); + assert_eq!(msg_of(id), "old H5E_LOGGING_g (maintained for binary compatibility)"); + continue; + } + assert_eq!(scrub(code.description().unwrap()), scrub(&msg_of(id)), "{code:?}"); + } + } + + fn msg_of(id: hid_t) -> String { + h5lock!(get_h5_str(|m, s| H5Eget_msg(id, ptr::null_mut(), m, s))).unwrap() + } + + /// Replace typos and the "atom" -> "ID" rename so that the string comparisons are stable across versions + fn scrub(s: &str) -> String { + s.replace("atom", "ID") + .replace("accessability", "accessibility") + .replace("accessibilty", "accessibility") + } + + /// Checks the codes captured for a real frame vs. that frame's own text. + /// The test above checks the id -> description mapping, this one validates that `expand` + /// converts the right id to the right enum. + #[test] + pub fn test_frame_codes_agree_with_frame_text() { + with_tmp_path(|path| { + fs::write(&path, b"garbage data").unwrap(); + let err = File::open(&path).unwrap_err(); + let stack = err.stack().unwrap(); + assert!(stack.len() >= 3, "expected a multi-frame stack, got {}", stack.len()); + + for frame in stack.iter() { + // detail() renders "Error in f(): desc [: ]" from the strings H5Eget_msg returned. + let major_code_desc = frame.major_code().description().unwrap(); + let minor_code_desc = frame.minor_code().description().unwrap(); + let maj_min_desc = format!("[{major_code_desc}: {minor_code_desc}]"); + let detail = frame.detail().unwrap(); + assert!( + scrub(&detail).ends_with(&scrub(&maj_min_desc)), + "{detail:?} does not end with {maj_min_desc:?}" + ); + } + assert_eq!(stack.major_codes().count(), stack.len()); + assert_eq!(stack.minor_codes().count(), stack.len()); + }); + } + + /// Every id the linked HDF5 registers must map to exactly one variant, and no two variants + /// may share an id. The reverse is not asserted: a variant whose symbol this HDF5 lacks is + /// deliberately unreachable. + #[test] + pub fn test_symbol_mapping_is_unambiguous() { + for (&id, &code) in MAJOR_CODES.iter() { + assert!(MajorErrorCode::all().contains(&code), "{code:?} is not a declared variant"); + assert_ne!(id, H5I_INVALID_HID); + } + for (&id, &code) in MINOR_CODES.iter() { + assert!(MinorErrorCode::all().contains(&code), "{code:?} is not a declared variant"); + assert_ne!(id, H5I_INVALID_HID); + } + // the map is populated: these two exist in every supported HDF5 + assert_eq!(MajorErrorCode::from_id(*H5E_FILE), MajorErrorCode::File); + assert_eq!(MinorErrorCode::from_id(*H5E_CANTOPENFILE), MinorErrorCode::CantOpenFile); + } + + #[test] + pub fn test_unknown_error_code_is_preserved() { + // H5I_INVALID_HID is never a registered message id + let minor = MinorErrorCode::from_id(H5I_INVALID_HID); + assert_eq!(minor, MinorErrorCode::Other(H5I_INVALID_HID)); + assert_eq!(minor.name(), None); + assert_eq!(minor.description(), None); + assert_eq!(minor.to_string(), format!("unknown error code ({H5I_INVALID_HID})")); + + let major = MajorErrorCode::from_id(H5I_INVALID_HID); + assert_eq!(major, MajorErrorCode::Other(H5I_INVALID_HID)); + assert_eq!(major.name(), None); + assert_eq!(major.description(), None); + assert_eq!(major.to_string(), format!("unknown error code ({H5I_INVALID_HID})")); + + // a major id must not resolve as a minor code, or vice versa + assert_eq!(MinorErrorCode::from_id(*H5E_FILE), MinorErrorCode::Other(*H5E_FILE)); + assert_eq!( + MajorErrorCode::from_id(*H5E_CANTOPENFILE), + MajorErrorCode::Other(*H5E_CANTOPENFILE) + ); + } + + #[test] + pub fn test_error_codes_on_stack() { + let err = h5lock!({ + let plist_id = H5Pcreate(*H5P_ROOT); + H5Pclose(plist_id); + H5Pclose(plist_id); + Error::query() + }) + .unwrap(); + let stack = err.stack().unwrap(); + let top = stack.top().unwrap(); + assert_eq!(top.major_code(), MajorErrorCode::PropertyList); + assert_eq!(top.major_code().name(), Some("H5E_PLIST")); + assert_eq!(top.minor_code(), MinorErrorCode::CantFree); + assert!(err.contains_major(MajorErrorCode::PropertyList)); + assert!(!err.contains_major(MajorErrorCode::File)); + // codes are resolved for every frame, not just the top one + assert!(stack.major_codes().all(|c| !matches!(c, MajorErrorCode::Other(_)))); + assert!(stack.minor_codes().all(|c| !matches!(c, MinorErrorCode::Other(_)))); + } + + /// The three file-open failure modes must be distinguishable by code, not by message text. + #[test] + pub fn test_file_error_codes_are_distinguishable() { + // a corrupt file is reported as NotHdf5 by an inner frame + with_tmp_path(|path| { + fs::write(&path, b"garbage data").unwrap(); + let err = File::open_rw(&path).unwrap_err(); + assert!(err.contains_minor(MinorErrorCode::NotHdf5), "{err:?}"); + assert!(err.contains_major(MajorErrorCode::File)); + // the VOL frames in a file-open stack must resolve to a named code (not Other) + #[cfg(feature = "1.12.0")] + assert!(err.contains_major(MajorErrorCode::VirtualObjectLayer), "{err:?}"); + let stack = err.stack().unwrap(); + assert!(stack.major_codes().all(|c| !matches!(c, MajorErrorCode::Other(_))), "{err:?}"); + assert!(stack.minor_codes().all(|c| !matches!(c, MinorErrorCode::Other(_))), "{err:?}"); + }); + + // a missing file reports CantOpenFile + with_tmp_path(|path| { + let err = File::open_rw(&path).unwrap_err(); + assert!(err.contains_minor(MinorErrorCode::CantOpenFile), "{err:?}"); + assert!(!err.contains_minor(MinorErrorCode::NotHdf5), "{err:?}"); + }); + + // The specific minor code for creating over an existing file varies by HDF5 version and + // build (CantCreate, FileExists and CantOpenFile have all been seen), and the EEXIST is + // only visible as errno text in the innermost frame. So assert on what's stable: it is a + // File error and not a corrupt-superblock read + with_tmp_path(|path| { + File::create(&path).unwrap(); + let err = File::create_excl(&path).unwrap_err(); + let minors: Vec<_> = err.stack().unwrap().minor_codes().collect(); + assert!(err.contains_major(MajorErrorCode::File), "minors={minors:?}: {err}"); + assert!(!err.contains_minor(MinorErrorCode::NotHdf5), "minors={minors:?}: {err}"); + }); + } + + /// Internal errors carry no HDF5 stack. + #[test] + pub fn test_internal_error_has_no_codes() { + let err = Error::Internal("nope".into()); + assert!(err.stack().is_none()); + assert!(!err.contains_minor(MinorErrorCode::NotHdf5)); + assert!(!err.contains_major(MajorErrorCode::File)); + } + + #[test] + pub fn test_error_code_display() { + assert_eq!(MinorErrorCode::NotHdf5.to_string(), "Not an HDF5 file"); + assert_eq!(MajorErrorCode::File.to_string(), "File accessibility"); + } + #[test] pub fn test_error_stack() { let stack = h5lock!({ @@ -384,18 +667,18 @@ pub mod tests { #[cfg(not(feature = "1.14.0"))] { - assert_eq!(stack[stack.len() - 1].description(), "H5I_dec_ref(): can't locate ID"); + assert_eq!(stack.last().unwrap().description(), "H5I_dec_ref(): can't locate ID"); assert_eq!( - &stack[stack.len() - 1].detail().unwrap(), + &stack.last().unwrap().detail().unwrap(), "Error in H5I_dec_ref(): can't locate ID \ [Object atom: Unable to find atom information (already closed?)]" ); } #[cfg(feature = "1.14.0")] { - assert_eq!(stack[stack.len() - 1].description(), "H5I__dec_ref(): can't locate ID"); + assert_eq!(stack.last().unwrap().description(), "H5I__dec_ref(): can't locate ID"); assert_eq!( - &stack[stack.len() - 1].detail().unwrap(), + &stack.last().unwrap().detail().unwrap(), "Error in H5I__dec_ref(): can't locate ID \ [Object ID: Unable to find ID information (already closed?)]" ); diff --git a/hdf5/src/error_codes.rs b/hdf5/src/error_codes.rs new file mode 100644 index 000000000..d7816a720 --- /dev/null +++ b/hdf5/src/error_codes.rs @@ -0,0 +1,893 @@ +//! Major and minor error codes reported by the HDF5 library. +//! +//! Each [`ErrorFrame`](crate::ErrorFrame) carries a major code for which part of the library +//! failed and a minor code for how it failed. HDF5 assigns these ids at runtime, so this module +//! resolves them into enums that can be matched. +//! +//! Variants are not gated on the HDF5 version, so naming one never requires a `#[cfg]`. A code +//! the linked HDF5 does not define is never returned by `from_id`, and a code this crate does +//! not know becomes `Other`. +//! +//! Codes HDF5 renamed (but kept for backwards compatibility) map to one variant, so a match works on any +//! version: [`MinorErrorCode::BadId`] resolves from `H5E_BADID` or `H5E_BADATOM`, and +//! [`MinorErrorCode::Logging`] from `H5E_LOGGING` or `H5E_LOGFAIL`. +//! +//! Names, descriptions and version gates come from HDF5's [`H5err.txt`], which generates the +//! `H5E_*_g` symbols. To check a gate, diff that file between two release tags, e.g. +//! `hdf5-1_10_2` (no `H5E_CONTEXT`) against `hdf5-1_10_3` (has it). The [user guide] describes +//! major and minor codes. +//! +//! [`H5err.txt`]: https://github.com/HDFGroup/hdf5/blob/hdf5_2.1.0/src/H5err.txt +//! [user guide]: https://support.hdfgroup.org/documentation/hdf5/latest/_h5_e__u_g.html +//! +//! # Example +//! +//! ```no_run +//! use hdf5_metno as hdf5; +//! use hdf5::MinorErrorCode; +//! +//! # let dir = tempfile::tempdir().unwrap(); +//! # let path = dir.path().join("corrupt.h5"); +//! # std::fs::write(&path, b"definitely not an HDF5 file").unwrap(); +//! match hdf5::File::open(&path) { +//! Err(err) if err.contains_minor(MinorErrorCode::NotHdf5) => { +//! // the file exists but its superblock is unreadable +//! } +//! _ => {} +//! } +//! ``` + +use std::collections::HashMap; +use std::fmt::{self, Display}; +use std::sync::LazyLock; + +use hdf5_sys::h5i::hid_t; + +use crate::globals::*; + +/// Defines an error-code enum from three lists: +/// +/// - `variants`: the enum body, never gated (no need for `#[cfg]` downstream). +/// - `symbols`: `hid_t` to variant, gated on the version introducing the symbol. Renamed codes +/// contribute one entry per spelling. +/// - `meta`: variant to C identifier and description. Gated only for renamed codes, where the +/// arms must be mutually exclusive and total. +macro_rules! error_codes { + ( + $(#[$emeta:meta])* + $name:ident, $table:ident; + + variants { $( $(#[$vdoc:meta])* $variant:ident, )* } + symbols { $( $([$scfg:meta])? $global:ident => $svariant:ident, )* } + meta { $( $([$mcfg:meta])? $mvariant:ident => ($cname:literal, $desc:literal), )* } + ) => { + $(#[$emeta])* + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + #[non_exhaustive] + pub enum $name { + $( + $(#[$vdoc])* + $variant, + )* + /// A code reported by HDF5 that this crate does not know. + /// + /// The raw message id is only valid for the lifetime of the process. + Other(hid_t), + } + + // Initialised from inside `h5lock`, which is only safe because `sync` forces + // `LIBRARY_INIT` before taking `LOCK`, so dereferencing the globals below cannot + // reach for the lock a second time. + pub(crate) static $table: LazyLock> = LazyLock::new(|| { + let mut map = HashMap::new(); + $( + $(#[cfg($scfg)])? + map.insert(*$global, $name::$svariant); + )* + map + }); + + impl $name { + /// Resolves a raw HDF5 message id, or [`Other`](Self::Other) if unknown. + #[must_use] + pub fn from_id(id: hid_t) -> Self { + $table.get(&id).copied().unwrap_or(Self::Other(id)) + } + + /// The C identifier, e.g. `"H5E_CANTOPENFILE"`, as spelled by the linked HDF5. + /// + /// `None` only for [`Other`](Self::Other). + #[must_use] + pub fn name(self) -> Option<&'static str> { + match self { + $( $(#[cfg($mcfg)])? Self::$mvariant => Some($cname), )* + Self::Other(_) => None, + } + } + + /// The description HDF5 documents for this code, as worded by the linked HDF5. + /// + /// `None` only for [`Other`](Self::Other). + #[must_use] + pub fn description(self) -> Option<&'static str> { + match self { + $( $(#[cfg($mcfg)])? Self::$mvariant => Some($desc), )* + Self::Other(_) => None, + } + } + + /// Every code this crate knows, including any the linked HDF5 cannot report. + #[must_use] + pub fn all() -> &'static [Self] { + &[ $( Self::$variant, )* ] + } + } + + impl Display for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Self::Other(id) => write!(f, "unknown error code ({id})"), + ref code => f.write_str(code.description().unwrap_or("unknown error code")), + } + } + } + }; +} + +error_codes! { + /// The major error code of an HDF5 error frame. + MajorErrorCode, MAJOR_CODES; + + variants { + /// `H5E_ARGS`: Invalid arguments to routine + Args, + /// `H5E_ATTR`: Attribute + Attr, + /// `H5E_BTREE`: B-Tree node + BTree, + /// `H5E_CACHE`: Object cache + Cache, + /// `H5E_CONTEXT`: API Context + Context, + /// `H5E_DATASET`: Dataset + Dataset, + /// `H5E_DATASPACE`: Dataspace + Dataspace, + /// `H5E_DATATYPE`: Datatype + Datatype, + /// `H5E_ERROR`: Error API + ErrorApi, + /// `H5E_EVENTSET`: Event Set + EventSet, + /// `H5E_EARRAY`: Extensible Array + ExtensibleArray, + /// `H5E_EFL`: External file list + ExternalFileList, + /// `H5E_FILE`: File accessibility + File, + /// `H5E_FARRAY`: Fixed Array + FixedArray, + /// `H5E_FSPACE`: Free Space Manager + FreeSpace, + /// `H5E_FUNC`: Function entry/exit + Func, + /// `H5E_HEAP`: Heap + Heap, + /// `H5E_ID` (`H5E_ATOM` in older HDF5): Object ID + Id, + /// `H5E_INTERNAL`: Internal error (too specific to document in detail) + Internal, + /// `H5E_IO`: Low-level I/O + Io, + /// `H5E_LIB`: General library infrastructure + Lib, + /// `H5E_LINK`: Links + Link, + /// `H5E_MAP`: Map + Map, + /// `H5E_NONE_MAJOR`: No error + NoneMajor, + /// `H5E_OHDR`: Object header + ObjectHeader, + /// `H5E_PAGEBUF`: Page Buffering + PageBuffering, + /// `H5E_PLINE`: Data filters + Pipeline, + /// `H5E_PLUGIN`: Plugin for dynamically loaded library + Plugin, + /// `H5E_PLIST`: Property lists + PropertyList, + /// `H5E_RTREE`: R-Tree spatial index + RTree, + /// `H5E_RS`: Reference Counted Strings + RefCountedString, + /// `H5E_REFERENCE`: References + Reference, + /// `H5E_RESOURCE`: Resource unavailable + Resource, + /// `H5E_SOHM`: Shared Object Header Messages + SharedObjectHeaderMessage, + /// `H5E_SLIST`: Skip Lists + SkipList, + /// `H5E_STORAGE`: Data storage + Storage, + /// `H5E_SYM`: Symbol table + SymbolTable, + /// `H5E_TST`: Ternary Search Trees + TernarySearchTree, + /// `H5E_THREADSAFE`: Threadsafety + Threadsafety, + /// `H5E_VFL`: Virtual File Layer + VirtualFileLayer, + /// `H5E_VOL`: Virtual Object Layer + VirtualObjectLayer, + } + + symbols { + H5E_ARGS => Args, + H5E_ATTR => Attr, + H5E_BTREE => BTree, + H5E_CACHE => Cache, + [feature = "1.10.3"] H5E_CONTEXT => Context, + H5E_DATASET => Dataset, + H5E_DATASPACE => Dataspace, + H5E_DATATYPE => Datatype, + H5E_ERROR => ErrorApi, + [feature = "1.14.0"] H5E_EVENTSET => EventSet, + [feature = "1.10.0"] H5E_EARRAY => ExtensibleArray, + H5E_EFL => ExternalFileList, + H5E_FILE => File, + [feature = "1.10.0"] H5E_FARRAY => FixedArray, + H5E_FSPACE => FreeSpace, + H5E_FUNC => Func, + H5E_HEAP => Heap, + [not(feature = "1.14.0")] H5E_ATOM => Id, + [feature = "1.14.0"] H5E_ID => Id, + H5E_INTERNAL => Internal, + H5E_IO => Io, + [feature = "1.12.1"] H5E_LIB => Lib, + H5E_LINK => Link, + [feature = "1.12.0"] H5E_MAP => Map, + H5E_NONE_MAJOR => NoneMajor, + H5E_OHDR => ObjectHeader, + [feature = "1.10.1"] H5E_PAGEBUF => PageBuffering, + H5E_PLINE => Pipeline, + [feature = "1.8.11"] H5E_PLUGIN => Plugin, + H5E_PLIST => PropertyList, + [feature = "2.0.0"] H5E_RTREE => RTree, + H5E_RS => RefCountedString, + H5E_REFERENCE => Reference, + H5E_RESOURCE => Resource, + H5E_SOHM => SharedObjectHeaderMessage, + H5E_SLIST => SkipList, + H5E_STORAGE => Storage, + H5E_SYM => SymbolTable, + H5E_TST => TernarySearchTree, + [feature = "2.0.0"] H5E_THREADSAFE => Threadsafety, + H5E_VFL => VirtualFileLayer, + [feature = "1.12.0"] H5E_VOL => VirtualObjectLayer, + } + + meta { + Args => ("H5E_ARGS", "Invalid arguments to routine"), + Attr => ("H5E_ATTR", "Attribute"), + BTree => ("H5E_BTREE", "B-Tree node"), + Cache => ("H5E_CACHE", "Object cache"), + Context => ("H5E_CONTEXT", "API Context"), + Dataset => ("H5E_DATASET", "Dataset"), + Dataspace => ("H5E_DATASPACE", "Dataspace"), + Datatype => ("H5E_DATATYPE", "Datatype"), + ErrorApi => ("H5E_ERROR", "Error API"), + EventSet => ("H5E_EVENTSET", "Event Set"), + ExtensibleArray => ("H5E_EARRAY", "Extensible Array"), + ExternalFileList => ("H5E_EFL", "External file list"), + File => ("H5E_FILE", "File accessibility"), + FixedArray => ("H5E_FARRAY", "Fixed Array"), + FreeSpace => ("H5E_FSPACE", "Free Space Manager"), + Func => ("H5E_FUNC", "Function entry/exit"), + Heap => ("H5E_HEAP", "Heap"), + [feature = "1.14.0"] Id => ("H5E_ID", "Object ID"), + [not(feature = "1.14.0")] Id => ("H5E_ATOM", "Object atom"), + Internal => ("H5E_INTERNAL", "Internal error (too specific to document in detail)"), + Io => ("H5E_IO", "Low-level I/O"), + Lib => ("H5E_LIB", "General library infrastructure"), + Link => ("H5E_LINK", "Links"), + Map => ("H5E_MAP", "Map"), + NoneMajor => ("H5E_NONE_MAJOR", "No error"), + ObjectHeader => ("H5E_OHDR", "Object header"), + PageBuffering => ("H5E_PAGEBUF", "Page Buffering"), + Pipeline => ("H5E_PLINE", "Data filters"), + Plugin => ("H5E_PLUGIN", "Plugin for dynamically loaded library"), + PropertyList => ("H5E_PLIST", "Property lists"), + RTree => ("H5E_RTREE", "R-Tree spatial index"), + RefCountedString => ("H5E_RS", "Reference Counted Strings"), + Reference => ("H5E_REFERENCE", "References"), + Resource => ("H5E_RESOURCE", "Resource unavailable"), + SharedObjectHeaderMessage => ("H5E_SOHM", "Shared Object Header Messages"), + SkipList => ("H5E_SLIST", "Skip Lists"), + Storage => ("H5E_STORAGE", "Data storage"), + SymbolTable => ("H5E_SYM", "Symbol table"), + TernarySearchTree => ("H5E_TST", "Ternary Search Trees"), + Threadsafety => ("H5E_THREADSAFE", "Threadsafety"), + VirtualFileLayer => ("H5E_VFL", "Virtual File Layer"), + VirtualObjectLayer => ("H5E_VOL", "Virtual Object Layer"), + } +} + +error_codes! { + /// The minor error code of an HDF5 error frame. + MinorErrorCode, MINOR_CODES; + + variants { + /// `H5E_ALIGNMENT`: Alignment error + Alignment, + /// `H5E_ALREADYEXISTS`: Object already exists + AlreadyExists, + /// `H5E_ALREADYINIT`: Object already initialized + AlreadyInit, + /// `H5E_BADFILE`: Bad file ID accessed + BadFile, + /// `H5E_BADGROUP`: Unable to find ID group information + BadGroup, + /// `H5E_BADID` (`H5E_BADATOM` in older HDF5): Unable to find ID information (already closed?) + BadId, + /// `H5E_BADITER`: Iteration failed + BadIter, + /// `H5E_BADMESG`: Unrecognized message + BadMessage, + /// `H5E_BADRANGE`: Out of range + BadRange, + /// `H5E_BADSELECT`: Invalid selection + BadSelect, + /// `H5E_BADSIZE`: Bad size for object + BadSize, + /// `H5E_BADTYPE`: Inappropriate type + BadType, + /// `H5E_BADVALUE`: Bad value + BadValue, + /// `H5E_CALLBACK`: Callback failed + Callback, + /// `H5E_CANAPPLY`: Error from filter 'can apply' callback + CanApply, + /// `H5E_CANTALLOC`: Can't allocate space + CantAlloc, + /// `H5E_CANTAPPEND`: Can't append object + CantAppend, + /// `H5E_CANTATTACH`: Can't attach object + CantAttach, + /// `H5E_CANTCANCEL`: Can't cancel operation + CantCancel, + /// `H5E_CANTCLEAN`: Unable to mark metadata as clean + CantClean, + /// `H5E_CANTCLIP`: Can't clip hyperslab region + CantClip, + /// `H5E_CANTCLOSEFILE`: Unable to close file + CantCloseFile, + /// `H5E_CANTCLOSEOBJ`: Can't close object + CantCloseObj, + /// `H5E_CANTCOMPARE`: Can't compare objects + CantCompare, + /// `H5E_CANTCOMPUTE`: Can't compute value + CantCompute, + /// `H5E_CANTCONVERT`: Can't convert datatypes + CantConvert, + /// `H5E_CANTCOPY`: Unable to copy object + CantCopy, + /// `H5E_CANTCORK`: Unable to cork an object + CantCork, + /// `H5E_CANTCOUNT`: Can't count elements + CantCount, + /// `H5E_CANTCREATE`: Unable to create file + CantCreate, + /// `H5E_CANTDEC`: Unable to decrement reference count + CantDec, + /// `H5E_CANTDECODE`: Unable to decode value + CantDecode, + /// `H5E_CANTDELETE`: Can't delete message + CantDelete, + /// `H5E_CANTDELETEFILE`: Unable to delete file + CantDeleteFile, + /// `H5E_CANTDEPEND`: Unable to create a flush dependency + CantDepend, + /// `H5E_CANTDIRTY`: Unable to mark metadata as dirty + CantDirty, + /// `H5E_CANTENCODE`: Unable to encode value + CantEncode, + /// `H5E_CANTEXPUNGE`: Unable to expunge a metadata cache entry + CantExpunge, + /// `H5E_CANTEXTEND`: Can't extend heap's space + CantExtend, + /// `H5E_CANTFILTER`: Filter operation failed + CantFilter, + /// `H5E_CANTFIND`: Unable to check for record + CantFind, + /// `H5E_CANTFLUSH`: Unable to flush data from cache + CantFlush, + /// `H5E_CANTFREE`: Unable to free object + CantFree, + /// `H5E_CANTGATHER`: Can't gather data + CantGather, + /// `H5E_CANTGC`: Unable to garbage collect + CantGc, + /// `H5E_CANTGET`: Can't get value + CantGet, + /// `H5E_CANTGETSIZE`: Unable to compute size + CantGetSize, + /// `H5E_CANTINC`: Unable to increment reference count + CantInc, + /// `H5E_CANTINIT`: Unable to initialize object + CantInit, + /// `H5E_CANTINS`: Unable to insert metadata into cache + CantIns, + /// `H5E_CANTINSERT`: Unable to insert object + CantInsert, + /// `H5E_CANTLIST`: Unable to list node + CantList, + /// `H5E_CANTLOAD`: Unable to load metadata into cache + CantLoad, + /// `H5E_CANTLOCK`: Unable to lock object + CantLock, + /// `H5E_CANTLOCKFILE`: Unable to lock file + CantLockFile, + /// `H5E_CANTMARKCLEAN`: Unable to mark a pinned entry as clean + CantMarkClean, + /// `H5E_CANTMARKDIRTY`: Unable to mark a pinned entry as dirty + CantMarkDirty, + /// `H5E_CANTMARKSERIALIZED`: Unable to mark an entry as serialized + CantMarkSerialized, + /// `H5E_CANTMARKUNSERIALIZED`: Unable to mark an entry as unserialized + CantMarkUnserialized, + /// `H5E_CANTMERGE`: Can't merge objects + CantMerge, + /// `H5E_CANTMODIFY`: Unable to modify record + CantModify, + /// `H5E_CANTMOVE`: Can't move object + CantMove, + /// `H5E_CANTNEXT`: Can't move to next iterator location + CantNext, + /// `H5E_CANTNOTIFY`: Unable to notify object about action + CantNotify, + /// `H5E_CANTOPENFILE`: Unable to open file + CantOpenFile, + /// `H5E_CANTOPENOBJ`: Can't open object + CantOpenObj, + /// `H5E_CANTOPERATE`: Can't operate on object + CantOperate, + /// `H5E_CANTPACK`: Can't pack messages + CantPack, + /// `H5E_CANTPIN`: Unable to pin cache entry + CantPin, + /// `H5E_CANTPROTECT`: Unable to protect metadata + CantProtect, + /// `H5E_CANTPUT`: Can't put value + CantPut, + /// `H5E_CANTRECV`: Can't receive data + CantRecv, + /// `H5E_CANTREDISTRIBUTE`: Unable to redistribute records + CantRedistribute, + /// `H5E_CANTREGISTER`: Unable to register new ID + CantRegister, + /// `H5E_CANTRELEASE`: Unable to release object + CantRelease, + /// `H5E_CANTREMOVE`: Unable to remove object + CantRemove, + /// `H5E_CANTRENAME`: Unable to rename object + CantRename, + /// `H5E_CANTRESET`: Can't reset object + CantReset, + /// `H5E_CANTRESIZE`: Unable to resize a metadata cache entry + CantResize, + /// `H5E_CANTRESTORE`: Can't restore condition + CantRestore, + /// `H5E_CANTREVIVE`: Can't revive object + CantRevive, + /// `H5E_CANTSELECT`: Can't select hyperslab + CantSelect, + /// `H5E_CANTSERIALIZE`: Unable to serialize data from cache + CantSerialize, + /// `H5E_CANTSET`: Can't set value + CantSet, + /// `H5E_CANTSHRINK`: Can't shrink container + CantShrink, + /// `H5E_CANTSORT`: Can't sort objects + CantSort, + /// `H5E_CANTSPLIT`: Unable to split node + CantSplit, + /// `H5E_CANTSWAP`: Unable to swap records + CantSwap, + /// `H5E_CANTTAG`: Unable to tag metadata in the cache + CantTag, + /// `H5E_CANTUNCORK`: Unable to uncork an object + CantUncork, + /// `H5E_CANTUNDEPEND`: Unable to destroy a flush dependency + CantUndepend, + /// `H5E_CANTUNLOCK`: Unable to unlock object + CantUnlock, + /// `H5E_CANTUNLOCKFILE`: Unable to unlock file + CantUnlockFile, + /// `H5E_CANTUNPIN`: Unable to un-pin cache entry + CantUnpin, + /// `H5E_CANTUNPROTECT`: Unable to unprotect metadata + CantUnprotect, + /// `H5E_CANTUNSERIALIZE`: Unable to mark metadata as unserialized + CantUnserialize, + /// `H5E_CANTUPDATE`: Can't update object + CantUpdate, + /// `H5E_CANTWAIT`: Can't wait on operation + CantWait, + /// `H5E_CLOSEERROR`: Close failed + CloseError, + /// `H5E_COMPLEN`: Name component is too long + CompLen, + /// `H5E_DUPCLASS`: Duplicate class name in parent class + DupClass, + /// `H5E_EXISTS`: Object already exists + Exists, + /// `H5E_FCNTL`: File control (fcntl) failed + Fcntl, + /// `H5E_FILEEXISTS`: File already exists + FileExists, + /// `H5E_FILEOPEN`: File already open + FileOpen, + /// `H5E_INCONSISTENTSTATE`: Internal states are inconsistent + InconsistentState, + /// `H5E_LINKCOUNT`: Bad object header link count + LinkCount, + /// `H5E_LOGGING` (`H5E_LOGFAIL` in older HDF5): Failure in the cache logging framework + Logging, + /// `H5E_MOUNT`: File mount error + Mount, + /// `H5E_MPI`: Some MPI function failed + Mpi, + /// `H5E_MPIERRSTR`: MPI Error String + MpiErrStr, + /// `H5E_NLINKS`: Too many soft links in path + NLinks, + /// `H5E_NOENCODER`: Filter present but encoding disabled + NoEncoder, + /// `H5E_NOFILTER`: Requested filter is not available + NoFilter, + /// `H5E_NOIDS`: Out of IDs for group + NoIds, + /// `H5E_NO_INDEPENDENT`: Can't perform independent IO + NoIndependent, + /// `H5E_NOSPACE`: No space available for allocation + NoSpace, + /// `H5E_NONE_MINOR`: No error + NoneMinor, + /// `H5E_NOTCACHED`: Metadata not currently cached + NotCached, + /// `H5E_NOTFOUND`: Object not found + NotFound, + /// `H5E_NOTHDF5`: Not an HDF5 file + NotHdf5, + /// `H5E_NOTREGISTERED`: Link class not registered + NotRegistered, + /// `H5E_OBJOPEN`: Object is already open + ObjOpen, + /// `H5E_OPENERROR`: Can't open directory or file + OpenError, + /// `H5E_OVERFLOW`: Address overflowed + Overflow, + /// `H5E_PATH`: Problem with path to object + Path, + /// `H5E_PROTECT`: Protected metadata error + Protect, + /// `H5E_READERROR`: Read failed + ReadError, + /// `H5E_SEEKERROR`: Seek failed + SeekError, + /// `H5E_SETDISALLOWED`: Disallowed operation + SetDisallowed, + /// `H5E_SETLOCAL`: Error from filter 'set local' callback + SetLocal, + /// `H5E_SYSERRSTR`: System error message + SysErrStr, + /// `H5E_SYSTEM`: Internal error detected + System, + /// `H5E_TRAVERSE`: Link traversal failure + Traverse, + /// `H5E_TRUNCATED`: File has been truncated + Truncated, + /// `H5E_UNINITIALIZED`: Information is uinitialized + Uninitialized, + /// `H5E_UNMOUNT`: File unmount error + Unmount, + /// `H5E_UNSUPPORTED`: Feature is unsupported + Unsupported, + /// `H5E_VERSION`: Wrong version number + Version, + /// `H5E_WRITEERROR`: Write failed + WriteError, + } + + symbols { + H5E_ALIGNMENT => Alignment, + H5E_ALREADYEXISTS => AlreadyExists, + H5E_ALREADYINIT => AlreadyInit, + H5E_BADFILE => BadFile, + H5E_BADGROUP => BadGroup, + [not(feature = "1.14.0")] H5E_BADATOM => BadId, + [feature = "1.14.0"] H5E_BADID => BadId, + H5E_BADITER => BadIter, + H5E_BADMESG => BadMessage, + H5E_BADRANGE => BadRange, + H5E_BADSELECT => BadSelect, + H5E_BADSIZE => BadSize, + H5E_BADTYPE => BadType, + H5E_BADVALUE => BadValue, + H5E_CALLBACK => Callback, + H5E_CANAPPLY => CanApply, + H5E_CANTALLOC => CantAlloc, + [feature = "1.10.0"] H5E_CANTAPPEND => CantAppend, + H5E_CANTATTACH => CantAttach, + [feature = "1.14.0"] H5E_CANTCANCEL => CantCancel, + [feature = "1.10.1"] H5E_CANTCLEAN => CantClean, + H5E_CANTCLIP => CantClip, + H5E_CANTCLOSEFILE => CantCloseFile, + H5E_CANTCLOSEOBJ => CantCloseObj, + H5E_CANTCOMPARE => CantCompare, + H5E_CANTCOMPUTE => CantCompute, + H5E_CANTCONVERT => CantConvert, + H5E_CANTCOPY => CantCopy, + [feature = "1.10.0"] H5E_CANTCORK => CantCork, + H5E_CANTCOUNT => CantCount, + H5E_CANTCREATE => CantCreate, + H5E_CANTDEC => CantDec, + H5E_CANTDECODE => CantDecode, + H5E_CANTDELETE => CantDelete, + [feature = "1.12.0"] H5E_CANTDELETEFILE => CantDeleteFile, + [feature = "1.10.0"] H5E_CANTDEPEND => CantDepend, + H5E_CANTDIRTY => CantDirty, + H5E_CANTENCODE => CantEncode, + H5E_CANTEXPUNGE => CantExpunge, + H5E_CANTEXTEND => CantExtend, + H5E_CANTFILTER => CantFilter, + [feature = "1.14.0"] H5E_CANTFIND => CantFind, + H5E_CANTFLUSH => CantFlush, + H5E_CANTFREE => CantFree, + [feature = "1.10.2"] H5E_CANTGATHER => CantGather, + H5E_CANTGC => CantGc, + H5E_CANTGET => CantGet, + H5E_CANTGETSIZE => CantGetSize, + H5E_CANTINC => CantInc, + H5E_CANTINIT => CantInit, + H5E_CANTINS => CantIns, + H5E_CANTINSERT => CantInsert, + H5E_CANTLIST => CantList, + H5E_CANTLOAD => CantLoad, + H5E_CANTLOCK => CantLock, + [any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1")] H5E_CANTLOCKFILE => CantLockFile, + [feature = "1.10.1"] H5E_CANTMARKCLEAN => CantMarkClean, + H5E_CANTMARKDIRTY => CantMarkDirty, + [feature = "1.10.1"] H5E_CANTMARKSERIALIZED => CantMarkSerialized, + [feature = "1.10.1"] H5E_CANTMARKUNSERIALIZED => CantMarkUnserialized, + H5E_CANTMERGE => CantMerge, + H5E_CANTMODIFY => CantModify, + H5E_CANTMOVE => CantMove, + H5E_CANTNEXT => CantNext, + [feature = "1.10.0"] H5E_CANTNOTIFY => CantNotify, + H5E_CANTOPENFILE => CantOpenFile, + H5E_CANTOPENOBJ => CantOpenObj, + H5E_CANTOPERATE => CantOperate, + H5E_CANTPACK => CantPack, + H5E_CANTPIN => CantPin, + H5E_CANTPROTECT => CantProtect, + [feature = "1.14.0"] H5E_CANTPUT => CantPut, + H5E_CANTRECV => CantRecv, + H5E_CANTREDISTRIBUTE => CantRedistribute, + H5E_CANTREGISTER => CantRegister, + H5E_CANTRELEASE => CantRelease, + H5E_CANTREMOVE => CantRemove, + H5E_CANTRENAME => CantRename, + H5E_CANTRESET => CantReset, + H5E_CANTRESIZE => CantResize, + H5E_CANTRESTORE => CantRestore, + H5E_CANTREVIVE => CantRevive, + H5E_CANTSELECT => CantSelect, + H5E_CANTSERIALIZE => CantSerialize, + H5E_CANTSET => CantSet, + H5E_CANTSHRINK => CantShrink, + H5E_CANTSORT => CantSort, + H5E_CANTSPLIT => CantSplit, + H5E_CANTSWAP => CantSwap, + [feature = "1.10.0"] H5E_CANTTAG => CantTag, + [feature = "1.10.0"] H5E_CANTUNCORK => CantUncork, + [feature = "1.10.0"] H5E_CANTUNDEPEND => CantUndepend, + H5E_CANTUNLOCK => CantUnlock, + [any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1")] H5E_CANTUNLOCKFILE => CantUnlockFile, + H5E_CANTUNPIN => CantUnpin, + H5E_CANTUNPROTECT => CantUnprotect, + [feature = "1.10.1"] H5E_CANTUNSERIALIZE => CantUnserialize, + H5E_CANTUPDATE => CantUpdate, + [feature = "1.14.0"] H5E_CANTWAIT => CantWait, + H5E_CLOSEERROR => CloseError, + H5E_COMPLEN => CompLen, + H5E_DUPCLASS => DupClass, + H5E_EXISTS => Exists, + H5E_FCNTL => Fcntl, + H5E_FILEEXISTS => FileExists, + H5E_FILEOPEN => FileOpen, + [feature = "1.10.7"] H5E_INCONSISTENTSTATE => InconsistentState, + H5E_LINKCOUNT => LinkCount, + [all(feature = "1.10.0", not(feature = "1.12.0"))] H5E_LOGFAIL => Logging, + [feature = "1.10.5"] H5E_LOGGING => Logging, + H5E_MOUNT => Mount, + H5E_MPI => Mpi, + H5E_MPIERRSTR => MpiErrStr, + H5E_NLINKS => NLinks, + H5E_NOENCODER => NoEncoder, + H5E_NOFILTER => NoFilter, + H5E_NOIDS => NoIds, + [feature = "1.10.2"] H5E_NO_INDEPENDENT => NoIndependent, + H5E_NOSPACE => NoSpace, + H5E_NONE_MINOR => NoneMinor, + H5E_NOTCACHED => NotCached, + H5E_NOTFOUND => NotFound, + H5E_NOTHDF5 => NotHdf5, + H5E_NOTREGISTERED => NotRegistered, + H5E_OBJOPEN => ObjOpen, + [feature = "1.8.11"] H5E_OPENERROR => OpenError, + H5E_OVERFLOW => Overflow, + H5E_PATH => Path, + H5E_PROTECT => Protect, + H5E_READERROR => ReadError, + H5E_SEEKERROR => SeekError, + [feature = "1.8.9"] H5E_SETDISALLOWED => SetDisallowed, + H5E_SETLOCAL => SetLocal, + H5E_SYSERRSTR => SysErrStr, + H5E_SYSTEM => System, + H5E_TRAVERSE => Traverse, + H5E_TRUNCATED => Truncated, + H5E_UNINITIALIZED => Uninitialized, + [feature = "1.14.0"] H5E_UNMOUNT => Unmount, + H5E_UNSUPPORTED => Unsupported, + H5E_VERSION => Version, + H5E_WRITEERROR => WriteError, + } + + meta { + Alignment => ("H5E_ALIGNMENT", "Alignment error"), + AlreadyExists => ("H5E_ALREADYEXISTS", "Object already exists"), + AlreadyInit => ("H5E_ALREADYINIT", "Object already initialized"), + BadFile => ("H5E_BADFILE", "Bad file ID accessed"), + BadGroup => ("H5E_BADGROUP", "Unable to find ID group information"), + [feature = "1.14.0"] BadId => ("H5E_BADID", "Unable to find ID information (already closed?)"), + [not(feature = "1.14.0")] BadId => ("H5E_BADATOM", "Unable to find atom information (already closed?)"), + BadIter => ("H5E_BADITER", "Iteration failed"), + BadMessage => ("H5E_BADMESG", "Unrecognized message"), + BadRange => ("H5E_BADRANGE", "Out of range"), + BadSelect => ("H5E_BADSELECT", "Invalid selection"), + BadSize => ("H5E_BADSIZE", "Bad size for object"), + BadType => ("H5E_BADTYPE", "Inappropriate type"), + BadValue => ("H5E_BADVALUE", "Bad value"), + Callback => ("H5E_CALLBACK", "Callback failed"), + CanApply => ("H5E_CANAPPLY", "Error from filter 'can apply' callback"), + CantAlloc => ("H5E_CANTALLOC", "Can't allocate space"), + CantAppend => ("H5E_CANTAPPEND", "Can't append object"), + CantAttach => ("H5E_CANTATTACH", "Can't attach object"), + CantCancel => ("H5E_CANTCANCEL", "Can't cancel operation"), + CantClean => ("H5E_CANTCLEAN", "Unable to mark metadata as clean"), + CantClip => ("H5E_CANTCLIP", "Can't clip hyperslab region"), + CantCloseFile => ("H5E_CANTCLOSEFILE", "Unable to close file"), + CantCloseObj => ("H5E_CANTCLOSEOBJ", "Can't close object"), + CantCompare => ("H5E_CANTCOMPARE", "Can't compare objects"), + CantCompute => ("H5E_CANTCOMPUTE", "Can't compute value"), + CantConvert => ("H5E_CANTCONVERT", "Can't convert datatypes"), + CantCopy => ("H5E_CANTCOPY", "Unable to copy object"), + CantCork => ("H5E_CANTCORK", "Unable to cork an object"), + CantCount => ("H5E_CANTCOUNT", "Can't count elements"), + CantCreate => ("H5E_CANTCREATE", "Unable to create file"), + CantDec => ("H5E_CANTDEC", "Unable to decrement reference count"), + CantDecode => ("H5E_CANTDECODE", "Unable to decode value"), + CantDelete => ("H5E_CANTDELETE", "Can't delete message"), + CantDeleteFile => ("H5E_CANTDELETEFILE", "Unable to delete file"), + CantDepend => ("H5E_CANTDEPEND", "Unable to create a flush dependency"), + CantDirty => ("H5E_CANTDIRTY", "Unable to mark metadata as dirty"), + CantEncode => ("H5E_CANTENCODE", "Unable to encode value"), + CantExpunge => ("H5E_CANTEXPUNGE", "Unable to expunge a metadata cache entry"), + CantExtend => ("H5E_CANTEXTEND", "Can't extend heap's space"), + CantFilter => ("H5E_CANTFILTER", "Filter operation failed"), + CantFind => ("H5E_CANTFIND", "Unable to check for record"), + CantFlush => ("H5E_CANTFLUSH", "Unable to flush data from cache"), + CantFree => ("H5E_CANTFREE", "Unable to free object"), + CantGather => ("H5E_CANTGATHER", "Can't gather data"), + CantGc => ("H5E_CANTGC", "Unable to garbage collect"), + CantGet => ("H5E_CANTGET", "Can't get value"), + CantGetSize => ("H5E_CANTGETSIZE", "Unable to compute size"), + CantInc => ("H5E_CANTINC", "Unable to increment reference count"), + CantInit => ("H5E_CANTINIT", "Unable to initialize object"), + CantIns => ("H5E_CANTINS", "Unable to insert metadata into cache"), + CantInsert => ("H5E_CANTINSERT", "Unable to insert object"), + CantList => ("H5E_CANTLIST", "Unable to list node"), + CantLoad => ("H5E_CANTLOAD", "Unable to load metadata into cache"), + CantLock => ("H5E_CANTLOCK", "Unable to lock object"), + CantLockFile => ("H5E_CANTLOCKFILE", "Unable to lock file"), + CantMarkClean => ("H5E_CANTMARKCLEAN", "Unable to mark a pinned entry as clean"), + CantMarkDirty => ("H5E_CANTMARKDIRTY", "Unable to mark a pinned entry as dirty"), + CantMarkSerialized => ("H5E_CANTMARKSERIALIZED", "Unable to mark an entry as serialized"), + CantMarkUnserialized => ("H5E_CANTMARKUNSERIALIZED", "Unable to mark an entry as unserialized"), + CantMerge => ("H5E_CANTMERGE", "Can't merge objects"), + CantModify => ("H5E_CANTMODIFY", "Unable to modify record"), + CantMove => ("H5E_CANTMOVE", "Can't move object"), + CantNext => ("H5E_CANTNEXT", "Can't move to next iterator location"), + CantNotify => ("H5E_CANTNOTIFY", "Unable to notify object about action"), + CantOpenFile => ("H5E_CANTOPENFILE", "Unable to open file"), + CantOpenObj => ("H5E_CANTOPENOBJ", "Can't open object"), + CantOperate => ("H5E_CANTOPERATE", "Can't operate on object"), + CantPack => ("H5E_CANTPACK", "Can't pack messages"), + CantPin => ("H5E_CANTPIN", "Unable to pin cache entry"), + CantProtect => ("H5E_CANTPROTECT", "Unable to protect metadata"), + CantPut => ("H5E_CANTPUT", "Can't put value"), + CantRecv => ("H5E_CANTRECV", "Can't receive data"), + CantRedistribute => ("H5E_CANTREDISTRIBUTE", "Unable to redistribute records"), + CantRegister => ("H5E_CANTREGISTER", "Unable to register new ID"), + CantRelease => ("H5E_CANTRELEASE", "Unable to release object"), + CantRemove => ("H5E_CANTREMOVE", "Unable to remove object"), + CantRename => ("H5E_CANTRENAME", "Unable to rename object"), + CantReset => ("H5E_CANTRESET", "Can't reset object"), + CantResize => ("H5E_CANTRESIZE", "Unable to resize a metadata cache entry"), + CantRestore => ("H5E_CANTRESTORE", "Can't restore condition"), + CantRevive => ("H5E_CANTREVIVE", "Can't revive object"), + CantSelect => ("H5E_CANTSELECT", "Can't select hyperslab"), + CantSerialize => ("H5E_CANTSERIALIZE", "Unable to serialize data from cache"), + CantSet => ("H5E_CANTSET", "Can't set value"), + CantShrink => ("H5E_CANTSHRINK", "Can't shrink container"), + CantSort => ("H5E_CANTSORT", "Can't sort objects"), + CantSplit => ("H5E_CANTSPLIT", "Unable to split node"), + CantSwap => ("H5E_CANTSWAP", "Unable to swap records"), + CantTag => ("H5E_CANTTAG", "Unable to tag metadata in the cache"), + CantUncork => ("H5E_CANTUNCORK", "Unable to uncork an object"), + CantUndepend => ("H5E_CANTUNDEPEND", "Unable to destroy a flush dependency"), + CantUnlock => ("H5E_CANTUNLOCK", "Unable to unlock object"), + CantUnlockFile => ("H5E_CANTUNLOCKFILE", "Unable to unlock file"), + CantUnpin => ("H5E_CANTUNPIN", "Unable to un-pin cache entry"), + CantUnprotect => ("H5E_CANTUNPROTECT", "Unable to unprotect metadata"), + CantUnserialize => ("H5E_CANTUNSERIALIZE", "Unable to mark metadata as unserialized"), + CantUpdate => ("H5E_CANTUPDATE", "Can't update object"), + CantWait => ("H5E_CANTWAIT", "Can't wait on operation"), + CloseError => ("H5E_CLOSEERROR", "Close failed"), + CompLen => ("H5E_COMPLEN", "Name component is too long"), + DupClass => ("H5E_DUPCLASS", "Duplicate class name in parent class"), + Exists => ("H5E_EXISTS", "Object already exists"), + Fcntl => ("H5E_FCNTL", "File control (fcntl) failed"), + FileExists => ("H5E_FILEEXISTS", "File already exists"), + FileOpen => ("H5E_FILEOPEN", "File already open"), + InconsistentState => ("H5E_INCONSISTENTSTATE", "Internal states are inconsistent"), + LinkCount => ("H5E_LINKCOUNT", "Bad object header link count"), + [all(feature = "1.10.0", not(feature = "1.10.5"))] Logging => ("H5E_LOGFAIL", "Failure in the cache logging framework"), + [not(all(feature = "1.10.0", not(feature = "1.10.5")))] Logging => ("H5E_LOGGING", "Failure in the cache logging framework"), + Mount => ("H5E_MOUNT", "File mount error"), + Mpi => ("H5E_MPI", "Some MPI function failed"), + MpiErrStr => ("H5E_MPIERRSTR", "MPI Error String"), + NLinks => ("H5E_NLINKS", "Too many soft links in path"), + NoEncoder => ("H5E_NOENCODER", "Filter present but encoding disabled"), + NoFilter => ("H5E_NOFILTER", "Requested filter is not available"), + NoIds => ("H5E_NOIDS", "Out of IDs for group"), + NoIndependent => ("H5E_NO_INDEPENDENT", "Can't perform independent IO"), + NoSpace => ("H5E_NOSPACE", "No space available for allocation"), + NoneMinor => ("H5E_NONE_MINOR", "No error"), + NotCached => ("H5E_NOTCACHED", "Metadata not currently cached"), + NotFound => ("H5E_NOTFOUND", "Object not found"), + NotHdf5 => ("H5E_NOTHDF5", "Not an HDF5 file"), + NotRegistered => ("H5E_NOTREGISTERED", "Link class not registered"), + ObjOpen => ("H5E_OBJOPEN", "Object is already open"), + OpenError => ("H5E_OPENERROR", "Can't open directory or file"), + Overflow => ("H5E_OVERFLOW", "Address overflowed"), + Path => ("H5E_PATH", "Problem with path to object"), + Protect => ("H5E_PROTECT", "Protected metadata error"), + ReadError => ("H5E_READERROR", "Read failed"), + SeekError => ("H5E_SEEKERROR", "Seek failed"), + SetDisallowed => ("H5E_SETDISALLOWED", "Disallowed operation"), + SetLocal => ("H5E_SETLOCAL", "Error from filter 'set local' callback"), + SysErrStr => ("H5E_SYSERRSTR", "System error message"), + System => ("H5E_SYSTEM", "Internal error detected"), + Traverse => ("H5E_TRAVERSE", "Link traversal failure"), + Truncated => ("H5E_TRUNCATED", "File has been truncated"), + Uninitialized => ("H5E_UNINITIALIZED", "Information is uinitialized"), + Unmount => ("H5E_UNMOUNT", "File unmount error"), + Unsupported => ("H5E_UNSUPPORTED", "Feature is unsupported"), + Version => ("H5E_VERSION", "Wrong version number"), + WriteError => ("H5E_WRITEERROR", "Write failed"), + } +} diff --git a/hdf5/src/globals.rs b/hdf5/src/globals.rs index 07ac09a57..c56ee0c59 100644 --- a/hdf5/src/globals.rs +++ b/hdf5/src/globals.rs @@ -3,15 +3,11 @@ use std::mem; use std::sync::LazyLock; -use lazy_static::lazy_static; - #[cfg(feature = "have-direct")] -use hdf5_sys::h5fd::H5FD_direct_init; -#[cfg(feature = "have-parallel")] -use hdf5_sys::h5fd::H5FD_mpio_init; -use hdf5_sys::h5fd::{ - H5FD_core_init, H5FD_family_init, H5FD_log_init, H5FD_multi_init, H5FD_sec2_init, - H5FD_stdio_init, +use hdf5_sys::h5p::H5Pset_fapl_direct; +use hdf5_sys::h5p::{ + H5Pclose, H5Pcreate, H5Pget_driver, H5Pset_fapl_core, H5Pset_fapl_family, H5Pset_fapl_log, + H5Pset_fapl_multi, H5Pset_fapl_sec2, H5Pset_fapl_stdio, }; use hdf5_sys::{h5e, h5p, h5t}; @@ -46,6 +42,18 @@ macro_rules! link_hid { }; } +/// Fetches the driver ID using the workaround from https://github.com/HDFGroup/hdf5/issues/1809 +/// as the _init functions seem to be removed in HDF5 2.0.0 +macro_rules! get_driver { + ($set_driver:expr) => {{ + let fapl = h5call!(H5Pcreate(*H5P_FILE_ACCESS)).expect("should always create FAPL"); + h5call!($set_driver(fapl)).expect("should always be able to set the driver"); + let id = h5call!(H5Pget_driver(fapl)).expect("should always be able to extract the driver"); + h5call!(H5Pclose(fapl)).expect("should always be able to close the FAPL"); + id + }}; +} + // Datatypes link_hid!(H5T_IEEE_F32BE, h5t::H5T_IEEE_F32BE); link_hid!(H5T_IEEE_F32LE, h5t::H5T_IEEE_F32LE); @@ -180,6 +188,7 @@ link_hid!(H5E_STORAGE, h5e::H5E_STORAGE); link_hid!(H5E_FILE, h5e::H5E_FILE); link_hid!(H5E_SOHM, h5e::H5E_SOHM); link_hid!(H5E_SYM, h5e::H5E_SYM); +#[cfg(feature = "1.8.11")] link_hid!(H5E_PLUGIN, h5e::H5E_PLUGIN); link_hid!(H5E_VFL, h5e::H5E_VFL); link_hid!(H5E_INTERNAL, h5e::H5E_INTERNAL); @@ -234,6 +243,7 @@ link_hid!(H5E_CANTRELEASE, h5e::H5E_CANTRELEASE); link_hid!(H5E_CANTGET, h5e::H5E_CANTGET); link_hid!(H5E_CANTSET, h5e::H5E_CANTSET); link_hid!(H5E_DUPCLASS, h5e::H5E_DUPCLASS); +#[cfg(feature = "1.8.9")] link_hid!(H5E_SETDISALLOWED, h5e::H5E_SETDISALLOWED); link_hid!(H5E_CANTMERGE, h5e::H5E_CANTMERGE); link_hid!(H5E_CANTREVIVE, h5e::H5E_CANTREVIVE); @@ -259,6 +269,7 @@ link_hid!(H5E_CANTCLOSEOBJ, h5e::H5E_CANTCLOSEOBJ); link_hid!(H5E_COMPLEN, h5e::H5E_COMPLEN); link_hid!(H5E_PATH, h5e::H5E_PATH); link_hid!(H5E_NONE_MINOR, h5e::H5E_NONE_MINOR); +#[cfg(feature = "1.8.11")] link_hid!(H5E_OPENERROR, h5e::H5E_OPENERROR); link_hid!(H5E_FILEEXISTS, h5e::H5E_FILEEXISTS); link_hid!(H5E_FILEOPEN, h5e::H5E_FILEOPEN); @@ -323,47 +334,128 @@ link_hid!(H5E_CANTMODIFY, h5e::H5E_CANTMODIFY); link_hid!(H5E_CANTREMOVE, h5e::H5E_CANTREMOVE); link_hid!(H5E_CANTCONVERT, h5e::H5E_CANTCONVERT); link_hid!(H5E_BADSIZE, h5e::H5E_BADSIZE); +#[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] +link_hid!(H5E_CANTLOCKFILE, h5e::H5E_CANTLOCKFILE); +#[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] +link_hid!(H5E_CANTUNLOCKFILE, h5e::H5E_CANTUNLOCKFILE); +#[cfg(feature = "1.12.1")] +link_hid!(H5E_LIB, h5e::H5E_LIB); +#[cfg(feature = "1.14.0")] +link_hid!(H5E_BADID, h5e::H5E_BADID); +#[cfg(feature = "1.14.0")] +link_hid!(H5E_CANTCANCEL, h5e::H5E_CANTCANCEL); +#[cfg(feature = "1.14.0")] +link_hid!(H5E_CANTFIND, h5e::H5E_CANTFIND); +#[cfg(feature = "1.14.0")] +link_hid!(H5E_CANTPUT, h5e::H5E_CANTPUT); +#[cfg(feature = "1.14.0")] +link_hid!(H5E_CANTWAIT, h5e::H5E_CANTWAIT); +#[cfg(feature = "1.14.0")] +link_hid!(H5E_EVENTSET, h5e::H5E_EVENTSET); +#[cfg(feature = "1.14.0")] +link_hid!(H5E_ID, h5e::H5E_ID); +#[cfg(feature = "1.14.0")] +link_hid!(H5E_UNMOUNT, h5e::H5E_UNMOUNT); +#[cfg(feature = "2.0.0")] +link_hid!(H5E_RTREE, h5e::H5E_RTREE); +#[cfg(feature = "2.0.0")] +link_hid!(H5E_THREADSAFE, h5e::H5E_THREADSAFE); +#[cfg(feature = "1.10.0")] +link_hid!(H5E_EARRAY, h5e::H5E_EARRAY); +#[cfg(feature = "1.10.0")] +link_hid!(H5E_FARRAY, h5e::H5E_FARRAY); +#[cfg(feature = "1.10.1")] +link_hid!(H5E_PAGEBUF, h5e::H5E_PAGEBUF); +#[cfg(feature = "1.10.3")] +link_hid!(H5E_CONTEXT, h5e::H5E_CONTEXT); +#[cfg(feature = "1.12.0")] +link_hid!(H5E_MAP, h5e::H5E_MAP); +#[cfg(feature = "1.12.0")] +link_hid!(H5E_VOL, h5e::H5E_VOL); +#[cfg(feature = "1.10.0")] +link_hid!(H5E_CANTAPPEND, h5e::H5E_CANTAPPEND); +#[cfg(feature = "1.10.0")] +link_hid!(H5E_CANTCORK, h5e::H5E_CANTCORK); +#[cfg(feature = "1.10.0")] +link_hid!(H5E_CANTDEPEND, h5e::H5E_CANTDEPEND); +#[cfg(feature = "1.10.0")] +link_hid!(H5E_CANTNOTIFY, h5e::H5E_CANTNOTIFY); +#[cfg(feature = "1.10.0")] +link_hid!(H5E_CANTTAG, h5e::H5E_CANTTAG); +#[cfg(feature = "1.10.0")] +link_hid!(H5E_CANTUNCORK, h5e::H5E_CANTUNCORK); +#[cfg(feature = "1.10.0")] +link_hid!(H5E_CANTUNDEPEND, h5e::H5E_CANTUNDEPEND); +#[cfg(all(feature = "1.10.0", not(feature = "1.12.0")))] +link_hid!(H5E_LOGFAIL, h5e::H5E_LOGFAIL); +#[cfg(feature = "1.10.1")] +link_hid!(H5E_CANTCLEAN, h5e::H5E_CANTCLEAN); +#[cfg(feature = "1.10.1")] +link_hid!(H5E_CANTMARKCLEAN, h5e::H5E_CANTMARKCLEAN); +#[cfg(feature = "1.10.1")] +link_hid!(H5E_CANTMARKSERIALIZED, h5e::H5E_CANTMARKSERIALIZED); +#[cfg(feature = "1.10.1")] +link_hid!(H5E_CANTMARKUNSERIALIZED, h5e::H5E_CANTMARKUNSERIALIZED); +#[cfg(feature = "1.10.1")] +link_hid!(H5E_CANTUNSERIALIZE, h5e::H5E_CANTUNSERIALIZE); +#[cfg(feature = "1.12.0")] +link_hid!(H5E_CANTDELETEFILE, h5e::H5E_CANTDELETEFILE); +#[cfg(feature = "1.10.2")] +link_hid!(H5E_CANTGATHER, h5e::H5E_CANTGATHER); +#[cfg(feature = "1.10.7")] +link_hid!(H5E_INCONSISTENTSTATE, h5e::H5E_INCONSISTENTSTATE); +#[cfg(feature = "1.10.5")] +link_hid!(H5E_LOGGING, h5e::H5E_LOGGING); +#[cfg(feature = "1.10.2")] +link_hid!(H5E_NO_INDEPENDENT, h5e::H5E_NO_INDEPENDENT); // H5R constants -lazy_static! { - pub static ref H5R_OBJ_REF_BUF_SIZE: usize = mem::size_of::(); - pub static ref H5R_DSET_REG_REF_BUF_SIZE: usize = mem::size_of::() + 4; -} +pub static H5R_OBJ_REF_BUF_SIZE: LazyLock = LazyLock::new(|| mem::size_of::()); +pub static H5R_DSET_REG_REF_BUF_SIZE: LazyLock = + LazyLock::new(|| mem::size_of::() + 4); // File drivers -lazy_static! { - pub static ref H5FD_CORE: hid_t = h5lock!(H5FD_core_init()); - pub static ref H5FD_SEC2: hid_t = h5lock!(H5FD_sec2_init()); - pub static ref H5FD_STDIO: hid_t = h5lock!(H5FD_stdio_init()); - pub static ref H5FD_FAMILY: hid_t = h5lock!(H5FD_family_init()); - pub static ref H5FD_LOG: hid_t = h5lock!(H5FD_log_init()); - pub static ref H5FD_MULTI: hid_t = h5lock!(H5FD_multi_init()); -} +pub static H5FD_CORE: LazyLock = + LazyLock::new(|| h5lock!(get_driver!(|fapl| H5Pset_fapl_core(fapl, 0, 0)))); +pub static H5FD_SEC2: LazyLock = + LazyLock::new(|| h5lock!(get_driver!(|fapl| H5Pset_fapl_sec2(fapl)))); +pub static H5FD_STDIO: LazyLock = + LazyLock::new(|| h5lock!(get_driver!(|fapl| H5Pset_fapl_stdio(fapl)))); +pub static H5FD_FAMILY: LazyLock = + LazyLock::new(|| h5lock!(get_driver!(|fapl| H5Pset_fapl_family(fapl, 0, 0)))); +pub static H5FD_LOG: LazyLock = + LazyLock::new(|| h5lock!(get_driver!(|fapl| H5Pset_fapl_log(fapl, std::ptr::null(), 0, 0)))); +pub static H5FD_MULTI: LazyLock = LazyLock::new(|| { + h5lock!(get_driver!(|fapl| H5Pset_fapl_multi( + fapl, + std::ptr::null(), + std::ptr::null(), + std::ptr::null(), + std::ptr::null(), + 0 + ))) +}); // MPI-IO file driver -#[cfg(feature = "have-parallel")] -lazy_static! { - pub static ref H5FD_MPIO: hid_t = h5lock!(H5FD_mpio_init()); -} -#[cfg(not(feature = "have-parallel"))] -lazy_static! { - pub static ref H5FD_MPIO: hid_t = H5I_INVALID_HID; -} +#[cfg(all(feature = "2.0.0", all(feature = "have-parallel", feature = "mpio")))] +pub static H5FD_MPIO: LazyLock = LazyLock::new(|| *hdf5_sys::h5fd::H5FD_MPIO); +#[cfg(all(feature = "2.0.0", not(all(feature = "have-parallel", feature = "mpio"))))] +pub static H5FD_MPIO: LazyLock = LazyLock::new(|| H5I_INVALID_HID); + +#[cfg(all(not(feature = "2.0.0"), all(feature = "have-parallel", feature = "mpio")))] +pub static H5FD_MPIO: LazyLock = LazyLock::new(|| h5lock!(hdf5_sys::h5fd::H5FD_mpio_init())); +#[cfg(all(not(feature = "2.0.0"), not(all(feature = "have-parallel", feature = "mpio"))))] +pub static H5FD_MPIO: LazyLock = LazyLock::new(|| H5I_INVALID_HID); // Direct VFD #[cfg(feature = "have-direct")] -lazy_static! { - pub static ref H5FD_DIRECT: hid_t = h5lock!(H5FD_direct_init()); -} +pub static H5FD_DIRECT: LazyLock = + LazyLock::new(|| h5lock!(get_driver!(|fapl| H5Pset_fapl_direct(fapl, 0, 0, 0)))); #[cfg(not(feature = "have-direct"))] -lazy_static! { - pub static ref H5FD_DIRECT: hid_t = H5I_INVALID_HID; -} +pub static H5FD_DIRECT: LazyLock = LazyLock::new(|| H5I_INVALID_HID); #[cfg(target_os = "windows")] -lazy_static! { - pub static ref H5FD_WINDOWS: hid_t = *H5FD_SEC2; -} +pub static H5FD_WINDOWS: LazyLock = LazyLock::new(|| *H5FD_SEC2); #[cfg(test)] mod tests { diff --git a/hdf5/src/hl.rs b/hdf5/src/hl.rs index 5cae2f0b9..a8fda6aed 100644 --- a/hdf5/src/hl.rs +++ b/hdf5/src/hl.rs @@ -26,7 +26,7 @@ pub use self::{ dataspace::Dataspace, datatype::{Conversion, Datatype}, file::{File, FileBuilder, OpenMode}, - group::{Group, LinkInfo, LinkType}, + group::{Group, GroupBuilder, LinkInfo, LinkType}, location::{Location, LocationInfo, LocationToken, LocationType}, object::Object, plist::PropertyList, diff --git a/hdf5/src/hl/attribute.rs b/hdf5/src/hl/attribute.rs index a7a457e97..107146c91 100644 --- a/hdf5/src/hl/attribute.rs +++ b/hdf5/src/hl/attribute.rs @@ -2,6 +2,8 @@ use std::fmt::{self, Debug}; use std::ops::Deref; use std::ptr::addr_of_mut; +use hdf5_sys::h5a::H5Aget_name; +use hdf5_sys::h5p::H5Pcreate; use hdf5_sys::{ h5::{H5_index_t, H5_iter_order_t}, h5a::{H5A_info_t, H5A_operator2_t, H5Acreate2, H5Adelete, H5Aiterate2}, @@ -9,6 +11,7 @@ use hdf5_sys::{ use hdf5_types::TypeDescriptor; use ndarray::ArrayView; +use crate::globals::H5P_ATTRIBUTE_CREATE; use crate::internal_prelude::*; /// Represents the HDF5 attribute object. @@ -46,6 +49,14 @@ impl Deref for Attribute { } impl Attribute { + /// Returns the name of the attribute. + pub fn name(&self) -> String { + // Note: We must use H5Aget_name() here. H5Iget_name() (called by + // Location::name()) would return the name of the object this + // attribute is attached to, not the attribute's own name. + h5lock!(get_h5_str(|m, s| H5Aget_name(self.id(), s, m)).unwrap_or_else(|_| String::new())) + } + /// Returns names of all the members in the group, non-recursively. pub fn attr_names(obj: &Location) -> Result> { unsafe extern "C" fn attributes_callback( @@ -86,18 +97,22 @@ pub struct AttributeBuilder { } impl AttributeBuilder { + /// Creates a builder for a new attribute on a named object. pub fn new(parent: &Location) -> Self { Self { builder: AttributeBuilderInner::new(parent) } } + /// Sets the attribute's type without initializing its data. pub fn empty(self) -> AttributeBuilderEmpty { self.empty_as(&T::type_descriptor()) } + /// Sets the attribute's type from a type descriptor without initializing its data. pub fn empty_as(self, type_desc: &TypeDescriptor) -> AttributeBuilderEmpty { AttributeBuilderEmpty { builder: self.builder, type_desc: type_desc.clone() } } + /// Sets the data to store in the attribute. pub fn with_data<'d, A, T, D>(self, data: A) -> AttributeBuilderData<'d, T, D> where A: Into>, @@ -107,6 +122,7 @@ impl AttributeBuilder { self.with_data_as::(data, &T::type_descriptor()) } + /// Sets the data to store in the attribute and sets its element type with a type descriptor. pub fn with_data_as<'d, A, T, D>( self, data: A, type_desc: &TypeDescriptor, ) -> AttributeBuilderData<'d, T, D> @@ -139,6 +155,7 @@ pub struct AttributeBuilderEmpty { } impl AttributeBuilderEmpty { + /// Sets the shape of the attribute's data. pub fn shape>(self, extents: S) -> AttributeBuilderEmptyShape { AttributeBuilderEmptyShape { builder: self.builder, @@ -146,6 +163,8 @@ impl AttributeBuilderEmpty { extents: extents.into(), } } + + /// Creates the attribute. pub fn create<'n, T: Into<&'n str>>(self, name: T) -> Result { self.shape(()).create(name) } @@ -167,6 +186,7 @@ pub struct AttributeBuilderEmptyShape { } impl AttributeBuilderEmptyShape { + /// Creates the attribute. pub fn create<'n, T: Into<&'n str>>(&self, name: T) -> Result { h5lock!(self.builder.create(&self.type_desc, name.into(), &self.extents)) } @@ -205,6 +225,7 @@ where self } + /// Creates the attribute. pub fn create<'n, N: Into<&'n str>>(&self, name: N) -> Result { ensure!( self.data.is_standard_layout(), @@ -262,15 +283,22 @@ impl AttributeBuilderInner { let dataspace = Dataspace::try_new(extents)?; + let acpl = PropertyList::from_id(h5call!(H5Pcreate(*H5P_ATTRIBUTE_CREATE))?)?; + // Set UTF-8 encoding for the attribute name, as Rust strings are UTF-8. + h5call!(hdf5_sys::h5p::H5Pset_char_encoding( + acpl.id(), + hdf5_sys::h5t::H5T_cset_t::H5T_CSET_UTF8 + ))?; + let name = to_cstring(name)?; Attribute::from_id(h5try!(H5Acreate2( parent.id(), name.as_ptr(), datatype.id(), dataspace.id(), - // these args are currently unused as if HDF5 1.12 - // see details: https://portal.hdfgroup.org/display/HDF5/H5A_CREATE2 - H5P_DEFAULT, + acpl.id(), + // Unused as of v1.14 + // see more: https://hdfgroup.github.io/hdf5/v1_14/group___h5_a.html#ga4f4e5248c09f689633079ed8afc0b308 H5P_DEFAULT, ))) } @@ -286,7 +314,7 @@ impl AttributeBuilderInner { #[cfg(test)] pub mod attribute_tests { use crate::internal_prelude::*; - use ndarray::{arr2, Array2}; + use ndarray::{Array2, arr2}; use std::str::FromStr; use types::VarLenUnicode; @@ -298,12 +326,14 @@ pub mod attribute_tests { assert_eq!(d.size(), 6); assert_eq!(d.ndim(), 2); assert_eq!(d.is_scalar(), false); + assert_eq!(d.name(), "name1"); let d = file.new_attr::().shape(()).create("name2").unwrap(); assert_eq!(d.shape(), vec![]); assert_eq!(d.size(), 1); assert_eq!(d.ndim(), 0); assert_eq!(d.is_scalar(), true); + assert_eq!(d.name(), "name2"); }) } @@ -314,9 +344,7 @@ pub mod attribute_tests { let _ = file.new_attr::().shape(()).create("name2").unwrap(); let attr_names = file.attr_names().unwrap(); - assert_eq!(attr_names.len(), 2); - assert!(attr_names.contains(&"name1".to_string())); - assert!(attr_names.contains(&"name2".to_string())); + assert_eq!(attr_names, vec!["name1".to_owned(), "name2".to_owned()]); }) } @@ -329,9 +357,7 @@ pub mod attribute_tests { let _ = ds.new_attr::().shape(()).create("name2").unwrap(); let attr_names = ds.attr_names().unwrap(); - assert_eq!(attr_names.len(), 2); - assert!(attr_names.contains(&"name1".to_string())); - assert!(attr_names.contains(&"name2".to_string())); + assert_eq!(attr_names, vec!["name1".to_owned(), "name2".to_owned()]); }) } @@ -368,9 +394,7 @@ pub mod attribute_tests { let attr = file.new_attr::().shape((1, 2)).create("foo").unwrap(); assert!(attr.is_valid()); assert_eq!(attr.shape(), vec![1, 2]); - // FIXME - attr.name() returns "/" here, which is the name the attribute is connected to, - // not the name of the attribute. - //assert_eq!(attr.name(), "foo"); + assert_eq!(attr.name(), "foo"); assert_eq!(file.attr("foo").unwrap().shape(), vec![1, 2]); }) } @@ -383,9 +407,7 @@ pub mod attribute_tests { let attr = file.new_attr_builder().with_data(&arr).create("foo").unwrap(); assert!(attr.is_valid()); assert_eq!(attr.shape(), vec![2, 3]); - // FIXME - attr.name() returns "/" here, which is the name the attribute is connected to, - // not the name of the attribute. - //assert_eq!(attr.name(), "foo"); + assert_eq!(attr.name(), "foo"); assert_eq!(file.attr("foo").unwrap().shape(), vec![2, 3]); let read_attr = file.attr("foo").unwrap(); diff --git a/hdf5/src/hl/container.rs b/hdf5/src/hl/container.rs index ca59b40f8..c184669a4 100644 --- a/hdf5/src/hl/container.rs +++ b/hdf5/src/hl/container.rs @@ -12,6 +12,7 @@ use hdf5_sys::h5p::H5Pcreate; use crate::internal_prelude::*; +/// A type for reading data from a [`Container`]. #[derive(Debug)] pub struct Reader<'a> { obj: &'a Container, @@ -138,6 +139,21 @@ impl<'a> Reader<'a> { }) } + /// Reads a dataset/attribute into an existing buffer in memory order. + pub fn read_into_raw(&self, buff: &mut [T]) -> Result + where + T: H5Type + Copy, + { + let obj_size = self.obj.space()?.size(); + + if buff.len() < obj_size { + fail!("Buffer size does not match with dataset"); + } + + self.read_into_buf(buff.as_mut_ptr(), None, None)?; + Ok(obj_size) + } + /// Reads a dataset/attribute into a 1-dimensional array. /// /// The dataset/attribute must be 1-dimensional. @@ -188,6 +204,7 @@ impl<'a> Reader<'a> { } } +/// A type for writing data into a [`Container`]. #[derive(Debug)] pub struct Writer<'a> { obj: &'a Container, @@ -347,6 +364,7 @@ impl<'a> Writer<'a> { } } +/// A reader for a 1-dimensional dataset of bytes. #[derive(Debug, Clone)] pub struct ByteReader { obj: Container, @@ -357,6 +375,14 @@ pub struct ByteReader { } impl ByteReader { + /// Creates a new `ByteReader` for the given [`Container`]. + /// + /// # Panics + /// Panics if `obj` is not 1-dimensional. + /// + /// # Errors + /// + /// Returns an error if `obj` does not contain bytes or if the underlying library calls fail. pub fn new(obj: &Container) -> Result { ensure!(!obj.is_attr(), "ByteReader cannot be used on attribute datasets"); @@ -382,6 +408,7 @@ impl ByteReader { self.dataset_len().saturating_sub(self.pos as usize) } + /// Returns `true` if the reader has no more bytes to read. pub fn is_empty(&self) -> bool { self.pos >= self.dataset_len() as u64 } @@ -479,13 +506,13 @@ impl Container { /// Creates a reader wrapper for this dataset/attribute, allowing to /// set custom type conversion options when reading. - pub fn as_reader(&self) -> Reader { + pub fn as_reader(&self) -> Reader<'_> { Reader::new(self) } /// Creates a writer wrapper for this dataset/attribute, allowing to /// set custom type conversion options when writing. - pub fn as_writer(&self) -> Writer { + pub fn as_writer(&self) -> Writer<'_> { Writer::new(self) } @@ -563,6 +590,11 @@ impl Container { self.as_reader().read_raw() } + /// Reads a dataset/attribute into a vector in memory order. + pub fn read_into_raw(&self, buffer: &mut [T]) -> Result { + self.as_reader().read_into_raw(buffer) + } + /// Reads a dataset/attribute into a 1-dimensional array. /// /// The dataset/attribute must be 1-dimensional. @@ -671,3 +703,129 @@ impl Container { self.as_writer().write_scalar(val) } } + +#[cfg(test)] +pub mod tests { + use ndarray::{arr1, arr2, s}; + + use crate::internal_prelude::*; + + /// Every failure below is caught in Rust before HDF5 is called, so none of them carries an + /// HDF5 error stack. If a check is dropped, HDF5 fails later and this catches it. + #[track_caller] + fn assert_is_internal(err: &Error) { + assert!(err.stack().is_none(), "expected a Rust-side error, got an HDF5 stack: {err:?}"); + } + + /// Reading with the wrong dimensionality. + #[test] + pub fn test_read_ndim_mismatch() { + with_tmp_file(|file| { + let ds = file.new_dataset::().shape([4, 3]).create("d").unwrap(); + ds.write(&arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])).unwrap(); + + let err = ds.read_1d::().unwrap_err(); + assert_eq!(err.to_string(), "ndim mismatch: expected 1, got 2"); + assert_is_internal(&err); + + // the same container logic backs attributes + let attr = ds.new_attr::().shape([2, 2]).create("a").unwrap(); + let err = attr.read_1d::().unwrap_err(); + assert_eq!(err.to_string(), "ndim mismatch: expected 1, got 2"); + assert_is_internal(&err); + + // matching read + assert_eq!(ds.read_2d::().unwrap().dim(), (4, 3)); + }) + } + + /// Reading a non-scalar as a scalar, and writing a scalar to a non-scalar. + #[test] + pub fn test_scalar_ndim_mismatch() { + with_tmp_file(|file| { + let ds = file.new_dataset::().shape([4, 3]).create("d").unwrap(); + let err = ds.read_scalar::().unwrap_err(); + assert_eq!(err.to_string(), "ndim mismatch: expected scalar, got 2"); + assert_is_internal(&err); + + let err = ds.write_scalar(&1i32).unwrap_err(); + assert_eq!(err.to_string(), "ndim mismatch: expected scalar, got 2"); + assert_is_internal(&err); + + let scalar = file.new_dataset::().shape(()).create("s").unwrap(); + scalar.write_scalar(&42i32).unwrap(); + assert_eq!(scalar.read_scalar::().unwrap(), 42); + }) + } + + /// Writing an array whose shape does not match the destination. + #[test] + pub fn test_write_shape_mismatch() { + with_tmp_file(|file| { + let ds = file.new_dataset::().shape([4, 3]).create("d").unwrap(); + let err = ds.write(&arr1(&[1i32, 2, 3])).unwrap_err(); + assert_eq!( + err.to_string(), + "shape mismatch when writing: memory = [3], destination = [4, 3]" + ); + assert_is_internal(&err); + + // correctly shaped write + ds.write(&arr2(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])).unwrap(); + }) + } + + /// Selecting outside the extents of the container. + #[test] + pub fn test_selection_out_of_bounds() { + with_tmp_file(|file| { + let ds = file.new_dataset::().shape([4, 3]).create("d").unwrap(); + let err = ds.read_slice_1d::(s![99, ..]).unwrap_err(); + assert_eq!(err.to_string(), "Index 99 out of bounds for axis 0 with size 4"); + assert_is_internal(&err); + + let err = ds.write_slice(&arr1(&[1i32, 2, 3]), s![99, ..]).unwrap_err(); + assert_eq!(err.to_string(), "Index 99 out of bounds for axis 0 with size 4"); + assert_is_internal(&err); + + // in-bounds selection + assert_eq!(ds.read_slice_1d::(s![3, ..]).unwrap().len(), 3); + }) + } + + /// Reading as a type with no conversion path from the stored type. + #[test] + pub fn test_no_conversion_path() { + with_tmp_file(|file| { + let ds = + file.new_dataset::>().shape([2]).create("s").unwrap(); + let err = ds.read_1d::().unwrap_err(); + assert_eq!( + err.to_string(), + "no conversion paths found from '' to ''" + ); + assert_is_internal(&err); + }) + } + + /// Demanding a stricter conversion than the types allow. + #[test] + pub fn test_conversion_level_too_strict() { + with_tmp_file(|file| { + let ds = file.new_dataset::().shape([2]).create("d").unwrap(); + ds.write(&arr1(&[1i32, 2])).unwrap(); + + // i32 -> i64 is a hard conversion, so demanding no-op must fail + let err = ds.as_reader().no_convert().read_1d::().unwrap_err(); + assert_eq!( + err.to_string(), + "Cannot convert from int32 to int64, required conversion no-op; available: hard" + ); + assert_is_internal(&err); + + // the default (soft) allows it, and no-op is fine for the exact type + assert_eq!(ds.read_1d::().unwrap(), arr1(&[1i64, 2])); + assert_eq!(ds.as_reader().no_convert().read_1d::().unwrap(), arr1(&[1i32, 2])); + }) + } +} diff --git a/hdf5/src/hl/dataset.rs b/hdf5/src/hl/dataset.rs index 77807c530..a9388dbe5 100644 --- a/hdf5/src/hl/dataset.rs +++ b/hdf5/src/hl/dataset.rs @@ -1,18 +1,12 @@ +//! Interfaces for `Dataset` objects. + use std::fmt::{self, Debug}; use std::ops::Deref; use ndarray::{self, ArrayView}; -use hdf5_sys::h5::HADDR_UNDEF; -use hdf5_sys::h5d::{ - H5Dcreate2, H5Dcreate_anon, H5Dget_access_plist, H5Dget_create_plist, H5Dget_offset, - H5Dset_extent, -}; -use hdf5_sys::h5l::H5Ldelete; -use hdf5_sys::h5p::H5P_DEFAULT; -use hdf5_sys::h5z::H5Z_filter_t; -use hdf5_types::{OwnedDynValue, TypeDescriptor}; - +#[cfg(feature = "zfp")] +use crate::hl; #[cfg(feature = "blosc")] use crate::hl::filters::{Blosc, BloscShuffle}; use crate::hl::filters::{Filter, SZip, ScaleOffset}; @@ -26,6 +20,17 @@ use crate::hl::plist::dataset_create::{ }; use crate::hl::plist::link_create::{CharEncoding, LinkCreate, LinkCreateBuilder}; use crate::internal_prelude::*; +use hdf5_sys::h5::HADDR_UNDEF; +use hdf5_sys::h5d::{ + H5Dcreate_anon, H5Dcreate2, H5Dget_access_plist, H5Dget_create_plist, H5Dget_offset, + H5Dset_extent, +}; +#[cfg(feature = "1.10.0")] +use hdf5_sys::h5d::{H5Dflush, H5Drefresh}; +use hdf5_sys::h5l::H5Ldelete; +use hdf5_sys::h5p::H5P_DEFAULT; +use hdf5_sys::h5z::H5Z_filter_t; +use hdf5_types::{OwnedDynValue, TypeDescriptor}; /// Default chunk size when filters are enabled and the chunk size is not specified. pub const DEFAULT_CHUNK_SIZE_KB: usize = 64 * 1024; @@ -154,6 +159,22 @@ impl Dataset { pub fn filters(&self) -> Vec { self.dcpl().map_or(Vec::default(), |pl| pl.filters()) } + + /// Flush the dataset metadata from the metadata cache to the file + #[cfg(feature = "1.10.0")] + pub fn flush(&self) -> Result<()> { + let id = self.id(); + h5call!(H5Dflush(id))?; + Ok(()) + } + + /// Refresh metadata items assosicated with the dataset + #[cfg(feature = "1.10.0")] + pub fn refresh(&self) -> Result<()> { + let id = self.id(); + h5call!(H5Drefresh(id))?; + Ok(()) + } } pub struct Maybe(Option); @@ -227,6 +248,34 @@ impl DatasetBuilder { conv: Conversion::Soft, } } + // + // #[cfg(feature = "zfp")] + // pub fn zfp_rate(self, rate: f64) -> Self { + // let new_ds = self.with_dcpl(|p| p.set_filters(&vec![Filter::zfp_rate(rate)])); + // + // new_ds + // } + // + // #[cfg(feature = "zfp")] + // pub fn zfp_precision(self, precision: u8) -> Self { + // let new_ds = self.with_dcpl(|p| p.set_filters(&vec![Filter::zfp_precision(precision)])); + // + // new_ds + // } + // + // #[cfg(feature = "zfp")] + // pub fn zfp_accuracy(self, accuracy: f64) -> Self { + // let new_ds = self.with_dcpl(|p| p.set_filters(&vec![Filter::zfp_accuracy(accuracy)])); + // + // new_ds + // } + // + // #[cfg(feature = "zfp")] + // pub fn zfp_reversible(self) -> Self { + // let new_ds = self.with_dcpl(|p| p.set_filters(&vec![Filter::zfp_reversible()])); + // + // new_ds + // } } #[derive(Clone)] @@ -311,11 +360,15 @@ where } } +/// Options for how to chunk data. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Chunk { - Exact(Vec), // exact chunk shape - MinKB(usize), // minimum chunk shape in KB - None, // leave it unchunked + /// Exact chunk shape. + Exact(Vec), + /// Minimum chunk shape in kilobytes. + MinKB(usize), + /// Leave the data unchunked. + None, } impl Default for Chunk { @@ -340,11 +393,7 @@ fn compute_chunk_shape(dims: &SimpleExtents, minimum_elements: usize) -> Vec // in dividing the chunk in two uneven parts, // we instead merge these into the same chunk // to prevent having small chunks - if 2 * wanted_size > maxdim + 1 { - maxdim - } else { - std::cmp::min(wanted_size, maxdim) - } + if 2 * wanted_size > maxdim + 1 { maxdim } else { std::cmp::min(wanted_size, maxdim) } }); product_cs *= *cs; @@ -695,6 +744,36 @@ impl DatasetBuilderInner { self.with_dcpl(|pl| pl.blosc_zstd(clevel, shuffle)); } + #[cfg(feature = "zfp")] + pub fn zfp_rate(&mut self, rate: f64, chunk_dims: Vec, n_bytes: u8) { + hl::filters::zfp::register_zfp().expect("Failed to register ZFP filter"); + self.with_dcpl(|p| { + p.set_filters(&vec![Filter::zfp_rate(rate, chunk_dims.clone(), n_bytes)]) + }); + } + + #[cfg(feature = "zfp")] + pub fn zfp_precision(&mut self, precision: u8, chunk_dims: Vec, n_bytes: u8) { + hl::filters::zfp::register_zfp().expect("Failed to register ZFP filter"); + self.with_dcpl(|p| { + p.set_filters(&vec![Filter::zfp_precision(precision, chunk_dims.clone(), n_bytes)]) + }); + } + + #[cfg(feature = "zfp")] + pub fn zfp_accuracy(&mut self, accuracy: f64, chunk_dims: Vec, n_bytes: u8) { + hl::filters::zfp::register_zfp().expect("Failed to register ZFP filter"); + self.with_dcpl(|pl| pl.zfp_accuracy(accuracy, chunk_dims.clone(), n_bytes)); + } + + #[cfg(feature = "zfp")] + pub fn zfp_reversible(&mut self, chunk_dims: Vec, n_bytes: u8) { + hl::filters::zfp::register_zfp().expect("Failed to register ZFP filter"); + self.with_dcpl(|p| { + p.set_filters(&vec![Filter::zfp_reversible(chunk_dims.clone(), n_bytes)]) + }); + } + pub fn add_filter(&mut self, id: H5Z_filter_t, cdata: &[c_uint]) { self.with_dcpl(|pl| pl.add_filter(id, cdata)); } @@ -825,7 +904,7 @@ impl DatasetBuilderInner { macro_rules! impl_builder { ($plist:ident: $name:ident/$short:ident) => { - paste::paste! { + pastey::paste! { #[inline] #[must_use] pub fn [](mut self, $short: &$plist) -> Self { self.builder.[]($short); self @@ -873,7 +952,7 @@ macro_rules! impl_builder { $(#[$meta:meta])* $plist:ident: $name:ident($($var:ident: $ty:ty),*) ) => { - paste::paste! { + pastey::paste! { $(#[$meta])* #[inline] #[must_use] #[doc = "\u{21b3} [`" $plist "Builder::" $name "`]" @@ -888,7 +967,7 @@ macro_rules! impl_builder { $(#[$meta:meta])* $plist:ident: $name:ident<$($gid:ident: $gty:path),+>($($var:ident: $ty:ty),*) ) => { - paste::paste! { + pastey::paste! { $(#[$meta])* #[inline] #[must_use] #[doc = "\u{21b3} [`" $plist "Builder::" $name "`]" @@ -954,6 +1033,25 @@ macro_rules! impl_builder_methods { #[cfg(feature = "blosc-zstd")] DatasetCreate: blosc_zstd(clevel: u8, shuffle: impl Into) ); + + impl_builder!( + #[cfg(feature = "zfp")] + DatasetCreate: zfp_rate(rate: f64,chunk_dims: Vec,n_bytes: u8) + ); + impl_builder!( + #[cfg(feature = "zfp")] + DatasetCreate: zfp_accuracy(accuracy: f64,chunk_dims: Vec,n_bytes: u8) + ); + impl_builder!( + #[cfg(feature = "zfp")] + DatasetCreate: zfp_precision(rate: u8,chunk_dims: Vec,n_bytes: u8) + ); + impl_builder!( + #[cfg(feature = "zfp")] + DatasetCreate: zfp_reversible(chunk_dims: Vec,n_bytes: u8) + ); + + impl_builder!(DatasetCreate: add_filter(id: H5Z_filter_t, cdata: &[c_uint])); impl_builder!(DatasetCreate: clear_filters()); impl_builder!(DatasetCreate: alloc_time(alloc_time: Option)); @@ -1009,7 +1107,7 @@ impl<'d, T2: H5Type, D2: ndarray::Dimension> DatasetBuilderData<'d, T2, D2> { #[cfg(test)] mod tests { - use super::{compute_chunk_shape, DatasetBuilder}; + use super::{DatasetBuilder, compute_chunk_shape}; use crate::filters::Filter; use crate::test::with_tmp_file; use crate::{Extent, Result, SimpleExtents}; diff --git a/hdf5/src/hl/dataspace.rs b/hdf5/src/hl/dataspace.rs index 58c9af2f6..a94c69299 100644 --- a/hdf5/src/hl/dataspace.rs +++ b/hdf5/src/hl/dataspace.rs @@ -8,9 +8,9 @@ use hdf5_sys::h5s::H5Sencode1; use hdf5_sys::h5s::H5Sencode2; use hdf5_sys::h5s::{ - H5S_class_t, H5Scopy, H5Screate, H5Screate_simple, H5Sdecode, H5Sget_select_npoints, - H5Sget_simple_extent_dims, H5Sget_simple_extent_ndims, H5Sget_simple_extent_npoints, - H5Sget_simple_extent_type, H5Sselect_valid, H5S_UNLIMITED, + H5S_UNLIMITED, H5S_class_t, H5Scopy, H5Screate, H5Screate_simple, H5Sdecode, + H5Sget_select_npoints, H5Sget_simple_extent_dims, H5Sget_simple_extent_ndims, + H5Sget_simple_extent_npoints, H5Sget_simple_extent_type, H5Sselect_valid, }; use crate::hl::extents::{Extent, Extents, Ix}; @@ -35,11 +35,7 @@ impl ObjectClass for Dataspace { } fn short_repr(&self) -> Option { - if let Ok(e) = self.extents() { - Some(format!("{e}")) - } else { - Some("(invalid)".into()) - } + if let Ok(e) = self.extents() { Some(format!("{e}")) } else { Some("(invalid)".into()) } } } @@ -78,46 +74,62 @@ unsafe fn get_simple_extents(space_id: hid_t) -> Result { } impl Dataspace { + /// Tries to construct a `Dataspace` from the given extents. + /// + /// # Errors + /// + /// Returns an error if the extents are invalid. pub fn try_new>(extents: T) -> Result { Self::from_extents(&extents.into()) } + /// Creates a new `Dataspace` that is a copy of the current one. pub fn copy(&self) -> Self { Self::from_id(h5lock!(H5Scopy(self.id()))).unwrap_or_else(|_| Self::invalid()) } + /// Returns the number of dimensions in the space. pub fn ndim(&self) -> usize { h5call!(H5Sget_simple_extent_ndims(self.id())).unwrap_or(0) as _ } + /// Returns a vector containing the current size of each dimension. pub fn shape(&self) -> Vec { h5lock!(get_shape(self.id())).unwrap_or_default() } + /// Returns a vector containing the current maximum size (if set) of each dimension. pub fn maxdims(&self) -> Vec> { self.extents().unwrap_or(Extents::Null).maxdims() } + /// Returns `true` if any dimension is resizable. pub fn is_resizable(&self) -> bool { self.maxdims().iter().any(Option::is_none) } + /// Returns `true` if the extent type is null. pub fn is_null(&self) -> bool { h5lock!(H5Sget_simple_extent_type(self.id())) == H5S_class_t::H5S_NULL } + /// Returns `true` if the extent type is scalar. pub fn is_scalar(&self) -> bool { h5lock!(H5Sget_simple_extent_type(self.id())) == H5S_class_t::H5S_SCALAR } + /// Returns `true` if the extent type is simple. pub fn is_simple(&self) -> bool { h5lock!(H5Sget_simple_extent_type(self.id())) == H5S_class_t::H5S_SIMPLE } + /// Returns `true` if the selection for the space is within the dataspace extent if the current + /// offset is used. pub fn is_valid(&self) -> bool { h5lock!(H5Sselect_valid(self.id())) > 0 } + /// Returns the number of elements in the space. pub fn size(&self) -> usize { match h5lock!(H5Sget_simple_extent_type(self.id())) { H5S_class_t::H5S_SIMPLE => { @@ -128,6 +140,11 @@ impl Dataspace { } } + /// Encodes the dataspace description into a new byte vector. + /// + /// # Errors + /// + /// Returns an error if the underlying encoding call fails. #[allow(deprecated)] pub fn encode(&self) -> Result> { cfg_if::cfg_if! { @@ -152,6 +169,11 @@ impl Dataspace { } } + /// Tries to decode a dataspace from a byte buffer. + /// + /// # Errors + /// + /// Returns an error if the buffer does not decode to a valid dataspace. pub fn decode(buf: T) -> Result where T: AsRef<[u8]>, @@ -163,7 +185,7 @@ impl Dataspace { h5lock!(Self::from_id(match extents { Extents::Null => H5Screate(H5S_class_t::H5S_NULL), Extents::Scalar => H5Screate(H5S_class_t::H5S_SCALAR), - Extents::Simple(ref e) => { + Extents::Simple(e) => { let (mut dims, mut maxdims) = (vec![], vec![]); for extent in e.iter() { dims.push(extent.dim as _); @@ -174,6 +196,11 @@ impl Dataspace { })) } + /// Returns the extents of the dataspace. + /// + /// # Errors + /// + /// Returns an error if the extents type is unsupported. #[allow(clippy::match_wildcard_for_single_variants)] pub fn extents(&self) -> Result { h5lock!(match H5Sget_simple_extent_type(self.id()) { @@ -184,6 +211,7 @@ impl Dataspace { }) } + /// Returns the number of elements in the dataspace selection. pub fn selection_size(&self) -> usize { h5call!(H5Sget_select_npoints(self.id())).ok().map_or(0, |x| x as _) } @@ -199,6 +227,7 @@ impl Dataspace { }) } + /// Selects part of the dataspace and returns a new dataspace selection object. pub fn select>(&self, selection: S) -> Result { let raw_sel = selection.into().into_raw(self.shape())?; self.select_raw(raw_sel) @@ -209,6 +238,7 @@ impl Dataspace { sync(|| unsafe { RawSelection::extract_from_dataspace(self.id()) }) } + /// Returns a description of the current dataspace selection. pub fn get_selection(&self) -> Result { let raw_sel = self.get_raw_selection()?; Selection::from_raw(raw_sel) diff --git a/hdf5/src/hl/datatype.rs b/hdf5/src/hl/datatype.rs index 6f4847313..7440fc942 100644 --- a/hdf5/src/hl/datatype.rs +++ b/hdf5/src/hl/datatype.rs @@ -5,12 +5,12 @@ use std::ops::Deref; use std::ptr::{addr_of, addr_of_mut}; use hdf5_sys::h5t::{ - H5T_cdata_t, H5T_class_t, H5T_cset_t, H5T_order_t, H5T_sign_t, H5T_str_t, H5Tarray_create2, - H5Tcompiler_conv, H5Tcopy, H5Tcreate, H5Tenum_create, H5Tenum_insert, H5Tequal, H5Tfind, - H5Tget_array_dims2, H5Tget_array_ndims, H5Tget_class, H5Tget_cset, H5Tget_member_name, - H5Tget_member_offset, H5Tget_member_type, H5Tget_member_value, H5Tget_nmembers, H5Tget_order, - H5Tget_sign, H5Tget_size, H5Tget_super, H5Tinsert, H5Tis_variable_str, H5Tset_cset, - H5Tset_size, H5Tset_strpad, H5Tvlen_create, H5T_VARIABLE, + H5T_VARIABLE, H5T_cdata_t, H5T_class_t, H5T_cset_t, H5T_order_t, H5T_sign_t, H5T_str_t, + H5Tarray_create2, H5Tcompiler_conv, H5Tcopy, H5Tcreate, H5Tdetect_class, H5Tenum_create, + H5Tenum_insert, H5Tequal, H5Tfind, H5Tget_array_dims2, H5Tget_array_ndims, H5Tget_class, + H5Tget_cset, H5Tget_member_name, H5Tget_member_offset, H5Tget_member_type, H5Tget_member_value, + H5Tget_nmembers, H5Tget_order, H5Tget_sign, H5Tget_size, H5Tget_super, H5Tinsert, + H5Tis_variable_str, H5Tset_cset, H5Tset_size, H5Tset_strpad, H5Tvlen_create, }; use hdf5_types::{ CompoundField, CompoundType, EnumMember, EnumType, FloatSize, H5Type, IntSize, TypeDescriptor, @@ -19,16 +19,60 @@ use hdf5_types::{ use crate::globals::{H5T_C_S1, H5T_NATIVE_INT, H5T_NATIVE_INT8}; use crate::internal_prelude::*; +enum EnumMembers<'a> { + Bool, + Enum(&'a [EnumMember]), +} + +const BOOL_MEMBER_NAMES: &[&str] = &["FALSE", "TRUE"]; + +impl EnumMembers<'_> { + /// Checks the fixed bool names or scans the enum member slice. + fn contains_name(&self, name: &str) -> bool { + match self { + Self::Bool => BOOL_MEMBER_NAMES.contains(&name), + Self::Enum(members) => members.iter().any(|member| member.name == name), + } + } + + /// Collects source names that the destination member set cannot resolve. + fn missing_from(&self, dst: &Self) -> Vec { + match self { + Self::Bool => BOOL_MEMBER_NAMES + .iter() + .filter(|name| !dst.contains_name(name)) + .map(|name| name.to_string()) + .collect(), + Self::Enum(members) => members + .iter() + .filter(|member| !dst.contains_name(&member.name)) + .map(|member| member.name.to_owned()) + .collect(), + } + } +} + +impl<'a> EnumMembers<'a> { + /// Borrows enum members from descriptors, bool uses its normalized HDF5 enum names. + fn from_descriptor(desc: &'a TypeDescriptor) -> Option { + match desc { + TypeDescriptor::Boolean => Some(Self::Bool), + TypeDescriptor::Enum(enum_type) => Some(Self::Enum(&enum_type.members)), + _ => None, + } + } +} + #[cfg(target_endian = "big")] use crate::globals::{ - H5T_IEEE_F32BE, H5T_IEEE_F64BE, H5T_STD_I16BE, H5T_STD_I32BE, H5T_STD_I64BE, H5T_STD_I8BE, - H5T_STD_U16BE, H5T_STD_U32BE, H5T_STD_U64BE, H5T_STD_U8BE, + H5T_IEEE_F32BE, H5T_IEEE_F64BE, H5T_STD_I8BE, H5T_STD_I16BE, H5T_STD_I32BE, H5T_STD_I64BE, + H5T_STD_U8BE, H5T_STD_U16BE, H5T_STD_U32BE, H5T_STD_U64BE, }; #[cfg(target_endian = "little")] use crate::globals::{ - H5T_IEEE_F32LE, H5T_IEEE_F64LE, H5T_STD_I16LE, H5T_STD_I32LE, H5T_STD_I64LE, H5T_STD_I8LE, - H5T_STD_U16LE, H5T_STD_U32LE, H5T_STD_U64LE, H5T_STD_U8LE, + H5T_IEEE_F32LE, H5T_IEEE_F64LE, H5T_STD_I8LE, H5T_STD_I16LE, H5T_STD_I32LE, H5T_STD_I64LE, + H5T_STD_U8LE, H5T_STD_U16LE, H5T_STD_U32LE, H5T_STD_U64LE, }; #[cfg(target_endian = "big")] @@ -62,7 +106,26 @@ impl ObjectClass for Datatype { &self.0 } - // TODO: short_repr() + fn short_repr(&self) -> Option { + let repr = match self.to_descriptor() { + Ok(s) => s.to_string(), + Err(e) => { + // Fallback: show basic HDF5 info if descriptor conversion fails + h5lock!({ + let class = H5Tget_class(self.id()); + let size = H5Tget_size(self.id()); + format!("Invalid datatype: {e} )") + }) + } + }; + Some(repr) + } +} + +impl Display for Datatype { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.short_repr().expect("short_repr is always implemented")) + } } impl Debug for Datatype { @@ -85,10 +148,14 @@ impl PartialEq for Datatype { } } +/// A level of possible conversion between two types. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Conversion { + /// No conversion. NoOp = 1, // TODO: rename to "None"? + /// Functions employing compiler casting. Hard, + /// Functions not employing compiler casting. Soft, } @@ -120,12 +187,18 @@ impl Default for Conversion { } } +/// The byte order of a datatype. #[derive(Copy, Debug, Clone, PartialEq, Eq)] pub enum ByteOrder { + /// Little endian. LittleEndian, + /// Big endian. BigEndian, + /// VAX mixed endian. Vax, + /// Compound type with mixed member orders. Mixed, + /// No particular order. None, } @@ -166,6 +239,7 @@ impl Datatype { h5lock!(H5Tget_order(self.id())).into() } + /// Returns the conversion function level from `self` to `dst`, if one exists. pub fn conv_path(&self, dst: D) -> Option where D: Borrow, @@ -187,33 +261,102 @@ impl Datatype { }) } + /// Returns the conversion function level from `self` to a concrete type, if one exists. pub fn conv_to(&self) -> Option { Self::from_type::().ok().and_then(|dtype| self.conv_path(dtype)) } + /// Returns the conversion function level from a concrete type to `self`, if one exists. pub fn conv_from(&self) -> Option { Self::from_type::().ok().and_then(|dtype| dtype.conv_path(self)) } + /// Returns `true` if `self` represents a concrete type. pub fn is(&self) -> bool { Self::from_type::().ok().map_or(false, |dtype| &dtype == self) } pub(crate) fn ensure_convertible(&self, dst: &Self, required: Conversion) -> Result<()> { - // TODO: more detailed error messages after Debug/Display are implemented for Datatype if let Some(conv) = self.conv_path(dst) { ensure!( conv <= required, - "{} conversion path required; available: {} conversion", - required, - conv + "Cannot convert from {self} to {dst}, required conversion {required}; available: {conv}", ); + if self.contains_enum()? || dst.contains_enum()? { + Self::ensure_enum_members_convertible( + &self.to_descriptor()?, + &dst.to_descriptor()?, + "datatype", + )?; + } Ok(()) } else { - fail!("no conversion paths found") + fail!("no conversion paths found from '{self:#?}' to '{dst:#?}'",) + } + } + + /// Uses HDF5 class detection to avoid descriptor conversion for non-enum datatypes. + fn contains_enum(&self) -> Result { + Ok(h5call!(H5Tdetect_class(self.id(), H5T_class_t::H5T_ENUM))? > 0) + } + + /// Compares enum leaves directly, otherwise follows matching nested descriptors. + fn ensure_enum_members_convertible( + src: &TypeDescriptor, dst: &TypeDescriptor, path: &str, + ) -> Result<()> { + if let Some((src_members, dst_members)) = Self::enum_member_sets(src, dst) { + Self::ensure_enum_member_names_present(&src_members, &dst_members, path) + } else { + Self::ensure_nested_enum_members_convertible(src, dst, path) + } + } + + /// Treats bool as the HDF5 FALSE/TRUE enum when both descriptors are enum-like. + fn enum_member_sets<'a>( + src: &'a TypeDescriptor, dst: &'a TypeDescriptor, + ) -> Option<(EnumMembers<'a>, EnumMembers<'a>)> { + Some((EnumMembers::from_descriptor(src)?, EnumMembers::from_descriptor(dst)?)) + } + + /// Fails when a source enum name is absent from the destination enum. + fn ensure_enum_member_names_present( + src_members: &EnumMembers<'_>, dst_members: &EnumMembers<'_>, path: &str, + ) -> Result<()> { + let missing = src_members.missing_from(dst_members); + ensure!( + missing.is_empty(), + "Cannot convert enum at {path}: destination is missing source member{pluralize}: {}", + missing.join(", "), + pluralize = if missing.len() == 1 { "" } else { "s" } + ); + Ok(()) + } + + /// Descends through matching compound fields and array element descriptors. + fn ensure_nested_enum_members_convertible( + src: &TypeDescriptor, dst: &TypeDescriptor, path: &str, + ) -> Result<()> { + match (src, dst) { + (TypeDescriptor::Compound(src_type), TypeDescriptor::Compound(dst_type)) => { + for &CompoundField { ref name, ref ty, offset: _, index: _ } in &src_type.fields { + if let Some(dst_field) = + dst_type.fields.iter().find(|dst_field| dst_field.name == *name) + { + let field_path = format!("{path}.{name}"); + Self::ensure_enum_members_convertible(&ty, &dst_field.ty, &field_path)?; + } + } + } + (TypeDescriptor::FixedArray(src_type, _), TypeDescriptor::FixedArray(dst_type, _)) + | (TypeDescriptor::VarLenArray(src_type), TypeDescriptor::VarLenArray(dst_type)) => { + Self::ensure_enum_members_convertible(src_type, dst_type, path)?; + } + _ => {} } + Ok(()) } + /// Returns a type descriptor for the datatype. pub fn to_descriptor(&self) -> Result { use hdf5_types::TypeDescriptor as TD; @@ -302,15 +445,30 @@ impl Datatype { let base_dt = Self::from_id(H5Tget_super(id))?; Ok(TD::VarLenArray(Box::new(base_dt.to_descriptor()?))) } + H5T_class_t::H5T_REFERENCE => { + #[cfg(feature = "1.12.0")] + if h5try!(H5Tequal(id, *crate::globals::H5T_STD_REF)) > 0 { + return Ok(TD::Reference(hdf5_types::Reference::Std)); + } + if h5try!(H5Tequal(id, *crate::globals::H5T_STD_REF_OBJ)) > 0 { + Ok(TD::Reference(hdf5_types::Reference::Object)) + } else if h5try!(H5Tequal(id, *crate::globals::H5T_STD_REF_DSETREG)) > 0 { + Ok(TD::Reference(hdf5_types::Reference::Region)) + } else { + Err("Unsupported reference datatype".into()) + } + } _ => Err("Unsupported datatype class".into()), } }) } + /// Creates a datatype from a concrete type. pub fn from_type() -> Result { Self::from_descriptor(&::type_descriptor()) } + /// Creates a datatype from a type descriptor. pub fn from_descriptor(desc: &TypeDescriptor) -> Result { use hdf5_types::TypeDescriptor as TD; @@ -356,7 +514,7 @@ impl Datatype { #[cfg(feature = "f16")] FloatSize::U2 => f16_type()?, FloatSize::U4 => be_le!(H5T_IEEE_F32BE, H5T_IEEE_F32LE), - FloatSize::U8 => be_le!(H5T_IEEE_I16BE, H5T_IEEE_F64LE), + FloatSize::U8 => be_le!(H5T_IEEE_F64BE, H5T_IEEE_F64LE), }), TD::Boolean => { let bool_id = h5try!(H5Tenum_create(*H5T_NATIVE_INT8)); @@ -427,3 +585,37 @@ impl Datatype { Self::from_id(datatype_id?) } } + +/// NOTE: tests of public functions are in hdf5/tests/test_datatype.rs +#[cfg(test)] +mod tests { + use super::*; + use hdf5_types::{FixedAscii, FixedUnicode}; + use pretty_assertions::assert_str_eq; + + #[test] + fn test_ensure_convertible_fail_err_msg() { + const SIZE: usize = 10; + let src = Datatype::from_type::>().unwrap(); + let dst = Datatype::from_type::>().unwrap(); + + let err_msg = src.ensure_convertible(&dst, Conversion::NoOp).unwrap_err().to_string(); + + assert_str_eq!( + err_msg, + "no conversion paths found from '' to ''" + ); + } + + #[test] + fn test_ensure_convertible_failed_required_conversion_hard_err_msg() { + let src = Datatype::from_type::().unwrap(); + let dst = Datatype::from_type::().unwrap(); + + let err_msg = src.ensure_convertible(&dst, Conversion::NoOp).unwrap_err().to_string(); + assert_str_eq!( + err_msg, + "Cannot convert from uint64 to int64, required conversion no-op; available: hard" + ); + } +} diff --git a/hdf5/src/hl/extents.rs b/hdf5/src/hl/extents.rs index 9eeba3d22..0cf2abbde 100644 --- a/hdf5/src/hl/extents.rs +++ b/hdf5/src/hl/extents.rs @@ -26,11 +26,7 @@ impl Display for Extent { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let dim = self.dim; if let Some(max) = self.max { - if self.dim == max { - write!(f, "{dim}") - } else { - write!(f, "{dim}..={max}") - } + if self.dim == max { write!(f, "{dim}") } else { write!(f, "{dim}..={max}") } } else { write!(f, "{dim}..") } @@ -68,6 +64,7 @@ impl + Clone> From<&T> for Extent { } impl Extent { + /// Creates a new extent with a current size and (optional) maximum size. pub fn new(dim: Ix, max: Option) -> Self { Self { dim, max } } @@ -82,18 +79,22 @@ impl Extent { Self { dim, max: None } } + /// Returns `true` if the current dimension size is at least the maximum size, if set. pub fn is_fixed(&self) -> bool { self.max.map_or(false, |max| self.dim >= max) } + /// Returns `true` if the dimension is resizable. pub fn is_resizable(&self) -> bool { self.max.is_none() } + /// Returns `true` if the dimension has unlimited maximum size. pub fn is_unlimited(&self) -> bool { self.is_resizable() } + /// Returns `true` if the current dimension size is not greater than the maximum. pub fn is_valid(&self) -> bool { self.max.unwrap_or(self.dim) >= self.dim } @@ -113,10 +114,12 @@ pub struct SimpleExtents { } impl SimpleExtents { + /// Creates extents from a vector of `Extent` values. pub fn from_vec(extents: Vec) -> Self { Self { inner: extents } } + /// Creates extents from an iterable of extent-like values. pub fn new(extents: T) -> Self where T: IntoIterator, @@ -125,6 +128,8 @@ impl SimpleExtents { Self::from_vec(extents.into_iter().map(Into::into).collect()) } + /// Creates extents from an iterable of dimension sizes and sets their + /// maximum sizes equal to their current sizes. pub fn fixed(extents: T) -> Self where T: IntoIterator, @@ -142,38 +147,48 @@ impl SimpleExtents { Self::from_vec(extents.into_iter().map(|x| Extent::resizable(*x.borrow())).collect()) } + /// Returns the number of dimensions. pub fn ndim(&self) -> usize { self.inner.len() } + /// Returns a vector containing the current size of each dimension. pub fn dims(&self) -> Vec { self.inner.iter().map(|e| e.dim).collect() } + /// Returns a vector containing the maximum size (if set) of each dimension. pub fn maxdims(&self) -> Vec> { self.inner.iter().map(|e| e.max).collect() } + /// Returns the number of elements that the space can hold. pub fn size(&self) -> usize { self.inner.iter().fold(1, |acc, x| acc * x.dim) } + /// Returns `true` if there is at least one dimension and they all have fixed maximum size. pub fn is_fixed(&self) -> bool { !self.inner.is_empty() && self.inner.iter().map(Extent::is_fixed).all(identity) } + /// Returns `true` if there is at least one resizable dimension. pub fn is_resizable(&self) -> bool { !self.inner.is_empty() && self.inner.iter().map(Extent::is_unlimited).any(identity) } + /// Returns `true` if there is at least one dimension of unlimited size. pub fn is_unlimited(&self) -> bool { self.inner.iter().map(Extent::is_unlimited).any(identity) } + /// Returns `true` if the extents are valid and the dimensionality does not exceed + /// HDF5's max rank. pub fn is_valid(&self) -> bool { self.inner.iter().map(Extent::is_valid).all(identity) && self.ndim() <= H5S_MAX_RANK as _ } + /// Returns an iterator over the `Extent` values for each dimension. pub fn iter( &self, ) -> impl ExactSizeIterator + DoubleEndedIterator { @@ -290,6 +305,7 @@ impl_from!(RangeFrom); impl_from!(RangeInclusive); impl_from!(Extent); +/// The dimensionality of a dataspace. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Extents { /// A null dataspace contains no data elements. @@ -324,6 +340,7 @@ pub enum Extents { } impl Extents { + /// Creates extents from a value. pub fn new>(extents: T) -> Self { extents.into() } @@ -345,7 +362,7 @@ impl Extents { fn as_simple(&self) -> Option<&SimpleExtents> { match self { - Self::Simple(ref e) => Some(e), + Self::Simple(e) => Some(e), _ => None, } } @@ -389,18 +406,22 @@ impl Extents { } } + /// Returns `true` if the extents are valid. pub fn is_valid(&self) -> bool { self.as_simple().map_or(true, SimpleExtents::is_valid) } + /// Returns `true` if the extents have unlimited maximum size. pub fn is_unlimited(&self) -> bool { self.as_simple().map_or(true, SimpleExtents::is_unlimited) } + /// Returns `true` if there is at least one resizable dimension. pub fn is_resizable(&self) -> bool { self.as_simple().map_or(true, SimpleExtents::is_resizable) } + /// Returns a version of `self` that is resizable along all dimensions. pub fn resizable(self) -> Self { match self { Self::Simple(extents) => SimpleExtents::resizable(extents.dims()).into(), @@ -408,21 +429,21 @@ impl Extents { } } + /// Returns an iterator of `Extent` values, one for each dimension. pub fn iter( &self, ) -> impl ExactSizeIterator + DoubleEndedIterator { ExtentsIter { inner: self.as_simple().map(SimpleExtents::iter) } } + /// Returns a slice containing an `Extent` for each dimension, or `None` if the extents are + /// null or scalar. pub fn slice(&self) -> Option<&[Extent]> { - if let Self::Simple(x) = self { - Some(x) - } else { - None - } + if let Self::Simple(x) = self { Some(x) } else { None } } } +/// An iterator over the dimensions of an `Extents`. pub struct ExtentsIter { inner: Option, } @@ -467,7 +488,7 @@ impl Display for Extents { match self { Self::Null => write!(f, "null"), Self::Scalar => write!(f, "scalar"), - Self::Simple(ref e) => write!(f, "{e}"), + Self::Simple(e) => write!(f, "{e}"), } } } @@ -475,11 +496,7 @@ impl Display for Extents { impl> From for Extents { fn from(extents: T) -> Self { let extents = extents.into(); - if extents.is_empty() { - Self::Scalar - } else { - Self::Simple(extents) - } + if extents.is_empty() { Self::Scalar } else { Self::Simple(extents) } } } diff --git a/hdf5/src/hl/file.rs b/hdf5/src/hl/file.rs index 81ad839dc..a0ab54165 100644 --- a/hdf5/src/hl/file.rs +++ b/hdf5/src/hl/file.rs @@ -4,10 +4,12 @@ use std::ops::Deref; use std::path::Path; use hdf5_sys::h5f::{ + H5F_ACC_DEFAULT, H5F_ACC_EXCL, H5F_ACC_RDONLY, H5F_ACC_RDWR, H5F_ACC_TRUNC, H5F_SCOPE_LOCAL, H5Fclose, H5Fcreate, H5Fflush, H5Fget_access_plist, H5Fget_create_plist, H5Fget_filesize, - H5Fget_freespace, H5Fget_intent, H5Fget_obj_count, H5Fget_obj_ids, H5Fopen, H5F_ACC_DEFAULT, - H5F_ACC_EXCL, H5F_ACC_RDONLY, H5F_ACC_RDWR, H5F_ACC_TRUNC, H5F_SCOPE_LOCAL, + H5Fget_freespace, H5Fget_intent, H5Fget_obj_count, H5Fget_obj_ids, H5Fopen, }; +#[cfg(feature = "1.10.0")] +use hdf5_sys::h5f::{H5F_ACC_SWMR_READ, H5Fstart_swmr_write}; use crate::hl::plist::{ file_access::{FileAccess, FileAccessBuilder}, @@ -17,9 +19,13 @@ use crate::internal_prelude::*; /// File opening mode. #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(not(feature = "1.10.0"), non_exhaustive)] pub enum OpenMode { /// Open a file as read-only, file must exist. Read, + /// Open a file as read-only in SWMR mode, file must exist. + #[cfg(feature = "1.10.0")] + ReadSWMR, /// Open a file as read/write, file must exist. ReadWrite, /// Create a file, truncate if exists. @@ -178,6 +184,14 @@ impl File { pub fn fcpl(&self) -> Result { self.create_plist() } + + #[cfg(feature = "1.10.0")] + /// Mark this file as ready for opening as SWMR + pub fn start_swmr(&self) -> Result<()> { + let id = self.id(); + h5call!(H5Fstart_swmr_write(id))?; + Ok(()) + } } /// File builder allowing to customize file access/creation property lists. @@ -222,8 +236,15 @@ impl FileBuilder { pub fn open_as>(&self, filename: P, mode: OpenMode) -> Result { let filename = filename.as_ref(); if mode == OpenMode::Append { - if let Ok(file) = self.open_as(filename, OpenMode::ReadWrite) { - return Ok(file); + match self.open_as(filename, OpenMode::ReadWrite) { + Ok(file) => return Ok(file), + Err(err) => { + // If the file exists it is unreadable (corrupt, locked, no permission), so + // report that instead of falling through to create, which would give a misleading EEXIST error. + if err.contains_minor(MinorErrorCode::NotHdf5) || filename.exists() { + return Err(err); + } + } } } let filename = to_cstring( @@ -231,9 +252,13 @@ impl FileBuilder { )?; let flags = match mode { OpenMode::Read => H5F_ACC_RDONLY, + #[cfg(feature = "1.10.0")] + OpenMode::ReadSWMR => H5F_ACC_RDONLY | H5F_ACC_SWMR_READ, OpenMode::ReadWrite => H5F_ACC_RDWR, OpenMode::Create => H5F_ACC_TRUNC, OpenMode::CreateExcl | OpenMode::Append => H5F_ACC_EXCL, + #[cfg(not(feature = "1.10.0"))] + _ => unreachable!(), }; let fname_ptr = filename.as_ptr(); h5lock!({ @@ -242,6 +267,8 @@ impl FileBuilder { OpenMode::Read | OpenMode::ReadWrite => { File::from_id(h5try!(H5Fopen(fname_ptr, flags, fapl.id()))) } + #[cfg(feature = "1.10.0")] + OpenMode::ReadSWMR => File::from_id(h5try!(H5Fopen(fname_ptr, flags, fapl.id()))), _ => { let fcpl = self.fcpl.finish()?; File::from_id(h5try!(H5Fcreate(fname_ptr, flags, fcpl.id(), fapl.id()))) @@ -360,16 +387,40 @@ pub mod tests { #[test] pub fn test_unable_to_open() { with_tmp_dir(|dir| { - assert_err_re!(File::open(&dir), "unable to (?:synchronously )?open file"); - assert_err_re!(File::open_rw(&dir), "unable to (?:synchronously )?open file"); - assert_err_re!(File::create_excl(&dir), "unable to (?:synchronously )?create file"); - assert_err_re!(File::create(&dir), "unable to (?:synchronously )?create file"); - assert_err_re!(File::append(&dir), "unable to (?:synchronously )?create file"); + // Opening a directory fails, but how it fails is not portable: read-only succeeds + // at the OS level on unix, so HDF5 gets as far as the superblock and reports + // NotHdf5, while read-write fails with EISDIR first. Only the shared codes are + // asserted here. The message text gained "synchronously" in HDF5 1.14, the codes + // did not move. + for err in [ + File::open(&dir).unwrap_err(), + File::open_rw(&dir).unwrap_err(), + File::append(&dir).unwrap_err(), + ] { + assert_err_re!(Err::<(), _>(err.clone()), "unable to (?:synchronously )?open file"); + assert!(err.contains_major(MajorErrorCode::File), "{err:?}"); + assert!(err.contains_minor(MinorErrorCode::CantOpenFile), "{err:?}"); + } + // Creating over a directory cannot report the underlying EISDIR/EEXIST as a code; + // HDF5 only ever says CantCreate here, with errno buried in the message text. + for err in [File::create_excl(&dir).unwrap_err(), File::create(&dir).unwrap_err()] { + assert_err_re!( + Err::<(), _>(err.clone()), + "unable to (?:synchronously )?create file" + ); + // The minor code varies by HDF5 version (CantCreate vs FileExists) + // The stable part is that it's a File error and not a corrupt-superblock read. + assert!(err.contains_major(MajorErrorCode::File), "{err:?}"); + assert!(!err.contains_minor(MinorErrorCode::NotHdf5), "{err:?}"); + } }); with_tmp_path(|path| { fs::File::create(&path).unwrap().write_all(b"foo").unwrap(); assert!(fs::metadata(&path).is_ok()); - assert_err_re!(File::open(&path), "unable to (?:synchronously )?open file"); + let err = File::open(&path).unwrap_err(); + assert_err_re!(Err::<(), _>(err.clone()), "unable to (?:synchronously )?open file"); + // The file exists but has no valid superblock + assert!(err.contains_minor(MinorErrorCode::NotHdf5), "{err:?}"); }) } @@ -388,7 +439,12 @@ pub mod tests { pub fn test_file_create_excl() { with_tmp_path(|path| { File::create_excl(&path).unwrap(); - assert_err_re!(File::create_excl(&path), "unable to (?:synchronously )?create file"); + let err = File::create_excl(&path).unwrap_err(); + assert_err_re!(Err::<(), _>(err.clone()), "unable to (?:synchronously )?create file"); + // The specific minor code varies by HDF5 version and build, assert on what's stable + let minors: Vec<_> = err.stack().unwrap().minor_codes().collect(); + assert!(err.contains_major(MajorErrorCode::File), "minors={minors:?}: {err}"); + assert!(!err.contains_minor(MinorErrorCode::NotHdf5), "minors={minors:?}: {err}"); }); } @@ -400,6 +456,18 @@ pub mod tests { }); } + #[test] + pub fn test_append_to_corrupt_file() { + with_tmp_path(|path| { + fs::File::create(&path).unwrap().write_all(b"garbage data").unwrap(); + // The file exists but is not HDF5, so append must report the read failure rather + // than a misleading "file exists" from falling through to create + let err = File::append(&path).unwrap_err(); + assert!(err.contains_minor(MinorErrorCode::NotHdf5), "{err:?}"); + assert_err_re!(Err::<(), _>(err), "unable to (?:synchronously )?open file"); + }); + } + #[test] pub fn test_file_open() { with_tmp_path(|path| { diff --git a/hdf5/src/hl/filters.rs b/hdf5/src/hl/filters.rs index a0056aa6e..bbc79e2b7 100644 --- a/hdf5/src/hl/filters.rs +++ b/hdf5/src/hl/filters.rs @@ -1,19 +1,22 @@ use std::collections::HashMap; use std::ptr::{self, addr_of_mut}; +#[cfg(feature = "zfp")] +use crate::globals::{H5E_CALLBACK, H5E_PLIST}; use hdf5_sys::h5p::{ H5Pget_filter2, H5Pget_nfilters, H5Pset_deflate, H5Pset_filter, H5Pset_fletcher32, H5Pset_nbit, H5Pset_scaleoffset, H5Pset_shuffle, H5Pset_szip, }; use hdf5_sys::h5t::H5T_class_t; use hdf5_sys::h5z::{ - H5Zfilter_avail, H5Zget_filter_info, H5Z_FILTER_CONFIG_DECODE_ENABLED, - H5Z_FILTER_CONFIG_ENCODE_ENABLED, H5Z_FILTER_DEFLATE, H5Z_FILTER_FLETCHER32, H5Z_FILTER_NBIT, - H5Z_FILTER_SCALEOFFSET, H5Z_FILTER_SHUFFLE, H5Z_FILTER_SZIP, H5Z_FLAG_OPTIONAL, - H5Z_SO_FLOAT_DSCALE, H5Z_SO_INT, H5_SZIP_EC_OPTION_MASK, H5_SZIP_MAX_PIXELS_PER_BLOCK, - H5_SZIP_NN_OPTION_MASK, + H5_SZIP_EC_OPTION_MASK, H5_SZIP_MAX_PIXELS_PER_BLOCK, H5_SZIP_NN_OPTION_MASK, + H5Z_FILTER_CONFIG_DECODE_ENABLED, H5Z_FILTER_CONFIG_ENCODE_ENABLED, H5Z_FILTER_DEFLATE, + H5Z_FILTER_FLETCHER32, H5Z_FILTER_NBIT, H5Z_FILTER_SCALEOFFSET, H5Z_FILTER_SHUFFLE, + H5Z_FILTER_SZIP, H5Z_FLAG_OPTIONAL, H5Z_SO_FLOAT_DSCALE, H5Z_SO_INT, H5Zfilter_avail, + H5Zget_filter_info, }; +/// A filter identifier. pub use hdf5_sys::h5z::H5Z_filter_t; use crate::internal_prelude::*; @@ -22,21 +25,33 @@ use crate::internal_prelude::*; mod blosc; #[cfg(feature = "lzf")] mod lzf; +#[cfg(feature = "zfp")] +pub(crate) mod zfp; +#[cfg(feature = "zfp")] +use zfp_sys::{zfp_type_zfp_type_double, zfp_type_zfp_type_float}; + +/// Coding methods for Szip compression. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum SZip { + /// Entropy coding method. Entropy, + /// Nearest-neighbor coding method. NearestNeighbor, } +/// Scaling methods for scale-offset compression. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ScaleOffset { + /// Integer scaling with some MinBits value. Integer(u16), + /// Floating-point D-scaling with some decimal scale factor. FloatDScale(u8), } #[cfg(feature = "blosc")] mod blosc_impl { + /// Available compressors for Blosc compression. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg(feature = "blosc")] #[non_exhaustive] @@ -72,11 +87,7 @@ mod blosc_impl { #[cfg(feature = "blosc")] impl From for BloscShuffle { fn from(shuffle: bool) -> Self { - if shuffle { - Self::Byte - } else { - Self::None - } + if shuffle { Self::Byte } else { Self::None } } } @@ -103,25 +114,84 @@ mod blosc_impl { #[cfg(feature = "blosc")] pub use blosc_impl::*; +#[cfg(feature = "zfp")] +mod zfp_impl { + use crate::filters::ZfpMode::Reversible; + + #[derive(Clone, Copy, Debug)] + pub enum ZfpMode { + FixedRate(f64), + FixedPrecision(u8), + FixedAccuracy(f64), + Reversible, + } + + // Bitwise compare f64 so NaN and signed zero are deterministic + impl PartialEq for ZfpMode { + fn eq(&self, other: &Self) -> bool { + use ZfpMode::*; + match (self, other) { + (FixedRate(a), FixedRate(b)) => a.to_bits() == b.to_bits(), + (FixedPrecision(a), FixedPrecision(b)) => a == b, + (FixedAccuracy(a), FixedAccuracy(b)) => a.to_bits() == b.to_bits(), + (Reversible, Reversible) => true, + _ => false, + } + } + } + impl Eq for ZfpMode {} + + impl Default for ZfpMode { + fn default() -> Self { + ZfpMode::FixedRate(4.0) + } + } + + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct FieldParam { + pub data_type_bytes: usize, + pub dims: Vec, + } +} + +#[cfg(feature = "zfp")] +pub use zfp_impl::*; + +/// An HDF5 filter configuration. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Filter { + /// Gzip compression (deflation) with some compression level. Deflate(u8), + /// Shuffle algorithm. Shuffle, + /// Fletcher32 checksum. Fletcher32, + /// SZIP compression with some coding method and pixels per block. SZip(SZip, u8), + /// N-bit compression. NBit, + /// Scale-offset compression with some scaling mode. ScaleOffset(ScaleOffset), + /// LZF compression. #[cfg(feature = "lzf")] LZF, + /// Blosc compression with some compressor, compression level, and shuffle mode. #[cfg(feature = "blosc")] Blosc(Blosc, u8, BloscShuffle), + #[cfg(feature = "zfp")] + Zfp(ZfpMode, Vec, u8), + /// A user-defined filter with some parameters. User(H5Z_filter_t, Vec), } +/// Information about whether a filter is available and enabled for encoding/decoding. #[derive(Default, Clone, Copy, Debug, PartialEq, Eq)] pub struct FilterInfo { + /// Whether the filter is available. pub is_available: bool, + /// Whether the filter is configured to encode data. pub encode_enabled: bool, + /// Whether the filter is configured to decode data. pub decode_enabled: bool, } @@ -135,6 +205,10 @@ pub(crate) fn register_filters() { if let Err(e) = blosc::register_blosc() { eprintln!("Error while registering Blosc filter: {e}"); } + #[cfg(feature = "zfp")] + if let Err(e) = zfp::register_zfp() { + eprintln!("Error while registering ZFP filter: {e}"); + } } /// Returns `true` if deflate filter is available. @@ -164,7 +238,13 @@ pub fn blosc_available() -> bool { h5lock!(H5Zfilter_avail(32001) == 1) } +/// Returns `true` if ZFP filter is available. +pub fn zfp_available() -> bool { + h5lock!(H5Zfilter_avail(32013) == 1) +} + impl Filter { + /// Returns the filter's identifier. pub fn id(&self) -> H5Z_filter_t { match self { Self::Deflate(_) => H5Z_FILTER_DEFLATE, @@ -177,10 +257,13 @@ impl Filter { Self::LZF => lzf::LZF_FILTER_ID, #[cfg(feature = "blosc")] Self::Blosc(_, _, _) => blosc::BLOSC_FILTER_ID, + #[cfg(feature = "zfp")] + Self::Zfp(_, _, _) => zfp::ZFP_FILTER_ID, Self::User(id, _) => *id, } } + /// Returns metadata for the filter with the given identifier. pub fn get_info(filter_id: H5Z_filter_t) -> FilterInfo { if !h5call!(H5Zfilter_avail(filter_id)).map(|x| x > 0).unwrap_or_default() { return FilterInfo::default(); @@ -194,47 +277,59 @@ impl Filter { } } + /// Returns `true` if the filter is available. pub fn is_available(&self) -> bool { Self::get_info(self.id()).is_available } + /// Returns `true` if the filter is configured to encode data. pub fn encode_enabled(&self) -> bool { Self::get_info(self.id()).encode_enabled } + /// Returns `true` if the filter is configured to decode data. pub fn decode_enabled(&self) -> bool { Self::get_info(self.id()).decode_enabled } + /// Creates a deflation filter configuration with some compression level. pub fn deflate(level: u8) -> Self { Self::Deflate(level) } + /// Returns the shuffle algorithm filter configuration. pub fn shuffle() -> Self { Self::Shuffle } + /// Returns the Fletcher32 checksum filter configuration. pub fn fletcher32() -> Self { Self::Fletcher32 } + /// Creates an Szip filter configuration with some coding method and pixels per block. pub fn szip(coding: SZip, px_per_block: u8) -> Self { Self::SZip(coding, px_per_block) } + /// Returns the N-bit compression filter configuration. pub fn nbit() -> Self { Self::NBit } + /// Creates a scale-offset compression filter configuration with some scaling mode. pub fn scale_offset(mode: ScaleOffset) -> Self { Self::ScaleOffset(mode) } + /// Returns the LZF compression filter. #[cfg(feature = "lzf")] pub fn lzf() -> Self { Self::LZF } + /// Creates a Blosc compression filter configuration with some compressor, + /// compression level, and shuffle mode. #[cfg(feature = "blosc")] pub fn blosc(complib: Blosc, clevel: u8, shuffle: T) -> Self where @@ -243,6 +338,8 @@ impl Filter { Self::Blosc(complib, clevel, shuffle.into()) } + /// Creates a Blosc LZ compression filter configuration with some compression level and + /// shuffle mode. #[cfg(feature = "blosc")] pub fn blosc_blosclz(clevel: u8, shuffle: T) -> Self where @@ -251,6 +348,8 @@ impl Filter { Self::blosc(Blosc::BloscLZ, clevel, shuffle) } + /// Creates a Blosc LZ4 compression filter configuration with some compression level and + /// shuffle mode. #[cfg(feature = "blosc-lz4")] pub fn blosc_lz4(clevel: u8, shuffle: T) -> Self where @@ -259,6 +358,8 @@ impl Filter { Self::blosc(Blosc::LZ4, clevel, shuffle) } + /// Creates a Blosc LZ4HC compression filter configuration with some compression level and + /// shuffle mode. #[cfg(feature = "blosc-lz4")] pub fn blosc_lz4hc(clevel: u8, shuffle: T) -> Self where @@ -267,6 +368,8 @@ impl Filter { Self::blosc(Blosc::LZ4HC, clevel, shuffle) } + /// Creates a Blosc Snappy compression filter configuration with some compression level and + /// shuffle mode. #[cfg(feature = "blosc-snappy")] pub fn blosc_snappy(clevel: u8, shuffle: T) -> Self where @@ -275,6 +378,8 @@ impl Filter { Self::blosc(Blosc::Snappy, clevel, shuffle) } + /// Creates a Blosc Zlib compression filter configuration with some compression level and + /// shuffle mode. #[cfg(feature = "blosc-zlib")] pub fn blosc_zlib(clevel: u8, shuffle: T) -> Self where @@ -283,6 +388,8 @@ impl Filter { Self::blosc(Blosc::ZLib, clevel, shuffle) } + /// Creates a Blosc Zstd compression filter configuration with some compression level and + /// shuffle mode. #[cfg(feature = "blosc-zstd")] pub fn blosc_zstd(clevel: u8, shuffle: T) -> Self where @@ -291,6 +398,32 @@ impl Filter { Self::blosc(Blosc::ZStd, clevel, shuffle) } + #[cfg(feature = "zfp")] + pub fn zfp(mode: ZfpMode, chunk_dims: Vec, n_bytes: u8) -> Self { + Self::Zfp(mode, chunk_dims, n_bytes) + } + + #[cfg(feature = "zfp")] + pub fn zfp_rate(rate: f64, chunk_dims: Vec, n_bytes: u8) -> Self { + Self::zfp(ZfpMode::FixedRate(rate), chunk_dims, n_bytes) + } + + #[cfg(feature = "zfp")] + pub fn zfp_precision(precision: u8, chunk_dims: Vec, n_bytes: u8) -> Self { + Self::zfp(ZfpMode::FixedPrecision(precision), chunk_dims, n_bytes) + } + + #[cfg(feature = "zfp")] + pub fn zfp_accuracy(accuracy: f64, chunk_dims: Vec, n_bytes: u8) -> Self { + Self::zfp(ZfpMode::FixedAccuracy(accuracy), chunk_dims, n_bytes) + } + + #[cfg(feature = "zfp")] + pub fn zfp_reversible(chunk_dims: Vec, n_bytes: u8) -> Self { + Self::zfp(ZfpMode::Reversible, chunk_dims, n_bytes) + } + + /// Creates a user-defined filter configuration with some filter identifier and parameters. pub fn user(id: H5Z_filter_t, cdata: &[c_uint]) -> Self { Self::User(id, cdata.to_vec()) } @@ -397,6 +530,37 @@ impl Filter { Ok(Self::blosc(complib, clevel, shuffle)) } + #[cfg(feature = "zfp")] + fn parse_zfp(cdata: &[c_uint]) -> Result { + ensure!(cdata.len() >= 8, "expected at least length 8 cdata for zfp filter"); + let chunk_dims = cdata[4..6].iter().map(|&x| x as _).collect::>(); + let n_bytes = cdata[6] as u8; + let mode = if cdata.len() >= 8 { cdata[7] } else { 1 }; + let param1 = if cdata.len() >= 9 { cdata[8] } else { 0 }; + let param2 = if cdata.len() >= 10 { cdata[9] } else { 0 }; + let zfp_mode = match mode { + 1 => { + let rate = f64::from_bits(((param1 as u64) << 32) | (param2 as u64)); + + ZfpMode::FixedRate(rate) + } + 2 => ZfpMode::FixedPrecision(param1 as u8), + 3 => { + let accuracy = f64::from_bits(((param1 as u64) << 32) | (param2 as u64)); + ZfpMode::FixedAccuracy(accuracy) + } + 5 => ZfpMode::Reversible, + _ => fail!("invalid zfp mode: {}", mode), + }; + Ok(Self::zfp(zfp_mode, chunk_dims, n_bytes)) + } + + /// Tries to create a filter configuration from a filter identifier and parameters. + /// + /// # Errors + /// + /// Returns an error if the identifier is invalid or the parameters are invalid + /// for the specified filter. pub fn from_raw(filter_id: H5Z_filter_t, cdata: &[c_uint]) -> Result { ensure!(filter_id > 0, "invalid filter id: {}", filter_id); match filter_id { @@ -410,6 +574,8 @@ impl Filter { lzf::LZF_FILTER_ID => Self::parse_lzf(cdata), #[cfg(feature = "blosc")] blosc::BLOSC_FILTER_ID => Self::parse_blosc(cdata), + #[cfg(feature = "zfp")] + zfp::ZFP_FILTER_ID => Self::parse_zfp(cdata), _ => Ok(Self::user(filter_id, cdata)), } } @@ -478,6 +644,82 @@ impl Filter { Self::apply_user(plist_id, blosc::BLOSC_FILTER_ID, &cdata) } + #[cfg(feature = "zfp")] + /// Applies the ZFP filter to the given property list. + /// + /// This function configures the ZFP filter for compression on the specified dataset. + /// It determines the data type, removes singleton dimensions, and encodes the mode + /// information into the filter header. + /// + /// # Safety + /// This function is marked as unsafe because it interacts with raw pointers and + /// performs operations that require careful handling to avoid undefined behavior. + /// + /// # Parameters + /// - `plist_id`: The property list identifier to which the ZFP filter will be applied. + /// - `n_bytes`: The number of bytes per data element (4 for `float`, 8 for `double`). + /// - `chunk_dims`: A vector containing the dimensions of the data chunks. + /// - `mode`: The ZFP compression mode, which can be fixed rate, precision, accuracy, or reversible. + /// + /// # Returns + /// - `herr_t`: Returns 0 on success, or a negative value on failure. + unsafe fn apply_zfp( + plist_id: hid_t, n_bytes: u8, chunk_dims: Vec, mode: ZfpMode, + ) -> herr_t { + // get the chunk dimensiosn out of it. Could not reliably get the chunk_dims from plist_id + // during testing so opted to just pass it in during the build + + let ndims = chunk_dims.len(); + // Convert to `usize` and trim to used dims. + let chunk_dims_usize: Vec = + chunk_dims[..(ndims as usize)].iter().map(|&d| d as usize).collect(); + + // remove the singletons from the data + let mut dims_no_singleton: Vec = Vec::new(); + for &dim in chunk_dims_usize.iter() { + if dim != 1 { + dims_no_singleton.push(dim as u64); + } + } + let ndims_no_singleton = dims_no_singleton.len(); + + assert!(dims_no_singleton.len() <= zfp::MAX_NDIMS); + + // Get the type of the input data + let dtype_id = match n_bytes { + 4 => zfp_type_zfp_type_float, + 8 => zfp_type_zfp_type_double, + _ => { + h5err!( + "ZFP filter only supports 4 or 8 byte floating point data", + H5E_PLIST, + H5E_CALLBACK + ); + return -1; + } + }; + + // Build the Mode Information we need + let (mode_val, param1, param2) = match mode { + ZfpMode::FixedRate(rate) => { + let bits = rate.to_bits(); + (1, (bits >> 32) as c_uint, bits as c_uint) + } + ZfpMode::FixedPrecision(precision) => (2, precision as c_uint, 0), + ZfpMode::FixedAccuracy(accuracy) => { + let bits = accuracy.to_bits(); + (3, (bits >> 32) as c_uint, bits as c_uint) + } + ZfpMode::Reversible => (5, 0, 0), + }; + + // update values and encode into the header + let (hdr_cd_values, _) = + zfp::compute_hdr_cd_values(dtype_id, ndims_no_singleton, &dims_no_singleton, mode); + let hdf_cd_values_pass = hdr_cd_values.iter().map(|x| *x).collect::>(); + Self::apply_user(plist_id, zfp::ZFP_FILTER_ID, &hdf_cd_values_pass) + } + unsafe fn apply_user(plist_id: hid_t, filter_id: H5Z_filter_t, cdata: &[c_uint]) -> herr_t { // We're setting custom filters to optional, same way h5py does it, since // the only mention of H5Z_FLAG_MANDATORY in the HDF5 source itself is @@ -502,7 +744,10 @@ impl Filter { Self::Blosc(complib, clevel, shuffle) => { Self::apply_blosc(id, *complib, *clevel, *shuffle) } - Self::User(filter_id, ref cdata) => Self::apply_user(id, *filter_id, cdata), + #[cfg(feature = "zfp")] + Self::Zfp(mode, chunk_dims, n_bytes) => + Self::apply_zfp(id, *n_bytes, chunk_dims.clone(), *mode), + Self::User(filter_id, cdata) => Self::apply_user(id, *filter_id, cdata), }); Ok(()) } @@ -535,7 +780,8 @@ impl Filter { } } -const COMP_FILTER_IDS: &[H5Z_filter_t] = &[H5Z_FILTER_DEFLATE, H5Z_FILTER_SZIP, 32000, 32001]; +const COMP_FILTER_IDS: &[H5Z_filter_t] = + &[H5Z_FILTER_DEFLATE, H5Z_FILTER_SZIP, 32000, 32001, 32013]; pub(crate) fn validate_filters(filters: &[Filter], type_class: H5T_class_t) -> Result<()> { let mut map: HashMap = HashMap::new(); @@ -588,13 +834,16 @@ pub(crate) fn validate_filters(filters: &[Filter], type_class: H5T_class_t) -> R #[cfg(test)] mod tests { use hdf5_sys::h5t::H5T_class_t; + use ndarray::{Array2, Axis}; + use std::io::{Seek, SeekFrom}; use super::{ - blosc_available, deflate_available, lzf_available, szip_available, validate_filters, - Filter, FilterInfo, SZip, ScaleOffset, + Filter, FilterInfo, SZip, ScaleOffset, blosc_available, deflate_available, lzf_available, + szip_available, validate_filters, }; + use crate::hl::filters::zfp_available; use crate::test::with_tmp_file; - use crate::{plist::DatasetCreate, Result}; + use crate::{Result, plist::DatasetCreate}; #[test] fn test_filter_pipeline() -> Result<()> { @@ -610,9 +859,9 @@ mod tests { { comp_filters.push(Filter::lzf()); } - assert_eq!(cfg!(feature = "blosc-all"), blosc_available()); #[cfg(feature = "blosc-all")] { + assert!(blosc_available()); use super::BloscShuffle; comp_filters.push(Filter::blosc_blosclz(1, false)); comp_filters.push(Filter::blosc_lz4(3, true)); @@ -621,6 +870,16 @@ mod tests { comp_filters.push(Filter::blosc_zstd(9, BloscShuffle::Byte)); comp_filters.push(Filter::blosc_snappy(0, BloscShuffle::Bit)); } + + #[cfg(feature = "zfp")] + assert_eq!(cfg!(feature = "zfp"), zfp_available()); + #[cfg(feature = "zfp")] + { + comp_filters.push(Filter::zfp_rate(8.0, vec![10_000, 20], 4)); + comp_filters.push(Filter::zfp_precision(16, vec![10_000, 20], 4)); + comp_filters.push(Filter::zfp_accuracy(1e-3, vec![10_000, 20], 4)); + } + for c in &comp_filters { assert!(c.is_available()); assert!(c.encode_enabled()); @@ -672,4 +931,299 @@ mod tests { Ok(()) } + + #[test] + #[cfg(feature = "zfp")] + fn test_zfp_accuracy() -> Result<()> { + use super::zfp_available; + + if !zfp_available() { + println!("ZFP filter not available, skipping test"); + assert_eq!(1, 0); + return Ok(()); + } + + // test 1D Data + with_tmp_file(|file| { + let data = ndarray::Array1::::linspace(0.0, 1.0, 1000); + file.new_dataset_builder() + .with_data(&data) + .chunk((1000,)) + .zfp_accuracy(0.125, vec![1000], 4) + .create("zfp_precision_1d") + .unwrap(); + + let ds = file.dataset("zfp_precision_1d").unwrap(); + + let read_data: Vec = ds.read_raw().unwrap(); + + // ZFP is lossy, so we check approximate equality + assert_eq!(read_data.len(), data.len()); + + for (i, (original, compressed)) in data.iter().zip(read_data.iter()).enumerate() { + let diff = (original - compressed).abs(); + assert!( + diff < 0.1, + "Index {}: difference too large: {} vs {} (diff: {})", + i, + original, + compressed, + diff + ); + } + }); + + // Test 2D data + with_tmp_file(|file| { + let data = ndarray::Array1::::linspace(0.0, 1.0, 1000); + let data = data.to_shape((10, 100)).unwrap(); + file.new_dataset_builder() + .with_data(&data) + .chunk((5, 10)) + .zfp_accuracy(0.125, vec![5, 10], 4) + .create("zfp_precision_1d") + .unwrap(); + + let ds = file.dataset("zfp_precision_1d").unwrap(); + + let read_data: Vec = ds.read_raw().unwrap(); + + // ZFP is lossy, so we check approximate equality + assert_eq!(read_data.len(), data.len()); + + for (i, (original, compressed)) in data.iter().zip(read_data.iter()).enumerate() { + let diff = (original - compressed).abs(); + assert!( + diff < 0.125, + "Index {}: difference too large: {} vs {} (diff: {})", + i, + original, + compressed, + diff + ); + } + }); + + // Test 3D data + with_tmp_file(|file| { + let data = ndarray::Array1::::linspace(0.0, 1.0, 10000); + let data = data.to_shape((10, 10, 100)).unwrap(); + + file.new_dataset_builder() + .with_data(&data) + .chunk((2, 5, 25)) + .zfp_accuracy(0.125, vec![2, 5, 25], 4) + .create("zfp_precision_3d") + .unwrap(); + + let ds = file.dataset("zfp_precision_3d").unwrap(); + + let read_data: Vec = ds.read_raw().unwrap(); + let data_raw = data.as_slice().unwrap(); + + // ZFP is lossy, so we check approximate equality + assert_eq!(read_data.len(), data_raw.len()); + + for (i, (original, compressed)) in data_raw.iter().zip(read_data.iter()).enumerate() { + let diff = (original - compressed).abs(); + assert!( + diff < 0.125, + "Index {}: difference too large: {} vs {} (diff: {})", + i, + original, + compressed, + diff + ); + } + }); + + // Test 4D data + with_tmp_file(|file| { + let data = ndarray::Array1::::linspace(0.0, 1.0, 100000); + let data = data.to_shape((10, 10, 10, 100)).unwrap(); + file.new_dataset_builder() + .with_data(&data) + .chunk((2, 2, 5, 50)) + .zfp_accuracy(0.125, vec![2, 2, 5, 50], 4) + .create("zfp_precision_1d") + .unwrap(); + + let ds = file.dataset("zfp_precision_1d").unwrap(); + + let read_data: Vec = ds.read_raw().unwrap(); + let data_raw = data.as_slice().unwrap(); + + // ZFP is lossy, so we check approximate equality + assert_eq!(read_data.len(), data_raw.len()); + + for (i, (original, compressed)) in data_raw.iter().zip(read_data.iter()).enumerate() { + let diff = (original - compressed).abs(); + assert!( + diff < 0.125, + "Index {}: difference too large: {} vs {} (diff: {})", + i, + original, + compressed, + diff + ); + } + }); + Ok(()) + } + + #[test] + #[cfg(feature = "zfp")] + fn test_over_dim_data() -> Result<()> { + use super::zfp_available; + + if !zfp_available() { + println!("ZFP filter not available, skipping test"); + assert_eq!(1, 0); + return Ok(()); + } + + // Test 5D data with 3D chunks but should still fail + // test 1D Data + with_tmp_file(|file| { + let data = ndarray::Array1::::linspace(0.0, 1.0, 50_000); + let data = data.to_shape((2, 5, 10, 10, 50)).unwrap(); + + let bad_result = file + .new_dataset_builder() + .with_data(&data) + .chunk((2, 5, 5, 1, 1)) + .zfp_accuracy(0.125, vec![2, 5, 5, 1, 1], 4) + .create("zfp_precision_1d") + .unwrap_err(); + + assert_err!(bad_result, "ZFP filter supports up to 4D data only"); + }); + + Ok(()) + } + + #[test] + #[cfg(feature = "zfp")] + fn test_zfp_reversible() -> Result<()> { + use super::zfp_available; + + if !zfp_available() { + println!("ZFP filter not available, skipping test"); + assert_eq!(1, 0); + return Ok(()); + } + with_tmp_file(|file| { + let data = ndarray::Array1::::linspace(0.0, 1.0, 9600); + let data = data.insert_axis(Axis(0)); + let data = data.insert_axis(Axis(0)); + file.new_dataset_builder() + .chunk((1, 1, 960)) + .zfp_reversible(vec![1, 1, 960], 4) + .with_data(&data) + .create("zfp_reversible") + .unwrap(); + + let ds = file.dataset("zfp_reversible").unwrap(); + + let read_data: Vec = ds.read_raw().unwrap(); + let n_bytes = file.size(); + + // ZFP is lossy, so we check approximate equality + assert_eq!(read_data.len(), data.len()); + dbg!(&data.clone().into_raw_vec_and_offset().0[0..15]); + dbg!(&read_data[0..15]); + let target_bytes = (data.len() * 4) as u64; + assert!( + n_bytes <= target_bytes, + "Dataset size {} exceeds target {}", + n_bytes, + target_bytes + ); + assert_eq!(n_bytes, 29432); + // + for (i, (original, compressed)) in data.iter().zip(read_data.iter()).enumerate() { + let diff = (original - compressed).abs(); + dbg!(&diff); + assert!( + diff == 0.0, + "Index {}: difference too large: {} vs {} (diff: {})", + i, + original, + compressed, + diff + ); + } + }); + + Ok(()) + } + + #[test] + #[cfg(feature = "zfp")] + fn test_zfp_rate() -> Result<()> { + use super::zfp_available; + + if !zfp_available() { + println!("ZFP filter not available, skipping test"); + assert_eq!(1, 0); + return Ok(()); + } + with_tmp_file(|file| { + let data = ndarray::Array1::::linspace(0.0, 1.0, 1000); + file.new_dataset_builder() + .with_data(&data) + .chunk((1000,)) + .zfp_rate(2.0, vec![1000], 4) + .create("zfp_rate") + .unwrap(); + + let ds = file.dataset("zfp_rate").unwrap(); + + let read_data: Vec = ds.read_raw().unwrap(); + + // ZFP is lossy, so we check approximate equality + assert_eq!(read_data.len(), data.len()); + dbg!(&data.clone().into_raw_vec_and_offset().0[0..15]); + dbg!(&read_data[0..15]); + for (i, (original, compressed)) in data.iter().zip(read_data.iter()).enumerate() { + let diff = (original - compressed).abs(); + dbg!(&diff); + } + }); + + // test full rate compression. Should be "lossless" + + with_tmp_file(|file| { + let data = ndarray::Array1::::linspace(0.0, 1.0, 1000); + file.new_dataset_builder() + .with_data(&data) + .chunk((1000,)) + .zfp_rate(32.0, vec![1000], 4) + .create("zfp_rate") + .unwrap(); + + let ds = file.dataset("zfp_rate").unwrap(); + + let read_data: Vec = ds.read_raw().unwrap(); + + // ZFP is lossy, so we check approximate equality + assert_eq!(read_data.len(), data.len()); + dbg!(&data.clone().into_raw_vec_and_offset().0[0..15]); + dbg!(&read_data[0..15]); + for (i, (original, compressed)) in data.iter().zip(read_data.iter()).enumerate() { + let diff = (original - compressed).abs(); + dbg!(&diff); + assert!( + diff == 0.0, + "Index {}: difference too large: {} vs {} (diff: {})", + i, + original, + compressed, + diff + ); + } + }); + + Ok(()) + } } diff --git a/hdf5/src/hl/filters/blosc.rs b/hdf5/src/hl/filters/blosc.rs index af8021ffd..b15a0e8c6 100644 --- a/hdf5/src/hl/filters/blosc.rs +++ b/hdf5/src/hl/filters/blosc.rs @@ -1,11 +1,10 @@ use std::ptr::{self, addr_of_mut}; use std::slice; - -use lazy_static::lazy_static; +use std::sync::LazyLock; use hdf5_sys::h5p::{H5Pget_chunk, H5Pget_filter_by_id2, H5Pmodify_filter}; -use hdf5_sys::h5t::{H5Tclose, H5Tget_class, H5Tget_size, H5Tget_super, H5T_ARRAY}; -use hdf5_sys::h5z::{H5Z_class2_t, H5Z_filter_t, H5Zregister, H5Z_CLASS_T_VERS, H5Z_FLAG_REVERSE}; +use hdf5_sys::h5t::{H5T_ARRAY, H5Tclose, H5Tget_class, H5Tget_size, H5Tget_super}; +use hdf5_sys::h5z::{H5Z_CLASS_T_VERS, H5Z_FLAG_REVERSE, H5Z_class2_t, H5Z_filter_t, H5Zregister}; use crate::error::H5ErrorCode; use crate::globals::{H5E_CALLBACK, H5E_PLIST}; @@ -48,18 +47,16 @@ const BLOSC_FILTER_INFO: &H5Z_class2_t = &H5Z_class2_t { filter: Some(filter_blosc), }; -lazy_static! { - static ref BLOSC_INIT: Result<(), &'static str> = { - unsafe { - blosc_init(); - } - let ret = unsafe { H5Zregister((BLOSC_FILTER_INFO as *const H5Z_class2_t).cast()) }; - if H5ErrorCode::is_err_code(ret) { - return Err("Can't register Blosc filter"); - } - Ok(()) - }; -} +static BLOSC_INIT: LazyLock> = LazyLock::new(|| { + unsafe { + blosc_init(); + } + let ret = unsafe { H5Zregister((BLOSC_FILTER_INFO as *const H5Z_class2_t).cast()) }; + if H5ErrorCode::is_err_code(ret) { + return Err("Can't register Blosc filter"); + } + Ok(()) +}); pub fn register_blosc() -> Result<(), &'static str> { *BLOSC_INIT @@ -119,11 +116,7 @@ extern "C" fn set_local_blosc(dcpl_id: hid_t, type_id: hid_t, _space_id: hid_t) } values[3] = bufsize as _; let r = unsafe { H5Pmodify_filter(dcpl_id, BLOSC_FILTER_ID, flags, nelmts, values.as_ptr()) }; - if r < 0 { - -1 - } else { - 1 - } + if r < 0 { -1 } else { 1 } } struct BloscConfig { @@ -148,6 +141,9 @@ impl Default for BloscConfig { } fn parse_blosc_cdata(cd_nelmts: size_t, cd_values: *const c_uint) -> Option { + if cd_values.is_null() || cd_nelmts < 1 { + return None; + } let cdata = unsafe { slice::from_raw_parts(cd_values, cd_nelmts as _) }; let mut cfg = BloscConfig { typesize: cdata[2] as _, @@ -193,6 +189,9 @@ unsafe extern "C" fn filter_blosc( buf_size: *mut size_t, buf: *mut *mut c_void, ) -> size_t { let cfg = if let Some(cfg) = parse_blosc_cdata(cd_nelmts, cd_values) { + if cfg.typesize == 0 { + return 0; + } cfg } else { return 0; diff --git a/hdf5/src/hl/filters/lzf.rs b/hdf5/src/hl/filters/lzf.rs index 33f8e79e6..cc55e6c49 100644 --- a/hdf5/src/hl/filters/lzf.rs +++ b/hdf5/src/hl/filters/lzf.rs @@ -1,12 +1,12 @@ use std::ptr::{self, addr_of_mut}; use std::slice; +use std::sync::LazyLock; -use lazy_static::lazy_static; -use lzf_sys::{lzf_compress, lzf_decompress, LZF_VERSION}; +use lzf_sys::{LZF_VERSION, lzf_compress, lzf_decompress}; use hdf5_sys::h5p::{H5Pget_chunk, H5Pget_filter_by_id2, H5Pmodify_filter}; use hdf5_sys::h5t::H5Tget_size; -use hdf5_sys::h5z::{H5Z_class2_t, H5Z_filter_t, H5Zregister, H5Z_CLASS_T_VERS, H5Z_FLAG_REVERSE}; +use hdf5_sys::h5z::{H5Z_CLASS_T_VERS, H5Z_FLAG_REVERSE, H5Z_class2_t, H5Z_filter_t, H5Zregister}; use crate::error::H5ErrorCode; use crate::globals::{H5E_CALLBACK, H5E_PLIST}; @@ -27,15 +27,13 @@ const LZF_FILTER_INFO: &H5Z_class2_t = &H5Z_class2_t { filter: Some(filter_lzf), }; -lazy_static! { - static ref LZF_INIT: Result<(), &'static str> = { - let ret = unsafe { H5Zregister((LZF_FILTER_INFO as *const H5Z_class2_t).cast()) }; - if H5ErrorCode::is_err_code(ret) { - return Err("Can't register LZF filter"); - } - Ok(()) - }; -} +static LZF_INIT: LazyLock> = LazyLock::new(|| { + let ret = unsafe { H5Zregister((LZF_FILTER_INFO as *const H5Z_class2_t).cast()) }; + if H5ErrorCode::is_err_code(ret) { + return Err("Can't register LZF filter"); + } + Ok(()) +}); pub fn register_lzf() -> Result<(), &'static str> { *LZF_INIT @@ -86,11 +84,7 @@ extern "C" fn set_local_lzf(dcpl_id: hid_t, type_id: hid_t, _space_id: hid_t) -> } values[2] = bufsize as _; let r = unsafe { H5Pmodify_filter(dcpl_id, LZF_FILTER_ID, flags, nelmts, values.as_ptr()) }; - if r < 0 { - -1 - } else { - 1 - } + if r < 0 { -1 } else { 1 } } unsafe extern "C" fn filter_lzf( @@ -127,7 +121,11 @@ unsafe fn filter_lzf_decompress( cd_nelmts: size_t, cd_values: *const c_uint, nbytes: size_t, buf_size: *mut size_t, buf: *mut *mut c_void, ) -> size_t { - let cdata = slice::from_raw_parts(cd_values, cd_nelmts as _); + let cdata = if cd_values.is_null() || cd_nelmts < 1 { + &[] + } else { + slice::from_raw_parts(cd_values, cd_nelmts as _) + }; let mut outbuf_size = if cd_nelmts >= 3 && cdata[2] != 0 { cdata[2] as _ } else { *buf_size }; let mut outbuf: *mut c_void; let mut status: c_uint; diff --git a/hdf5/src/hl/filters/zfp.rs b/hdf5/src/hl/filters/zfp.rs new file mode 100644 index 000000000..1a05fde0b --- /dev/null +++ b/hdf5/src/hl/filters/zfp.rs @@ -0,0 +1,710 @@ +use std::ptr::{self, addr_of_mut}; +use std::slice; +use std::sync::LazyLock; + +use hdf5_sys::h5p::{H5Pget_chunk, H5Pget_filter_by_id2, H5Pmodify_filter}; +use hdf5_sys::h5t::{H5T_FLOAT, H5Tclose, H5Tget_class, H5Tget_size, H5Tget_super}; +use hdf5_sys::h5z::{H5Z_CLASS_T_VERS, H5Z_FLAG_REVERSE, H5Z_class2_t, H5Z_filter_t, H5Zregister}; + +use crate::error::H5ErrorCode; +use crate::globals::{H5E_CALLBACK, H5E_PLIST}; +use crate::internal_prelude::*; + +use zfp_sys::zfp_stream; +pub use zfp_sys::{ + ZFP_HEADER_FULL, ZFP_HEADER_MAGIC, ZFP_HEADER_MAX_BITS, ZFP_HEADER_META, ZFP_HEADER_MODE, + ZFP_VERSION_MAJOR, ZFP_VERSION_MINOR, ZFP_VERSION_PATCH, ZFP_VERSION_TWEAK, bitstream, + stream_close, stream_open, zfp_codec_version, zfp_compress, zfp_decompress, zfp_field, + zfp_field_1d, zfp_field_2d, zfp_field_3d, zfp_field_4d, zfp_field_alloc, + zfp_field_dimensionality, zfp_field_free, zfp_field_metadata, zfp_field_size, zfp_field_type, + zfp_library_version, zfp_mode, zfp_mode_zfp_mode_fixed_accuracy, + zfp_mode_zfp_mode_fixed_precision, zfp_mode_zfp_mode_fixed_rate, zfp_read_header, + zfp_stream_accuracy, zfp_stream_close, zfp_stream_compression_mode, zfp_stream_flush, + zfp_stream_maximum_size, zfp_stream_open, zfp_stream_precision, zfp_stream_rate, + zfp_stream_rewind, zfp_stream_set_accuracy, zfp_stream_set_bit_stream, + zfp_stream_set_precision, zfp_stream_set_rate, zfp_stream_set_reversible, zfp_type, + zfp_type_zfp_type_double, zfp_type_zfp_type_float, zfp_write_header, +}; + +use crate::filters::ZfpMode; + +/// Major edits are needed to be in alignmeht with the H5Z-ZFP. What was previously implemented was +/// effectively a new implementation of H5Z_ZFP but was incompatible with any library built against +/// it. This results in bad c_data vectors being created and produces erratic behavior. + +pub(crate) const MAX_NDIMS: usize = 4; + +const ZFP_FILTER_NAME: &[u8] = b"zfp\0"; +pub const ZFP_FILTER_ID: H5Z_filter_t = 32013; +const ZFP_FILTER_VERSION: c_uint = 1; + +// ZFP mode constants +const ZFP_MODE_RATE: c_uint = 2; +const ZFP_MODE_PRECISION: c_uint = 3; +const ZFP_MODE_ACCURACY: c_uint = 4; +const ZFP_MODE_REVERSIBLE: c_uint = 5; +const ZFP_MODE_EXPERT: c_uint = 1; + +const ZFP_FILTER_INFO: &H5Z_class2_t = &H5Z_class2_t { + version: H5Z_CLASS_T_VERS as _, + id: ZFP_FILTER_ID, + encoder_present: 1, + decoder_present: 1, + name: ZFP_FILTER_NAME.as_ptr().cast(), + can_apply: Some(can_apply_zfp), + set_local: Some(set_local_zfp), + filter: Some(filter_zfp), +}; + +static ZFP_INIT: LazyLock> = LazyLock::new(|| { + let ret = unsafe { H5Zregister((ZFP_FILTER_INFO as *const H5Z_class2_t).cast()) }; + if H5ErrorCode::is_err_code(ret) { + return Err("Can't register ZFP filter"); + } + Ok(()) +}); + +pub fn register_zfp() -> Result<(), &'static str> { + *ZFP_INIT +} + +extern "C" fn can_apply_zfp(_dcpl_id: hid_t, type_id: hid_t, _space_id: hid_t) -> i32 { + let type_class = unsafe { H5Tget_class(type_id) }; + if type_class == H5T_FLOAT { 1 } else { 0 } +} + +/// Sets the local properties for the ZFP filter. +/// +/// This function is called during the creation of a dataset or attribute to set +/// the local properties of the ZFP filter. It retrieves the filter's configuration +/// data, validates the chunk dimensions, and updates the filter's parameters. +/// +/// # Parameters +/// - `dcpl_id`: The dataset creation property list identifier. +/// - `type_id`: The datatype identifier of the dataset or attribute. +/// - `_space_id`: The dataspace identifier (not used in this function). +/// +/// # Returns +/// - `herr_t`: Returns 1 on success, or -1 on failure. +extern "C" fn set_local_zfp(dcpl_id: hid_t, type_id: hid_t, _space_id: hid_t) -> herr_t { + const MAX_NDIMS: usize = 4; + let mut flags: c_uint = 0; + let mut nelmts: size_t = 4; + // start with a small buffer; H5Pget_filter_by_id2 will return the stored cdata (mode/params) + let mut values: Vec = vec![0; 4]; + let ret = unsafe { + H5Pget_filter_by_id2( + dcpl_id, + ZFP_FILTER_ID, + addr_of_mut!(flags), + addr_of_mut!(nelmts), + values.as_mut_ptr(), + 0, + ptr::null_mut(), + ptr::null_mut(), + ) + }; + if ret < 0 { + return -1; + } + // Preserve original small cdata (mode/params) returned by H5Pget_filter_by_id2. + let orig = values.clone(); + // ensure we have enough space for header + dims + parameters (we need at least indices up to 9) + nelmts = nelmts.max(10); + values.resize(nelmts as usize, 0); + // set version and header entries + values[0] = ZFP_FILTER_VERSION; + + let mut chunkdims: Vec = vec![0; MAX_NDIMS]; + let ndims: c_int = unsafe { H5Pget_chunk(dcpl_id, MAX_NDIMS as _, chunkdims.as_mut_ptr()) }; + if ndims < 0 { + return -1; + } + if ndims > MAX_NDIMS as _ { + h5err!("ZFP supports up to 4 dimensions", H5E_PLIST, H5E_CALLBACK); + return -1; + } + + let typesize: size_t = unsafe { H5Tget_size(type_id) }; + if typesize == 0 { + return -1; + } + + // fill header fields (ndims, typesize) and chunk dimensions + values[1] = ndims as c_uint; + values[2] = typesize as c_uint; + for i in 0..(ndims as usize).min(values.len().saturating_sub(3)) { + values[i + 3] = chunkdims[i] as c_uint; + } + // The Filter::apply_zfp() originally stored mode/param1/param2 at indices 0..2. + // parse_zfp expects these at indices 7..9 in the final cdata layout. Move/preserve them. + if values.len() >= 10 { + values[7] = orig.get(0).copied().unwrap_or(0); + values[8] = orig.get(1).copied().unwrap_or(0); + values[9] = orig.get(2).copied().unwrap_or(0); + } + // temp overrid and changed line 133 to orig instead of values + let nelmts = 4; + + let r = unsafe { H5Pmodify_filter(dcpl_id, ZFP_FILTER_ID, flags, nelmts, orig.as_ptr()) }; + if r < 0 { -1 } else { 1 } +} + +const H5Z_ZFP_CD_NELMTS_MAX: usize = 8; // whatever the header says; set correctly. + +/// Computes the header and configuration data values for the ZFP filter. +/// +/// This function generates the header and configuration data values (`cd_values`) +/// required for the ZFP filter. It creates a dummy ZFP field based on the provided +/// dimensions and data type, sets the compression mode, and writes the full header +/// into the `cd_values` buffer. +/// +/// # Parameters +/// - `zt`: The ZFP data type (e.g., `zfp_type_zfp_type_float` or `zfp_type_zfp_type_double`). +/// - `ndims_used`: The number of dimensions used in the data. +/// - `dims_used`: A slice containing the sizes of the dimensions. +/// - `mode`: The ZFP compression mode, which can be fixed rate, precision, accuracy, or reversible. +/// +/// # Returns +/// A tuple containing: +/// - `Vec`: The header and configuration data values. +/// - `usize`: The number of elements in the `cd_values` array. +/// +/// # Panics +/// This function will panic if the number of dimensions exceeds the supported range (1 to 4). +pub unsafe fn compute_hdr_cd_values( + zt: zfp_type, + ndims_used: usize, + dims_used: &[u64], + mode: ZfpMode, // your enum wrapping rate/precision/accuracy/reversible +) -> (Vec, usize) { + // 1. Build dummy_field like H5Z_zfp_set_local + let dummy_field: *mut zfp_field = match ndims_used { + 1 => zfp_field_1d(ptr::null_mut(), zt, dims_used[0].try_into().unwrap()), + 2 => zfp_field_2d( + ptr::null_mut(), + zt, + dims_used[1].try_into().unwrap(), + dims_used[0].try_into().unwrap(), + ), + 3 => zfp_field_3d( + ptr::null_mut(), + zt, + dims_used[2].try_into().unwrap(), + dims_used[1].try_into().unwrap(), + dims_used[0].try_into().unwrap(), + ), + 4 => zfp_field_4d( + ptr::null_mut(), + zt, + dims_used[3].try_into().unwrap(), + dims_used[2].try_into().unwrap(), + dims_used[1].try_into().unwrap(), + dims_used[0].try_into().unwrap(), + ), + _ => panic!("ZFP supports 1..4 non-unity dims"), + }; + assert!(!dummy_field.is_null()); + + // 2. Prepare the cd_values array like C code: u32 buffer + let mut hdr_cd_values = vec![0u32; H5Z_ZFP_CD_NELMTS_MAX]; + + // 3. Version word (use the macro layout: (ZFP_VERSION_NO<<16)|(ZFP_CODEC<<12)|H5Z_FILTER_ZFP_VERSION_NO) + hdr_cd_values[0] = make_version_word(); // see previous message + + // 4. Treat &hdr_cd_values[1] as bitstream buffer + let ptr_bytes = hdr_cd_values[1..].as_mut_ptr() as *mut c_void; + let bytes_len = (hdr_cd_values.len() - 1) * std::mem::size_of::(); + + let dummy_bstr: *mut bitstream = stream_open(ptr_bytes, bytes_len as usize); + + let dummy_zstr: *mut zfp_stream = zfp_stream_open(dummy_bstr); + // 5. Set mode the same way H5Z_zfp_set_local does + match mode { + ZfpMode::Reversible => { + zfp_stream_set_reversible(dummy_zstr); + } + ZfpMode::FixedAccuracy(acc) => { + zfp_stream_set_accuracy(dummy_zstr, acc); + } + + ZfpMode::FixedRate(rate) => { + zfp_stream_set_rate(dummy_zstr, rate, zt, ndims_used as u32, 0); + } + ZfpMode::FixedPrecision(precision) => { + zfp_stream_set_precision(dummy_zstr, precision as u32); + } + // handle Rate/Precision/Accuracy/Expert as needed + _ => unimplemented!(), + } + + // 6. Write FULL header (critical!) into the hdr_cd_values[1..] buffer + let hdr_bits = zfp_write_header(dummy_zstr, dummy_field, ZFP_HEADER_FULL as u32); + assert!(hdr_bits != 0); + + // 7. Flush and close (exactly like C) + zfp_stream_flush(dummy_zstr); + zfp_stream_close(dummy_zstr); + stream_close(dummy_bstr); + zfp_field_free(dummy_field); + + // 8. Compute hdr_bytes/hdr_cd_nelmts as in C + let hdr_bytes = 1 + ((hdr_bits - 1) / 8); + let mut hdr_cd_nelmts = 1 + ((hdr_bytes - 1) / std::mem::size_of::()); + hdr_cd_nelmts += 1; // for slot 0 + + (hdr_cd_values, hdr_cd_nelmts) +} + +/// Constructs a version word for the ZFP filter. +/// +/// This function generates a 32-bit version word that encodes the ZFP library version, +/// codec version, and filter version. The version word is structured as follows: +/// - High 24 bits: ZFP library version (major, minor, patch, tweak). +/// - Middle 8 bits: Codec version. +/// - Low 8 bits: Filter version. +/// +/// # Returns +/// A 32-bit unsigned integer representing the version word. +unsafe fn make_version_word() -> u32 { + // 0xM M P T: for 1.0.0.0 → 0x1000 + const ZFP_VERSION_NO: u32 = (ZFP_VERSION_MAJOR << 12) + | (ZFP_VERSION_MINOR << 8) + | (ZFP_VERSION_PATCH << 4) + | (ZFP_VERSION_TWEAK); + + const ZFP_CODEC: u32 = ZFP_VERSION_MINOR; // or 5 if you know you want codec 5 + + // Filter version: 1.1.0 → 0x0110 + const H5Z_FILTER_ZFP_VERSION_MAJOR: u32 = 1; + const H5Z_FILTER_ZFP_VERSION_MINOR: u32 = 1; + const H5Z_FILTER_ZFP_VERSION_PATCH: u32 = 0; + + const H5Z_FILTER_ZFP_VERSION_NO: u32 = (H5Z_FILTER_ZFP_VERSION_MAJOR << 8) + | (H5Z_FILTER_ZFP_VERSION_MINOR << 4) + | (H5Z_FILTER_ZFP_VERSION_PATCH); + + // One simple scheme: low 8 bits = codec, high 24 bits = lib version truncated. + (ZFP_VERSION_NO << 16) | (ZFP_CODEC << 12) | H5Z_FILTER_ZFP_VERSION_NO +} + +#[derive(Debug)] +struct ZfpConfig { + pub ndims: c_int, + pub typesize: size_t, + pub dims: [size_t; 4], + pub mode: c_uint, + pub rate: f64, + pub precision: u32, + pub accuracy: f64, +} + +/// Parses ZFP filter configuration data from the given input. +/// +/// This function extracts metadata and compression parameters from the +/// provided `cd_values` array, which represents the ZFP filter's configuration +/// data. It handles endian mismatches, validates the header, and retrieves +/// information such as dimensions, data type, and compression mode. +/// +/// # Safety +/// This function is marked as unsafe because it performs raw pointer +/// dereferencing and interacts with low-level C APIs, which require careful +/// handling to avoid undefined behavior. +/// +/// # Parameters +/// - `cd_nelmts`: The number of elements in the `cd_values` array. +/// - `cd_values`: A pointer to the array of configuration data values. +/// +/// # Returns +/// - `Option`: Returns a `ZfpConfig` struct containing the parsed +/// metadata and compression parameters if successful, or `None` if the +/// parsing fails. +pub unsafe fn parse_zfp_cdata(cd_nelmts: usize, cd_values: *const c_uint) -> Option { + if cd_nelmts < 2 || cd_values.is_null() { + return None; + } + + // Full cd array from HDF5: [version_word, header_words...] + let cdata: &[u32] = slice::from_raw_parts(cd_values, cd_nelmts); + + // ignore the version word, + let _version_word = cdata[0]; + + // ZFP header bitstream. + let header_words = &cdata[1..]; + if header_words.is_empty() { + return None; + } + + // Make a mutable copy so we can endian-swap in place if needed. + let mut header_copy: Vec = header_words.to_vec(); + let header_bytes = header_copy.len() * std::mem::size_of::(); + + // Open bitstream on the header buffer (like get_zfp_info_from_cd_values) + let bstr: *mut bitstream = stream_open(header_copy.as_mut_ptr() as *mut c_void, header_bytes); + if bstr.is_null() { + return None; + } + + // Open zfp_stream on that bitstream + let zstr: *mut zfp_stream = zfp_stream_open(bstr); + if zstr.is_null() { + stream_close(bstr); + return None; + } + + // Allocate a field for header metadata + let zfld: *mut zfp_field = zfp_field_alloc(); + if zfld.is_null() { + zfp_stream_close(zstr); + stream_close(bstr); + return None; + } + + //First read only MAGIC, to detect endian or codec mismatch + let mut bits = zfp_read_header(zstr, zfld, ZFP_HEADER_MAGIC); + if bits == 0 { + // Possible endian mismatch: byte-swap each u32 and retry. + for w in &mut header_copy { + *w = w.swap_bytes(); + } + + zfp_stream_rewind(zstr); + bits = zfp_read_header(zstr, zfld, ZFP_HEADER_MAGIC); + if bits == 0 { + zfp_field_free(zfld); + zfp_stream_close(zstr); + stream_close(bstr); + return None; + } + } + + //Rewind and read the full header. + zfp_stream_rewind(zstr); + if zfp_read_header(zstr, zfld, ZFP_HEADER_FULL) == 0 { + zfp_field_free(zfld); + zfp_stream_close(zstr); + stream_close(bstr); + return None; + } + + //Extract array metadata + let ndims = zfp_field_dimensionality(zfld) as i32; + + // zfp_field_size can fill per-dimension sizes; pass a buffer. + let mut size_per_dim: [usize; 4] = [0; 4]; + if ndims > 0 { + // zfp_field_size returns total number of elements and optionally fills size[i]. + // The C signature uses size_t*; just alias &mut [usize] here. + zfp_field_size(zfld, size_per_dim.as_mut_ptr() as *mut _); + } + + let mut dims: [usize; 4] = [0; 4]; + for i in 0..(ndims as usize).min(4) { + dims[i] = size_per_dim[i]; + } + + // Scalar type → element size in bytes. + let zt: zfp_type = zfp_field_type(zfld); + let typesize: usize = match zt { + // Adjust these to match the actual enum variants in your bindings + x if x == zfp_sys::zfp_type_zfp_type_int32 => std::mem::size_of::(), + x if x == zfp_sys::zfp_type_zfp_type_int64 => std::mem::size_of::(), + x if x == zfp_sys::zfp_type_zfp_type_float => std::mem::size_of::(), + x if x == zfp_sys::zfp_type_zfp_type_double => std::mem::size_of::(), + _ => { + zfp_field_free(zfld); + zfp_stream_close(zstr); + stream_close(bstr); + return None; + } + }; + // Extract compression mode and parameters from the stream itself. + let zmode_enum: zfp_mode = zfp_stream_compression_mode(zstr); + let mode = zmode_enum as u32; + + let mut rate: f64 = 0.0; + let mut precision: u32 = 0; + let mut accuracy: f64 = 0.0; + + // These getters are available on modern zfp (1.0+). + match zmode_enum { + m if m == zfp_sys::zfp_mode_zfp_mode_fixed_rate => { + rate = zfp_stream_rate(zstr, ndims as u32); + } + m if m == zfp_sys::zfp_mode_zfp_mode_fixed_precision => { + precision = zfp_stream_precision(zstr); + } + m if m == zfp_sys::zfp_mode_zfp_mode_fixed_accuracy => { + accuracy = zfp_stream_accuracy(zstr); + } + m if m == zfp_sys::zfp_mode_zfp_mode_reversible => { + // no params needed + } + + // Expert or reversible -> we don’t have a single scalar parameter to expose + _ => {} + } + + //Cleanup + zfp_field_free(zfld); + zfp_stream_close(zstr); + stream_close(bstr); + + Some(ZfpConfig { ndims, typesize, dims, mode, rate, precision, accuracy }) +} + +/// Applies the ZFP filter for compression or decompression. +/// +/// This function serves as the entry point for the ZFP filter, determining whether +/// to compress or decompress the data based on the provided flags. It parses the +/// filter configuration data, validates it, and then delegates the operation to +/// either the compression or decompression function. +/// +/// # Safety +/// This function is marked as unsafe because it interacts with raw pointers and +/// performs operations that require careful handling to avoid undefined behavior. +/// +/// # Parameters +/// - `flags`: A bitmask indicating the operation mode (e.g., compression or decompression). +/// - `cd_nelmts`: The number of elements in the `cd_values` array. +/// - `cd_values`: A pointer to the array of configuration data values. +/// - `nbytes`: The size of the input buffer in bytes. +/// - `buf_size`: A pointer to the size of the output buffer. +/// - `buf`: A pointer to the input/output buffer. +/// +/// # Returns +/// - `size_t`: The size of the processed data (compressed or decompressed) on success, +/// or 0 on failure. +unsafe extern "C" fn filter_zfp( + flags: c_uint, cd_nelmts: size_t, cd_values: *const c_uint, nbytes: size_t, + buf_size: *mut size_t, buf: *mut *mut c_void, +) -> size_t { + let cfg = if let Some(cfg) = parse_zfp_cdata(cd_nelmts, cd_values) { + cfg + } else { + return 0; + }; + + if flags & H5Z_FLAG_REVERSE == 0 { + unsafe { filter_zfp_compress(&cfg, buf_size, buf) } + } else { + unsafe { filter_zfp_decompress(&cfg, nbytes, buf_size, buf) } + } +} + +unsafe fn filter_zfp_compress( + cfg: &ZfpConfig, buf_size: *mut size_t, buf: *mut *mut c_void, +) -> size_t { + let zfp_stream = zfp_stream_open(ptr::null_mut()); + if zfp_stream.is_null() { + h5err!("Failed to open ZFP stream", H5E_PLIST, H5E_CALLBACK); + return 0; + } + + match cfg.mode { + ZFP_MODE_RATE => { + zfp_stream_set_rate(zfp_stream, cfg.rate, cfg.typesize as _, cfg.ndims as _, 0); + } + ZFP_MODE_PRECISION => { + zfp_stream_set_precision(zfp_stream, cfg.precision); + } + ZFP_MODE_ACCURACY => { + zfp_stream_set_accuracy(zfp_stream, cfg.accuracy); + } + ZFP_MODE_REVERSIBLE => zfp_stream_set_reversible(zfp_stream), + _ => { + zfp_stream_close(zfp_stream); + return 0; + } + } + + let field = if cfg.typesize == 4 { + match cfg.ndims { + 1 => zfp_field_1d((*buf).cast(), zfp_type_zfp_type_float, cfg.dims[0]), + 2 => zfp_field_2d((*buf).cast(), zfp_type_zfp_type_float, cfg.dims[0], cfg.dims[1]), + 3 => zfp_field_3d( + (*buf).cast(), + zfp_type_zfp_type_float, + cfg.dims[0], + cfg.dims[1], + cfg.dims[2], + ), + 4 => zfp_field_4d( + (*buf).cast(), + zfp_type_zfp_type_float, + cfg.dims[0], + cfg.dims[1], + cfg.dims[2], + cfg.dims[3], + ), + _ => ptr::null_mut(), + } + } else { + match cfg.ndims { + 1 => zfp_field_1d((*buf).cast(), zfp_type_zfp_type_double, cfg.dims[0]), + 2 => zfp_field_2d((*buf).cast(), zfp_type_zfp_type_double, cfg.dims[0], cfg.dims[1]), + 3 => zfp_field_3d( + (*buf).cast(), + zfp_type_zfp_type_double, + cfg.dims[0], + cfg.dims[1], + cfg.dims[2], + ), + 4 => zfp_field_4d( + (*buf).cast(), + zfp_type_zfp_type_double, + cfg.dims[0], + cfg.dims[1], + cfg.dims[2], + cfg.dims[3], + ), + _ => ptr::null_mut(), + } + }; + if field.is_null() { + zfp_stream_close(zfp_stream); + h5err!("Failed to create ZFP field", H5E_PLIST, H5E_CALLBACK); + return 0; + } + + let maxsize = zfp_stream_maximum_size(zfp_stream, field); + let outbuf = libc::malloc(maxsize); + if outbuf.is_null() { + zfp_field_free(field); + zfp_stream_close(zfp_stream); + h5err!("Can't allocate compression buffer", H5E_PLIST, H5E_CALLBACK); + return 0; + } + + let bitstream = stream_open(outbuf.cast(), maxsize); + zfp_stream_set_bit_stream(zfp_stream, bitstream); + zfp_stream_rewind(zfp_stream); + + let compressed_size = zfp_compress(zfp_stream, field); + stream_close(bitstream); + zfp_field_free(field); + zfp_stream_close(zfp_stream); + + if compressed_size == 0 { + libc::free(outbuf); + h5err!("ZFP compression failed", H5E_PLIST, H5E_CALLBACK); + return 0; + } + + libc::free(*buf); + *buf = outbuf; + *buf_size = compressed_size; + compressed_size +} + +unsafe fn filter_zfp_decompress( + cfg: &ZfpConfig, nbytes: size_t, buf_size: *mut size_t, buf: *mut *mut c_void, +) -> size_t { + let zfp_stream = zfp_stream_open(ptr::null_mut()); + if zfp_stream.is_null() { + h5err!("Failed to open ZFP stream", H5E_PLIST, H5E_CALLBACK); + return 0; + } + + match cfg.mode { + ZFP_MODE_RATE => { + zfp_stream_set_rate(zfp_stream, cfg.rate, cfg.typesize as _, cfg.ndims as _, 0); + } + ZFP_MODE_PRECISION => { + zfp_stream_set_precision(zfp_stream, cfg.precision); + } + ZFP_MODE_ACCURACY => { + zfp_stream_set_accuracy(zfp_stream, cfg.accuracy); + } + ZFP_MODE_REVERSIBLE => zfp_stream_set_reversible(zfp_stream), + _ => { + zfp_stream_close(zfp_stream); + return 0; + } + } + + let mut outbuf_size = cfg.typesize; + for i in 0..cfg.ndims as usize { + outbuf_size *= cfg.dims[i]; + } + + let outbuf = libc::malloc(outbuf_size); + if outbuf.is_null() { + zfp_stream_close(zfp_stream); + h5err!("Can't allocate decompression buffer", H5E_PLIST, H5E_CALLBACK); + return 0; + } + + let field = if cfg.typesize == 4 { + match cfg.ndims { + 1 => zfp_field_1d(outbuf.cast(), zfp_type_zfp_type_float, cfg.dims[0]), + 2 => zfp_field_2d(outbuf.cast(), zfp_type_zfp_type_float, cfg.dims[0], cfg.dims[1]), + 3 => zfp_field_3d( + outbuf.cast(), + zfp_type_zfp_type_float, + cfg.dims[0], + cfg.dims[1], + cfg.dims[2], + ), + 4 => zfp_field_4d( + outbuf.cast(), + zfp_type_zfp_type_float, + cfg.dims[0], + cfg.dims[1], + cfg.dims[2], + cfg.dims[3], + ), + _ => ptr::null_mut(), + } + } else { + match cfg.ndims { + 1 => zfp_field_1d(outbuf.cast(), zfp_type_zfp_type_double, cfg.dims[0]), + 2 => zfp_field_2d(outbuf.cast(), zfp_type_zfp_type_double, cfg.dims[0], cfg.dims[1]), + 3 => zfp_field_3d( + outbuf.cast(), + zfp_type_zfp_type_double, + cfg.dims[0], + cfg.dims[1], + cfg.dims[2], + ), + 4 => zfp_field_4d( + outbuf.cast(), + zfp_type_zfp_type_double, + cfg.dims[0], + cfg.dims[1], + cfg.dims[2], + cfg.dims[3], + ), + _ => ptr::null_mut(), + } + }; + + if field.is_null() { + libc::free(outbuf); + zfp_stream_close(zfp_stream); + h5err!("Failed to create ZFP field", H5E_PLIST, H5E_CALLBACK); + return 0; + } + + let bitstream = stream_open((*buf).cast(), nbytes); + zfp_stream_set_bit_stream(zfp_stream, bitstream); + zfp_stream_rewind(zfp_stream); + + let status = zfp_decompress(zfp_stream, field); + + stream_close(bitstream); + zfp_field_free(field); + zfp_stream_close(zfp_stream); + + if status == 0 { + libc::free(outbuf); + h5err!("ZFP decompression failed", H5E_PLIST, H5E_CALLBACK); + return 0; + } + + libc::free(*buf); + *buf = outbuf; + *buf_size = outbuf_size; + outbuf_size +} diff --git a/hdf5/src/hl/group.rs b/hdf5/src/hl/group.rs index ea85c361f..47d5beb79 100644 --- a/hdf5/src/hl/group.rs +++ b/hdf5/src/hl/group.rs @@ -4,18 +4,21 @@ use std::panic; use std::ptr::addr_of_mut; use hdf5_sys::{ - h5::{hsize_t, H5_index_t, H5_iter_order_t}, + h5::{H5_index_t, H5_iter_order_t, hsize_t}, h5d::H5Dopen2, - h5g::{H5G_info_t, H5Gcreate2, H5Gget_info, H5Gopen2}, + h5g::{H5G_info_t, H5Gcreate_anon, H5Gcreate2, H5Gget_create_plist, H5Gget_info, H5Gopen2}, h5l::{ - H5L_info_t, H5L_iterate_t, H5L_type_t, H5Lcreate_external, H5Lcreate_hard, H5Lcreate_soft, - H5Ldelete, H5Lexists, H5Literate, H5Lmove, H5L_SAME_LOC, + H5L_SAME_LOC, H5L_info_t, H5L_iterate_t, H5L_type_t, H5Lcreate_external, H5Lcreate_hard, + H5Lcreate_soft, H5Ldelete, H5Lexists, H5Literate, H5Lmove, }, h5p::{H5Pcreate, H5Pset_create_intermediate_group}, h5t::H5T_cset_t, }; use crate::globals::H5P_LINK_CREATE; +use crate::hl::dataset::Maybe; +use crate::hl::plist::group_create::{GroupCreate, GroupCreateBuilder}; +use crate::hl::plist::link_create::{CharEncoding, LinkCreate, LinkCreateBuilder}; use crate::internal_prelude::*; use crate::{Location, LocationType}; @@ -86,17 +89,15 @@ impl Group { /// Create a new group in a file or group. pub fn create_group(&self, name: &str) -> Result { // TODO: &mut self? - h5lock!({ - let lcpl = make_lcpl()?; - let name = to_cstring(name)?; - Self::from_id(h5try!(H5Gcreate2( - self.id(), - name.as_ptr(), - lcpl.id(), - H5P_DEFAULT, - H5P_DEFAULT - ))) - }) + self.create_group_builder().create(name) + } + + /// Instantiates a new group builder for configuring group creation properties. + /// + /// Intermediate groups are created automatically (as with [`create_group`](Self::create_group)) + /// unless disabled via [`GroupBuilder::create_intermediate_group`]. + pub fn create_group_builder(&self) -> GroupBuilder { + GroupBuilder::new(self) } /// Opens an existing group in a file or group. @@ -215,6 +216,207 @@ impl Group { let name = to_cstring(name)?; Dataset::from_id(h5try!(H5Dopen2(self.id(), name.as_ptr(), H5P_DEFAULT))) } + + /// Returns a copy of the group creation property list. + pub fn create_plist(&self) -> Result { + h5lock!(GroupCreate::from_id(h5try!(H5Gget_create_plist(self.id())))) + } + + /// A short alias for `create_plist()`. + pub fn gcpl(&self) -> Result { + self.create_plist() + } +} + +/// A builder for creating a new [`Group`]. +/// +/// Created via [`Group::create_group_builder`]. Allows configuring the group +/// creation property list (e.g. [`obj_track_times`](Self::obj_track_times)) and the +/// link creation property list before creating the group. +#[derive(Clone)] +pub struct GroupBuilder { + parent: Result, + gcpl_base: Option, + gcpl_builder: GroupCreateBuilder, + lcpl_base: Option, + lcpl_builder: LinkCreateBuilder, +} + +impl GroupBuilder { + /// Creates a new group builder with the given parent location. + pub fn new(parent: &Group) -> Self { + // enable creation of intermediate groups by default, matching `create_group` + let mut lcpl_builder = LinkCreateBuilder::default(); + lcpl_builder.create_intermediate_group(true); + Self { + parent: parent.try_borrow(), + gcpl_base: None, + gcpl_builder: GroupCreateBuilder::default(), + lcpl_base: None, + lcpl_builder, + } + } + + /// Uses an existing group creation property list as the base. + #[inline] + #[must_use] + pub fn set_create_plist(mut self, gcpl: &GroupCreate) -> Self { + self.gcpl_base = Some(gcpl.clone()); + self + } + + /// Alias for [`set_create_plist`](Self::set_create_plist). + #[inline] + #[must_use] + pub fn set_gcpl(self, gcpl: &GroupCreate) -> Self { + self.set_create_plist(gcpl) + } + + /// Returns a mutable reference to the group creation property list builder. + #[inline] + pub fn create_plist(&mut self) -> &mut GroupCreateBuilder { + &mut self.gcpl_builder + } + + /// Alias for [`create_plist`](Self::create_plist). + #[inline] + pub fn gcpl(&mut self) -> &mut GroupCreateBuilder { + self.create_plist() + } + + /// Applies a closure to the group creation property list builder. + #[inline] + #[must_use] + pub fn with_create_plist(mut self, func: F) -> Self + where + F: Fn(&mut GroupCreateBuilder) -> &mut GroupCreateBuilder, + { + func(&mut self.gcpl_builder); + self + } + + /// Alias for [`with_create_plist`](Self::with_create_plist). + #[inline] + #[must_use] + pub fn with_gcpl(self, func: F) -> Self + where + F: Fn(&mut GroupCreateBuilder) -> &mut GroupCreateBuilder, + { + self.with_create_plist(func) + } + + #[inline] + #[must_use] + #[doc = "\u{21b3} [`GroupCreateBuilder::obj_track_times`](crate::plist::GroupCreateBuilder::obj_track_times)"] + pub fn obj_track_times(mut self, track_times: bool) -> Self { + self.gcpl_builder.obj_track_times(track_times); + self + } + + /// Uses an existing link creation property list as the base. + #[inline] + #[must_use] + pub fn set_link_create_plist(mut self, lcpl: &LinkCreate) -> Self { + self.lcpl_base = Some(lcpl.clone()); + self + } + + /// Alias for [`set_link_create_plist`](Self::set_link_create_plist). + #[inline] + #[must_use] + pub fn set_lcpl(self, lcpl: &LinkCreate) -> Self { + self.set_link_create_plist(lcpl) + } + + /// Returns a mutable reference to the link creation property list builder. + #[inline] + pub fn link_create_plist(&mut self) -> &mut LinkCreateBuilder { + &mut self.lcpl_builder + } + + /// Alias for [`link_create_plist`](Self::link_create_plist). + #[inline] + pub fn lcpl(&mut self) -> &mut LinkCreateBuilder { + self.link_create_plist() + } + + /// Applies a closure to the link creation property list builder. + #[inline] + #[must_use] + pub fn with_link_create_plist(mut self, func: F) -> Self + where + F: Fn(&mut LinkCreateBuilder) -> &mut LinkCreateBuilder, + { + func(&mut self.lcpl_builder); + self + } + + /// Alias for [`with_link_create_plist`](Self::with_link_create_plist). + #[inline] + #[must_use] + pub fn with_lcpl(self, func: F) -> Self + where + F: Fn(&mut LinkCreateBuilder) -> &mut LinkCreateBuilder, + { + self.with_link_create_plist(func) + } + + #[inline] + #[must_use] + #[doc = "\u{21b3} [`LinkCreateBuilder::create_intermediate_group`](crate::plist::LinkCreateBuilder::create_intermediate_group)"] + pub fn create_intermediate_group(mut self, create: bool) -> Self { + self.lcpl_builder.create_intermediate_group(create); + self + } + + #[inline] + #[must_use] + #[doc = "\u{21b3} [`LinkCreateBuilder::char_encoding`](crate::plist::LinkCreateBuilder::char_encoding)"] + pub fn char_encoding(mut self, encoding: CharEncoding) -> Self { + self.lcpl_builder.char_encoding(encoding); + self + } + + fn build_gcpl(&self) -> Result { + let mut gcpl = match &self.gcpl_base { + Some(gcpl) => gcpl.clone(), + None => GroupCreate::try_new()?, + }; + self.gcpl_builder.apply(&mut gcpl).map(|()| gcpl) + } + + fn build_lcpl(&self) -> Result { + let mut lcpl = match &self.lcpl_base { + Some(lcpl) => lcpl.clone(), + None => LinkCreate::try_new()?, + }; + self.lcpl_builder.apply(&mut lcpl).map(|()| lcpl) + } + + /// Creates the group. + /// + /// Passing a name creates a named (linked) group. Passing `None` creates an + /// anonymous group that is not linked into the file until linked explicitly. + pub fn create<'n, T: Into>>(&self, name: T) -> Result { + h5lock!({ + let parent = try_ref_clone!(self.parent); + let gcpl = self.build_gcpl()?; + let name: Option<&str> = name.into().into(); + if let Some(name) = name { + let lcpl = self.build_lcpl()?; + let name = to_cstring(name)?; + Group::from_id(h5try!(H5Gcreate2( + parent.id(), + name.as_ptr(), + lcpl.id(), + gcpl.id(), + H5P_DEFAULT + ))) + } else { + Group::from_id(h5try!(H5Gcreate_anon(parent.id(), gcpl.id(), H5P_DEFAULT))) + } + }) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -261,10 +463,14 @@ impl From for H5_iter_order_t { } } +/// The type of an object link. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum LinkType { + /// A hard link to an object within a single file. Hard, + /// A symbolic link to an object within a single file. Soft, + /// A symbolic link to an object in a different file. External, } @@ -278,6 +484,7 @@ impl From for LinkType { } } +/// Metadata describing an object link. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct LinkInfo { pub link_type: LinkType, @@ -454,6 +661,95 @@ pub mod tests { }) } + #[test] + pub fn test_create_group_builder() { + with_tmp_file(|file| { + // the builder creates a named group + let group = file.create_group_builder().create("foo").unwrap(); + assert_eq!(group.name(), "/foo"); + + // intermediate groups are created by default, and can be disabled + file.create_group_builder().create("a/b/c").unwrap(); + assert!(file.group("/a/b/c").is_ok()); + assert!( + file.create_group_builder() + .create_intermediate_group(false) + .create("x/y/z") + .is_err() + ); + }) + } + + #[test] + pub fn test_create_group_anon() { + with_tmp_file(|file| { + // passing `None` creates an anonymous group + let group = file.create_group_builder().create(None).unwrap(); + assert!(group.is_valid()); + // an anonymous group is not linked anywhere yet + assert_eq!(file.len(), 0); + }) + } + + #[test] + pub fn test_group_track_times_default() { + with_tmp_file(|file| { + // groups track object times by default + let group = file.create_group("default").unwrap(); + assert!(group.create_plist().unwrap().obj_track_times()); + // and the builder can request it explicitly + let enabled = + file.create_group_builder().obj_track_times(true).create("enabled").unwrap(); + assert!(enabled.create_plist().unwrap().obj_track_times()); + }) + } + + // `obj_track_times` maps to a flag bit in the object header. Only a version-2 + // object header carries that flag (`H5O_HDR_STORE_TIMES` in the header prefix), + // so only a v2 header can record the setting and report it back through + // `H5Gget_create_plist`. A version-1 header has no such flag, so a disabled + // setting is not stored and reads back as the default (enabled). Version-2 + // headers need at least the v18 file format. libhdf5 2.0 makes that the + // default, older versions have to opt in with the library version bounds. The + // `libver_*` API itself only exists from 1.10.2 on. See the format spec section + // IV.A.1.b, where bit 5 of the version-2 prefix Flags field stores the times + // (the version-1 prefix in IV.A.1.a has no Flags field): + // https://support.hdfgroup.org/documentation/hdf5/latest/_f_m_t2.html#subsubsec_fmt2_dataobject_hdr_prefix_two + #[cfg(feature = "1.10.2")] + #[test] + pub fn test_group_track_times_disabled() { + use crate::hl::plist::file_access::LibraryVersion; + // exercise both the minimum v18 format and the newest one + for low in [LibraryVersion::V18, LibraryVersion::latest()] { + with_tmp_path(|path| { + let file = File::with_options() + .with_fapl(|fapl| fapl.libver_bounds(low, LibraryVersion::latest())) + .create(&path) + .unwrap(); + let group = file.create_group_builder().obj_track_times(false).create("g").unwrap(); + // the disabled setting round-trips through the created group + assert!(!group.create_plist().unwrap().obj_track_times()); + // `gcpl` is an alias for `create_plist` and reports the same value + assert!(!file.group("g").unwrap().gcpl().unwrap().obj_track_times()); + }) + } + } + + #[cfg(feature = "1.10.2")] + #[test] + pub fn test_group_track_times_anon() { + with_tmp_path(|path| { + let file = + File::with_options().with_fapl(|fapl| fapl.libver_v18()).create(&path).unwrap(); + // an anonymous group also honors the disabled time tracking setting + let group = file.create_group_builder().obj_track_times(false).create(None).unwrap(); + assert!(group.is_valid()); + assert!(!group.create_plist().unwrap().obj_track_times()); + // and it is still not linked anywhere in the file + assert_eq!(file.len(), 0); + }) + } + #[test] pub fn test_clone() { with_tmp_file(|file| { @@ -585,6 +881,22 @@ pub mod tests { }) } + #[test] + pub fn test_missing_group_error_codes() { + with_tmp_file(|file| { + file.create_group("a").unwrap(); + // Both a missing intermediate and a missing leaf report the same codes, so callers + // can detect "no such object" without matching on the message text. + for path in ["/foo/baz", "/a/baz"] { + let err = file.group(path).unwrap_err(); + assert!(err.contains_major(MajorErrorCode::SymbolTable), "{path}: {err:?}"); + assert!(err.contains_minor(MinorErrorCode::NotFound), "{path}: {err:?}"); + assert!(!err.contains_minor(MinorErrorCode::NotHdf5), "{path}: {err:?}"); + } + file.group("a").unwrap(); + }) + } + #[test] pub fn test_unlink() { with_tmp_file(|file| { @@ -617,9 +929,14 @@ pub mod tests { file.new_dataset::().no_chunk().shape((10, 20)).create("a/foo").unwrap(); file.new_dataset::().no_chunk().shape((10, 20)).create("a/123").unwrap(); file.new_dataset::().no_chunk().shape((10, 20)).create("a/bar").unwrap(); - assert_eq!(group_a.member_names().unwrap(), vec!["123", "bar", "foo"]); + let group_a_names = group_a.member_names().unwrap(); + assert!(group_a_names.contains(&"123".to_string())); + assert!(group_a_names.contains(&"bar".to_string())); + assert!(group_a_names.contains(&"foo".to_string())); assert_eq!(group_b.member_names().unwrap().len(), 0); - assert_eq!(file.member_names().unwrap(), vec!["a", "b"]); + let file_names = file.member_names().unwrap(); + assert!(file_names.contains(&"a".to_string())); + assert!(file_names.contains(&"b".to_string())); }) } diff --git a/hdf5/src/hl/location.rs b/hdf5/src/hl/location.rs index 2f68e378b..4ec558930 100644 --- a/hdf5/src/hl/location.rs +++ b/hdf5/src/hl/location.rs @@ -3,18 +3,19 @@ use std::mem::MaybeUninit; use std::ops::Deref; use std::ptr; +use hdf5_sys::h5o::H5Ocopy; #[allow(deprecated)] use hdf5_sys::h5o::H5Oset_comment; +#[cfg(feature = "1.10.3")] +use hdf5_sys::h5o::{H5O_INFO_BASIC, H5O_INFO_NUM_ATTRS, H5O_INFO_TIME}; #[cfg(feature = "1.12.0")] use hdf5_sys::h5o::{ - H5O_info2_t, H5O_token_t, H5Oget_info3, H5Oget_info_by_name3, H5Oopen_by_token, + H5O_info2_t, H5O_token_t, H5Oget_info_by_name3, H5Oget_info3, H5Oopen_by_token, }; #[cfg(not(feature = "1.10.3"))] -use hdf5_sys::h5o::{H5Oget_info1, H5Oget_info_by_name1}; +use hdf5_sys::h5o::{H5Oget_info_by_name1, H5Oget_info1}; #[cfg(all(feature = "1.10.3", not(feature = "1.12.0")))] -use hdf5_sys::h5o::{H5Oget_info2, H5Oget_info_by_name2}; -#[cfg(feature = "1.10.3")] -use hdf5_sys::h5o::{H5O_INFO_BASIC, H5O_INFO_NUM_ATTRS, H5O_INFO_TIME}; +use hdf5_sys::h5o::{H5Oget_info_by_name2, H5Oget_info2}; #[cfg(not(feature = "1.12.0"))] use hdf5_sys::{h5::haddr_t, h5o::H5O_info1_t, h5o::H5Oopen_by_addr}; use hdf5_sys::{ @@ -91,7 +92,7 @@ impl Location { comment.and_then(|c| if c.is_empty() { None } else { Some(c) }) } - /// Set or the comment attached to the named object. + /// Set the comment attached to the named object. #[deprecated(note = "attributes are preferred to comments")] pub fn set_comment(&self, comment: &str) -> Result<()> { // TODO: &mut self? @@ -108,19 +109,27 @@ impl Location { h5call!(H5Oset_comment(self.id(), ptr::null_mut())).and(Ok(())) } + /// Create a builder for a new attribute of known type. pub fn new_attr(&self) -> AttributeBuilderEmpty { AttributeBuilder::new(self).empty::() } + /// Create a builder for a new attribute. pub fn new_attr_builder(&self) -> AttributeBuilder { AttributeBuilder::new(self) } + /// Create a new named attribute on the object. pub fn attr(&self, name: &str) -> Result { let name = to_cstring(name)?; Attribute::from_id(h5try!(H5Aopen(self.id(), name.as_ptr(), H5P_DEFAULT))) } + /// Return the names of all attributes on the object. + /// + /// # Errors + /// + /// Returns an error if an underlying library call fails. pub fn attr_names(&self) -> Result> { Attribute::attr_names(self) } @@ -131,24 +140,37 @@ impl Location { Ok(()) } + /// Returns the object's metadata. pub fn loc_info(&self) -> Result { H5O_get_info(self.id(), true) } + /// Returns the object's type. pub fn loc_type(&self) -> Result { Ok(H5O_get_info(self.id(), false)?.loc_type) } + /// Returns the metadata of another object with name relative to `self`. + /// + /// # Errors + /// + /// Returns an error if the name is invalid. pub fn loc_info_by_name(&self, name: &str) -> Result { let name = to_cstring(name)?; H5O_get_info_by_name(self.id(), name.as_ptr(), true) } + /// Returns the type of another object with name relative to `self`. + /// + /// # Errors + /// + /// Returns an error if the name is invalid. pub fn loc_type_by_name(&self, name: &str) -> Result { let name = to_cstring(name)?; Ok(H5O_get_info_by_name(self.id(), name.as_ptr(), false)?.loc_type) } + /// Opens an object using its location token. pub fn open_by_token(&self, token: LocationToken) -> Result { H5O_open_by_token(self.id(), token) } @@ -166,14 +188,59 @@ impl Location { pub fn dereference(&self, reference: &R) -> Result { reference.dereference(self) } + + /// Copy this object to a destination location with default properties + pub fn copy_to(&self, dst_loc: &Location, dst_name: &str) -> Result<()> { + self.copy_to_with_props(dst_loc, dst_name, None, None) + } + + /// Copy this object to a destination location with custom property lists + /// + /// # Arguments + /// * `dst_loc` - Destination location (file or group) + /// * `dst_name` - Name for the copied object at the destination + /// * `ocpypl` - Optional object copy property list (controls copy behavior) + /// * `lcpl` - Optional link creation property list (controls link properties) + pub fn copy_to_with_props( + &self, dst_loc: &Location, dst_name: &str, ocpypl: Option<&PropertyList>, + lcpl: Option<&PropertyList>, + ) -> Result<()> { + // Validate property list classes if provided + if let Some(pl) = ocpypl { + if !pl.is_class(PropertyListClass::ObjectCopy) { + fail!("Property list must be of class ObjectCopy"); + } + } + if let Some(pl) = lcpl { + if !pl.is_class(PropertyListClass::LinkCreate) { + fail!("Property list must be of class LinkCreate"); + } + } + + let dst_name = to_cstring(dst_name)?; + let ocpypl_id = ocpypl.map_or(H5P_DEFAULT, |p| p.id()); + let lcpl_id = lcpl.map_or(H5P_DEFAULT, |p| p.id()); + + h5call!(H5Ocopy( + self.id(), // src_loc_id + b".\0".as_ptr() as *const c_char, // src_name (current object) + dst_loc.id(), // dst_loc_id + dst_name.as_ptr(), // dst_name + ocpypl_id, // ocpypl_id + lcpl_id // lcpl_id + ))?; + Ok(()) + } } +/// A token containing the address or identifier of a [`Location`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct LocationToken( #[cfg(not(feature = "1.12.0"))] haddr_t, #[cfg(feature = "1.12.0")] H5O_token_t, ); +/// The type of an object in a [`Location`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum LocationType { Group, @@ -271,11 +338,7 @@ impl From for LocationInfo { #[cfg(feature = "1.10.3")] fn info_fields(full: bool) -> c_uint { - if full { - H5O_INFO_BASIC | H5O_INFO_NUM_ATTRS | H5O_INFO_TIME - } else { - H5O_INFO_BASIC - } + if full { H5O_INFO_BASIC | H5O_INFO_NUM_ATTRS | H5O_INFO_TIME } else { H5O_INFO_BASIC } } #[allow(non_snake_case, unused_variables)] @@ -320,7 +383,7 @@ fn H5O_open_by_token(loc_id: hid_t, token: LocationToken) -> Result { #[cfg(test)] pub mod tests { - use crate::internal_prelude::*; + use crate::{hl::plist::object_copy::ObjectCopy, internal_prelude::*, plist::LinkCreate}; #[test] pub fn test_filename() { @@ -437,4 +500,143 @@ pub mod tests { assert!(file.loc_info_by_name("gibberish").is_err()); }) } + + #[test] + pub fn test_copy_dataset_between_files() { + with_tmp_path(|src_path| { + with_tmp_path(|dst_path| { + // Create source file with a dataset + let src_file = File::create(&src_path).unwrap(); + let src_group = src_file.create_group("src_group").unwrap(); + + let src_dataset = + src_group.new_dataset::().shape([5]).create("src_group").unwrap(); + src_dataset.write(&[1, 2, 3, 4, 5]).unwrap(); + + let src_attr = src_dataset.new_attr::().create("src_attr").unwrap(); + src_attr.write_scalar(&42.0).unwrap(); + + let dst_file = File::create(&dst_path).unwrap(); + let dst_group = dst_file.create_group("dst_group").unwrap(); + + // Copy the dataset from source to destination and verify contents + src_dataset.copy_to(&dst_group, "copied_dataset").unwrap(); + + let copied_dataset = dst_group.dataset("copied_dataset").unwrap(); + let read_data: Vec = copied_dataset.read_1d().unwrap().to_vec(); + assert_eq!(read_data, &[1, 2, 3, 4, 5]); + + let copied_attr = copied_dataset.attr("src_attr").unwrap(); + let attr_value: f64 = copied_attr.read_scalar().unwrap(); + assert_eq!(attr_value, 42.0); + }) + }) + } + + #[test] + pub fn test_copy_group_with_nested_content() { + with_tmp_path(|src_path| { + with_tmp_path(|dst_path| { + // Create source file with nested structure + let src_file = File::create(&src_path).unwrap(); + let group1 = src_file.create_group("group1").unwrap(); + let subgroup = group1.create_group("subgroup").unwrap(); + + let ds1 = group1.new_dataset::().shape([3]).create("dataset1").unwrap(); + ds1.write(&[10, 20, 30]).unwrap(); + let ds2 = subgroup.new_dataset::().shape([2]).create("dataset2").unwrap(); + ds2.write(&[1.5, 2.5]).unwrap(); + + // Create destination file and copy entire group structure + let dst_file = File::create(&dst_path).unwrap(); + group1.copy_to(&dst_file, "copied_group1").unwrap(); + + // Verify the copied structure + let copied_group = dst_file.group("copied_group1").unwrap(); + + let copied_ds1 = copied_group.dataset("dataset1").unwrap(); + let data1: Vec = copied_ds1.read_1d().unwrap().to_vec(); + assert_eq!(data1, vec![10, 20, 30]); + + let copied_subgroup = copied_group.group("subgroup").unwrap(); + let copied_ds2 = copied_subgroup.dataset("dataset2").unwrap(); + let data2: Vec = copied_ds2.read_1d().unwrap().to_vec(); + assert_eq!(data2, vec![1.5, 2.5]); + }) + }) + } + + #[test] + pub fn test_copy_without_attributes() { + with_tmp_path(|src_path| { + with_tmp_path(|dst_path| { + let src_file = File::create(&src_path).unwrap(); + + let dataset = src_file.new_dataset::().shape([3]).create("data").unwrap(); + dataset.write(&[10, 20, 30]).unwrap(); + + let src_attr = dataset.new_attr::().create("foo_attr").unwrap(); + src_attr.write_scalar(&100).unwrap(); + + let dst_file = File::create(&dst_path).unwrap(); + + let ocpypl = ObjectCopy::build().copy_without_attr(true).finish().unwrap(); + + // Copy without attributes + dataset + .copy_to_with_props(&dst_file, "copied_no_attrs", Some(&ocpypl), None) + .unwrap(); + + src_file.close().unwrap(); + + let copied = dst_file.dataset("copied_no_attrs").unwrap(); + let copied_data: Vec = copied.read_1d().unwrap().to_vec(); + assert_eq!(copied_data, vec![10, 20, 30]); + + // Verify attributes were NOT copied + let attr_names = copied.attr_names().unwrap(); + assert!(attr_names.is_empty(), "Expected no attributes, but found: {attr_names:?}",); + }) + }) + } + + #[test] + pub fn test_copy_with_link_create_intermediate_groups() { + with_tmp_path(|src_path| { + with_tmp_path(|dst_path| { + let src_file = File::create(&src_path).unwrap(); + + let dataset = src_file.new_dataset::().shape([3]).create("data").unwrap(); + dataset.write(&[100, 200, 300]).unwrap(); + + let dst_file = File::create(&dst_path).unwrap(); + + // Fails without setting LinkCreate plist + assert!(dataset.copy_to(&dst_file, "level1/level2/level3/copied_data").is_err()); + + // Succeeds with LinkCreate + // Create link create property list that creates intermediate groups + dataset + .copy_to_with_props( + &dst_file, + "level1/level2/level3/copied_data", + None, + Some( + &LinkCreate::build().create_intermediate_group(true).finish().unwrap(), + ), + ) + .unwrap(); + + // Verify the intermediate groups were created + assert!(dst_file.group("level1").is_ok()); + assert!(dst_file.group("level1/level2").is_ok()); + assert!(dst_file.group("level1/level2/level3").is_ok()); + + // Verify the data + let copied = dst_file.dataset("level1/level2/level3/copied_data").unwrap(); + let data: Vec = copied.read_1d().unwrap().to_vec(); + assert_eq!(data, vec![100, 200, 300]); + }) + }) + } } diff --git a/hdf5/src/hl/object.rs b/hdf5/src/hl/object.rs index cabe0e970..d7d383872 100644 --- a/hdf5/src/hl/object.rs +++ b/hdf5/src/hl/object.rs @@ -29,6 +29,7 @@ impl Debug for Object { } impl Object { + /// Returns the object's identifier. pub fn id(&self) -> hid_t { self.0.id() } @@ -57,7 +58,7 @@ impl Object { macro_rules! impl_downcast { ($func:ident, $tp:ty) => { impl Object { - #[doc = "Downcast the object into $tp if possible."] + #[doc = concat!("Downcast the object into `", stringify!($tp), "` if possible.")] pub fn $func(&self) -> Result<$tp> { self.clone().cast() } @@ -149,10 +150,20 @@ pub mod tests { #[test] pub fn test_incref_decref_drop() { use std::mem::ManuallyDrop; - let mut obj = TestObject::from_id(h5call!(H5Pcreate(*H5P_FILE_ACCESS)).unwrap()).unwrap(); - let obj_id = obj.id(); - obj = TestObject::from_id(h5call!(H5Pcreate(*H5P_FILE_ACCESS)).unwrap()).unwrap(); - assert_ne!(obj_id, obj.id()); + // Hold the lock across both allocations and the comparison. libhdf5 hands + // out identifiers from a single global pool shared with the other test + // threads, and it recycles freed ones. On the old non-thread-safe 1.8 + // builds, allocations and frees interleaved from other threads can make + // these two H5Pcreate calls return the same identifier, which flakily + // trips the assert_ne. Serialising the sequence keeps the ids distinct. + let obj = h5lock!({ + let mut obj = + TestObject::from_id(h5call!(H5Pcreate(*H5P_FILE_ACCESS)).unwrap()).unwrap(); + let obj_id = obj.id(); + obj = TestObject::from_id(h5call!(H5Pcreate(*H5P_FILE_ACCESS)).unwrap()).unwrap(); + assert_ne!(obj_id, obj.id()); + obj + }); assert!(obj.id() > 0); assert!(obj.is_valid()); assert!(obj.handle().is_valid_id()); diff --git a/hdf5/src/hl/plist.rs b/hdf5/src/hl/plist.rs index f80ea91c2..7649cf197 100644 --- a/hdf5/src/hl/plist.rs +++ b/hdf5/src/hl/plist.rs @@ -16,7 +16,9 @@ pub mod dataset_access; pub mod dataset_create; pub mod file_access; pub mod file_create; +pub mod group_create; pub mod link_create; +pub mod object_copy; /// Represents the HDF5 property list. #[repr(transparent)] @@ -93,7 +95,7 @@ pub enum PropertyListClass { LinkCreate, /// Properties for object copying process. ObjectCopy, - /// Properties for object creatio. + /// Properties for object creation. ObjectCreate, /// Properties for character encoding. StringCreate, @@ -210,6 +212,7 @@ impl PropertyList { }) } + /// Returns `true` if the property list is a member of `class`. pub fn is_class(&self, class: PropertyListClass) -> bool { use crate::globals::*; h5lock!({ diff --git a/hdf5/src/hl/plist/dataset_access.rs b/hdf5/src/hl/plist/dataset_access.rs index 5a123aea2..4670c20d4 100644 --- a/hdf5/src/hl/plist/dataset_access.rs +++ b/hdf5/src/hl/plist/dataset_access.rs @@ -91,10 +91,13 @@ impl Clone for DatasetAccess { } } +/// Options for including or excluding missing mapped elements in a virtual dataset view. #[cfg(feature = "1.10.0")] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum VirtualView { + /// Include all data before the first missing mapped data. FirstMissing, + /// Include all available mapped data, filling missing data with a fill value. LastAvailable, } @@ -165,29 +168,35 @@ impl DatasetAccessBuilder { Ok(builder) } + /// Sets the [`ChunkCache`] options. pub fn chunk_cache(&mut self, nslots: usize, nbytes: usize, w0: f64) -> &mut Self { self.chunk_cache = Some(ChunkCache { nslots, nbytes, w0 }); self } + /// Sets the external dataset storage file prefix. #[cfg(feature = "1.8.17")] pub fn efile_prefix(&mut self, prefix: &str) -> &mut Self { self.efile_prefix = Some(prefix.into()); self } + /// Sets the [`VirtualView`] options. #[cfg(feature = "1.10.0")] pub fn virtual_view(&mut self, view: VirtualView) -> &mut Self { self.virtual_view = Some(view); self } + /// Sets the maximum number of files/datasets allowed to be missing when determining the extent + /// of an unlimited virtual dataset with printf-style mappings. #[cfg(feature = "1.10.0")] pub fn virtual_printf_gap(&mut self, gap_size: usize) -> &mut Self { self.virtual_printf_gap = Some(gap_size); self } + /// Sets metadata I/O mode for read options to collective or independent. #[cfg(all(feature = "1.10.0", feature = "have-parallel"))] pub fn all_coll_metadata_ops(&mut self, is_collective: bool) -> &mut Self { self.all_coll_metadata_ops = Some(is_collective); @@ -223,10 +232,12 @@ impl DatasetAccessBuilder { Ok(()) } + /// Copies the builder settings into a dataset access property list. pub fn apply(&self, plist: &mut DatasetAccess) -> Result<()> { h5lock!(self.populate_plist(plist.id())) } + /// Constructs a new dataset access property list. pub fn finish(&self) -> Result { h5lock!({ let mut plist = DatasetAccess::try_new()?; @@ -237,14 +248,17 @@ impl DatasetAccessBuilder { /// Dataset access property list. impl DatasetAccess { + /// Creates a new dataset access property list. pub fn try_new() -> Result { Self::from_id(h5try!(H5Pcreate(*H5P_DATASET_ACCESS))) } + /// Creates a copy of the property list. pub fn copy(&self) -> Self { unsafe { self.deref().copy().cast_unchecked() } } + /// Creates a new dataset access property list builder. pub fn build() -> DatasetAccessBuilder { DatasetAccessBuilder::new() } @@ -260,6 +274,7 @@ impl DatasetAccess { ) } + /// Returns the raw data chunk cache parameters. pub fn chunk_cache(&self) -> ChunkCache { self.get_chunk_cache().unwrap_or_else(|_| ChunkCache::default()) } @@ -270,6 +285,7 @@ impl DatasetAccess { h5lock!(get_h5_str(|m, s| H5Pget_efile_prefix(self.id(), m, s))) } + /// Returns the external dataset storage file prefix. #[cfg(feature = "1.8.17")] pub fn efile_prefix(&self) -> String { self.get_efile_prefix().ok().unwrap_or_default() @@ -281,6 +297,7 @@ impl DatasetAccess { h5get!(H5Pget_virtual_view(self.id()): H5D_vds_view_t).map(Into::into) } + /// Returns the virtual dataset view options. #[cfg(feature = "1.10.0")] pub fn virtual_view(&self) -> VirtualView { self.get_virtual_view().ok().unwrap_or_default() @@ -292,6 +309,8 @@ impl DatasetAccess { h5get!(H5Pget_virtual_printf_gap(self.id()): hsize_t).map(|x| x as _) } + /// Returns the maximum number of files/datasets allowed to be missing when determining the + /// extent of an unlimited virtual dataset with printf-style mappings. #[cfg(feature = "1.10.0")] pub fn virtual_printf_gap(&self) -> usize { self.get_virtual_printf_gap().unwrap_or(0) @@ -303,6 +322,7 @@ impl DatasetAccess { h5get!(H5Pget_all_coll_metadata_ops(self.id()): hbool_t).map(|x| x > 0) } + /// Returns `true` if metadata I/O reads are set to collective, or `false` if independent. #[cfg(all(feature = "1.10.0", feature = "have-parallel"))] pub fn all_coll_metadata_ops(&self) -> bool { self.get_all_coll_metadata_ops().unwrap_or(false) diff --git a/hdf5/src/hl/plist/dataset_create.rs b/hdf5/src/hl/plist/dataset_create.rs index 0661c87f5..0bd21df45 100644 --- a/hdf5/src/hl/plist/dataset_create.rs +++ b/hdf5/src/hl/plist/dataset_create.rs @@ -30,11 +30,12 @@ use hdf5_sys::{ use hdf5_types::{OwnedDynValue, TypeDescriptor}; use crate::dim::Dimension; + use crate::globals::H5P_DATASET_CREATE; use crate::hl::datatype::Datatype; -use crate::hl::filters::{validate_filters, Filter, SZip, ScaleOffset}; #[cfg(feature = "blosc")] use crate::hl::filters::{Blosc, BloscShuffle}; +use crate::hl::filters::{Filter, SZip, ScaleOffset, validate_filters}; pub use crate::hl::plist::common::{AttrCreationOrder, AttrPhaseChange}; use crate::internal_prelude::*; @@ -107,11 +108,16 @@ impl Clone for DatasetCreate { } } +/// Options for how to store raw data for a dataset. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Layout { + /// Raw data is stored in the file's object header. Compact, + /// Raw data is stored in a contiguous chunk in the file, outside the object header. Contiguous, + /// Raw data is stored in separate chunks in the file. Chunked, + /// Raw data is drawn from multiple datasets in different files. #[cfg(feature = "1.10.0")] Virtual, } @@ -148,8 +154,10 @@ impl From for H5D_layout_t { #[cfg(feature = "1.10.0")] bitflags! { + /// Edge chunk option flags. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ChunkOpts: u32 { + /// Disable applying filters to partial edge chunks. const DONT_FILTER_PARTIAL_CHUNKS = H5D_CHUNK_DONT_FILTER_PARTIAL_CHUNKS; } } @@ -161,10 +169,14 @@ impl Default for ChunkOpts { } } +/// Options for when to allocate dataset storage space. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum AllocTime { + /// Allocate all space when the dataset is created. Early, + /// Allocate space incrementally as data is written to the dataset. Incr, + /// Allocate all space when data is first written to the dataset. Late, } @@ -188,10 +200,15 @@ impl From for H5D_alloc_time_t { } } +/// Options for when to write fill values to a dataset. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum FillTime { + /// Write fill values to the dataset when storage is allocated only if a user-defined fill + /// value is set. IfSet, + /// Write fill values to the dataset when storage is allocated. Alloc, + /// Never write fill values to the dataset. Never, } @@ -221,10 +238,14 @@ impl From for H5D_fill_time_t { } } +/// The status of a dataset creation property list's fill value. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum FillValue { + /// Fill value is undefined. Undefined, + /// Fill value is the library default. Default, + /// Fill value is defined by the application. UserDefined, } @@ -254,26 +275,38 @@ impl From for H5D_fill_value_t { } } +/// Properties of data stored in an external file. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ExternalFile { + /// The name of the file. pub name: String, + /// The offset in bytes from the start of the file to the location where the data starts. pub offset: usize, + /// The number of bytes reserved in the file for data. pub size: usize, } +/// Properties of a mapping between virtual and source datasets. #[cfg(feature = "1.10.0")] #[derive(Clone, Debug, PartialEq, Eq)] pub struct VirtualMapping { + /// The name of the HDF5 file containing the source dataset. pub src_filename: String, + /// The path to the source dataset inside the file. pub src_dataset: String, + /// The dimensionality of the source dataset. pub src_extents: Extents, + /// The selection of the source dataset to be mapped. pub src_selection: Selection, + /// The dimensionality of the virtual dataset. pub vds_extents: Extents, + /// The selection of the virtual dataset to be mapped. pub vds_selection: Selection, } #[cfg(feature = "1.10.0")] impl VirtualMapping { + /// Constructs a `VirtualMapping` with the given parameters. pub fn new( src_filename: F, src_dataset: D, src_extents: E1, src_selection: S1, vds_extents: E2, vds_selection: S2, @@ -365,41 +398,49 @@ impl DatasetCreateBuilder { Ok(builder) } + /// Sets the dataset filters from a slice of filter specifiers. pub fn set_filters(&mut self, filters: &[Filter]) -> &mut Self { self.filters = filters.to_owned(); self } + /// Adds a deflation filter with some compression level to the dataset. pub fn deflate(&mut self, level: u8) -> &mut Self { self.filters.push(Filter::deflate(level)); self } + /// Adds a shuffle filter to the dataset. pub fn shuffle(&mut self) -> &mut Self { self.filters.push(Filter::shuffle()); self } + /// Adds a Fletcher32 checksum filter to the dataset. pub fn fletcher32(&mut self) -> &mut Self { self.filters.push(Filter::fletcher32()); self } + /// Adds an Szip compression filter with some coding method and pixels per block to the dataset. pub fn szip(&mut self, coding: SZip, px_per_block: u8) -> &mut Self { self.filters.push(Filter::szip(coding, px_per_block)); self } + /// Adds an N-bit compression filter to the dataset. pub fn nbit(&mut self) -> &mut Self { self.filters.push(Filter::nbit()); self } + /// Adds a scale-offset compression filter with some scaling mode to the dataset. pub fn scale_offset(&mut self, mode: ScaleOffset) -> &mut Self { self.filters.push(Filter::scale_offset(mode)); self } + /// Adds an LZF compression filter to the dataset. #[cfg(feature = "lzf")] pub fn lzf(&mut self) -> &mut Self { self.filters.push(Filter::lzf()); @@ -421,6 +462,7 @@ impl DatasetCreateBuilder { self } + /// Adds a Blosc filter with LZ compression to the dataset. #[cfg(feature = "blosc")] pub fn blosc_blosclz(&mut self, clevel: u8, shuffle: T) -> &mut Self where @@ -430,6 +472,7 @@ impl DatasetCreateBuilder { self } + /// Adds a Blosc filter with LZ4 compression to the dataset. #[cfg(feature = "blosc-lz4")] pub fn blosc_lz4(&mut self, clevel: u8, shuffle: T) -> &mut Self where @@ -439,6 +482,7 @@ impl DatasetCreateBuilder { self } + /// Adds a Blosc filter with LZ4HC compression to the dataset. #[cfg(feature = "blosc-lz4")] pub fn blosc_lz4hc(&mut self, clevel: u8, shuffle: T) -> &mut Self where @@ -448,6 +492,7 @@ impl DatasetCreateBuilder { self } + /// Adds a Blosc filter with Snappy compression to the dataset. #[cfg(feature = "blosc-snappy")] pub fn blosc_snappy(&mut self, clevel: u8, shuffle: T) -> &mut Self where @@ -457,6 +502,7 @@ impl DatasetCreateBuilder { self } + /// Adds a Blosc filter with Zlib compression to the dataset. #[cfg(feature = "blosc-zlib")] pub fn blosc_zlib(&mut self, clevel: u8, shuffle: T) -> &mut Self where @@ -466,6 +512,7 @@ impl DatasetCreateBuilder { self } + /// Adds a Blosc filter with Zstd compression to the dataset. #[cfg(feature = "blosc-zstd")] pub fn blosc_zstd(&mut self, clevel: u8, shuffle: T) -> &mut Self where @@ -475,21 +522,53 @@ impl DatasetCreateBuilder { self } + #[cfg(feature = "zfp")] + pub fn zfp_accuracy( + &mut self, accuracy: f64, chunk_dims: Vec, n_bytes: u8, + ) -> &mut Self { + self.filters.push(Filter::zfp_accuracy(accuracy, chunk_dims, n_bytes)); + self + } + + #[cfg(feature = "zfp")] + pub fn zfp_rate(&mut self, rate: f64, chunk_dims: Vec, n_bytes: u8) -> &mut Self { + self.filters.push(Filter::zfp_rate(rate, chunk_dims, n_bytes)); + self + } + + #[cfg(feature = "zfp")] + pub fn zfp_precision( + &mut self, precision: u8, chunk_dims: Vec, n_bytes: u8, + ) -> &mut Self { + self.filters.push(Filter::zfp_precision(precision, chunk_dims, n_bytes)); + self + } + + #[cfg(feature = "zfp")] + pub fn zfp_reversible(&mut self, chunk_dims: Vec, n_bytes: u8) -> &mut Self { + self.filters.push(Filter::zfp_reversible(chunk_dims, n_bytes)); + self + } + + /// Adds a user-defined filter with the given identifier and parameters to the dataset. pub fn add_filter(&mut self, id: H5Z_filter_t, cdata: &[c_uint]) -> &mut Self { self.filters.push(Filter::user(id, cdata)); self } + /// Removes all filters from the dataset. pub fn clear_filters(&mut self) -> &mut Self { self.filters.clear(); self } + /// Sets the dataset's storage space allocation timing. pub fn alloc_time(&mut self, alloc_time: Option) -> &mut Self { self.alloc_time = Some(alloc_time); self } + /// Sets the time when fill values should be written to the dataset. pub fn fill_time(&mut self, fill_time: FillTime) -> &mut Self { self.fill_time = Some(fill_time); self @@ -499,11 +578,13 @@ impl DatasetCreateBuilder { self.fill_time.is_some() } + /// Sets the dataset's fill value. pub fn fill_value>(&mut self, fill_value: T) -> &mut Self { self.fill_value = Some(fill_value.into()); self } + /// Clears the dataset's fill value. pub fn no_fill_value(&mut self) -> &mut Self { self.fill_value = None; self @@ -521,27 +602,32 @@ impl DatasetCreateBuilder { self } + /// Clears the dataset's chunking settings. pub fn no_chunk(&mut self) -> &mut Self { self.chunk = None; self } + /// Sets the dataset's raw data layout. pub fn layout(&mut self, layout: Layout) -> &mut Self { self.layout = Some(layout); self } + /// Sets the dataset's edge chunk options. #[cfg(feature = "1.10.0")] pub fn chunk_opts(&mut self, opts: ChunkOpts) -> &mut Self { self.chunk_opts = Some(opts); self } + /// Adds an external file to the dataset. pub fn external(&mut self, name: &str, offset: usize, size: usize) -> &mut Self { self.external.push(ExternalFile { name: name.to_owned(), offset, size }); self } + /// Adds a mapping between virtual and source datasets. #[cfg(feature = "1.10.0")] pub fn virtual_map( &mut self, src_filename: F, src_dataset: D, src_extents: E1, src_selection: S1, @@ -566,16 +652,19 @@ impl DatasetCreateBuilder { self } + /// Sets whether to record time data for the dataset. pub fn obj_track_times(&mut self, track_times: bool) -> &mut Self { self.obj_track_times = Some(track_times); self } + /// Sets the dataset's attribute storage phase change thresholds. pub fn attr_phase_change(&mut self, max_compact: u32, min_dense: u32) -> &mut Self { self.attr_phase_change = Some(AttrPhaseChange { max_compact, min_dense }); self } + /// Sets whether to track and/or index the dataset's attribute creation order. pub fn attr_creation_order(&mut self, attr_creation_order: AttrCreationOrder) -> &mut Self { self.attr_creation_order = Some(attr_creation_order); self @@ -650,10 +739,12 @@ impl DatasetCreateBuilder { !self.filters.is_empty() } + /// Copies the builder settings into a dataset creation property list. pub fn apply(&self, plist: &mut DatasetCreate) -> Result<()> { h5lock!(self.populate_plist(plist.id())) } + /// Constructs a new dataset creation property list. pub fn finish(&self) -> Result { h5lock!({ let mut plist = DatasetCreate::try_new()?; @@ -664,18 +755,22 @@ impl DatasetCreateBuilder { /// Dataset creation property list. impl DatasetCreate { + /// Constructs a new dataset creation property list. pub fn try_new() -> Result { Self::from_id(h5try!(H5Pcreate(*H5P_DATASET_CREATE))) } + /// Returns a copy of the dataset creation property list. pub fn copy(&self) -> Self { unsafe { self.deref().copy().cast_unchecked() } } + /// Returns a builder for configuring a dataset creation property list. pub fn build() -> DatasetCreateBuilder { DatasetCreateBuilder::new() } + /// Returns `true` if all required filters are available. pub fn all_filters_avail(&self) -> bool { h5lock!(H5Pall_filters_avail(self.id())) > 0 } @@ -685,10 +780,12 @@ impl DatasetCreate { Filter::extract_pipeline(self.id()) } + /// Returns a vector of the dataset's filter configurations. pub fn filters(&self) -> Vec { self.get_filters().unwrap_or_default() } + /// Returns `true` if there is at least one filter configured. pub fn has_filters(&self) -> bool { !self.filters().is_empty() } @@ -698,6 +795,7 @@ impl DatasetCreate { h5get!(H5Pget_alloc_time(self.id()): H5D_alloc_time_t).map(Into::into) } + /// Returns the storage allocation timing settings. pub fn alloc_time(&self) -> AllocTime { self.get_alloc_time().unwrap_or(AllocTime::Late) } @@ -707,6 +805,7 @@ impl DatasetCreate { h5get!(H5Pget_fill_time(self.id()): H5D_fill_time_t).map(Into::into) } + /// Returns the fill value timing settings. pub fn fill_time(&self) -> FillTime { self.get_fill_time().unwrap_or_default() } @@ -716,6 +815,7 @@ impl DatasetCreate { h5get!(H5Pfill_value_defined(self.id()): H5D_fill_value_t).map(Into::into) } + /// Returns the fill value status. pub fn fill_value_defined(&self) -> FillValue { self.get_fill_value_defined().unwrap_or(FillValue::Undefined) } @@ -736,6 +836,7 @@ impl DatasetCreate { } } + /// Returns the fill value converted to a dynamic type, or `None` if not set. pub fn fill_value(&self, tp: &TypeDescriptor) -> Option { self.get_fill_value(tp).unwrap_or_default() } @@ -752,6 +853,7 @@ impl DatasetCreate { .transpose() } + /// Returns the fill value converted to a concrete type, or `None` if not set. pub fn fill_value_as(&self) -> Option { self.get_fill_value_as::().unwrap_or_default() } @@ -768,6 +870,7 @@ impl DatasetCreate { } } + /// Returns a vector of chunk dimensions for the dataset, or `None` if it is not chunked. pub fn chunk(&self) -> Option> { self.get_chunk().unwrap_or_default() } @@ -779,6 +882,7 @@ impl DatasetCreate { Ok(layout.into()) } + /// Returns the layout setting for the dataset's raw data. pub fn layout(&self) -> Layout { self.get_layout().unwrap_or_default() } @@ -794,6 +898,7 @@ impl DatasetCreate { } } + /// Returns the edge chunk option setting, or `None` if the dataset is not chunked. #[cfg(feature = "1.10.0")] pub fn chunk_opts(&self) -> Option { self.get_chunk_opts().unwrap_or_default() @@ -828,6 +933,7 @@ impl DatasetCreate { }) } + /// Returns a vector of external file specifiers for the dataset. pub fn external(&self) -> Vec { self.get_external().unwrap_or_default() } @@ -868,6 +974,7 @@ impl DatasetCreate { }) } + /// Returns a vector of virtual mapping specifiers for the dataset. #[cfg(feature = "1.10.0")] pub fn virtual_map(&self) -> Vec { self.get_virtual_map().unwrap_or_default() @@ -878,6 +985,7 @@ impl DatasetCreate { h5get!(H5Pget_obj_track_times(self.id()): hbool_t).map(|x| x > 0) } + /// Returns `true` if object time tracking is enabled for the dataset. pub fn obj_track_times(&self) -> bool { self.get_obj_track_times().unwrap_or(true) } @@ -888,6 +996,7 @@ impl DatasetCreate { .map(|(mc, md)| AttrPhaseChange { max_compact: mc as _, min_dense: md as _ }) } + /// Returns the attribute storage phase change thresholds. pub fn attr_phase_change(&self) -> AttrPhaseChange { self.get_attr_phase_change().unwrap_or_default() } @@ -898,6 +1007,7 @@ impl DatasetCreate { .map(AttrCreationOrder::from_bits_truncate) } + /// Returns flags for whether attribute creation order will be tracked/indexed. pub fn attr_creation_order(&self) -> AttrCreationOrder { self.get_attr_creation_order().unwrap_or_default() } diff --git a/hdf5/src/hl/plist/file_access.rs b/hdf5/src/hl/plist/file_access.rs index 3d76f3dda..8130ad747 100644 --- a/hdf5/src/hl/plist/file_access.rs +++ b/hdf5/src/hl/plist/file_access.rs @@ -18,12 +18,12 @@ use std::ptr::{self, addr_of, addr_of_mut}; use bitflags::bitflags; use hdf5_sys::h5ac::{ - H5AC_cache_config_t, H5AC_METADATA_WRITE_STRATEGY__DISTRIBUTED, - H5AC_METADATA_WRITE_STRATEGY__PROCESS_0_ONLY, H5AC__CURR_CACHE_CONFIG_VERSION, - H5AC__MAX_TRACE_FILE_NAME_LEN, + H5AC__CURR_CACHE_CONFIG_VERSION, H5AC__MAX_TRACE_FILE_NAME_LEN, + H5AC_METADATA_WRITE_STRATEGY__DISTRIBUTED, H5AC_METADATA_WRITE_STRATEGY__PROCESS_0_ONLY, + H5AC_cache_config_t, }; use hdf5_sys::h5c::{H5C_cache_decr_mode, H5C_cache_flash_incr_mode, H5C_cache_incr_mode}; -use hdf5_sys::h5f::{H5F_close_degree_t, H5F_mem_t, H5F_FAMILY_DEFAULT}; +use hdf5_sys::h5f::{H5F_FAMILY_DEFAULT, H5F_close_degree_t, H5F_mem_t}; use hdf5_sys::h5fd::H5FD_MEM_NTYPES; use hdf5_sys::h5fd::{ H5FD_LOG_ALL, H5FD_LOG_FILE_IO, H5FD_LOG_FILE_READ, H5FD_LOG_FILE_WRITE, H5FD_LOG_FLAVOR, @@ -48,7 +48,7 @@ use hdf5_sys::h5p::{H5Pget_fapl_direct, H5Pset_fapl_direct}; use hdf5_sys::h5p::{H5Pget_fapl_mpio, H5Pset_fapl_mpio}; #[cfg(feature = "1.10.1")] -use hdf5_sys::h5ac::{H5AC_cache_image_config_t, H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE}; +use hdf5_sys::h5ac::{H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE, H5AC_cache_image_config_t}; #[cfg(feature = "1.10.2")] use hdf5_sys::h5f::H5F_libver_t; #[cfg(all(feature = "1.10.0", feature = "have-parallel"))] @@ -65,6 +65,8 @@ use hdf5_sys::h5p::{ H5Pget_evict_on_close, H5Pget_mdc_image_config, H5Pget_page_buffer_size, H5Pset_evict_on_close, H5Pset_mdc_image_config, H5Pset_page_buffer_size, }; +#[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] +use hdf5_sys::h5p::{H5Pget_file_locking, H5Pset_file_locking}; #[cfg(feature = "1.10.2")] use hdf5_sys::h5p::{H5Pget_libver_bounds, H5Pset_libver_bounds}; #[cfg(feature = "1.10.0")] @@ -116,6 +118,8 @@ impl Debug for FileAccess { formatter.field("fclose_degree", &self.fclose_degree()); formatter.field("gc_references", &self.gc_references()); formatter.field("small_data_block_size", &self.small_data_block_size()); + #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] + formatter.field("file_locking", &self.file_locking()); #[cfg(feature = "1.10.2")] formatter.field("libver_bounds", &self.libver_bounds()); #[cfg(feature = "1.8.7")] @@ -164,10 +168,14 @@ impl Clone for FileAccess { } } +/// Core file driver properties. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct CoreDriver { + /// Size, in bytes, of memory increments. pub increment: usize, + /// Whether to write the file contents to disk when the file is closed. pub filebacked: bool, + /// Size, in bytes, of write aggregation pages. Setting to 1 enables tracking with no paging. #[cfg(feature = "1.8.13")] pub write_tracking: usize, } @@ -183,8 +191,10 @@ impl Default for CoreDriver { } } +/// Family file driver properties. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct FamilyDriver { + /// Size in bytes of each file member. pub member_size: usize, } @@ -195,32 +205,63 @@ impl Default for FamilyDriver { } bitflags! { + /// Flags specifying types of logging activity for the logging virtual file driver. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct LogFlags: u64 { + /// Track truncate operations. const TRUNCATE = H5FD_LOG_TRUNCATE; + /// Track "meta" operations (e.g. truncate). const META_IO = H5FD_LOG_META_IO; + /// Track the location of every read. const LOC_READ = H5FD_LOG_LOC_READ; + /// Track the location of every write. const LOC_WRITE = H5FD_LOG_LOC_WRITE; + /// Track the location of every seek. const LOC_SEEK = H5FD_LOG_LOC_SEEK; + /// Track all I/O locations and lengths. + /// Equivalent to `LOC_READ | LOC_WRITE | LOC_SEEK`. const LOC_IO = H5FD_LOG_LOC_IO; + /// Track the number of times each byte is read. const FILE_READ = H5FD_LOG_FILE_READ; + /// Track the number of times each byte is written. const FILE_WRITE = H5FD_LOG_FILE_WRITE; + /// Track the number of all types of I/O operations. + /// Equivalent to `FILE_READ | FILE_WRITE`. const FILE_IO = H5FD_LOG_FILE_IO; + /// Track the type of information stored at each byte. const FLAVOR = H5FD_LOG_FLAVOR; + /// Track the total number of read operations. const NUM_READ = H5FD_LOG_NUM_READ; + /// Track the total number of write operations. const NUM_WRITE = H5FD_LOG_NUM_WRITE; + /// Track the total number of seek operations. const NUM_SEEK = H5FD_LOG_NUM_SEEK; + /// Track the total number of truncate operations. const NUM_TRUNCATE = H5FD_LOG_NUM_TRUNCATE; + /// Track the total number of all types of I/O operations. + /// Equivalent to `NUM_READ | NUM_WRITE | NUM_SEEK | NUM_TRUNCATE`. const NUM_IO = H5FD_LOG_NUM_IO; + /// Track the time spent in open operations. const TIME_OPEN = H5FD_LOG_TIME_OPEN; + /// Track the time spent in stat operations. const TIME_STAT = H5FD_LOG_TIME_STAT; + /// Track the time spent in read operations. const TIME_READ = H5FD_LOG_TIME_READ; + /// Track the time spent in write operations. const TIME_WRITE = H5FD_LOG_TIME_WRITE; + /// Track the time spent in seek operations. const TIME_SEEK = H5FD_LOG_TIME_SEEK; + /// Track the time spent in truncate operations. const TIME_TRUNCATE = H5FD_LOG_TIME_TRUNCATE; + /// Track the time spent in close operations. const TIME_CLOSE = H5FD_LOG_TIME_CLOSE; + /// Track the time spent in each I/O operation. + /// Equivalent to `TIME_OPEN | TIME_STAT | TIME_READ | TIME_WRITE | TIME_SEEK + /// | TIME_TRUNCATE | TIME_CLOSE`. const TIME_IO = H5FD_LOG_TIME_IO; + /// Track releases of space in the file. const FREE = H5FD_LOG_FREE; + /// Track everything. const ALL = H5FD_LOG_ALL; } } @@ -231,6 +272,7 @@ impl Default for LogFlags { } } +/// Logging virtual file driver properties. #[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct LogOptions { logfile: Option, @@ -248,25 +290,36 @@ static FD_MEM_TYPES: &[H5F_mem_t] = &[ H5F_mem_t::H5FD_MEM_OHDR, ]; +/// Properties for a data storage used by the multi-file driver. #[derive(Clone, Debug, PartialEq, Eq)] pub struct MultiFile { + /// Name of the member file. pub name: String, + /// Offset within virtual address space where the storage begins. pub addr: u64, } impl MultiFile { + /// Creates a new `MultiFile`. pub fn new(name: &str, addr: u64) -> Self { Self { name: name.into(), addr } } } +/// A mapping of memory usage types to storage indices. #[derive(Clone, Debug, PartialEq, Eq)] pub struct MultiLayout { + /// Index of the superblock. pub mem_super: u8, + /// Index of the B-tree data. pub mem_btree: u8, + /// Index of the raw data. pub mem_draw: u8, + /// Index of the global heap data. pub mem_gheap: u8, + /// Index of the local heap data. pub mem_lheap: u8, + /// Index of the object headers. pub mem_object: u8, } @@ -302,10 +355,14 @@ impl MultiLayout { } } +/// Multi-file driver properties. #[derive(Clone, Debug, PartialEq, Eq)] pub struct MultiDriver { + /// The names and offsets of each type of data storage. pub files: Vec, + /// The mapping of memory usage types to file indices. pub layout: MultiLayout, + /// Whether to allow read-only access to incomplete file sets. pub relax: bool, } @@ -345,9 +402,12 @@ impl MultiDriver { } } +/// Split file driver properties. #[derive(Clone, Debug, PartialEq, Eq)] pub struct SplitDriver { + /// Metadata filename extension. pub meta_ext: String, + /// Raw data filename extension. pub raw_ext: String, } @@ -394,11 +454,14 @@ mod mpio { use mpi_sys::{MPI_Comm, MPI_Info}; - use super::{c_int, Result}; + use super::{Result, c_int}; + /// MPI-I/O file driver properties. #[derive(Debug)] pub struct MpioDriver { + /// MPI-2 communicator. pub comm: MPI_Comm, + /// MPI-2 info object. pub info: MPI_Info, } @@ -453,11 +516,15 @@ mod mpio { #[cfg(feature = "mpio")] pub use self::mpio::*; +/// Direct I/O driver properties. #[cfg(feature = "have-direct")] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct DirectDriver { + /// Required memory alignment boundary. pub alignment: usize, + /// File system block size. pub block_size: usize, + /// Size in bytes of the copy buffer. pub cbuf_size: usize, } @@ -468,26 +535,43 @@ impl Default for DirectDriver { } } +/// A low-level file driver configuration. #[derive(Clone, Debug)] pub enum FileDriver { + /// Uses POSIX filesystem functions to perform unbuffered access to a single file. Sec2, + /// Uses functions from the standard C `stdio.h` to perform buffered access to a single file. Stdio, + /// SEC2 with logging capabilities. Log, + /// Keeps file contents in memory until the file is closed, enabling faster access. Core(CoreDriver), + /// Partitions file address space into pieces and sends them to separate storage files. Family(FamilyDriver), + /// Allows data to be stored in multiple files according to the type of data. Multi(MultiDriver), + /// Special case of the Multi driver that stores metadata and raw data in separate files. Split(SplitDriver), + /// Uses the MPI standard for communication and I/O. #[cfg(feature = "mpio")] Mpio(MpioDriver), + /// SEC2 except data is accessed synchronously without being cached by the system. #[cfg(feature = "have-direct")] Direct(DirectDriver), } +/// Options for what to do when trying to close a file while there are open objects inside it. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum FileCloseDegree { + /// Let the driver choose the behavior. + /// + /// All drivers set this to `Weak`, except for MPI-I/O, which sets it to `Semi`. Default, + /// Terminate file identifier access, but delay closing until all objects are closed. Weak, + /// Return an error if the file has open objects. Semi, + /// Close all open objects, then close the file. Strong, } @@ -519,22 +603,33 @@ impl From for H5F_close_degree_t { } } +/// File alignment properties. +/// +/// Any file object with size of at least `threshold` bytes will be aligned on an address that is +/// a multiple of `alignment`. Addresses are relative to the end of the user block. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Alignment { + /// The byte size threshold. pub threshold: u64, + /// The alignment value. pub alignment: u64, } impl Default for Alignment { + /// Returns the default threshold and alignment of 1 (i.e. no alignment). fn default() -> Self { Self { threshold: 1, alignment: 1 } } } +/// Raw data chunk cache parameters. #[derive(Clone, Copy, Debug, PartialEq)] pub struct ChunkCache { + /// The number of objects in the cache. pub nslots: usize, + /// The total size of the cache in bytes. pub nbytes: usize, + /// The chunk preemption policy. pub w0: f64, } @@ -546,16 +641,25 @@ impl Default for ChunkCache { impl Eq for ChunkCache {} +/// Page buffer size properties. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct PageBufferSize { + /// Maximum size, in bytes, of the page buffer. pub buf_size: usize, + /// Minimum metadata percentage to keep in the buffer before allowing pages with metadata to be + /// evicted. pub min_meta_perc: u32, + /// Minimum raw data percentage to keep in the buffer before allowing pages with raw data to be + /// evicted. pub min_raw_perc: u32, } +/// Automatic cache size increase mode. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CacheIncreaseMode { + /// Automatic increase is disabled. Off, + /// Automatic increase uses the hit rate threshold algorithm. Threshold, } @@ -577,9 +681,12 @@ impl From for H5C_cache_incr_mode { } } +/// Flash cache size increase mode. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum FlashIncreaseMode { + /// Flash cache size increase is disabled. Off, + /// Flash cache size increase uses the add space algorithm. AddSpace, } @@ -601,11 +708,16 @@ impl From for H5C_cache_flash_incr_mode { } } +/// Automatic cache size decrease mode. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CacheDecreaseMode { + /// Automatic decrease is disabled. Off, + /// Automatic decrease uses the hit rate threshold algorithm. Threshold, + /// Automatic decrease uses the ageout algorithm. AgeOut, + /// Automatic decrease uses the ageout with hit rate threshold algorithm. AgeOutWithThreshold, } @@ -631,9 +743,13 @@ impl From for H5C_cache_decr_mode { } } +/// A strategy for writing metadata to disk. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum MetadataWriteStrategy { + /// Only process zero is allowed to write dirty metadata to disk. ProcessZeroOnly, + /// Process zero decides what entries to flush, but the flushes are distributed across + /// processes. Distributed, } @@ -661,36 +777,76 @@ impl From for c_int { } } +/// Metadata cache configuration. #[derive(Clone, Debug, PartialEq)] pub struct MetadataCacheConfig { + /// Whether the adaptive cache resize report function is enabled. pub rpt_fcn_enabled: bool, + /// Whether `trace_file_name` should be used to open a trace file for the cache. pub open_trace_file: bool, + /// Whether the current trace file (if any) should be closed. pub close_trace_file: bool, + /// Full path of the trace file to be opened if `open_trace_file` is `true`. pub trace_file_name: String, + /// Whether evictions from the metadata cache are enabled. pub evictions_enabled: bool, + /// Whether the cache should be created with a user-specified initial size. pub set_initial_size: bool, + /// Initial cache size in bytes if `set_initial_size` is `true`. pub initial_size: usize, + /// Minimum fraction of the cache that must be kept clean or empty. pub min_clean_fraction: f64, + /// Maximum number of bytes that adaptive cache resizing can select as the maximum cache size. pub max_size: usize, + /// Minimum number of bytes that adaptive cache resizing can select as the minimum cache size. pub min_size: usize, + /// Number of cache accesses between runs of the adaptive cache resize code. pub epoch_length: i64, + /// Automatic cache size increase mode. pub incr_mode: CacheIncreaseMode, + /// Hit rate threshold for the hit rate threshold cache size increment algorithm. pub lower_hr_threshold: f64, + /// Factor by which the hit rate threshold cache size increment algorithm multiplies the current + /// cache max size to obtain a tentative new size. pub increment: f64, + /// Whether to apply an upper limit to the size of cache size increases. pub apply_max_increment: bool, + /// Maximum number of bytes by which cache size can be increased in a single step, + /// if applicable. pub max_increment: usize, + /// Flash cache size increase mode. pub flash_incr_mode: FlashIncreaseMode, + /// Factor by which the size of the triggering entry / entry size increase is multiplied to + /// obtain the initial cache size increment. pub flash_multiple: f64, + /// Factor by which the current maximum cache size is multiplied to obtain the minimum size + /// entry / entry size increase which may trigger a flash cache size increase. pub flash_threshold: f64, + /// Automatic cache size decrease mode. pub decr_mode: CacheDecreaseMode, + /// Hit rate threshold for hit-rate-based cache size decrease algorithms. pub upper_hr_threshold: f64, + /// Factor by which the hit rate threshold cache size decrease algorithm multiplies the current + /// cache max size to obtain a tentative new size. pub decrement: f64, + /// Whether an upper limit should be applied to the size of cache size decreases. pub apply_max_decrement: bool, + /// Maximum number of bytes by which cache size can be decreased in a single step, + /// if applicable. pub max_decrement: usize, + /// Minimum number of epochs that an entry must remain unaccessed in cache before ageout-based + /// reduction algorithms try to evict it. pub epochs_before_eviction: i32, + /// Whether ageout-based decrement algorithms will maintain an empty reserve. pub apply_empty_reserve: bool, + /// Empty reserve as a fraction of maximum cache size. + /// Ageout-based algorithms will not decrease the maximum size unless the empty reserve can be + /// met. pub empty_reserve: f64, + /// Threshold number of bytes of dirty metadata that will trigger synchronization of + /// parallel metadata caches. pub dirty_bytes_threshold: usize, + /// Strategy for writing metadata to disk. pub metadata_write_strategy: MetadataWriteStrategy, } @@ -816,10 +972,15 @@ impl From for MetadataCacheConfig { mod cache_image_config { use super::*; + /// Metadata cache image configuration. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct CacheImageConfig { + /// Whether a cache image should be created on file close. pub generate_image: bool, + /// Whether the cache image should include the adaptive cache resize configuration and + /// status. pub save_resize_status: bool, + /// Maximum number of times a prefetched entry can appear in subsequent cache images. pub entry_ageout: i32, } @@ -858,11 +1019,15 @@ mod cache_image_config { #[cfg(feature = "1.10.1")] pub use self::cache_image_config::*; +/// Metadata cache logging options. #[cfg(feature = "1.10.0")] #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct CacheLogOptions { + /// Whether logging is enabled. pub is_enabled: bool, + /// File path of the log. (Must be ASCII on Windows) pub location: String, + /// Whether to begin logging as soon as the file is opened pub start_on_access: bool, } @@ -870,18 +1035,24 @@ pub struct CacheLogOptions { mod libver { use super::*; + /// Options for which library format version to use when storing objects. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum LibraryVersion { + /// Use the earliest possible format. Earliest = 0, + /// Use the latest v18 format. V18 = 1, + /// Use the latest v110 format. V110 = 2, } impl LibraryVersion { + /// Returns `true` if the version is set to `Earliest`. pub fn is_earliest(self) -> bool { self == Self::Earliest } + /// Returns the latest library version. pub const fn latest() -> Self { Self::V110 } @@ -907,9 +1078,12 @@ mod libver { } } + /// Library format version bounds for writing objects to a file. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct LibVerBounds { + /// The earliest version to use for writing objects. pub low: LibraryVersion, + /// The latest version to use for writing objects. pub high: LibraryVersion, } @@ -968,6 +1142,8 @@ pub struct FileAccessBuilder { small_data_block_size: Option, #[cfg(feature = "1.10.2")] libver_bounds: Option, + #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] + file_locking: Option, } impl FileAccessBuilder { @@ -1024,6 +1200,10 @@ impl FileAccessBuilder { builder.write_tracking(drv.write_tracking); } } + #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] + { + builder.file_locking(plist.get_file_locking()?); + } Ok(builder) } @@ -1037,27 +1217,32 @@ impl FileAccessBuilder { self } + /// Sets the file alignment parameters. pub fn alignment(&mut self, threshold: u64, alignment: u64) -> &mut Self { self.alignment = Some(Alignment { threshold, alignment }); self } + /// Sets the raw data chunk cache parameters. pub fn chunk_cache(&mut self, nslots: usize, nbytes: usize, w0: f64) -> &mut Self { self.chunk_cache = Some(ChunkCache { nslots, nbytes, w0 }); self } + /// Sets the number of files that can be held open in an external link open file cache. #[cfg(feature = "1.8.7")] pub fn elink_file_cache_size(&mut self, efc_size: u32) -> &mut Self { self.elink_file_cache_size = Some(efc_size); self } + /// Sets the minimum metadata block size in bytes. pub fn meta_block_size(&mut self, size: u64) -> &mut Self { self.meta_block_size = Some(size); self } + /// Sets the page buffer size properties. #[cfg(feature = "1.10.1")] pub fn page_buffer_size( &mut self, buf_size: usize, min_meta_perc: u32, min_raw_perc: u32, @@ -1066,28 +1251,34 @@ impl FileAccessBuilder { self } + /// Sets the maximum size of the data sieve buffer. pub fn sieve_buf_size(&mut self, size: usize) -> &mut Self { self.sieve_buf_size = Some(size); self } + /// Sets whether object metadata should be evicted from cache when an object is closed. #[cfg(feature = "1.10.1")] pub fn evict_on_close(&mut self, evict_on_close: bool) -> &mut Self { self.evict_on_close = Some(evict_on_close); self } + /// Sets the number of reads that the library will try when reading checksummed metadata in a + /// file opened with SWMR access. #[cfg(feature = "1.10.0")] pub fn metadata_read_attempts(&mut self, attempts: u32) -> &mut Self { self.metadata_read_attempts = Some(attempts); self } + /// Sets the metadata cache configuration. pub fn mdc_config(&mut self, config: &MetadataCacheConfig) -> &mut Self { self.mdc_config = Some(config.clone()); self } + /// Sets whether a cache image should be created on file close. #[cfg(feature = "1.10.1")] pub fn mdc_image_config(&mut self, generate_image: bool) -> &mut Self { self.mdc_image_config = Some(CacheImageConfig { @@ -1098,6 +1289,7 @@ impl FileAccessBuilder { self } + /// Sets metadata cache logging options. #[cfg(feature = "1.10.0")] pub fn mdc_log_options( &mut self, is_enabled: bool, location: &str, start_on_access: bool, @@ -1107,67 +1299,87 @@ impl FileAccessBuilder { self } + /// Sets whether metadata reads are collective. #[cfg(all(feature = "1.10.0", feature = "have-parallel"))] pub fn all_coll_metadata_ops(&mut self, is_collective: bool) -> &mut Self { self.all_coll_metadata_ops = Some(is_collective); self } + /// Sets whether metadata writes are collective. #[cfg(all(feature = "1.10.0", feature = "have-parallel"))] pub fn coll_metadata_write(&mut self, is_collective: bool) -> &mut Self { self.coll_metadata_write = Some(is_collective); self } + /// Sets whether reference garbage collection is enabled. pub fn gc_references(&mut self, gc_ref: bool) -> &mut Self { self.gc_references = Some(gc_ref); self } + /// Sets whether file locking is enabled. + #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] + pub fn file_locking(&mut self, file_locking: bool) -> &mut Self { + self.file_locking = Some(file_locking); + self + } + + /// Sets the maximum size in bytes of a contiguous block reserved for small data. pub fn small_data_block_size(&mut self, size: u64) -> &mut Self { self.small_data_block_size = Some(size); self } + /// Sets the range of library versions to use when writing objects. #[cfg(feature = "1.10.2")] pub fn libver_bounds(&mut self, low: LibraryVersion, high: LibraryVersion) -> &mut Self { self.libver_bounds = Some(LibVerBounds { low, high }); self } + /// Allows use of the earliest library version when writing objects. #[cfg(feature = "1.10.2")] pub fn libver_earliest(&mut self) -> &mut Self { self.libver_bounds(LibraryVersion::Earliest, LibraryVersion::latest()) } + /// Sets the earliest library version for writing objects to v18. #[cfg(feature = "1.10.2")] pub fn libver_v18(&mut self) -> &mut Self { self.libver_bounds(LibraryVersion::V18, LibraryVersion::latest()) } + /// Sets the earliest library version for writing objects to v110. #[cfg(feature = "1.10.2")] pub fn libver_v110(&mut self) -> &mut Self { self.libver_bounds(LibraryVersion::V110, LibraryVersion::latest()) } + /// Allows only the latest library version when writing objects. #[cfg(feature = "1.10.2")] pub fn libver_latest(&mut self) -> &mut Self { self.libver_bounds(LibraryVersion::latest(), LibraryVersion::latest()) } + /// Sets which file driver to use. pub fn driver(&mut self, file_driver: &FileDriver) -> &mut Self { self.file_driver = Some(file_driver.clone()); self } + /// Sets the file driver to SEC2 (POSIX). pub fn sec2(&mut self) -> &mut Self { self.driver(&FileDriver::Sec2) } + /// Sets the file driver to STDIO. pub fn stdio(&mut self) -> &mut Self { self.driver(&FileDriver::Stdio) } + /// Sets the file driver to SEC2 with logging and configures it. pub fn log_options( &mut self, logfile: Option<&str>, flags: LogFlags, buf_size: usize, ) -> &mut Self { @@ -1177,38 +1389,46 @@ impl FileAccessBuilder { self.driver(&FileDriver::Log) } + /// Sets the file driver to SEC2 with logging. pub fn log(&mut self) -> &mut Self { self.log_options(None, LogFlags::LOC_IO, 0) } + /// Sets the file driver to Core and configures it. pub fn core_options(&mut self, increment: usize, filebacked: bool) -> &mut Self { let drv = CoreDriver { increment, filebacked, ..CoreDriver::default() }; self.driver(&FileDriver::Core(drv)) } + /// Sets the file driver to Core and sets whether to write file contents to disk upon closing. pub fn core_filebacked(&mut self, filebacked: bool) -> &mut Self { let drv = CoreDriver { filebacked, ..CoreDriver::default() }; self.driver(&FileDriver::Core(drv)) } + /// Sets the file driver to Core. pub fn core(&mut self) -> &mut Self { self.driver(&FileDriver::Core(CoreDriver::default())) } + /// Sets the write tracking page size for the Core file driver. #[cfg(feature = "1.8.13")] pub fn write_tracking(&mut self, page_size: usize) -> &mut Self { self.write_tracking = Some(page_size); self } + /// Sets the file driver to Family. pub fn family(&mut self) -> &mut Self { self.driver(&FileDriver::Family(FamilyDriver::default())) } + /// Sets the file driver to Family and configures the file member size. pub fn family_options(&mut self, member_size: usize) -> &mut Self { self.driver(&FileDriver::Family(FamilyDriver { member_size })) } + /// Sets the file driver to Multi and configures it. pub fn multi_options( &mut self, files: &[MultiFile], layout: &MultiLayout, relax: bool, ) -> &mut Self { @@ -1219,10 +1439,12 @@ impl FileAccessBuilder { })) } + /// Sets the file driver to Multi. pub fn multi(&mut self) -> &mut Self { self.driver(&FileDriver::Multi(MultiDriver::default())) } + /// Sets the file driver to Split and configures it. pub fn split_options(&mut self, meta_ext: &str, raw_ext: &str) -> &mut Self { self.driver(&FileDriver::Split(SplitDriver { meta_ext: meta_ext.into(), @@ -1230,16 +1452,19 @@ impl FileAccessBuilder { })) } + /// Sets the file driver to Split. pub fn split(&mut self) -> &mut Self { self.driver(&FileDriver::Split(SplitDriver::default())) } + /// Sets the file driver to MPI-I/O and configures it. #[cfg(feature = "mpio")] pub fn mpio(&mut self, comm: mpi_sys::MPI_Comm, info: Option) -> &mut Self { // We use .unwrap() here since MPI will almost surely terminate the process anyway. self.driver(&FileDriver::Mpio(MpioDriver::try_new(comm, info).unwrap())) } + /// Sets the file driver to Direct and configures it. #[cfg(feature = "have-direct")] pub fn direct_options( &mut self, alignment: usize, block_size: usize, cbuf_size: usize, @@ -1247,6 +1472,7 @@ impl FileAccessBuilder { self.driver(&FileDriver::Direct(DirectDriver { alignment, block_size, cbuf_size })) } + /// Sets the file driver to Direct. #[cfg(feature = "have-direct")] pub fn direct(&mut self) -> &mut Self { self.driver(&FileDriver::Direct(DirectDriver::default())) @@ -1484,13 +1710,23 @@ impl FileAccessBuilder { let v = v.clone().into(); h5try!(H5Pset_mdc_config(id, addr_of!(v))); } + + #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] + { + if let Some(v) = self.file_locking { + let ignore_when_disabled = true; + h5try!(H5Pset_file_locking(id, v as _, ignore_when_disabled as _)); + } + } Ok(()) } + /// Copies the builder settings into a file access property list. pub fn apply(&self, plist: &mut FileAccess) -> Result<()> { h5lock!(self.populate_plist(plist.id())) } + /// Constructs a new file access property list. pub fn finish(&self) -> Result { h5lock!({ let mut plist = FileAccess::try_new()?; @@ -1501,14 +1737,17 @@ impl FileAccessBuilder { /// File access property list. impl FileAccess { + /// Creates a new file access property list. pub fn try_new() -> Result { Self::from_id(h5try!(H5Pcreate(*H5P_FILE_ACCESS))) } + /// Creates a copy of the property list. pub fn copy(&self) -> Self { unsafe { self.deref().copy().cast_unchecked() } } + /// Creates a new file access property list builder. pub fn build() -> FileAccessBuilder { FileAccessBuilder::new() } @@ -1571,11 +1810,9 @@ impl FileAccess { ensure!(j < N, "member map index out of bounds: {} (expected 0-{})", j, N - 1); if mapping[j] == 0 { mapping[j] = 0xff - (files.len() as u8); - files.push(MultiFile::new( - // SAFETY: name produced by HDF5 is nul-terminated and valid UTF-8 - unsafe { &string_from_cstr(name) }, - addr as _, - )); + // SAFETY: name produced by HDF5 is nul-terminated and valid UTF-8 + let tmp_string = unsafe { string_from_cstr(name) }; + files.push(MultiFile::new(&tmp_string, addr as _)); } *layout.get_mut(i - 1) = 0xff - mapping[j]; } @@ -1640,6 +1877,7 @@ impl FileAccess { } } + /// Returns the file driver properties. pub fn driver(&self) -> FileDriver { self.get_driver().unwrap_or(FileDriver::Sec2) } @@ -1649,6 +1887,7 @@ impl FileAccess { h5get!(H5Pget_fclose_degree(self.id()): H5F_close_degree_t).map(Into::into) } + /// Returns the file close degree. pub fn fclose_degree(&self) -> FileCloseDegree { self.get_fclose_degree().unwrap_or_else(|_| FileCloseDegree::default()) } @@ -1660,6 +1899,7 @@ impl FileAccess { }) } + /// Returns the file alignment properties. pub fn alignment(&self) -> Alignment { self.get_alignment().unwrap_or_else(|_| Alignment::default()) } @@ -1675,6 +1915,7 @@ impl FileAccess { ) } + /// Returns the raw data chunk cache properties. pub fn chunk_cache(&self) -> ChunkCache { self.get_chunk_cache().unwrap_or_else(|_| ChunkCache::default()) } @@ -1695,6 +1936,7 @@ impl FileAccess { h5get!(H5Pget_meta_block_size(self.id()): hsize_t).map(|x| x as _) } + /// Returns the metadata block size. pub fn meta_block_size(&self) -> u64 { self.get_meta_block_size().unwrap_or(2048) } @@ -1711,6 +1953,7 @@ impl FileAccess { ) } + /// Returns the page buffer size properties. #[cfg(feature = "1.10.1")] pub fn page_buffer_size(&self) -> PageBufferSize { self.get_page_buffer_size().unwrap_or_else(|_| PageBufferSize::default()) @@ -1721,6 +1964,7 @@ impl FileAccess { h5get!(H5Pget_sieve_buf_size(self.id()): size_t).map(|x| x as _) } + /// Returns the maximum data sieve buffer size. pub fn sieve_buf_size(&self) -> usize { self.get_sieve_buf_size().unwrap_or(64 * 1024) } @@ -1731,6 +1975,8 @@ impl FileAccess { h5get!(H5Pget_evict_on_close(self.id()): hbool_t).map(|x| x > 0) } + /// Returns `true` if an object will be evicted from the metadata cache when the object is + /// closed. #[cfg(feature = "1.10.1")] pub fn evict_on_close(&self) -> bool { self.get_evict_on_close().unwrap_or(false) @@ -1742,6 +1988,7 @@ impl FileAccess { h5get!(H5Pget_metadata_read_attempts(self.id()): c_uint).map(|x| x as _) } + /// Returns the number of read attempts for SWMR access. #[cfg(feature = "1.10.0")] pub fn metadata_read_attempts(&self) -> u32 { self.get_metadata_read_attempts().unwrap_or(1) @@ -1754,6 +2001,7 @@ impl FileAccess { h5call!(H5Pget_mdc_config(self.id(), &mut config)).map(|_| config.into()) } + /// Returns the metadata cache configuration. pub fn mdc_config(&self) -> MetadataCacheConfig { self.get_mdc_config().ok().unwrap_or_default() } @@ -1766,6 +2014,7 @@ impl FileAccess { h5call!(H5Pget_mdc_image_config(self.id(), &mut config)).map(|_| config.into()) } + /// Returns the metadata cache image configuration. #[cfg(feature = "1.10.1")] pub fn mdc_image_config(&self) -> CacheImageConfig { self.get_mdc_image_config().ok().unwrap_or_default() @@ -1801,6 +2050,7 @@ impl FileAccess { }) } + /// Returns the metadata cache logging options. #[cfg(feature = "1.10.0")] pub fn mdc_log_options(&self) -> CacheLogOptions { self.get_mdc_log_options().ok().unwrap_or_default() @@ -1812,6 +2062,7 @@ impl FileAccess { h5get!(H5Pget_all_coll_metadata_ops(self.id()): hbool_t).map(|x| x > 0) } + /// Returns `true` if metadata reads are collective. #[cfg(all(feature = "1.10.0", feature = "have-parallel"))] pub fn all_coll_metadata_ops(&self) -> bool { self.get_all_coll_metadata_ops().unwrap_or(false) @@ -1823,6 +2074,7 @@ impl FileAccess { h5get!(H5Pget_coll_metadata_write(self.id()): hbool_t).map(|x| x > 0) } + /// Returns `true` if metadata writes are collective. #[cfg(all(feature = "1.10.0", feature = "have-parallel"))] pub fn coll_metadata_write(&self) -> bool { self.get_coll_metadata_write().unwrap_or(false) @@ -1833,6 +2085,7 @@ impl FileAccess { h5get!(H5Pget_gc_references(self.id()): c_uint).map(|x| x > 0) } + /// Returns `true` if reference garbage collection is enabled. pub fn gc_references(&self) -> bool { self.get_gc_references().unwrap_or(false) } @@ -1842,6 +2095,7 @@ impl FileAccess { h5get!(H5Pget_small_data_block_size(self.id()): hsize_t).map(|x| x as _) } + /// Returns the size setting in bytes of the small data block. pub fn small_data_block_size(&self) -> u64 { self.get_small_data_block_size().unwrap_or(2048) } @@ -1853,13 +2107,27 @@ impl FileAccess { .map(|(low, high)| LibVerBounds { low: low.into(), high: high.into() }) } + /// Returns the library format version bounds for writing objects to a file. #[cfg(feature = "1.10.2")] pub fn libver_bounds(&self) -> LibVerBounds { self.get_libver_bounds().ok().unwrap_or_default() } + /// Returns the lower library format version bound for writing objects to a file. #[cfg(feature = "1.10.2")] pub fn libver(&self) -> LibraryVersion { self.get_libver_bounds().ok().unwrap_or_default().low } + + #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] + #[doc(hidden)] + pub fn get_file_locking(&self) -> Result { + h5get!(H5Pget_file_locking(self.id()): hbool_t, hbool_t).map(|(x, _)| x > 0) + } + + /// Returns ``true`` if file locking is enabled + #[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] + pub fn file_locking(&self) -> bool { + self.get_file_locking().unwrap_or(true) + } } diff --git a/hdf5/src/hl/plist/file_create.rs b/hdf5/src/hl/plist/file_create.rs index d4e109bde..bc2e9c994 100644 --- a/hdf5/src/hl/plist/file_create.rs +++ b/hdf5/src/hl/plist/file_create.rs @@ -424,10 +424,12 @@ impl FileCreateBuilder { Ok(()) } + /// Copies the builder settings into a file creation property list. pub fn apply(&self, plist: &mut FileCreate) -> Result<()> { h5lock!(self.populate_plist(plist.id())) } + /// Constructs a new file creation property list. pub fn finish(&self) -> Result { h5lock!({ let mut plist = FileCreate::try_new()?; @@ -438,14 +440,17 @@ impl FileCreateBuilder { /// File creation property list. impl FileCreate { + /// Creates a new file creation property list. pub fn try_new() -> Result { Self::from_id(h5try!(H5Pcreate(*H5P_FILE_CREATE))) } + /// Creates a copy of the file creation property list. pub fn copy(&self) -> Self { unsafe { self.deref().copy().cast_unchecked() } } + /// Returns a builder for configuring a file creation property list. pub fn build() -> FileCreateBuilder { FileCreateBuilder::new() } diff --git a/hdf5/src/hl/plist/group_create.rs b/hdf5/src/hl/plist/group_create.rs new file mode 100644 index 000000000..281f6c52e --- /dev/null +++ b/hdf5/src/hl/plist/group_create.rs @@ -0,0 +1,141 @@ +//! Group creation properties. + +use std::fmt::{self, Debug}; +use std::ops::Deref; + +use hdf5_sys::h5p::{H5Pcreate, H5Pget_obj_track_times, H5Pset_obj_track_times}; + +use crate::globals::H5P_GROUP_CREATE; +use crate::internal_prelude::*; + +/// Group creation properties. +#[repr(transparent)] +pub struct GroupCreate(Handle); + +impl ObjectClass for GroupCreate { + const NAME: &'static str = "group create property list"; + const VALID_TYPES: &'static [H5I_type_t] = &[H5I_GENPROP_LST]; + + fn from_handle(handle: Handle) -> Self { + Self(handle) + } + + fn handle(&self) -> &Handle { + &self.0 + } + + fn validate(&self) -> Result<()> { + ensure!( + self.is_class(PropertyListClass::GroupCreate), + "expected group create property list, got {:?}", + self.class() + ); + Ok(()) + } +} + +impl Clone for GroupCreate { + fn clone(&self) -> Self { + unsafe { self.deref().clone().cast_unchecked() } + } +} + +impl Debug for GroupCreate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut formatter = f.debug_struct("GroupCreate"); + formatter.field("obj_track_times", &self.obj_track_times()); + formatter.finish() + } +} + +impl Deref for GroupCreate { + type Target = PropertyList; + + fn deref(&self) -> &PropertyList { + unsafe { self.transmute() } + } +} + +impl PartialEq for GroupCreate { + fn eq(&self, other: &Self) -> bool { + ::eq(self, other) + } +} + +impl Eq for GroupCreate {} + +/// Builder used to create a group creation property list. +#[derive(Clone, Debug, Default)] +pub struct GroupCreateBuilder { + obj_track_times: Option, +} + +impl GroupCreateBuilder { + /// Creates a new group creation property list builder. + pub fn new() -> Self { + Self::default() + } + + /// Creates a new builder from an existing property list. + pub fn from_plist(plist: &GroupCreate) -> Result { + let mut builder = Self::default(); + builder.obj_track_times(plist.get_obj_track_times()?); + Ok(builder) + } + + /// Sets a property that governs the recording of times associated with an object. + /// + /// If true, time data will be recorded; if false, time data will not be recorded. + pub fn obj_track_times(&mut self, track_times: bool) -> &mut Self { + self.obj_track_times = Some(track_times); + self + } + + fn populate_plist(&self, id: hid_t) -> Result<()> { + if let Some(v) = self.obj_track_times { + h5try!(H5Pset_obj_track_times(id, hbool_t::from(v))); + } + Ok(()) + } + + /// Copies the builder settings into a group creation property list. + pub fn apply(&self, plist: &mut GroupCreate) -> Result<()> { + h5lock!(self.populate_plist(plist.id())) + } + + /// Constructs a new group creation property list. + pub fn finish(&self) -> Result { + h5lock!({ + let mut plist = GroupCreate::try_new()?; + self.apply(&mut plist).map(|()| plist) + }) + } +} + +/// Group creation property list. +impl GroupCreate { + /// Creates a new group creation property list. + pub fn try_new() -> Result { + Self::from_id(h5try!(H5Pcreate(*H5P_GROUP_CREATE))) + } + + /// Creates a copy of the group creation property list. + pub fn copy(&self) -> Self { + unsafe { self.deref().copy().cast_unchecked() } + } + + /// Returns a builder for configuring a group creation property list. + pub fn build() -> GroupCreateBuilder { + GroupCreateBuilder::new() + } + + #[doc(hidden)] + pub fn get_obj_track_times(&self) -> Result { + h5get!(H5Pget_obj_track_times(self.id()): hbool_t).map(|x| x > 0) + } + + /// Returns true if the time data is recorded. + pub fn obj_track_times(&self) -> bool { + self.get_obj_track_times().unwrap_or(true) + } +} diff --git a/hdf5/src/hl/plist/link_create.rs b/hdf5/src/hl/plist/link_create.rs index 2bb6c7c74..3c95c5706 100644 --- a/hdf5/src/hl/plist/link_create.rs +++ b/hdf5/src/hl/plist/link_create.rs @@ -7,7 +7,7 @@ use hdf5_sys::h5p::{ H5Pcreate, H5Pget_char_encoding, H5Pget_create_intermediate_group, H5Pset_char_encoding, H5Pset_create_intermediate_group, }; -use hdf5_sys::h5t::{H5T_cset_t, H5T_CSET_ASCII, H5T_CSET_UTF8}; +use hdf5_sys::h5t::{H5T_CSET_ASCII, H5T_CSET_UTF8, H5T_cset_t}; use crate::globals::H5P_LINK_CREATE; use crate::internal_prelude::*; @@ -69,9 +69,12 @@ impl Clone for LinkCreate { } } +/// The character encoding used to create a link or attribute name. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CharEncoding { + /// US ASCII. Ascii, + /// UTF-8. Utf8, } @@ -96,11 +99,13 @@ impl LinkCreateBuilder { Ok(builder) } + /// Sets whether to create intermediate groups upon creation of an object. pub fn create_intermediate_group(&mut self, create: bool) -> &mut Self { self.create_intermediate_group = Some(create); self } + /// Sets the character encoding to use when creating links. pub fn char_encoding(&mut self, encoding: CharEncoding) -> &mut Self { self.char_encoding = Some(encoding); self @@ -120,10 +125,12 @@ impl LinkCreateBuilder { Ok(()) } + /// Copies the builder settings into a link creation property list. pub fn apply(&self, plist: &mut LinkCreate) -> Result<()> { h5lock!(self.populate_plist(plist.id())) } + /// Constructs a new link creation property list. pub fn finish(&self) -> Result { h5lock!({ let mut plist = LinkCreate::try_new()?; @@ -134,14 +141,17 @@ impl LinkCreateBuilder { /// Link create property list. impl LinkCreate { + /// Creates a new link creation property list. pub fn try_new() -> Result { Self::from_id(h5try!(H5Pcreate(*H5P_LINK_CREATE))) } + /// Creates a copy of the link creation property list. pub fn copy(&self) -> Self { unsafe { self.deref().copy().cast_unchecked() } } + /// Returns a builder for configuring a link creation property list. pub fn build() -> LinkCreateBuilder { LinkCreateBuilder::new() } @@ -151,6 +161,7 @@ impl LinkCreate { h5get!(H5Pget_create_intermediate_group(self.id()): c_uint).map(|x| x > 0) } + /// Returns `true` if intermediate groups will be created upon object creation. pub fn create_intermediate_group(&self) -> bool { self.get_create_intermediate_group().unwrap_or(false) } @@ -164,6 +175,7 @@ impl LinkCreate { }) } + /// Returns the character encoding used to create links. pub fn char_encoding(&self) -> CharEncoding { self.get_char_encoding().unwrap_or(CharEncoding::Ascii) } diff --git a/hdf5/src/hl/plist/object_copy.rs b/hdf5/src/hl/plist/object_copy.rs new file mode 100644 index 000000000..6d09184f1 --- /dev/null +++ b/hdf5/src/hl/plist/object_copy.rs @@ -0,0 +1,212 @@ +//! Object copy properties. +use std::fmt::{self, Debug}; +use std::ops::Deref; + +use hdf5_sys::h5o::{ + H5O_COPY_EXPAND_EXT_LINK_FLAG, H5O_COPY_EXPAND_SOFT_LINK_FLAG, H5O_COPY_SHALLOW_HIERARCHY_FLAG, + H5O_COPY_WITHOUT_ATTR_FLAG, +}; +use hdf5_sys::h5p::{H5Pcreate, H5Pget_copy_object, H5Pset_copy_object}; + +use crate::globals::H5P_OBJECT_COPY; +use crate::internal_prelude::*; + +/// Object copy properties. +#[repr(transparent)] +pub struct ObjectCopy(Handle); + +impl ObjectClass for ObjectCopy { + const NAME: &'static str = "object copy property list"; + const VALID_TYPES: &'static [H5I_type_t] = &[H5I_GENPROP_LST]; + + fn from_handle(handle: Handle) -> Self { + Self(handle) + } + + fn handle(&self) -> &Handle { + &self.0 + } + + fn validate(&self) -> Result<()> { + ensure!( + self.is_class(PropertyListClass::ObjectCopy), + "expected object copy property list, got {:?}", + self.class() + ); + Ok(()) + } +} + +impl Debug for ObjectCopy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut formatter = f.debug_struct("ObjectCopy"); + formatter.field("copy_without_attr", &self.copy_without_attr()); + formatter.field("shallow_hierarchy", &self.shallow_hierarchy()); + formatter.field("expand_soft_links", &self.expand_soft_links()); + formatter.field("expand_ext_links", &self.expand_ext_links()); + formatter.finish() + } +} + +impl Deref for ObjectCopy { + type Target = PropertyList; + + fn deref(&self) -> &PropertyList { + unsafe { self.transmute() } + } +} + +impl PartialEq for ObjectCopy { + fn eq(&self, other: &Self) -> bool { + ::eq(self, other) + } +} + +impl Eq for ObjectCopy {} + +impl Clone for ObjectCopy { + fn clone(&self) -> Self { + unsafe { self.deref().clone().cast_unchecked() } + } +} + +/// Builder used to create object copy property list. +#[derive(Clone, Debug, Default)] +pub struct ObjectCopyBuilder { + copy_without_attr: Option, + shallow_hierarchy: Option, + expand_soft_links: Option, + expand_ext_links: Option, +} + +impl ObjectCopyBuilder { + /// Creates a new object copy property list builder. + pub fn new() -> Self { + Self::default() + } + + /// Creates a new builder from an existing property list. + pub fn from_plist(plist: &ObjectCopy) -> Result { + let mut builder = Self::default(); + let flags = plist.get_flags()?; + builder.copy_without_attr = Some(flags & H5O_COPY_WITHOUT_ATTR_FLAG != 0); + builder.shallow_hierarchy = Some(flags & H5O_COPY_SHALLOW_HIERARCHY_FLAG != 0); + builder.expand_soft_links = Some(flags & H5O_COPY_EXPAND_SOFT_LINK_FLAG != 0); + builder.expand_ext_links = Some(flags & H5O_COPY_EXPAND_EXT_LINK_FLAG != 0); + Ok(builder) + } + + /// Copy object without copying attributes. + pub fn copy_without_attr(&mut self, enable: bool) -> &mut Self { + self.copy_without_attr = Some(enable); + self + } + + /// Copy only immediate members of a group (shallow copy). + pub fn shallow_hierarchy(&mut self, enable: bool) -> &mut Self { + self.shallow_hierarchy = Some(enable); + self + } + + /// Expand soft links into new objects. + pub fn expand_soft_links(&mut self, enable: bool) -> &mut Self { + self.expand_soft_links = Some(enable); + self + } + + /// Expand external links into new objects. + pub fn expand_ext_links(&mut self, enable: bool) -> &mut Self { + self.expand_ext_links = Some(enable); + self + } + + fn populate_plist(&self, id: hid_t) -> Result<()> { + let mut flags = 0u32; + + if self.copy_without_attr.unwrap_or(false) { + flags |= H5O_COPY_WITHOUT_ATTR_FLAG; + } + if self.shallow_hierarchy.unwrap_or(false) { + flags |= H5O_COPY_SHALLOW_HIERARCHY_FLAG; + } + if self.expand_soft_links.unwrap_or(false) { + flags |= H5O_COPY_EXPAND_SOFT_LINK_FLAG; + } + if self.expand_ext_links.unwrap_or(false) { + flags |= H5O_COPY_EXPAND_EXT_LINK_FLAG; + } + + if flags != 0 { + h5try!(H5Pset_copy_object(id, flags)); + } + + Ok(()) + } + + pub fn apply(&self, plist: &mut ObjectCopy) -> Result<()> { + h5lock!(self.populate_plist(plist.id())) + } + + pub fn finish(&self) -> Result { + h5lock!({ + let mut plist = ObjectCopy::try_new()?; + self.apply(&mut plist).map(|()| plist) + }) + } +} + +/// Object copy property list. +impl ObjectCopy { + pub fn try_new() -> Result { + Self::from_id(h5try!(H5Pcreate(*H5P_OBJECT_COPY))) + } + + pub fn copy(&self) -> Self { + unsafe { self.deref().copy().cast_unchecked() } + } + + pub fn build() -> ObjectCopyBuilder { + ObjectCopyBuilder::new() + } + + #[doc(hidden)] + pub fn get_flags(&self) -> Result { + h5get!(H5Pget_copy_object(self.id()): c_uint) + } + + pub fn copy_without_attr(&self) -> bool { + self.get_flags().map(|f| f & H5O_COPY_WITHOUT_ATTR_FLAG != 0).unwrap_or(false) + } + + pub fn shallow_hierarchy(&self) -> bool { + self.get_flags().map(|f| f & H5O_COPY_SHALLOW_HIERARCHY_FLAG != 0).unwrap_or(false) + } + + pub fn expand_soft_links(&self) -> bool { + self.get_flags().map(|f| f & H5O_COPY_EXPAND_SOFT_LINK_FLAG != 0).unwrap_or(false) + } + + pub fn expand_ext_links(&self) -> bool { + self.get_flags().map(|f| f & H5O_COPY_EXPAND_EXT_LINK_FLAG != 0).unwrap_or(false) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_object_copy_builder_from_plist() { + let ocpypl = + ObjectCopy::build().copy_without_attr(true).expand_soft_links(true).finish().unwrap(); + + let builder = ObjectCopyBuilder::from_plist(&ocpypl).unwrap(); + let ocpypl2 = builder.finish().unwrap(); + + assert_eq!(ocpypl.copy_without_attr(), ocpypl2.copy_without_attr()); + assert_eq!(ocpypl.shallow_hierarchy(), ocpypl2.shallow_hierarchy()); + assert_eq!(ocpypl.expand_soft_links(), ocpypl2.expand_soft_links()); + assert_eq!(ocpypl.expand_ext_links(), ocpypl2.expand_ext_links()); + assert_eq!(ocpypl, ocpypl2); + } +} diff --git a/hdf5/src/hl/references.rs b/hdf5/src/hl/references.rs index 1fcece4de..d1dc63720 100644 --- a/hdf5/src/hl/references.rs +++ b/hdf5/src/hl/references.rs @@ -1,5 +1,5 @@ -use crate::internal_prelude::*; use crate::Location; +use crate::internal_prelude::*; mod legacy; diff --git a/hdf5/src/hl/references/legacy.rs b/hdf5/src/hl/references/legacy.rs index d60ae0c6c..04286a151 100644 --- a/hdf5/src/hl/references/legacy.rs +++ b/hdf5/src/hl/references/legacy.rs @@ -5,7 +5,7 @@ use crate::internal_prelude::*; use hdf5_sys::{ h5o::H5O_type_t, h5p::H5P_DEFAULT, - h5r::{hobj_ref_t, H5Rcreate, H5Rdereference, H5Rget_obj_type2}, + h5r::{H5Rcreate, H5Rdereference, H5Rget_obj_type2, hobj_ref_t}, }; use hdf5_types::H5Type; @@ -14,7 +14,7 @@ use hdf5_sys::h5r::H5R_OBJECT as H5R_OBJECT1; #[cfg(feature = "1.12.0")] use hdf5_sys::h5r::H5R_OBJECT1; -use super::{private::ObjectReferencePrivate, ObjectReference}; +use super::{ObjectReference, private::ObjectReferencePrivate}; use crate::Location; #[repr(transparent)] diff --git a/hdf5/src/hl/references/standard.rs b/hdf5/src/hl/references/standard.rs index edd35682b..9d5efbe52 100644 --- a/hdf5/src/hl/references/standard.rs +++ b/hdf5/src/hl/references/standard.rs @@ -6,9 +6,9 @@ use hdf5_sys::h5o::H5O_type_t; use hdf5_sys::h5r::H5R_type_t::H5R_OBJECT2; use hdf5_sys::h5r::{H5R_ref_t, H5Rcreate_object, H5Rdestroy, H5Rget_obj_type3, H5Ropen_object}; -use super::{private::ObjectReferencePrivate, ObjectReference}; -use crate::internal_prelude::*; +use super::{ObjectReference, private::ObjectReferencePrivate}; use crate::Location; +use crate::internal_prelude::*; /// A reference to a HDF5 item that can be stored in attributes or datasets. #[repr(transparent)] diff --git a/hdf5/src/hl/selection.rs b/hdf5/src/hl/selection.rs index 3d7db831a..403515d88 100644 --- a/hdf5/src/hl/selection.rs +++ b/hdf5/src/hl/selection.rs @@ -5,12 +5,12 @@ use std::mem; use std::ops::{Deref, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; use std::slice; -use ndarray::{self, s, Array1, Array2, ArrayView1, ArrayView2}; +use ndarray::{self, Array1, Array2, ArrayView1, ArrayView2, s}; use hdf5_sys::h5s::{ - H5S_sel_type, H5Sget_select_elem_npoints, H5Sget_select_elem_pointlist, H5Sget_select_type, - H5Sget_simple_extent_ndims, H5Sselect_all, H5Sselect_elements, H5Sselect_hyperslab, - H5Sselect_none, H5S_SELECT_SET, H5S_UNLIMITED, + H5S_SELECT_SET, H5S_UNLIMITED, H5S_sel_type, H5Sget_select_elem_npoints, + H5Sget_select_elem_pointlist, H5Sget_select_type, H5Sget_simple_extent_ndims, H5Sselect_all, + H5Sselect_elements, H5Sselect_hyperslab, H5Sselect_none, }; #[cfg(feature = "1.10.0")] use hdf5_sys::h5s::{H5Sget_regular_hyperslab, H5Sis_regular_hyperslab}; @@ -201,8 +201,8 @@ impl RawSelection { Self::All => { h5check(H5Sselect_all(space_id))?; } - Self::Points(ref coords) => set_points_selection(space_id, coords.view())?, - Self::RegularHyperslab(ref hyper) => set_regular_hyperslab(space_id, hyper)?, + Self::Points(coords) => set_points_selection(space_id, coords.view())?, + Self::RegularHyperslab(hyper) => set_regular_hyperslab(space_id, hyper)?, Self::ComplexHyperslab => fail!("Complex hyperslabs are not supported"), }; Ok(()) @@ -772,8 +772,11 @@ impl Display for Hyperslab { #[derive(Clone, Debug, PartialEq, Eq)] /// A selection used for reading and writing to a [`Container`](Container). pub enum Selection { + /// The entire dataset. All, + /// A sequence of points. Points(Array2), + /// A hyperslab or compound hyperslab. Hyperslab(Hyperslab), } @@ -784,10 +787,16 @@ impl Default for Selection { } impl Selection { + /// Creates a new selection. pub fn new>(selection: T) -> Self { selection.into() } + /// Tries to create a new selection. + /// + /// # Errors + /// + /// Returns an error if the conversion method fails. pub fn try_new>(selection: T) -> Result { selection.try_into() } @@ -829,37 +838,43 @@ impl Selection { }) } + /// Returns the required number of dimensions for the input dataspace, + /// or `None` if selecting everything. pub fn in_ndim(&self) -> Option { match self { Self::All => None, - Self::Points(ref points) => { + Self::Points(points) => { if points.shape() == [0, 0] { None } else { Some(points.shape()[1]) } } - Self::Hyperslab(ref hyper) => Some(hyper.len()), + Self::Hyperslab(hyper) => Some(hyper.len()), } } + /// Returns the number of dimensions in the output dataspace, or `None` if selecting everything. pub fn out_ndim(&self) -> Option { match self { Self::All => None, - Self::Points(ref points) => Some(usize::from(points.shape() != [0, 0])), - Self::Hyperslab(ref hyper) => { - Some(hyper.iter().map(|&s| usize::from(s.is_slice())).sum()) - } + Self::Points(points) => Some(usize::from(points.shape() != [0, 0])), + Self::Hyperslab(hyper) => Some(hyper.iter().map(|&s| usize::from(s.is_slice())).sum()), } } + /// Returns the output shape that would result from applying the selection to some input shape. + /// + /// # Errors + /// + /// Returns an error if the input shape is incompatible with the selection. pub fn out_shape>(&self, in_shape: S) -> Result> { let in_shape = in_shape.as_ref(); match self { Self::All => Ok(in_shape.to_owned()), - Self::Points(ref points) => check_coords(points, in_shape) + Self::Points(points) => check_coords(points, in_shape) .and(Ok(if points.shape() == [0, 0] { vec![] } else { vec![points.shape()[0]] })), - Self::Hyperslab(ref hyper) => hyper + Self::Hyperslab(hyper) => hyper .clone() .into_raw(in_shape)? .iter() @@ -875,26 +890,22 @@ impl Selection { } } + /// Returns `true` if the selection is for the entire dataset. pub fn is_all(&self) -> bool { self == &Self::All } + /// Returns `true` if the selection is a sequence of points. pub fn is_points(&self) -> bool { - if let Self::Points(ref points) = self { - points.shape() != [0, 0] - } else { - false - } + if let Self::Points(points) = self { points.shape() != [0, 0] } else { false } } + /// Returns `true` if the selection is empty. pub fn is_none(&self) -> bool { - if let Self::Points(points) = self { - points.shape() == [0, 0] - } else { - false - } + if let Self::Points(points) = self { points.shape() == [0, 0] } else { false } } + /// Returns `true` if the selection is a hyperslab. pub fn is_hyperslab(&self) -> bool { matches!(self, Self::Hyperslab(_)) } @@ -904,7 +915,7 @@ impl Display for Selection { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::All => write!(f, ".."), - Self::Points(ref points) => { + Self::Points(points) => { if points.shape() == [0, 0] { write!(f, "[]") } else { @@ -1061,7 +1072,7 @@ impl_tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } #[cfg(test)] mod test { - use ndarray::{arr1, arr2, s, Array2}; + use ndarray::{Array2, arr1, arr2, s}; use pretty_assertions::assert_eq; use super::{ @@ -1419,14 +1430,18 @@ mod test { Some(Selection::new(&[])), ); - assert!(Hyperslab::try_new( - s![.., ..;2, 1.., 1..;2, -3..=1, -3..=-1;2, ..=-1, ..=-1;3, 0..-1, 2..=-1] - ) - .is_err()); - assert!(Hyperslab::try_new( - s![.., ..;2, 1.., 1..;2, -3..=1, -3..=-1;2, ..=-1, ..=-1;3, 0..-1, 2..=-1] - ) - .is_err()); + assert!( + Hyperslab::try_new( + s![.., ..;2, 1.., 1..;2, -3..=1, -3..=-1;2, ..=-1, ..=-1;3, 0..-1, 2..=-1] + ) + .is_err() + ); + assert!( + Hyperslab::try_new( + s![.., ..;2, 1.., 1..;2, -3..=1, -3..=-1;2, ..=-1, ..=-1;3, 0..-1, 2..=-1] + ) + .is_err() + ); assert!(Hyperslab::try_new(s![-5.., -10, 1..-1;2, 1],).is_err()); assert!(Hyperslab::try_new(s![5..10, 0..1, 1..8;2, 1..2],).is_ok()); diff --git a/hdf5/src/lib.rs b/hdf5/src/lib.rs index 34d283eb1..24005a800 100644 --- a/hdf5/src/lib.rs +++ b/hdf5/src/lib.rs @@ -46,6 +46,7 @@ // To build docs locally: // RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --features blosc,lzf #![cfg_attr(docsrs, feature(doc_cfg))] +#![allow(unsafe_op_in_unsafe_fn)] #[cfg(all(feature = "mpio", not(feature = "have-parallel")))] compile_error!("Enabling \"mpio\" feature requires HDF5 library built with MPI support"); @@ -54,16 +55,17 @@ mod export { pub use crate::{ class::from_id, dim::{Dimension, Ix}, - error::{silence_errors, Error, ErrorFrame, ErrorStack, ExpandedErrorStack, Result}, + error::{Error, ErrorFrame, ErrorStack, ExpandedErrorStack, Result, silence_errors}, + error_codes::{MajorErrorCode, MinorErrorCode}, hl::extents::{Extent, Extents, SimpleExtents}, hl::selection::{Hyperslab, Selection, SliceOrIndex}, hl::{ - references::{ObjectReference, ObjectReference1, ReferencedObject}, Attribute, AttributeBuilder, AttributeBuilderData, AttributeBuilderEmpty, AttributeBuilderEmptyShape, ByteReader, Container, Conversion, Dataset, DatasetBuilder, DatasetBuilderData, DatasetBuilderEmpty, DatasetBuilderEmptyShape, Dataspace, Datatype, - File, FileBuilder, Group, LinkInfo, LinkType, Location, LocationInfo, LocationToken, - LocationType, Object, OpenMode, PropertyList, Reader, Writer, + File, FileBuilder, Group, GroupBuilder, LinkInfo, LinkType, Location, LocationInfo, + LocationToken, LocationType, Object, OpenMode, PropertyList, Reader, Writer, + references::{ObjectReference, ObjectReference1, ReferencedObject}, }, }; @@ -76,10 +78,12 @@ mod export { pub use hdf5_derive::H5Type; pub use hdf5_types::H5Type; + /// Base types and interfaces for creating compound data types. pub mod types { pub use hdf5_types::*; } + /// Multi-dimensional datasets. pub mod dataset { #[cfg(feature = "1.10.5")] pub use crate::hl::chunks::ChunkInfo; @@ -90,40 +94,56 @@ mod export { pub use crate::hl::plist::dataset_create::*; } + /// Datatype objects for defining the layout of a data element. pub mod datatype { pub use crate::hl::datatype::{ByteOrder, Conversion, Datatype}; } + /// HDF5 file objects. pub mod file { pub use crate::hl::file::{File, FileBuilder, OpenMode}; pub use crate::hl::plist::file_access::*; pub use crate::hl::plist::file_create::*; } + /// Property list objects. pub mod plist { pub use crate::hl::plist::dataset_access::{DatasetAccess, DatasetAccessBuilder}; pub use crate::hl::plist::dataset_create::{DatasetCreate, DatasetCreateBuilder}; pub use crate::hl::plist::file_access::{FileAccess, FileAccessBuilder}; pub use crate::hl::plist::file_create::{FileCreate, FileCreateBuilder}; + pub use crate::hl::plist::group_create::{GroupCreate, GroupCreateBuilder}; pub use crate::hl::plist::link_create::{LinkCreate, LinkCreateBuilder}; + pub use crate::hl::plist::object_copy::{ObjectCopy, ObjectCopyBuilder}; pub use crate::hl::plist::{PropertyList, PropertyListClass}; + /// Dataset access property lists. pub mod dataset_access { pub use crate::hl::plist::dataset_access::*; } + /// Dataset creation property lists. pub mod dataset_create { pub use crate::hl::plist::dataset_create::*; } + /// File access property lists. pub mod file_access { pub use crate::hl::plist::file_access::*; } + /// File creation property lists. pub mod file_create { pub use crate::hl::plist::file_create::*; } + /// Group creation property lists. + pub mod group_create { + pub use crate::hl::plist::group_create::*; + } + /// Link creation property lists. pub mod link_create { pub use crate::hl::plist::link_create::*; } } + + /// Filters for data compression and validation during file I/O. pub mod filters { pub use crate::hl::filters::*; } @@ -138,6 +158,7 @@ mod class; mod dim; mod error; +mod error_codes; #[doc(hidden)] pub mod globals; mod handle; @@ -155,7 +176,7 @@ mod internal_prelude { pub use hdf5_sys::{ h5::{haddr_t, hbool_t, herr_t, hsize_t}, h5i::H5I_type_t::{self, *}, - h5i::{hid_t, H5I_INVALID_HID}, + h5i::{H5I_INVALID_HID, hid_t}, h5p::H5P_DEFAULT, h5s::H5S_ALL, }; @@ -211,8 +232,8 @@ pub const HDF5_VERSION: hdf5_sys::Version = hdf5_sys::HDF5_VERSION; #[cfg(test)] pub mod tests { - use crate::library_version; use crate::HDF5_VERSION; + use crate::library_version; #[test] pub fn test_minimum_library_version() { diff --git a/hdf5/src/sync.rs b/hdf5/src/sync.rs index 7783325d1..bd4a4b843 100644 --- a/hdf5/src/sync.rs +++ b/hdf5/src/sync.rs @@ -1,5 +1,5 @@ -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::LazyLock; +use std::sync::atomic::{AtomicBool, Ordering}; pub(crate) use hdf5_sys::LOCK; @@ -43,14 +43,12 @@ where #[cfg(test)] mod tests { - use lazy_static::lazy_static; use parking_lot::ReentrantMutex; + use std::sync::LazyLock; #[test] pub fn test_reentrant_mutex() { - lazy_static! { - static ref LOCK: ReentrantMutex<()> = ReentrantMutex::new(()); - } + static LOCK: LazyLock> = LazyLock::new(|| ReentrantMutex::new(())); let g1 = LOCK.try_lock(); assert!(g1.is_some()); let g2 = LOCK.lock(); diff --git a/hdf5/tests/common/gen.rs b/hdf5/tests/common/generate.rs similarity index 99% rename from hdf5/tests/common/gen.rs rename to hdf5/tests/common/generate.rs index 296fdaf25..6145741c6 100644 --- a/hdf5/tests/common/gen.rs +++ b/hdf5/tests/common/generate.rs @@ -2,8 +2,8 @@ use std::convert::TryFrom; use std::fmt::{self, Debug}; use std::iter; -use hdf5::types::{FixedAscii, FixedUnicode, VarLenArray, VarLenAscii, VarLenUnicode}; use hdf5::H5Type; +use hdf5::types::{FixedAscii, FixedUnicode, VarLenArray, VarLenAscii, VarLenUnicode}; use hdf5_metno as hdf5; use half::f16; @@ -11,8 +11,8 @@ use ndarray::{ArrayD, SliceInfo, SliceInfoElem}; use num_complex::Complex; use rand::distr::StandardUniform; use rand::distr::{Alphanumeric, Uniform}; -use rand::prelude::Rng; use rand::prelude::{Distribution, IndexedRandom}; +use rand::prelude::{Rng, RngExt}; pub fn gen_shape(rng: &mut R, ndim: usize) -> Vec { iter::repeat(()).map(|_| rng.random_range(0..11)).take(ndim).collect() diff --git a/hdf5/tests/common/mod.rs b/hdf5/tests/common/mod.rs index 81cd6ae50..ee73c2355 100644 --- a/hdf5/tests/common/mod.rs +++ b/hdf5/tests/common/mod.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -pub mod gen; +pub mod generate; pub mod util; #[macro_use] pub mod macros; diff --git a/hdf5/tests/common/util.rs b/hdf5/tests/common/util.rs index 47073fb0e..3aa522714 100644 --- a/hdf5/tests/common/util.rs +++ b/hdf5/tests/common/util.rs @@ -1,4 +1,4 @@ -use super::gen::gen_ascii; +use super::generate::gen_ascii; use hdf5_metno as hdf5; diff --git a/hdf5/tests/test_compressing_varlenunicode.rs b/hdf5/tests/test_compressing_varlenunicode.rs new file mode 100644 index 000000000..33d883d69 --- /dev/null +++ b/hdf5/tests/test_compressing_varlenunicode.rs @@ -0,0 +1,143 @@ +use std::str::FromStr; + +use hdf5::types::VarLenUnicode; +use hdf5_metno as hdf5; + +mod common; + +use self::common::util::new_in_memory_file; + +#[test] +#[cfg(feature = "blosc")] +fn blosc_blosclz() -> Result<(), Box> { + let file = new_in_memory_file()?; + + let dset_name = "x"; + let n_samples = 10; + let input_data = vec![VarLenUnicode::from_str("test").unwrap(); n_samples]; + + let ds_builder = file + .new_dataset::() + .shape((n_samples,)) + .chunk((n_samples,)) + .blosc_blosclz(5, true); + + let ds = ds_builder.create(dset_name)?; + ds.write(&input_data)?; + + let read: ndarray::Array1 = + file.dataset(dset_name)?.read::()?; + assert_eq!(read.to_vec(), input_data, "read data must match written data"); + Ok(()) +} + +#[test] +#[cfg(feature = "blosc-lz4")] +fn blosc_lz4() -> Result<(), Box> { + let file = new_in_memory_file()?; + + let dset_name = "x"; + let n_samples = 10; + let input_data = vec![VarLenUnicode::from_str("test")?; n_samples]; + let ds_builder = file + .new_dataset::() + .shape((n_samples,)) + .chunk((n_samples,)) + .blosc_lz4(5, true); + + let ds = ds_builder.create(dset_name)?; + ds.write(&input_data)?; + + let read: ndarray::Array1 = + file.dataset(dset_name)?.read::()?; + assert_eq!(read.to_vec(), input_data, "read data must match written data"); + Ok(()) +} + +#[test] +#[cfg(feature = "blosc-zlib")] +fn blosc_zlib() -> Result<(), Box> { + let file = new_in_memory_file()?; + + let dset_name = "x"; + let n_samples = 10; + let input_data = vec![VarLenUnicode::from_str("test")?; n_samples]; + let ds_builder = file + .new_dataset::() + .shape((n_samples,)) + .chunk((n_samples,)) + .blosc_zlib(5, true); + + let ds = ds_builder.create(dset_name)?; + ds.write(&input_data)?; + + let read: ndarray::Array1 = + file.dataset(dset_name)?.read::()?; + assert_eq!(read.to_vec(), input_data, "read data must match written data"); + Ok(()) +} + +#[test] +#[cfg(feature = "lzf")] +fn lzf() -> Result<(), Box> { + let file = new_in_memory_file()?; + + let dset_name = "x"; + let n_samples = 10; + let input_data = vec![VarLenUnicode::from_str("test")?; n_samples]; + let ds_builder = + file.new_dataset::().shape((n_samples,)).chunk((n_samples,)).lzf(); + + let ds = ds_builder.create(dset_name)?; + ds.write(&input_data)?; + + let read: ndarray::Array1 = + file.dataset(dset_name)?.read::()?; + assert_eq!(read.to_vec(), input_data, "read data must match written data"); + Ok(()) +} + +#[test] +fn szip() -> Result<(), Box> { + if !hdf5::filters::szip_available() { + return Ok(()); + } + let file = new_in_memory_file()?; + + let dset_name = "x"; + let n_samples = 10; + let input_data = vec![VarLenUnicode::from_str("test")?; n_samples]; + let ds_builder = file + .new_dataset::() + .shape((n_samples,)) + .chunk((n_samples,)) + .szip(hdf5::filters::SZip::Entropy, 8); + + let ds = ds_builder.create(dset_name)?; + ds.write(&input_data)?; + + let read: ndarray::Array1 = + file.dataset(dset_name)?.read::()?; + assert_eq!(read.to_vec(), input_data, "read data must match written data"); + Ok(()) +} + +#[test] +#[cfg(feature = "zlib")] +fn deflate() -> Result<(), Box> { + let file = new_in_memory_file()?; + + let dset_name = "x"; + let n_samples = 10; + let input_data = vec![VarLenUnicode::from_str("test")?; n_samples]; + let ds_builder = + file.new_dataset::().shape((n_samples,)).chunk((n_samples,)).deflate(5); + + let ds = ds_builder.create(dset_name)?; + ds.write(&input_data)?; + + let read: ndarray::Array1 = + file.dataset(dset_name)?.read::()?; + assert_eq!(read.to_vec(), input_data, "read data must match written data"); + Ok(()) +} diff --git a/hdf5/tests/test_dataset.rs b/hdf5/tests/test_dataset.rs index 46938d232..8c37c47ac 100644 --- a/hdf5/tests/test_dataset.rs +++ b/hdf5/tests/test_dataset.rs @@ -2,17 +2,17 @@ use std::convert::TryFrom; use std::fmt; use std::io::{Read, Seek, SeekFrom}; -use ndarray::{s, Array1, Array2, ArrayD, IxDyn, SliceInfo}; -use rand::prelude::{Rng, SeedableRng, SmallRng}; +use ndarray::{Array1, Array2, ArrayD, IxDyn, SliceInfo, s}; +use rand::prelude::{Rng, RngExt, SeedableRng, SmallRng}; use hdf5_metno as hdf5; use hdf5_types::TypeDescriptor; mod common; -use self::common::gen::{ - gen_arr, gen_slice, Enum, FixedStruct, Gen, RenameEnum, RenameStruct, RenameTupleStruct, - TupleStruct, VarLenStruct, +use self::common::generate::{ + Enum, FixedStruct, Gen, RenameEnum, RenameStruct, RenameTupleStruct, TupleStruct, VarLenStruct, + gen_arr, gen_slice, }; use self::common::util::new_in_memory_file; @@ -150,6 +150,38 @@ where Ok(()) } +fn test_read_into(ds: &hdf5::Dataset, arr: &ArrayD, v0: T) -> hdf5::Result<()> +where + T: hdf5::H5Type + fmt::Debug + PartialEq + Copy, +{ + ds.write(arr)?; + + let arr_size = arr.len(); + + //Allocates exactly what is needed + let mut buffer: Vec = vec![v0; arr_size]; + + let read_size = ds.read_into_raw(&mut buffer)?; + assert_eq!(read_size, arr_size); + assert_eq!(buffer.as_slice(), arr.as_slice().unwrap()); + + //Allocates more bytes than needed + let mut buffer: Vec = vec![v0; 2 * arr_size]; + let read_size = ds.read_into_raw(&mut buffer)?; + assert_eq!(read_size, arr_size); + assert_eq!(&buffer[..arr_size], arr.as_slice().unwrap()); + + //Allocates less bytes than needed + if arr_size != 0 { + let mut buffer: Vec = vec![v0; arr_size - 1]; + eprintln!("{}", arr_size); + let read_size = ds.read_into_raw(&mut buffer); + assert!(read_size.is_err()); + } + + Ok(()) +} + fn test_write(ds: &hdf5::Dataset, arr: &ArrayD, ndim: usize) -> hdf5::Result<()> where T: hdf5::H5Type + fmt::Debug + PartialEq + Gen, @@ -262,13 +294,14 @@ where packed.push(true); } - let mut rng = SmallRng::seed_from_u64(42); let file = new_in_memory_file()?; - + let mut rng = SmallRng::seed_from_u64(42); for packed in &packed { for ndim in 0..=4 { for _ in 0..=20 { for mode in 0..4 { + // let (ds, arr) = make_test_dataset::(&mut rng, &file, *packed, ndim)?; + let arr: ArrayD = gen_arr(&mut rng, ndim); let ds: hdf5::Dataset = @@ -296,6 +329,42 @@ where Ok(()) } +fn test_read_into_write() -> hdf5::Result<()> +where + T: hdf5::H5Type + fmt::Debug + PartialEq + Gen + Copy, +{ + let td = T::type_descriptor(); + let mut packed = vec![false]; + if let TypeDescriptor::Compound(_) = td { + packed.push(true); + } + + let mut rng = SmallRng::seed_from_u64(42); + let file = new_in_memory_file()?; + + for packed in &packed { + for ndim in 0..=4 { + for _ in 0..=20 { + let arr: ArrayD = gen_arr(&mut rng, ndim); + + let ds: hdf5::Dataset = + file.new_dataset::().packed(*packed).shape(arr.shape()).create("x")?; + let ds = scopeguard::guard(ds, |ds| { + drop(ds); + drop(file.unlink("x")); + }); + + let v0 = T::random(&mut rng); + //v0 stands for default value needed to allocated buffer + //This value avoid restrincting Default to T + test_read_into(&ds, &arr, v0)?; + } + } + } + + Ok(()) +} + #[test] fn test_read_write_primitive() -> hdf5::Result<()> { test_read_write::()?; @@ -309,6 +378,19 @@ fn test_read_write_primitive() -> hdf5::Result<()> { test_read_write::()?; test_read_write::()?; test_read_write::()?; + + test_read_into_write::()?; + test_read_into_write::()?; + test_read_into_write::()?; + test_read_into_write::()?; + test_read_into_write::()?; + test_read_into_write::()?; + test_read_into_write::()?; + test_read_into_write::()?; + test_read_into_write::()?; + test_read_into_write::()?; + test_read_into_write::()?; + Ok(()) } @@ -316,6 +398,7 @@ fn test_read_write_primitive() -> hdf5::Result<()> { #[test] fn test_read_write_f16() -> hdf5::Result<()> { test_read_write::<::half::f16>()?; + test_read_into_write::<::half::f16>()?; Ok(()) } @@ -324,17 +407,205 @@ fn test_read_write_f16() -> hdf5::Result<()> { fn test_read_write_complex() -> hdf5::Result<()> { test_read_write::<::num_complex::Complex32>()?; test_read_write::<::num_complex::Complex64>()?; + test_read_into_write::<::num_complex::Complex32>()?; + test_read_into_write::<::num_complex::Complex64>()?; Ok(()) } #[test] fn test_read_write_enum() -> hdf5::Result<()> { - test_read_write::() + test_read_write::()?; + test_read_into_write::() +} + +#[derive(hdf5::H5Type, Clone, Copy, Debug, PartialEq)] +#[repr(u8)] +#[allow(dead_code)] +enum ShortEnum { + A = 0, + B = 1, + C = 2, +} + +#[derive(hdf5::H5Type, Clone, Copy, Debug, PartialEq)] +#[repr(u8)] +enum ExtendedEnum { + A = 0, + B = 1, + C = 2, + D = 3, + E = 4, +} + +#[derive(hdf5::H5Type, Clone, Copy, Debug, PartialEq)] +#[repr(C)] +struct ShortEnumRecord { + value: ShortEnum, +} + +#[derive(hdf5::H5Type, Clone, Copy, Debug, PartialEq)] +#[repr(C)] +struct ExtendedEnumRecord { + value: ExtendedEnum, +} + +#[test] +fn test_read_enum_rejects_missing_destination_variants() -> hdf5::Result<()> { + // Regression test for https://github.com/metno/hdf5-rust/issues/131 + let file = new_in_memory_file()?; + let data = + [ExtendedEnum::A, ExtendedEnum::B, ExtendedEnum::C, ExtendedEnum::D, ExtendedEnum::E]; + let ds = file.new_dataset::().shape(data.len()).create("extended")?; + ds.write(&data)?; + let actual = ds.read_1d::()?; + + match ds.read_1d::() { + Ok(values) => panic!( + "reading enum values not representable by the requested type unexpectedly succeeded\nread as short enum: {:?}\nactual extended enum values: {:?}", + values.as_slice().unwrap(), + actual.as_slice().unwrap(), + ), + // Naming the members keeps this from passing on an unrelated failure. + Err(err) => { + assert_eq!( + err.to_string(), + "Cannot convert enum at datatype: destination is missing source members: D, E" + ); + Ok(()) + } + } +} + +#[test] +fn test_read_enum_allows_destination_superset() -> hdf5::Result<()> { + let file = new_in_memory_file()?; + let data = [ShortEnum::A, ShortEnum::B, ShortEnum::C]; + let ds = file.new_dataset::().shape(data.len()).create("short")?; + ds.write(&data)?; + + let values = ds.read_1d::()?; + assert_eq!(values.as_slice().unwrap(), &[ExtendedEnum::A, ExtendedEnum::B, ExtendedEnum::C],); + Ok(()) +} + +#[test] +fn test_read_enum_converts_by_member_name() -> hdf5::Result<()> { + // HDF5 enum types are symbol/value pairs, and enum conversion has a "No field" case: + // https://support.hdfgroup.org/documentation/hdf5/latest/_h5_t__u_g.html#subsubsec_datatype_other_enum + // https://support.hdfgroup.org/documentation/hdf5/latest/_h5_t__u_g.html#subsec_datatype_transfer + #[derive(hdf5::H5Type, Clone, Copy, Debug, PartialEq)] + #[repr(u8)] + enum StoredNumberedEnum { + A = 0, + B = 1, + C = 2, + } + + #[derive(hdf5::H5Type, Clone, Copy, Debug, PartialEq)] + #[repr(u8)] + enum MemoryNumberedEnum { + A = 2, + B = 1, + C = 0, + } + + let file = new_in_memory_file()?; + let data = [StoredNumberedEnum::A, StoredNumberedEnum::B, StoredNumberedEnum::C]; + let ds = file.new_dataset::().shape(data.len()).create("numbered")?; + ds.write(&data)?; + + let values = ds.read_1d::()?; + assert_eq!( + values.as_slice().unwrap(), + &[MemoryNumberedEnum::A, MemoryNumberedEnum::B, MemoryNumberedEnum::C], + ); + Ok(()) +} + +#[test] +fn test_write_enum_rejects_missing_dataset_variants() -> hdf5::Result<()> { + let file = new_in_memory_file()?; + let data = + [ExtendedEnum::A, ExtendedEnum::B, ExtendedEnum::C, ExtendedEnum::D, ExtendedEnum::E]; + let ds = file.new_dataset::().shape(data.len()).create("short")?; + + match ds.write(&data) { + Ok(()) => { + let stored = ds.read_1d::()?; + panic!( + "writing enum values not representable by the dataset type unexpectedly succeeded\nwritten as extended enum: {:?}\nstored as short enum: {:?}", + data, + stored.as_slice().unwrap(), + ); + } + Err(_) => Ok(()), + } +} + +#[test] +fn test_read_compound_enum_rejects_missing_destination_variants() -> hdf5::Result<()> { + let file = new_in_memory_file()?; + let data = [ + ExtendedEnumRecord { value: ExtendedEnum::A }, + ExtendedEnumRecord { value: ExtendedEnum::B }, + ExtendedEnumRecord { value: ExtendedEnum::C }, + ExtendedEnumRecord { value: ExtendedEnum::D }, + ExtendedEnumRecord { value: ExtendedEnum::E }, + ]; + let ds = file.new_dataset::().shape(data.len()).create("extended")?; + ds.write(&data)?; + let actual = ds.read_1d::()?; + + match ds.read_1d::() { + Ok(values) => panic!( + "reading compound enum values not representable by the requested type unexpectedly succeeded\nread as short record: {:?}\nactual extended record values: {:?}", + values.as_slice().unwrap(), + actual.as_slice().unwrap(), + ), + Err(_) => Ok(()), + } +} + +#[test] +fn test_read_compound_enum_accepts_superset() -> hdf5::Result<()> { + let file = new_in_memory_file()?; + let data = [ + ShortEnumRecord { value: ShortEnum::A }, + ShortEnumRecord { value: ShortEnum::B }, + ShortEnumRecord { value: ShortEnum::C }, + ShortEnumRecord { value: ShortEnum::A }, + ShortEnumRecord { value: ShortEnum::B }, + ]; + let ds = file.new_dataset::().shape(data.len()).create("extended")?; + ds.write(&data)?; + + ds.read_1d::().map(|_| ()) +} + +#[test] +fn test_read_attribute_enum_rejects_missing_destination_variants() -> hdf5::Result<()> { + let file = new_in_memory_file()?; + let data = + [ExtendedEnum::A, ExtendedEnum::B, ExtendedEnum::C, ExtendedEnum::D, ExtendedEnum::E]; + let attr = file.new_attr::().shape(data.len()).create("extended")?; + attr.write(&data)?; + let actual = attr.read_1d::()?; + + match attr.read_1d::() { + Ok(values) => panic!( + "reading attribute enum values not representable by the requested type unexpectedly succeeded\nread as short enum: {:?}\nactual extended enum values: {:?}", + values.as_slice().unwrap(), + actual.as_slice().unwrap(), + ), + Err(_) => Ok(()), + } } #[test] fn test_read_write_tuple_struct() -> hdf5::Result<()> { - test_read_write::() + test_read_write::()?; + + test_read_into_write::() } #[test] @@ -480,3 +751,31 @@ fn remove_attr() { ds.delete_attr("bar").unwrap(); assert!(ds.attr("bar").is_err()); } + +#[test] +fn test_resize_non_resizable_dataset() { + let file = new_in_memory_file().unwrap(); + // A dataset with contiguous storage has no maxshape and cannot be resized + let ds = file.new_dataset::().shape([4]).create("fixed").unwrap(); + let err = ds.resize([8]).unwrap_err(); + // Minor code varies by HDF5 version, only check the major + let minors: Vec<_> = err.stack().unwrap().minor_codes().collect(); + assert!(err.contains_major(hdf5::MajorErrorCode::Dataset), "minors={minors:?}: {err}"); + + // A chunked dataset with an unlimited axis resizes without error. + let ds = file.new_dataset::().shape((1.., 3)).chunk((4, 3)).create("resizable").unwrap(); + ds.resize([5, 3]).unwrap(); + assert_eq!(ds.shape(), [5, 3]); +} + +#[test] +fn test_chunk_rank_must_match_dataset_rank() { + let file = new_in_memory_file().unwrap(); + // A 1-D chunk on a 2-D dataset is rejected in Rust before reaching FFI + let err = file.new_dataset::().shape([4, 3]).chunk([2]).create("d").unwrap_err(); + assert_eq!(err.to_string(), "Expected chunk ndim 2, got 1"); + assert!(err.stack().is_none(), "expected a Rust-side error, got a stack: {err:?}"); + + // matching rank + file.new_dataset::().shape([4, 3]).chunk([2, 3]).create("ok").unwrap(); +} diff --git a/hdf5/tests/test_datatypes.rs b/hdf5/tests/test_datatypes.rs index 14d86558d..da66fab54 100644 --- a/hdf5/tests/test_datatypes.rs +++ b/hdf5/tests/test_datatypes.rs @@ -2,10 +2,10 @@ mod common; use hdf5::types::{TypeDescriptor as TD, *}; -use hdf5::{from_id, Datatype, H5Type}; +use hdf5::{Datatype, H5Type, from_id}; use hdf5_metno as hdf5; - use hdf5_sys::h5i::H5I_INVALID_HID; +use pretty_assertions::{assert_eq, assert_str_eq}; macro_rules! check_roundtrip { ($ty:ty, $desc:expr) => {{ @@ -148,6 +148,74 @@ pub fn test_eq() { } #[test] -pub fn test_debug() { - assert_eq!(format!("{:?}", Datatype::from_type::().unwrap()), ""); +fn test_print_display_debug_datatype_bool() { + let dt = Datatype::from_type::().unwrap(); + + assert_str_eq!(format!("{dt}"), "bool"); + assert_str_eq!(format!("{dt:?}"), ""); + assert_str_eq!(format!("{dt:#?}"), ""); +} + +#[test] +fn test_print_display_debug_datatype_f64() { + let dt = Datatype::from_type::().unwrap(); + + assert_str_eq!(format!("{dt}"), "float64"); + assert_str_eq!(format!("{dt:?}"), ""); + assert_str_eq!(format!("{dt:#?}"), ""); +} + +#[test] +fn test_print_display_debug_datatype_color_enum() { + #[allow(dead_code)] // "we use the type, we just don't construct it" + #[derive(H5Type)] + #[repr(u8)] + enum Color { + R = 1, + G = 2, + B = 3, + } + let dt = Datatype::from_type::().unwrap(); + + assert_eq!( + dt.to_descriptor().unwrap(), + TD::Enum(EnumType { + size: IntSize::U1, + signed: false, + members: vec![ + EnumMember { name: "R".into(), value: 1 }, + EnumMember { name: "G".into(), value: 2 }, + EnumMember { name: "B".into(), value: 3 } + ] + }) + ); + + assert_str_eq!(format!("{dt}"), "enum (uint8)"); + assert_str_eq!(format!("{dt:?}"), ""); + assert_str_eq!(format!("{dt:#?}"), ""); +} + +#[test] +fn test_print_display_debug_datatype_var_len_unicode() { + let dt = Datatype::from_type::().unwrap(); + assert!(dt.is::()); + + assert_eq!(dt.to_descriptor().unwrap(), TD::VarLenUnicode); + + assert_str_eq!(format!("{dt}"), "unicode (var len)"); + assert_str_eq!(format!("{dt:?}"), ""); + assert_str_eq!(format!("{dt:#?}"), ""); +} + +#[test] +fn test_print_display_debug_datatype_fixed_len_unicode() { + const SIZE: usize = 10; + let dt = Datatype::from_type::>().unwrap(); + assert!(dt.is::>()); + + assert_eq!(dt.to_descriptor().unwrap(), TD::FixedUnicode(SIZE)); + + assert_str_eq!(format!("{dt}"), "unicode (len 10)"); + assert_str_eq!(format!("{dt:?}"), ""); + assert_str_eq!(format!("{dt:#?}"), ""); } diff --git a/hdf5/tests/test_object_references.rs b/hdf5/tests/test_object_references.rs index ad1888d5a..22957dc90 100644 --- a/hdf5/tests/test_object_references.rs +++ b/hdf5/tests/test_object_references.rs @@ -153,6 +153,60 @@ fn test_reference_in_datatype() { } } +fn test_reference_in_enum_datatype() { + let dummy_data = [1, 2, 3, 4]; + let file = new_in_memory_file().unwrap(); + let _ds1 = file.new_dataset_builder().with_data(&dummy_data).create("ds1").unwrap(); + let ref1 = file.reference::("ds1").unwrap(); + let _ds2 = file.new_dataset_builder().with_data(&dummy_data).create("ds2").unwrap(); + let ref2 = file.reference::("ds2").unwrap(); + + #[derive(H5Type, Debug, PartialEq)] + #[repr(u8)] + enum State { + First = 0, + Second = 1, + } + + #[derive(H5Type)] + #[repr(C)] + struct RefData { + dataset: R, + state: State, + } + + let ds3 = file + .new_dataset_builder() + .with_data(&[ + RefData { dataset: ref1, state: State::First }, + RefData { dataset: ref2, state: State::Second }, + ]) + .create("ds3") + .unwrap(); + + let read_data = ds3.read_1d::>().unwrap(); + assert_eq!(read_data[0].state, State::First); + assert_eq!(read_data[1].state, State::Second); + match file.dereference(&read_data[0].dataset).unwrap() { + ReferencedObject::Dataset(ds) => { + assert_eq!(ds.name(), "/ds1"); + assert_eq!(ds.read_1d::().unwrap().as_slice().unwrap(), &dummy_data); + } + _ => { + panic!("Expected a dataset reference"); + } + } + match file.dereference(&read_data[1].dataset).unwrap() { + ReferencedObject::Dataset(ds) => { + assert_eq!(ds.name(), "/ds2"); + assert_eq!(ds.read_1d::().unwrap().as_slice().unwrap(), &dummy_data); + } + _ => { + panic!("Expected a dataset reference"); + } + } +} + /* TODO: Should this be possible? Reference not implementing Copy blocks this in a few places. #[test] fn test_references_in_array_types() { @@ -218,6 +272,12 @@ fn test_reference_in_datatype_object_reference1() { test_reference_in_datatype::(); } +// Compound datatypes can mix reference and enum fields without enum checks inspecting references. +#[test] +fn test_reference_in_enum_datatype_object_reference1() { + test_reference_in_enum_datatype::(); +} + #[cfg(feature = "1.12.1")] #[test] fn test_group_references_with_objectreference2() { @@ -246,3 +306,10 @@ fn test_reference_errors_on_attribute_object_reference2() { fn test_reference_in_datatype_object_reference2() { test_reference_in_datatype::(); } + +// Compound datatypes can mix reference and enum fields without enum checks inspecting references. +#[cfg(feature = "1.12.1")] +#[test] +fn test_reference_in_enum_datatype_object_reference2() { + test_reference_in_enum_datatype::(); +} diff --git a/hdf5/tests/test_plist.rs b/hdf5/tests/test_plist.rs index e5c960703..de80a49df 100644 --- a/hdf5/tests/test_plist.rs +++ b/hdf5/tests/test_plist.rs @@ -16,7 +16,7 @@ macro_rules! test_pl { b.$field($($arg,)+); let fapl = b.finish()?; $(assert_eq!(fapl.$field().$name, $value);)+ - paste::paste! { $(assert_eq!(fapl.[]()?.$name, $value);)+ } + pastey::paste! { $(assert_eq!(fapl.[]()?.$name, $value);)+ } }); ($ty:ident, $field:ident: $($name:ident=$value:expr),+) => ( @@ -32,7 +32,7 @@ macro_rules! test_pl { b.$field($arg); let fapl = b.finish()?; assert_eq!(fapl.$field(), $value); - paste::paste! { assert_eq!(fapl.[]()?, $value); } + pastey::paste! { assert_eq!(fapl.[]()?, $value); } }); ($ty:ident, $field:ident: $value:expr) => ({ @@ -317,7 +317,7 @@ fn test_fapl_driver_mpio() -> hdf5::Result<()> { use std::os::raw::c_int; use std::ptr; - use mpi_sys::{MPI_Comm_compare, MPI_Init, MPI_Initialized, MPI_CONGRUENT, RSMPI_COMM_WORLD}; + use mpi_sys::{MPI_CONGRUENT, MPI_Comm_compare, MPI_Init, MPI_Initialized, RSMPI_COMM_WORLD}; let mut initialized: c_int = 1; unsafe { MPI_Initialized(&mut initialized) }; @@ -329,6 +329,8 @@ fn test_fapl_driver_mpio() -> hdf5::Result<()> { let mut b = FileAccess::build(); b.mpio(world_comm, None); + let driver = b.finish()?.get_driver()?; + println!("{:?}", driver); let d = check_matches!(b.finish()?.get_driver()?, d, FileDriver::Mpio(d)); let mut cmp = mem::MaybeUninit::uninit(); unsafe { MPI_Comm_compare(d.comm, world_comm, cmp.as_mut_ptr()) }; @@ -623,6 +625,30 @@ fn test_dapl_set_virtual_printf_gap() -> hdf5::Result<()> { Ok(()) } +type GC = GroupCreate; +type GCB = GroupCreateBuilder; + +#[test] +fn test_gcpl_common() -> hdf5::Result<()> { + test_pl_common!(GC, PropertyListClass::GroupCreate, |b: &mut GCB| b + .obj_track_times(false) + .finish()); + Ok(()) +} + +#[test] +fn test_gcpl_obj_track_times() -> hdf5::Result<()> { + assert_eq!(GC::try_new()?.get_obj_track_times()?, true); + assert_eq!(GC::try_new()?.obj_track_times(), true); + test_pl!(GC, obj_track_times: true); + test_pl!(GC, obj_track_times: false); + assert_eq!( + GCB::from_plist(&GCB::new().obj_track_times(false).finish()?)?.finish()?.obj_track_times(), + false + ); + Ok(()) +} + type DC = DatasetCreate; type DCB = DatasetCreateBuilder; @@ -933,3 +959,35 @@ fn test_lcpl_char_encoding() -> hdf5::Result<()> { assert_eq!(LCB::from_plist(&pl)?.finish()?.get_char_encoding()?, CharEncoding::Utf8); Ok(()) } + +#[test] +#[cfg(any(all(feature = "1.10.7", not(feature = "1.12.0")), feature = "1.12.1"))] +fn test_fapl_file_locking() -> hdf5::Result<()> { + test_pl!(FA, file_locking: true); + test_pl!(FA, file_locking: false); + Ok(()) +} + +#[test] +fn test_dcpl_filter_requires_chunking() -> hdf5::Result<()> { + let mut b = DatasetCreate::build(); + b.deflate(4); + let err = b.finish().unwrap_err(); + assert_eq!(err.to_string(), "Filter requires dataset to be chunked"); + assert!(err.stack().is_none(), "expected a Rust-side error, got a stack: {err:?}"); + + // Adding a chunk makes it valid + b.chunk([4]); + b.finish()?; + Ok(()) +} + +#[test] +fn test_property_list_class_from_str() { + assert_eq!( + PropertyListClass::from_str("dataset create").unwrap(), + PropertyListClass::DatasetCreate + ); + let err = PropertyListClass::from_str("not a class").unwrap_err(); + assert_eq!(err.to_string(), "invalid property list class: not a class"); +}