Add H5Pmodify_filter_by_idx

Implements the API specified in RFC-HDFG-2026-001 sec:modify-filter,
which had no implementation on this branch.

H5Pmodify_filter is the only existing way to change a filter's
parameters, and it takes raw cd_values, so it always clears the entry's
stored configuration string. The documented read-mutate-write pattern
therefore silently downgrades an entry from an exact, plugin-free
configuration string to get_config reconstruction, with no error and no
diagnostic. The only workaround was H5Premove_filter plus a fresh
H5Pappend_filter, which moves the entry to the end of the pipeline and
so changes filter order -- for order-sensitive pipelines such as
shuffle-then-deflate, that changes the compressed result.

H5Pmodify_filter_by_idx replaces the configuration of the entry already
at a given position, leaving its position and filter ID alone. It takes
the same H5Z_params_t as H5Pappend_filter: the STRING form resolves
through set_config (same two-pass protocol) and replaces both cd_values
and the stored string, canonicalised as on the append path; the
CDVALUES form replaces cd_values and clears the string, matching
H5Pmodify_filter.

Addressing is by index, not filter ID, because a pipeline may legally
contain the same filter ID more than once and an ID-addressed modify
silently edits the first match. The index is the one H5Pget_filter2 and
H5Pget_filter_params_by_idx already use.

All allocations are staged before the entry is touched, so a rejected
edit -- bad index, invalid flags, set_config failure, or an allocation
failure -- leaves the entry exactly as it was rather than holding a
string that disagrees with its cd_values.

H5Pmodify_filter is unchanged and undeprecated.

Adds six tests: in-place string replacement, canonicalization on the
modify path, cd_values clearing the string, index addressing with
duplicate filter IDs, errors leaving the entry intact, and the
replacement string reaching disk (RFC fmt-07). The duplicate-ID test
was verified to fail against a deliberately ID-addressed
implementation.
This commit is contained in:
Scot Breitenfeld
2026-08-20 20:37:15 -05:00
parent 41b5802819
commit 6ede3c5882
3 changed files with 505 additions and 0 deletions
+219
View File
@@ -2027,6 +2027,225 @@ done:
FUNC_LEAVE_API(ret_value)
} /* end H5Pappend_filter() */
/*-------------------------------------------------------------------------
* Function: H5Pmodify_filter_by_idx
*
* Purpose: Replaces the configuration of the filter already at position
* FILTER_IDX in PLIST_ID's pipeline, leaving its position and
* filter ID unchanged. PARAMS is interpreted exactly as
* H5Pappend_filter interprets it:
*
* H5Z_PARAMS_STRING - resolved through the filter's
* set_config callback; both the resulting
* cd_values and the canonical string
* replace the entry's current ones, so the
* entry keeps a stored configuration
* string.
* H5Z_PARAMS_CDVALUES - the cd_values array replaces the
* entry's current one and any stored
* string is cleared, matching
* H5Pmodify_filter.
* NULL - equivalent to CDVALUES with
* cd_nelmts = 0.
*
* This exists because H5Pmodify_filter takes only raw
* cd_values and therefore always clears the stored string,
* silently downgrading an entry from an exact, plugin-free
* configuration string to get_config reconstruction. The only
* alternative was H5Premove_filter plus a fresh
* H5Pappend_filter, which moves the entry to the end of the
* pipeline and so changes filter order.
*
* Addressing is by index rather than by filter ID because a
* pipeline may legally contain the same filter ID more than
* once, where an ID-addressed modify silently edits the first
* match. The index is the same one H5Pget_filter2 and
* H5Pget_filter_params_by_idx use.
*
* On failure the entry is left exactly as it was: the new
* cd_values and string are staged and swapped in only after
* set_config succeeds, so a rejected edit cannot leave an entry
* holding a string that disagrees with its cd_values.
*
* Return: Non-negative on success / Negative on failure
*
* Since: 3.0.0
*-------------------------------------------------------------------------
*/
herr_t
H5Pmodify_filter_by_idx(hid_t plist_id, unsigned filter_idx, unsigned flags, const H5Z_params_t *params)
{
H5P_genplist_t *plist;
H5O_pline_t pline;
H5Z_filter_info_t *fi;
H5Z_entry_t *entry = NULL;
const unsigned *cd_values = NULL;
unsigned *allocated_cd_values = NULL; /* owns heap mem for string path */
char *canon_config = NULL; /* owns the buffer retain_config points into */
const char *retain_config = NULL; /* canonical string to persist (STRING path only) */
unsigned *staged_cd_values = NULL; /* staged replacement for fi->cd_values */
char *staged_config = NULL; /* staged replacement for fi->config */
size_t cd_nelmts = 0;
H5Z_filter_t filter;
herr_t ret_value = SUCCEED;
FUNC_ENTER_API(FAIL)
/* Validate flags */
if (flags & ~((unsigned)H5Z_FLAG_DEFMASK))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid flags");
/* Get the plist and its pipeline. The entry's existing filter ID selects
* the class whose set_config resolves a parameter string, so the index
* must be validated before params can be interpreted. */
if (NULL == (plist = H5P_object_verify(plist_id, H5P_OBJECT_CREATE, false)))
HGOTO_ERROR(H5E_ID, H5E_BADID, FAIL, "can't find object for ID");
if (H5P_peek(plist, H5O_CRT_PIPELINE_NAME, &pline) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get pipeline");
if (filter_idx >= pline.nused)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "filter index out of range");
fi = &pline.filter[filter_idx];
filter = fi->id;
if (params == NULL || params->type == H5Z_PARAMS_CDVALUES) {
/* Raw cd_values path - behaves identically to H5Pmodify_filter */
if (params) {
cd_nelmts = params->u.raw.cd_nelmts;
cd_values = params->u.raw.cd_values;
if (cd_nelmts > 0 && cd_values == NULL)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cd_values is NULL but cd_nelmts > 0");
}
}
else if (params->type == H5Z_PARAMS_STRING) {
/* String path - invoke set_config to translate into cd_values, using
* the same two-pass protocol as H5Pappend_filter. */
const char *param_str = params->u.str;
bool empty_input = (!param_str || *param_str == '\0');
size_t cd_nelmts2 = 0;
size_t alloc_nelmts = 0;
if (!empty_input && strlen(param_str) > H5Z_CONFIG_STRING_MAX)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "params string exceeds H5Z_CONFIG_STRING_MAX");
/* Trigger dynamic plugin load if filter is not already registered */
{
htri_t filter_avail;
if ((filter_avail = H5Z_filter_avail(filter)) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't check filter availability");
if (!filter_avail)
HGOTO_ERROR(H5E_PLINE, H5E_NOFILTER, FAIL, "filter not found; register or load it first");
}
/* Get the internal entry to access v3 callbacks */
if (H5Z_find_entry(false, filter, &entry) < 0 || entry == NULL)
HGOTO_ERROR(H5E_PLINE, H5E_NOTFOUND, FAIL, "filter entry not found after availability check");
if (!entry->set_config) {
/* No set_config: empty input means "no parameters"; a non-empty
* parameter string has nowhere to go and is an error. */
if (!empty_input)
HGOTO_ERROR(H5E_ARGS, H5E_UNSUPPORTED, FAIL,
"filter does not support string configuration (no set_config callback)");
cd_nelmts = 0;
cd_values = NULL;
}
else {
const char *cfg_str = empty_input ? NULL : param_str;
/* Canonicalise the string that will be persisted, for the same
* reasons as in H5Pappend_filter (valid TOML v1.0.0 on disk). */
if (!empty_input) {
if (NULL == (canon_config = H5Z_canonicalize_params(param_str)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
"can't canonicalize filter parameter string");
retain_config = canon_config;
}
/* Pass 1: determine cd_nelmts (size-query; cd_values is NULL) */
if (entry->set_config(cfg_str, &flags, &cd_nelmts, NULL, 0) < 0)
HGOTO_ERROR(H5E_PLINE, H5E_CANTINIT, FAIL, "set_config size-query call failed");
if (cd_nelmts > H5Z_MAX_CD_NELMTS)
HGOTO_ERROR(H5E_PLINE, H5E_BADVALUE, FAIL,
"cd_nelmts from set_config exceeds H5Z_MAX_CD_NELMTS (%u)", H5Z_MAX_CD_NELMTS);
/* Allocate cd_values (at least 1 to avoid zero-length alloc) */
alloc_nelmts = cd_nelmts ? cd_nelmts : 1;
if (NULL == (allocated_cd_values = (unsigned *)H5MM_malloc(alloc_nelmts * sizeof(unsigned))))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for cd_values");
cd_values = allocated_cd_values;
cd_nelmts2 = cd_nelmts;
/* Pass 2: populate cd_values */
if (entry->set_config(cfg_str, &flags, &cd_nelmts2, allocated_cd_values, alloc_nelmts) < 0)
HGOTO_ERROR(H5E_PLINE, H5E_CANTINIT, FAIL, "set_config populate call failed");
if (cd_nelmts2 != cd_nelmts)
HGOTO_ERROR(H5E_PLINE, H5E_BADVALUE, FAIL,
"set_config returned different cd_nelmts on second call (contract violation)");
/* Re-validate flags: the callback may have modified them */
if (flags & ~((unsigned)H5Z_FLAG_DEFMASK))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "set_config callback returned invalid flags");
}
}
else {
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unrecognised H5Z_params_t type field");
}
/* ---- Stage every allocation before disturbing the entry, so that an
* allocation failure here leaves the entry exactly as it was. ---- */
if (cd_nelmts > H5Z_COMMON_CD_VALUES) {
if (NULL == (staged_cd_values = (unsigned *)H5MM_malloc(cd_nelmts * sizeof(unsigned))))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for filter parameters");
H5MM_memcpy(staged_cd_values, cd_values, cd_nelmts * sizeof(unsigned));
}
if (retain_config) {
if (NULL == (staged_config = (char *)H5MM_strdup(retain_config)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
"memory allocation failed for filter config string");
}
/* ---- Commit: nothing below can fail. ---- */
/* Release what the entry currently owns */
if (fi->cd_values != NULL && fi->cd_values != fi->_cd_values)
H5MM_xfree(fi->cd_values);
H5MM_xfree(fi->config);
fi->flags = flags;
fi->cd_nelmts = cd_nelmts;
if (cd_nelmts == 0)
fi->cd_values = NULL;
else if (staged_cd_values) {
fi->cd_values = staged_cd_values; /* ownership transfers to the entry */
staged_cd_values = NULL;
}
else {
/* Fits the entry's internal buffer */
fi->cd_values = fi->_cd_values;
H5MM_memcpy(fi->cd_values, cd_values, cd_nelmts * sizeof(unsigned));
}
/* NULL for the CDVALUES path, which clears the stored string */
fi->config = staged_config; /* ownership transfers to the entry */
staged_config = NULL;
/* Store the updated pipeline back in the property list */
if (H5P_poke(plist, H5O_CRT_PIPELINE_NAME, &pline) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set pipeline");
done:
H5MM_xfree(staged_cd_values);
H5MM_xfree(staged_config);
H5MM_xfree(canon_config);
H5MM_xfree(allocated_cd_values);
FUNC_LEAVE_API(ret_value)
} /* end H5Pmodify_filter_by_idx() */
/*-------------------------------------------------------------------------
* Function: H5Pget_filter_params_by_idx
*
+52
View File
@@ -2852,6 +2852,58 @@ H5_DLL herr_t H5Pset_filter(hid_t plist_id, H5Z_filter_t filter, unsigned int fl
*/
H5_DLL herr_t H5Pappend_filter(hid_t plist_id, H5Z_filter_t filter, unsigned int flags,
const H5Z_params_t *params);
/**
* \ingroup OCPL
*
* \brief Replaces the configuration of the filter at a given pipeline index
*
* \ocpl_id{plist_id}
* \param[in] filter_idx Zero-based index of the filter in the pipeline
* \param[in] flags Replacement flags for the entry, subject to the same
* \c set_config adjustment rules as H5Pappend_filter()
* \param[in] params New configuration, or NULL for a parameterless filter
*
* \return \herr_t
*
* \details H5Pmodify_filter_by_idx() replaces the configuration of the filter
* already at position \p filter_idx in \p plist_id's pipeline, leaving
* its position and filter ID unchanged. \p params is interpreted
* exactly as H5Pappend_filter() interprets it:
*
* - #H5Z_PARAMS_STRING - the string is validated and resolved through
* the filter's \c set_config callback, and both the resulting
* \c cd_values and the stored configuration string are replaced, so
* the entry keeps a stored string.
* - #H5Z_PARAMS_CDVALUES - the \c cd_values array is replaced and any
* stored string is cleared, matching H5Pmodify_filter().
*
* Use this in preference to H5Pmodify_filter() when the entry was
* configured with a parameter string. H5Pmodify_filter() takes only
* raw \c cd_values and therefore always clears the stored string,
* silently downgrading the entry from an exact, plugin-free
* configuration string to \c get_config reconstruction. The only
* alternative was H5Premove_filter() followed by a fresh
* H5Pappend_filter(), which moves the entry to the end of the pipeline
* and so changes filter order.
*
* Entries are addressed by index rather than by filter ID because a
* pipeline may legally contain the same filter ID more than once,
* where an ID-addressed modify silently edits the first match. The
* index is the same one H5Pget_filter2() and
* H5Pget_filter_params_by_idx() use, so the index a caller reads a
* configuration with is the index it writes one back with.
*
* It is an error if \p filter_idx is out of range, if \p params uses
* #H5Z_PARAMS_STRING for a filter whose registered class does not
* implement \c set_config, or if \c set_config rejects the string. On
* any failure the entry is left exactly as it was.
*
* H5Pmodify_filter() is unchanged and remains supported.
*
* \since 3.0.0
*/
H5_DLL herr_t H5Pmodify_filter_by_idx(hid_t plist_id, unsigned filter_idx, unsigned flags,
const H5Z_params_t *params);
/**
* \ingroup OCPL
*
+234
View File
@@ -2761,6 +2761,237 @@ error:
return -1;
}
/* -----------------------------------------------------------------------
* H5Pmodify_filter_by_idx (RFC-HDFG-2026-001 sec:modify-filter)
*
* Reuses the canon filter (CANON_FILTER_ID, one double "rate") so the
* stored string and the recovered value can both be checked.
* ---------------------------------------------------------------------- */
static int
test_modify_filter_by_idx(hid_t fapl)
{
hid_t dcpl = H5I_INVALID_HID, sid = H5I_INVALID_HID, file = H5I_INVALID_HID;
hid_t dset = H5I_INVALID_HID, dcpl_out = H5I_INVALID_HID;
hsize_t dims[2] = {8, 8};
char filename[1024];
char pbuf[H5Z_CONFIG_STRING_MAX + 1];
size_t plen = 0;
double got = 0.0;
unsigned flags_out = 0;
unsigned cd[8];
size_t cd_nelmts = 8;
char nm[64];
unsigned cfg = 0;
H5Z_params_t p;
if (H5Zregister(&canon_cls) < 0)
TEST_ERROR;
/* --- mod-01: STRING replaces cd_values and keeps a stored string --- */
TESTING("H5Pmodify_filter_by_idx: string form replaces config in place");
if ((dcpl = canon_make_dcpl("rate = 1.5")) < 0)
TEST_ERROR;
p.type = H5Z_PARAMS_STRING;
p.u.str = "rate = 2.5";
if (H5Pmodify_filter_by_idx(dcpl, 0, H5Z_FLAG_MANDATORY, &p) < 0)
TEST_ERROR;
if (H5Pget_filter_params_by_idx(dcpl, 0, pbuf, sizeof(pbuf), &plen) < 0)
TEST_ERROR;
if (strcmp(pbuf, "rate = 2.5") != 0)
TEST_ERROR;
if (canon_stored_double(dcpl, &got) < 0)
TEST_ERROR;
if (memcmp(&got, &(double){2.5}, sizeof(got)) != 0)
TEST_ERROR;
/* Position and filter ID unchanged, and still exactly one entry */
if (H5Pget_nfilters(dcpl) != 1)
TEST_ERROR;
cd_nelmts = 8;
if (H5Pget_filter2(dcpl, 0, &flags_out, &cd_nelmts, cd, sizeof(nm), nm, &cfg) != CANON_FILTER_ID)
TEST_ERROR;
if (H5Pclose(dcpl) < 0)
TEST_ERROR;
dcpl = H5I_INVALID_HID;
PASSED();
/* --- mod-02: canonicalization applies on the modify path too --- */
TESTING("H5Pmodify_filter_by_idx: replacement string is canonicalized");
if ((dcpl = canon_make_dcpl("rate = 1.5")) < 0)
TEST_ERROR;
p.type = H5Z_PARAMS_STRING;
p.u.str = "{rate = 0x1.8p+1}";
if (H5Pmodify_filter_by_idx(dcpl, 0, H5Z_FLAG_MANDATORY, &p) < 0)
TEST_ERROR;
if (H5Pget_filter_params_by_idx(dcpl, 0, pbuf, sizeof(pbuf), &plen) < 0)
TEST_ERROR;
if (strcmp(pbuf, "rate = 3.00000000000000000e+00") != 0)
TEST_ERROR;
if (H5Pclose(dcpl) < 0)
TEST_ERROR;
dcpl = H5I_INVALID_HID;
PASSED();
/* --- mod-03: CDVALUES form clears the stored string --- */
TESTING("H5Pmodify_filter_by_idx: cd_values form clears the stored string");
if ((dcpl = canon_make_dcpl("rate = 1.5")) < 0)
TEST_ERROR;
{
double v = 4.5;
unsigned raw[2] = {0, 0};
memcpy(raw, &v, sizeof(v));
p.type = H5Z_PARAMS_CDVALUES;
p.u.raw.cd_nelmts = 2;
p.u.raw.cd_values = raw;
if (H5Pmodify_filter_by_idx(dcpl, 0, H5Z_FLAG_MANDATORY, &p) < 0)
TEST_ERROR;
}
if (H5Pget_filter_params_by_idx(dcpl, 0, pbuf, sizeof(pbuf), &plen) < 0)
TEST_ERROR;
/* Stored string gone -> get_config reconstruction, which uses %.17e */
if (strcmp(pbuf, "rate = 4.50000000000000000e+00") != 0) {
fprintf(stderr, "\n got \"%s\"\n", pbuf);
TEST_ERROR;
}
if (H5Pclose(dcpl) < 0)
TEST_ERROR;
dcpl = H5I_INVALID_HID;
PASSED();
/* --- mod-04: index addressing distinguishes duplicate filter IDs --- */
TESTING("H5Pmodify_filter_by_idx: duplicate filter IDs addressed by index");
if ((dcpl = canon_make_dcpl("rate = 1.5")) < 0)
TEST_ERROR;
p.type = H5Z_PARAMS_STRING;
p.u.str = "rate = 2.5";
if (H5Pappend_filter(dcpl, CANON_FILTER_ID, H5Z_FLAG_MANDATORY, &p) < 0)
TEST_ERROR;
if (H5Pget_nfilters(dcpl) != 2)
TEST_ERROR;
/* Edit the second entry only */
p.u.str = "rate = 9.5";
if (H5Pmodify_filter_by_idx(dcpl, 1, H5Z_FLAG_MANDATORY, &p) < 0)
TEST_ERROR;
if (H5Pget_filter_params_by_idx(dcpl, 0, pbuf, sizeof(pbuf), &plen) < 0)
TEST_ERROR;
if (strcmp(pbuf, "rate = 1.5") != 0) /* entry 0 untouched */
TEST_ERROR;
if (H5Pget_filter_params_by_idx(dcpl, 1, pbuf, sizeof(pbuf), &plen) < 0)
TEST_ERROR;
if (strcmp(pbuf, "rate = 9.5") != 0)
TEST_ERROR;
if (H5Pclose(dcpl) < 0)
TEST_ERROR;
dcpl = H5I_INVALID_HID;
PASSED();
/* --- mod-05: errors, and the entry survives a rejected edit --- */
TESTING("H5Pmodify_filter_by_idx: errors leave the entry unchanged");
if ((dcpl = canon_make_dcpl("rate = 1.5")) < 0)
TEST_ERROR;
/* index out of range */
p.type = H5Z_PARAMS_STRING;
p.u.str = "rate = 2.5";
H5E_BEGIN_TRY
{
if (H5Pmodify_filter_by_idx(dcpl, 1, H5Z_FLAG_MANDATORY, &p) >= 0)
TEST_ERROR;
}
H5E_END_TRY
/* invalid flags */
H5E_BEGIN_TRY
{
if (H5Pmodify_filter_by_idx(dcpl, 0, 0xFFFFFFFFu, &p) >= 0)
TEST_ERROR;
}
H5E_END_TRY
/* set_config rejects the string (canon_set_config fails a type mismatch) */
p.u.str = "rate = \"not a number\"";
H5E_BEGIN_TRY
{
if (H5Pmodify_filter_by_idx(dcpl, 0, H5Z_FLAG_MANDATORY, &p) >= 0)
TEST_ERROR;
}
H5E_END_TRY
/* After every rejected edit the original configuration is intact */
if (H5Pget_filter_params_by_idx(dcpl, 0, pbuf, sizeof(pbuf), &plen) < 0)
TEST_ERROR;
if (strcmp(pbuf, "rate = 1.5") != 0) {
fprintf(stderr, "\n entry was disturbed: \"%s\"\n", pbuf);
TEST_ERROR;
}
if (canon_stored_double(dcpl, &got) < 0)
TEST_ERROR;
if (memcmp(&got, &(double){1.5}, sizeof(got)) != 0)
TEST_ERROR;
if (H5Pclose(dcpl) < 0)
TEST_ERROR;
dcpl = H5I_INVALID_HID;
PASSED();
/* --- mod-06 (fmt-07): the replacement string is what reaches disk --- */
TESTING("H5Pmodify_filter_by_idx: replacement string persists, not the original");
h5_fixname(FILENAME[1], fapl, filename, sizeof(filename));
if ((dcpl = canon_make_dcpl("rate = 1.5")) < 0)
TEST_ERROR;
p.type = H5Z_PARAMS_STRING;
p.u.str = "rate = 7.25";
if (H5Pmodify_filter_by_idx(dcpl, 0, H5Z_FLAG_MANDATORY, &p) < 0)
TEST_ERROR;
if ((sid = H5Screate_simple(2, dims, NULL)) < 0)
TEST_ERROR;
if ((file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
TEST_ERROR;
if ((dset = H5Dcreate2(file, "dset", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
TEST_ERROR;
if (H5Dclose(dset) < 0 || H5Pclose(dcpl) < 0 || H5Fclose(file) < 0)
TEST_ERROR;
dset = dcpl = file = H5I_INVALID_HID;
/* Drop the plugin so only the persisted string can answer */
if (H5Zunregister(CANON_FILTER_ID) < 0)
TEST_ERROR;
if ((file = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
TEST_ERROR;
if ((dset = H5Dopen2(file, "dset", H5P_DEFAULT)) < 0)
TEST_ERROR;
if ((dcpl_out = H5Dget_create_plist(dset)) < 0)
TEST_ERROR;
if (H5Pget_filter_params_by_idx(dcpl_out, 0, pbuf, sizeof(pbuf), &plen) < 0)
TEST_ERROR;
if (strcmp(pbuf, "rate = 7.25") != 0) {
fprintf(stderr, "\n expected \"rate = 7.25\" on disk, got \"%s\"\n", pbuf);
TEST_ERROR;
}
if (H5Pclose(dcpl_out) < 0 || H5Dclose(dset) < 0 || H5Fclose(file) < 0 || H5Sclose(sid) < 0)
TEST_ERROR;
dcpl_out = dset = file = sid = H5I_INVALID_HID;
if (H5Zregister(&canon_cls) < 0)
TEST_ERROR;
PASSED();
if (H5Zunregister(CANON_FILTER_ID) < 0)
TEST_ERROR;
return 0;
error:
H5E_BEGIN_TRY
{
H5Pclose(dcpl);
H5Pclose(dcpl_out);
H5Dclose(dset);
H5Fclose(file);
H5Sclose(sid);
H5Zunregister(CANON_FILTER_ID);
}
H5E_END_TRY
return -1;
}
/* -----------------------------------------------------------------------
* main
* ---------------------------------------------------------------------- */
@@ -2831,6 +3062,9 @@ main(void)
/* Canonicalization of the persisted configuration string */
nerrors += test_config_canonicalization(fapl) < 0 ? 1 : 0;
/* H5Pmodify_filter_by_idx */
nerrors += test_modify_filter_by_idx(fapl) < 0 ? 1 : 0;
if (H5Fclose(file) < 0)
goto error;