From 34b567a9c761083cbebda028d153ceb74655da37 Mon Sep 17 00:00:00 2001 From: Scot Breitenfeld Date: Thu, 20 Aug 2026 22:27:19 -0500 Subject: [PATCH] Add Fortran binding for H5Pmodify_filter_by_idx Implements the binding specified in RFC-HDFG-2026-001 sec:modify-bindings, mirroring the existing h5pappend_filter_f machinery exactly. Fortran cannot represent H5Z_params_t because it contains a union, so two thin C helpers in H5Zf.c construct the struct by field assignment and call the C API -- one per calling mode, matching H5Pappend_filter_str_c / H5Pappend_filter_raw_c. On the Fortran side a generic interface h5pmodify_filter_by_idx_f resolves to two specific procedures, each a wrapper containing an internal INTERFACE block bound to its C helper; the string form NUL-terminates before forwarding. The only argument that differs from append is filter_idx in place of the filter ID. Both specifics are added to hdf5_fortrandll.def.in. Without that, MSVC/Intel Fortran builds link-fail with LNK2019 on the module procedures, which is how the equivalent omission surfaced for the blob binding. Extends filter_config_test in fortran/test/tH5Z.F90 to cover both generic forms: the string form replacing a configuration in place and the raw cd_values form clearing the stored string. Verified the test is actually exercised by breaking its expectation and confirming FORTRAN_testhdf5_fortran fails. --- fortran/src/H5Pff.F90 | 106 +++++++++++++++++++++++++++++ fortran/src/H5Zf.c | 24 +++++++ fortran/src/hdf5_fortrandll.def.in | 2 + fortran/test/tH5Z.F90 | 51 +++++++++++++- 4 files changed, 182 insertions(+), 1 deletion(-) diff --git a/fortran/src/H5Pff.F90 b/fortran/src/H5Pff.F90 index 31919a857b0..435d93354ad 100644 --- a/fortran/src/H5Pff.F90 +++ b/fortran/src/H5Pff.F90 @@ -57,6 +57,7 @@ MODULE H5P PRIVATE h5pregister_integer, h5pregister_ptr PRIVATE h5pinsert_integer, h5pinsert_char, h5pinsert_ptr PRIVATE h5pappend_filter_str_f, h5pappend_filter_raw_f + PRIVATE h5pmodify_filter_by_idx_str_f, h5pmodify_filter_by_idx_raw_f #ifdef H5_HAVE_PARALLEL PRIVATE MPI_INTEGER_KIND PRIVATE h5pset_fapl_mpio_f90, h5pget_fapl_mpio_f90 @@ -122,6 +123,11 @@ MODULE H5P MODULE PROCEDURE h5pappend_filter_raw_f END INTERFACE h5pappend_filter_f + INTERFACE h5pmodify_filter_by_idx_f + MODULE PROCEDURE h5pmodify_filter_by_idx_str_f + MODULE PROCEDURE h5pmodify_filter_by_idx_raw_f + END INTERFACE h5pmodify_filter_by_idx_f + INTERFACE INTEGER(C_INT) FUNCTION H5Pset_fill_value(prp_id, type_id, fillvalue) & BIND(C, NAME='H5Pset_fill_value') @@ -1634,6 +1640,106 @@ CONTAINS #endif +#ifdef H5_DOXYGEN +!> +!! \ingroup FH5P +!! +!! \brief Replaces the configuration of the filter at a given pipeline index. +!! +!! \param prp_id Property list identifier. +!! \param filter_idx Zero-based index of the filter in the pipeline. +!! \param flags Bit vector specifying general filter properties. +!! \param params Parameter string in \c "key=value" format. +!! \param hdferr \fortran_error +!! +!! See C API: @ref H5Pmodify_filter_by_idx() +!! + SUBROUTINE h5pmodify_filter_by_idx_f(prp_id, filter_idx, flags, params, hdferr) + IMPLICIT NONE + INTEGER(HID_T), INTENT(IN) :: prp_id + INTEGER, INTENT(IN) :: filter_idx + INTEGER, INTENT(IN) :: flags + CHARACTER(LEN=*), INTENT(IN) :: params + INTEGER, INTENT(OUT) :: hdferr + END SUBROUTINE h5pmodify_filter_by_idx_f + +!> +!! \ingroup FH5P +!! +!! \brief Replaces the configuration of the filter at a given pipeline index. +!! +!! \param prp_id Property list identifier. +!! \param filter_idx Zero-based index of the filter in the pipeline. +!! \param flags Bit vector specifying general filter properties. +!! \param cd_nelmts Number of elements in \p cd_values. +!! \param cd_values Filter parameter array. This form clears any stored +!! configuration string on the entry. +!! \param hdferr \fortran_error +!! +!! See C API: @ref H5Pmodify_filter_by_idx() +!! + SUBROUTINE h5pmodify_filter_by_idx_f(prp_id, filter_idx, flags, cd_nelmts, cd_values, hdferr) + IMPLICIT NONE + INTEGER(HID_T), INTENT(IN) :: prp_id + INTEGER, INTENT(IN) :: filter_idx + INTEGER, INTENT(IN) :: flags + INTEGER(SIZE_T), INTENT(IN) :: cd_nelmts + INTEGER, DIMENSION(*), INTENT(IN) :: cd_values + INTEGER, INTENT(OUT) :: hdferr + END SUBROUTINE h5pmodify_filter_by_idx_f + +#else + + SUBROUTINE h5pmodify_filter_by_idx_str_f(prp_id, filter_idx, flags, params, hdferr) + IMPLICIT NONE + INTEGER(HID_T), INTENT(IN) :: prp_id + INTEGER, INTENT(IN) :: filter_idx + INTEGER, INTENT(IN) :: flags + CHARACTER(LEN=*), INTENT(IN) :: params + INTEGER, INTENT(OUT) :: hdferr + + CHARACTER(LEN=LEN_TRIM(params)+1,KIND=C_CHAR) :: c_params + INTERFACE + INTEGER(C_INT) FUNCTION H5Pmodify_filter_by_idx_str_c(plist_id, filter_idx_c, flags_c, params_c) & + BIND(C,NAME='H5Pmodify_filter_by_idx_str_c') + IMPORT :: HID_T, C_INT, C_CHAR + INTEGER(HID_T), VALUE :: plist_id + INTEGER(C_INT), VALUE :: filter_idx_c + INTEGER(C_INT), VALUE :: flags_c + CHARACTER(KIND=C_CHAR), DIMENSION(*), INTENT(IN) :: params_c + END FUNCTION H5Pmodify_filter_by_idx_str_c + END INTERFACE + c_params = TRIM(params)//C_NULL_CHAR + hdferr = INT(H5Pmodify_filter_by_idx_str_c(prp_id, INT(filter_idx, C_INT), & + INT(flags, C_INT), c_params)) + END SUBROUTINE h5pmodify_filter_by_idx_str_f + + SUBROUTINE h5pmodify_filter_by_idx_raw_f(prp_id, filter_idx, flags, cd_nelmts, cd_values, hdferr) + IMPLICIT NONE + INTEGER(HID_T), INTENT(IN) :: prp_id + INTEGER, INTENT(IN) :: filter_idx + INTEGER, INTENT(IN) :: flags + INTEGER(SIZE_T), INTENT(IN) :: cd_nelmts + INTEGER, DIMENSION(*), INTENT(IN) :: cd_values + INTEGER, INTENT(OUT) :: hdferr + INTERFACE + INTEGER(C_INT) FUNCTION H5Pmodify_filter_by_idx_raw_c(plist_id, filter_idx_c, flags_c, & + cd_nelmts_c, cd_vals) & + BIND(C,NAME='H5Pmodify_filter_by_idx_raw_c') + IMPORT :: HID_T, C_INT, SIZE_T + INTEGER(HID_T), VALUE :: plist_id + INTEGER(C_INT), VALUE :: filter_idx_c + INTEGER(C_INT), VALUE :: flags_c + INTEGER(SIZE_T), VALUE :: cd_nelmts_c + INTEGER(C_INT), DIMENSION(*), INTENT(IN) :: cd_vals + END FUNCTION H5Pmodify_filter_by_idx_raw_c + END INTERFACE + hdferr = INT(H5Pmodify_filter_by_idx_raw_c(prp_id, INT(filter_idx, C_INT), INT(flags, C_INT), & + INT(cd_nelmts, SIZE_T), cd_values)) + END SUBROUTINE h5pmodify_filter_by_idx_raw_f + +#endif + !> !! \ingroup FH5P !! diff --git a/fortran/src/H5Zf.c b/fortran/src/H5Zf.c index fb064104a58..be60a3abfb9 100644 --- a/fortran/src/H5Zf.c +++ b/fortran/src/H5Zf.c @@ -44,3 +44,27 @@ H5Pappend_filter_raw_c(hid_t plist, H5Z_filter_t id, unsigned flags, size_t cd_n p.u.raw.cd_values = cd_values; return H5Pappend_filter(plist, id, flags, &p); } + +/* The same two calling modes for H5Pmodify_filter_by_idx. The only + difference from the append helpers above is that the entry is selected + by pipeline index rather than by filter ID. */ + +herr_t +H5Pmodify_filter_by_idx_str_c(hid_t plist, unsigned filter_idx, unsigned flags, const char *params) +{ + H5Z_params_t p; + p.type = H5Z_PARAMS_STRING; + p.u.str = params; + return H5Pmodify_filter_by_idx(plist, filter_idx, flags, &p); +} + +herr_t +H5Pmodify_filter_by_idx_raw_c(hid_t plist, unsigned filter_idx, unsigned flags, size_t cd_nelmts, + const unsigned *cd_values) +{ + H5Z_params_t p; + p.type = H5Z_PARAMS_CDVALUES; + p.u.raw.cd_nelmts = cd_nelmts; + p.u.raw.cd_values = cd_values; + return H5Pmodify_filter_by_idx(plist, filter_idx, flags, &p); +} diff --git a/fortran/src/hdf5_fortrandll.def.in b/fortran/src/hdf5_fortrandll.def.in index be1e5fb3a97..97c239af6a2 100644 --- a/fortran/src/hdf5_fortrandll.def.in +++ b/fortran/src/hdf5_fortrandll.def.in @@ -316,6 +316,8 @@ H5P_mp_H5PGET_LAYOUT_F H5P_mp_H5PSET_FILTER_F H5P_mp_H5PAPPEND_FILTER_STR_F H5P_mp_H5PAPPEND_FILTER_RAW_F +H5P_mp_H5PMODIFY_FILTER_BY_IDX_STR_F +H5P_mp_H5PMODIFY_FILTER_BY_IDX_RAW_F H5P_mp_H5PGET_NFILTERS_F H5P_mp_H5PGET_FILTER_F H5P_mp_H5PGET_FILTER_PARAMS_BY_IDX_F diff --git a/fortran/test/tH5Z.F90 b/fortran/test/tH5Z.F90 index 23e09f6eacd..8012bc7df66 100644 --- a/fortran/test/tH5Z.F90 +++ b/fortran/test/tH5Z.F90 @@ -164,7 +164,8 @@ CONTAINS END SUBROUTINE filters_test SUBROUTINE filter_config_test(total_error) -! Tests h5pappend_filter_f, h5pget_filter_params_by_idx_f, h5zconfig_get_param_f +! Tests h5pappend_filter_f, h5pmodify_filter_by_idx_f, +! h5pget_filter_params_by_idx_f, h5zconfig_get_param_f USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_INT64_T, C_DOUBLE, C_NULL_CHAR IMPLICIT NONE @@ -311,6 +312,54 @@ CONTAINS END IF RETURN +! +! h5pmodify_filter_by_idx_f: both generic forms +! + CALL h5zfilter_avail_f(H5Z_FILTER_DEFLATE_F, avail, error) + CALL check("h5zfilter_avail_f", error, total_error) + IF (avail) THEN + CALL h5pcreate_f(H5P_DATASET_CREATE_F, dcpl, error) + CALL check("h5pcreate_f", error, total_error) + CALL h5pappend_filter_f(dcpl, H5Z_FILTER_DEFLATE_F, 0, "level=1"//C_NULL_CHAR, error) + CALL check("h5pappend_filter_f(deflate,str)", error, total_error) + + ! String form: replace the configuration in place + CALL h5pmodify_filter_by_idx_f(dcpl, 0, 0, "level=9"//C_NULL_CHAR, error) + CALL check("h5pmodify_filter_by_idx_f(str)", error, total_error) + CALL h5pget_nfilters_f(dcpl, nfilters, error) + CALL check("h5pget_nfilters_f", error, total_error) + IF (nfilters /= 1) THEN + WRITE(*,*) "h5pmodify_filter_by_idx_f: expected 1 filter, got ", nfilters + total_error = total_error + 1 + END IF + pbuf = "" + plen = 0_SIZE_T + CALL h5pget_filter_params_by_idx_f(dcpl, 0, error, params_buf=pbuf, params_len=plen) + CALL check("h5pget_filter_params_by_idx_f(after modify)", error, total_error) + IF (INDEX(pbuf, "level=9") == 0) THEN + WRITE(*,*) "h5pmodify_filter_by_idx_f(str): expected level=9, got ", TRIM(pbuf) + total_error = total_error + 1 + END IF + + ! Raw cd_values form: replaces cd_values and clears the stored string + cd_nelmts = 1_SIZE_T + cd_dummy(1) = 4 + CALL h5pmodify_filter_by_idx_f(dcpl, 0, 0, cd_nelmts, cd_dummy, error) + CALL check("h5pmodify_filter_by_idx_f(raw)", error, total_error) + pbuf = "" + plen = 0_SIZE_T + CALL h5pget_filter_params_by_idx_f(dcpl, 0, error, params_buf=pbuf, params_len=plen) + CALL check("h5pget_filter_params_by_idx_f(after raw modify)", error, total_error) + ! Stored string gone -> get_config reconstruction reports the new level + IF (INDEX(pbuf, "4") == 0) THEN + WRITE(*,*) "h5pmodify_filter_by_idx_f(raw): expected level 4, got ", TRIM(pbuf) + total_error = total_error + 1 + END IF + + CALL h5pclose_f(dcpl, error) + CALL check("h5pclose_f", error, total_error) + END IF + END SUBROUTINE filter_config_test SUBROUTINE szip_test(szip_flag, cleanup, total_error)