Apply filters to variable-length data (#6178)

Closes can_apply and set_local filter callbacks skipped for H5T_VARIABLE dtypes #5942. A more detailed explanation of the rationale for this PR is in the linked issue.

Mandatory filters can be now applied to variable-length data. This notably includes h5py object strings and NpyStrings.

Optional filters applied to variable-length data no longer skip can_apply and set_local, only to jump directly to filter. This fixes hdf5_blosc and HDF5_Blosc2, which rely on set_local. Note that all filters which have neither can_apply nor set_local have always worked fine with vlen data, as long as they were tagged as optional. Notable examples are deflate, lzf, bzip2 (from hdf5plugin), and lz4 (also from hdf5plugin).

Lossy filters, such as scale-offset, are and remain untested and most likely broken, as I expect them to treat the vlen metadata as numbers and corrupt it. The right place to ensure proper behaviour is their can_apply function, which however is broken (filters: can_apply does nothing for optional filters #6161).
This commit is contained in:
Guido Imperiale
2026-02-24 17:05:58 -06:00
committed by GitHub
parent a7ec64a857
commit b074586d9e
4 changed files with 734 additions and 77 deletions
+1 -1
View File
@@ -1265,7 +1265,7 @@ H5D__create(H5F_t *file, hid_t type_id, const H5S_t *space, hid_t dcpl_id, hid_t
H5O_efl_t *efl; /* Dataset's external file list info */
htri_t ignore_filters = false; /* Ignore optional filters or not */
if ((ignore_filters = H5Z_ignore_filters(new_dset->shared->dcpl_id, dt, space)) < 0)
if ((ignore_filters = H5Z_ignore_filters(new_dset->shared->dcpl_id, space)) < 0)
HGOTO_ERROR(H5E_ARGS, H5E_CANTINIT, NULL, "H5Z_has_optional_filter() failed");
if (false == ignore_filters) {
+10 -15
View File
@@ -1058,12 +1058,14 @@ done:
* Purpose: Determine whether filters can be ignored.
*
* Description:
* When the filters are optional (i.e., H5Z_FLAG_OPTIONAL is provided,)
* When the filters are optional (i.e., H5Z_FLAG_OPTIONAL is provided),
* if any of the following conditions is met, the filters will be ignored:
* - dataspace is either H5S_NULL or H5S_SCALAR
* - datatype is variable-length (string or non-string)
* However, if any of these conditions exists and a filter is not
* optional, the function will produce an error.
* This function runs before the can_apply callback of the filters and
* its negated return value is effectively OR'ed to the return value
* of can_apply.
*
* Return: Non-negative(true/false) on success
* Negative on failure
@@ -1071,14 +1073,12 @@ done:
*-------------------------------------------------------------------------
*/
htri_t
H5Z_ignore_filters(hid_t dcpl_id, const H5T_t *type, const H5S_t *space)
H5Z_ignore_filters(hid_t dcpl_id, const H5S_t *space)
{
H5P_genplist_t *dc_plist; /* Dataset creation property list object */
H5O_pline_t pline; /* Object's I/O pipeline information */
H5S_class_t space_class; /* To check class of space */
H5T_class_t type_class; /* To check if type is VL */
bool bad_for_filters = false; /* Suitable to have filters */
htri_t ret_value = false; /* true for ignoring filters */
H5P_genplist_t *dc_plist; /* Dataset creation property list object */
H5O_pline_t pline; /* Object's I/O pipeline information */
H5S_class_t space_class; /* To check class of space */
htri_t ret_value = false; /* true for ignoring filters */
FUNC_ENTER_NOAPI(FAIL)
@@ -1091,15 +1091,10 @@ H5Z_ignore_filters(hid_t dcpl_id, const H5T_t *type, const H5S_t *space)
/* Get datatype and dataspace classes for quick access */
space_class = H5S_GET_EXTENT_TYPE(space);
type_class = H5T_get_class(type, false);
/* These conditions are not suitable for filters */
bad_for_filters = (H5S_NULL == space_class || H5S_SCALAR == space_class || H5T_VLEN == type_class ||
(H5T_STRING == type_class && true == H5T_is_variable_str(type)));
/* When these conditions occur, if there are required filters in pline,
then report a failure, otherwise, set flag that they can be ignored */
if (bad_for_filters) {
if (space_class == H5S_NULL || space_class == H5S_SCALAR) {
size_t ii;
if (pline.nused > 0) {
for (ii = 0; ii < pline.nused; ii++) {
+1 -1
View File
@@ -83,7 +83,7 @@ H5_DLL herr_t H5Z_can_apply(hid_t dcpl_id, hid_t type_id);
H5_DLL herr_t H5Z_set_local(hid_t dcpl_id, hid_t type_id);
H5_DLL herr_t H5Z_can_apply_direct(const struct H5O_pline_t *pline);
H5_DLL herr_t H5Z_set_local_direct(const struct H5O_pline_t *pline);
H5_DLL htri_t H5Z_ignore_filters(hid_t dcpl_id, const H5T_t *type, const H5S_t *space);
H5_DLL htri_t H5Z_ignore_filters(hid_t dcpl_id, const H5S_t *space);
H5_DLL H5Z_filter_info_t *H5Z_filter_info(const struct H5O_pline_t *pline, H5Z_filter_t filter);
H5_DLL htri_t H5Z_filter_in_pline(const struct H5O_pline_t *pline, H5Z_filter_t filter);
H5_DLL htri_t H5Z_all_filters_avail(const struct H5O_pline_t *pline);
+722 -60
View File
@@ -115,8 +115,9 @@ static const char *FILENAME[] = {"dataset", /* 0 */
#define DSET_SHUF_DEF_FLET_NAME "shuffle+deflate+fletcher32"
#define DSET_SHUF_DEF_FLET_NAME_2 "shuffle+deflate+fletcher32_2"
#endif
#define DSET_OPTIONAL_SCALAR "dataset_with_scalar_space"
#define DSET_OPTIONAL_VLEN "dataset_with_vlen_type"
#define DSET_OPTIONAL_SCALAR "dataset_with_scalar_space"
#define DSET_OPTIONAL_SCALAR2 "dataset_with_scalar_space2"
#define DSET_OPTIONAL_NULL "dataset_with_null_space"
#ifdef H5_HAVE_FILTER_SZIP
#define DSET_SZIP_NAME "szip"
#define DSET_SHUF_SZIP_FLET_NAME "shuffle+szip+fletcher32"
@@ -130,29 +131,32 @@ static const char *FILENAME[] = {"dataset", /* 0 */
#ifdef H5_HAVE_FILTER_SZIP
#define DSET_CAN_APPLY_SZIP_NAME "can_apply_szip"
#endif /* H5_HAVE_FILTER_SZIP */
#define DSET_SET_LOCAL_NAME "set_local"
#define DSET_SET_LOCAL_NAME_2 "set_local_2"
#define DSET_ONEBYTE_SHUF_NAME "onebyte_shuffle"
#define DSET_NBIT_INT_NAME "nbit_int"
#define DSET_NBIT_FLOAT_NAME "nbit_float"
#define DSET_NBIT_DOUBLE_NAME "nbit_double"
#define DSET_NBIT_ARRAY_NAME "nbit_array"
#define DSET_NBIT_COMPOUND_NAME "nbit_compound"
#define DSET_NBIT_COMPOUND_NAME_2 "nbit_compound_2"
#define DSET_NBIT_COMPOUND_NAME_3 "nbit_compound_3"
#define DSET_NBIT_INT_SIZE_NAME "nbit_int_size"
#define DSET_NBIT_FLT_SIZE_NAME "nbit_flt_size"
#define DSET_SCALEOFFSET_INT_NAME "scaleoffset_int"
#define DSET_SCALEOFFSET_INT_NAME_2 "scaleoffset_int_2"
#define DSET_SCALEOFFSET_FLOAT_NAME "scaleoffset_float"
#define DSET_SCALEOFFSET_FLOAT_NAME_2 "scaleoffset_float_2"
#define DSET_SCALEOFFSET_DOUBLE_NAME "scaleoffset_double"
#define DSET_SCALEOFFSET_DOUBLE_NAME_2 "scaleoffset_double_2"
#define DSET_COMPARE_DCPL_NAME "compare_dcpl"
#define DSET_COMPARE_DCPL_NAME_2 "compare_dcpl_2"
#define DSET_COPY_DCPL_NAME_1 "copy_dcpl_1"
#define DSET_COPY_DCPL_NAME_2 "copy_dcpl_2"
#define COPY_DCPL_EXTFILE_NAME "ext_file"
#define DSET_SET_LOCAL_NAME "set_local"
#define DSET_SET_LOCAL_NAME_2 "set_local_2"
#define DSET_SET_LOCAL_UPDATES_CD_NAME "set_local_updates_cd"
#define DSET_SET_LOCAL_UPDATES_CD_VLEN_NAME "set_local_updates_cd_vlen"
#define DSET_DEFLATE_VLEN_NAME "deflate_vlen"
#define DSET_ONEBYTE_SHUF_NAME "onebyte_shuffle"
#define DSET_NBIT_INT_NAME "nbit_int"
#define DSET_NBIT_FLOAT_NAME "nbit_float"
#define DSET_NBIT_DOUBLE_NAME "nbit_double"
#define DSET_NBIT_ARRAY_NAME "nbit_array"
#define DSET_NBIT_COMPOUND_NAME "nbit_compound"
#define DSET_NBIT_COMPOUND_NAME_2 "nbit_compound_2"
#define DSET_NBIT_COMPOUND_NAME_3 "nbit_compound_3"
#define DSET_NBIT_INT_SIZE_NAME "nbit_int_size"
#define DSET_NBIT_FLT_SIZE_NAME "nbit_flt_size"
#define DSET_SCALEOFFSET_INT_NAME "scaleoffset_int"
#define DSET_SCALEOFFSET_INT_NAME_2 "scaleoffset_int_2"
#define DSET_SCALEOFFSET_FLOAT_NAME "scaleoffset_float"
#define DSET_SCALEOFFSET_FLOAT_NAME_2 "scaleoffset_float_2"
#define DSET_SCALEOFFSET_DOUBLE_NAME "scaleoffset_double"
#define DSET_SCALEOFFSET_DOUBLE_NAME_2 "scaleoffset_double_2"
#define DSET_COMPARE_DCPL_NAME "compare_dcpl"
#define DSET_COMPARE_DCPL_NAME_2 "compare_dcpl_2"
#define DSET_COPY_DCPL_NAME_1 "copy_dcpl_1"
#define DSET_COPY_DCPL_NAME_2 "copy_dcpl_2"
#define COPY_DCPL_EXTFILE_NAME "ext_file"
#ifndef H5_NO_DEPRECATED_SYMBOLS
#define DSET_DEPREC_NAME "deprecated"
#define DSET_DEPREC_NAME_CHUNKED "deprecated_chunked"
@@ -191,6 +195,8 @@ static const char *FILENAME[] = {"dataset", /* 0 */
#define H5Z_FILTER_CAN_APPLY_TEST2 311
#define H5Z_FILTER_COUNT 312
#define H5Z_FILTER_EXPAND2 313
#define H5Z_FILTER_DO_NOT_RUN 314
#define H5Z_FILTER_UPDATE_CD 315
/* Flags for testing filters */
#define DISABLE_FLETCHER32 0
@@ -295,6 +301,7 @@ double **check_dbl = NULL;
double *check_dbl_data = NULL;
size_t count_nbytes_read = 0;
size_t count_nbytes_written = 0;
size_t count_dnr_calls = 0;
/* Temporary buffer dimensions */
#define DSET_TMP_DIM1 50
@@ -329,6 +336,13 @@ static size_t filter_bogus2(unsigned int flags, size_t cd_nelmts, const unsigned
size_t nbytes, size_t *buf_size, void **buf);
static size_t filter_bogus3(unsigned int flags, size_t cd_nelmts, const unsigned int *cd_values,
size_t nbytes, size_t *buf_size, void **buf);
static htri_t can_apply_do_not_run(hid_t dcpl_id, hid_t type_id, hid_t space_id);
static herr_t set_local_do_not_run(hid_t dcpl_id, hid_t type_id, hid_t space_id);
static size_t filter_do_not_run(unsigned int flags, size_t cd_nelmts, const unsigned int *cd_values,
size_t nbytes, size_t *buf_size, void **buf);
static herr_t set_local_update_cd(hid_t dcpl_id, hid_t type_id, hid_t space_id);
static size_t filter_update_cd(unsigned int flags, size_t cd_nelmts, const unsigned int *cd_values,
size_t nbytes, size_t *buf_size, void **buf);
static size_t filter_corrupt(unsigned int flags, size_t cd_nelmts, const unsigned int *cd_values,
size_t nbytes, size_t *buf_size, void **buf);
static size_t filter_expand(unsigned int flags, size_t cd_nelmts, const unsigned int *cd_values,
@@ -1914,6 +1928,110 @@ filter_bogus3(unsigned int H5_ATTR_UNUSED flags, size_t H5_ATTR_UNUSED cd_nelmts
return 0;
} /* end filter_bogus3() */
/*------------------------------------------------------------------------
* Function: can_apply_do_not_run
*
* Purpose: A 'can apply' callback that lets the calling test detect
* if it's been executed
*------------------------------------------------------------------------
*/
static htri_t
can_apply_do_not_run(hid_t H5_ATTR_UNUSED dcpl_id, hid_t H5_ATTR_UNUSED type_id,
hid_t H5_ATTR_UNUSED space_id)
{
count_dnr_calls += 1;
return FAIL;
} /* end can_apply_do_not_run() */
/*---------------------------------------------------------------------
* Function: set_local_do_not_run
*
* Purpose: A 'set local' callback that lets the calling test detect
* if it's been executed
*---------------------------------------------------------------------
*/
static herr_t
set_local_do_not_run(hid_t H5_ATTR_UNUSED dcpl_id, hid_t H5_ATTR_UNUSED type_id,
hid_t H5_ATTR_UNUSED space_id)
{
count_dnr_calls += 1;
return FAIL;
} /* end set_local_do_not_run() */
/*------------------------------------------------------------------
* Function: filter_do_not_run
*
* Purpose: A 'filter' callback that lets the calling test detect
* if it's been executed
*------------------------------------------------------------------
*/
static size_t
filter_do_not_run(unsigned int H5_ATTR_UNUSED flags, size_t H5_ATTR_UNUSED cd_nelmts,
const unsigned int H5_ATTR_UNUSED *cd_values, size_t H5_ATTR_UNUSED nbytes,
size_t H5_ATTR_UNUSED *buf_size, void H5_ATTR_UNUSED **buf)
{
count_dnr_calls += 1;
return 0;
} /* end filter_do_not_run() */
/*-------------------------------------------------------------------------
* Function: set_local_update_cd
*
* Purpose: A 'set local' callback that modifies cd_nelmts and
* cd_values.
* This mocks the behaviour of hdf5_blosc and HDF5_Blosc2,
* which rely on this functionality.
*
* Return: Success: non-negative
* Failure: negative
*-------------------------------------------------------------------------
*/
static herr_t
set_local_update_cd(hid_t dcpl_id, hid_t H5_ATTR_UNUSED type_id, hid_t H5_ATTR_UNUSED space_id)
{
unsigned flags; /* Filter flags */
size_t cd_nelmts = 3; /* Number of filter parameters */
unsigned cd_values[3] = {0, 0, 0}; /* Filter parameters */
/* Get the filter's current parameters */
if (H5Pget_filter_by_id2(dcpl_id, H5Z_FILTER_UPDATE_CD, &flags, &cd_nelmts, cd_values, (size_t)0, NULL,
NULL) < 0)
return FAIL;
if (cd_nelmts != 0)
return FAIL;
/* Modify cd_nelmts and cd_values */
cd_nelmts = 1;
cd_values[0] = 42;
/* Modify the filter's parameters for this dataset */
if (H5Pmodify_filter(dcpl_id, H5Z_FILTER_UPDATE_CD, flags, cd_nelmts, cd_values) < 0)
return FAIL;
return SUCCEED;
} /* end set_local_update_cd() */
/*-------------------------------------------------------------------------
* Function: filter_update_cd
*
* Purpose: A filter callback that tests that cd_values was modified by
* set_local_update_cd
*
* Return: Success: Data chunk size
* Failure: 0
*-------------------------------------------------------------------------
*/
static size_t
filter_update_cd(unsigned int H5_ATTR_UNUSED flags, size_t cd_nelmts, const unsigned int *cd_values,
size_t nbytes, size_t H5_ATTR_UNUSED *buf_size, void H5_ATTR_UNUSED **buf)
{
if (cd_nelmts != 1 || cd_values[0] != 42)
// This makes the test fail because the filter is mandatory
return 0;
return nbytes;
} /* end filter_update_cd() */
/* This message derives from H5Z */
const H5Z_class2_t H5Z_CORRUPT[1] = {{
H5Z_CLASS_T_VERS, /* H5Z_class_t version */
@@ -1925,6 +2043,28 @@ const H5Z_class2_t H5Z_CORRUPT[1] = {{
filter_corrupt, /* The actual filter function */
}};
/* A filter where callbacks let the calling test notice if they've been run
through a global variable
*/
const H5Z_class2_t H5Z_DO_NOT_RUN[1] = {{
H5Z_CLASS_T_VERS, H5Z_FILTER_DO_NOT_RUN, /* Filter id number */
1, 1, "do_not_run", /* Filter name for debugging */
can_apply_do_not_run, /* The "can apply" callback */
set_local_do_not_run, /* The "set local" callback */
filter_do_not_run, /* The actual filter function */
}};
/* A filter where set_local updates the cd_values and filter tests that
cd_values was updated
*/
const H5Z_class2_t H5Z_SET_LOCAL_UPDATE_CD[1] = {{
H5Z_CLASS_T_VERS, H5Z_FILTER_UPDATE_CD, /* Filter id number */
1, 1, "set_local_update_cd", /* Filter name for debugging */
NULL, /* The "can apply" callback */
set_local_update_cd, /* The "set local" callback */
filter_update_cd, /* The actual filter function */
}};
/*-------------------------------------------------------------------------
* Function: filter_corrupt
*
@@ -6585,7 +6725,7 @@ error:
*
* Purpose: Tests that H5Dcreate2 will not fail when a combination of
* type, space, etc... doesn't work for a filter and filter is
* optional.
* optional. This test uses the real DEFLATE filter.
*
* Return: Success: SUCCEED
* Failure: FAIL
@@ -6598,12 +6738,10 @@ test_optional_filters(hid_t file)
unsigned int level = 9;
unsigned int cd_values[1] = {level};
size_t cd_nelmts = 1;
hsize_t dim1d[1]; /* Dataspace dimensions */
hid_t dsid = H5I_INVALID_HID; /* Dataset ID */
hid_t sid = H5I_INVALID_HID; /* Dataspace ID */
hid_t strtid = H5I_INVALID_HID; /* Datatype ID for string */
hid_t vlentid = H5I_INVALID_HID; /* Datatype ID for vlen */
hid_t dcplid = H5I_INVALID_HID; /* Dataspace creation property list ID */
hid_t dsid = H5I_INVALID_HID; /* Dataset ID */
hid_t sid = H5I_INVALID_HID; /* Dataspace ID */
hid_t strtid = H5I_INVALID_HID; /* Datatype ID for string */
hid_t dcplid = H5I_INVALID_HID; /* Dataspace creation property list ID */
TESTING("dataset with optional filters");
@@ -6639,33 +6777,6 @@ test_optional_filters(hid_t file)
if (H5Tclose(strtid) < 0)
TEST_ERROR;
/* Set dataspace dimensions */
dim1d[0] = DIM1;
/* Create a non-scalar dataspace */
if ((sid = H5Screate_simple(1, dim1d, NULL)) < 0)
TEST_ERROR;
/* Create a vlen datatype */
if ((vlentid = H5Tvlen_create(H5T_NATIVE_INT)) < 0)
TEST_ERROR;
/* Create dataset with optional filter */
if ((dsid = H5Dcreate2(file, DSET_OPTIONAL_VLEN, vlentid, sid, H5P_DEFAULT, dcplid, H5P_DEFAULT)) < 0)
TEST_ERROR;
/* Close dataset */
if (H5Dclose(dsid) < 0)
TEST_ERROR;
/* Close dataspace */
if (H5Sclose(sid) < 0)
TEST_ERROR;
/* Close datatype */
if (H5Tclose(vlentid) < 0)
TEST_ERROR;
/* Close dataset creation property list */
if (H5Pclose(dcplid) < 0)
TEST_ERROR;
@@ -6680,12 +6791,197 @@ error:
H5Sclose(sid);
H5Pclose(dcplid);
H5Tclose(strtid);
H5Tclose(vlentid);
}
H5E_END_TRY
return FAIL;
} /* end test_optional_filters() */
/*-------------------------------------------------------------------------
* Function: test_optional_filters_scalar
*
* Purpose: Tests that a filter is quietly skipped for scalar datasets
* if filter is optional.
*
* Return: Success: SUCCEED
* Failure: FAIL
*
*-------------------------------------------------------------------------
*/
static herr_t
test_optional_filters_scalar(hid_t file)
{
hid_t dsid = H5I_INVALID_HID; /* Dataset ID */
hid_t sid = H5I_INVALID_HID; /* Dataspace ID */
hid_t dcplid = H5I_INVALID_HID; /* Dataspace creation property list ID */
const int wdata = 42;
int rdata = -1;
TESTING("scalar dataset with optional filters");
/* Create dcpl with special filter */
if ((dcplid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
TEST_ERROR;
if (H5Zregister(H5Z_DO_NOT_RUN) < 0)
TEST_ERROR;
/* The filter is optional. Test that callbacks are never invoked. */
count_dnr_calls = 0;
if (H5Pset_filter(dcplid, H5Z_FILTER_DO_NOT_RUN, H5Z_FLAG_OPTIONAL, 0, NULL) < 0)
TEST_ERROR;
/* Create the data space */
if ((sid = H5Screate(H5S_SCALAR)) < 0)
TEST_ERROR;
/* Create dataset with optional filter */
if ((dsid = H5Dcreate2(file, DSET_OPTIONAL_SCALAR2, H5T_NATIVE_INT, sid, H5P_DEFAULT, dcplid,
H5P_DEFAULT)) < 0)
TEST_ERROR;
/* Write data to the dataset */
if (H5Dwrite(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wdata) < 0)
TEST_ERROR;
/* Close dataset */
if (H5Dclose(dsid) < 0)
TEST_ERROR;
/* Flush the file (to clear the cache) */
if (H5Fflush(file, H5F_SCOPE_GLOBAL) < 0)
TEST_ERROR;
/* Reopen the dataset */
if ((dsid = H5Dopen2(file, DSET_OPTIONAL_SCALAR2, H5P_DEFAULT)) < 0)
TEST_ERROR;
/* Read data back */
if (H5Dread(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rdata) < 0)
TEST_ERROR;
/* Compare data */
if (rdata != wdata)
TEST_ERROR;
/* Close dataset */
if (H5Dclose(dsid) < 0)
TEST_ERROR;
/* Close dataspace */
if (H5Sclose(sid) < 0)
TEST_ERROR;
/* Close dataset creation property list */
if (H5Pclose(dcplid) < 0)
TEST_ERROR;
if (count_dnr_calls)
TEST_ERROR;
PASSED();
return SUCCEED;
error:
H5E_BEGIN_TRY
{
H5Dclose(dsid);
H5Sclose(sid);
H5Pclose(dcplid);
}
H5E_END_TRY
return FAIL;
} /* end test_optional_filters_scalar() */
/*-------------------------------------------------------------------------
* Function: test_optional_filters_null
*
* Purpose: Tests that a filter is quietly skipped for null datasets
* if filter is optional.
*
* Return: Success: SUCCEED
* Failure: FAIL
*
*-------------------------------------------------------------------------
*/
static herr_t
test_optional_filters_null(hid_t file)
{
hid_t dsid = H5I_INVALID_HID; /* Dataset ID */
hid_t sid = H5I_INVALID_HID; /* Dataspace ID */
hid_t dcplid = H5I_INVALID_HID; /* Dataspace creation property list ID */
TESTING("null dataset with optional filters");
/* Create dcpl with special filter */
if ((dcplid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
TEST_ERROR;
if (H5Zregister(H5Z_DO_NOT_RUN) < 0)
TEST_ERROR;
/* The filter is optional. Test that callbacks are never invoked. */
count_dnr_calls = 0;
if (H5Pset_filter(dcplid, H5Z_FILTER_DO_NOT_RUN, H5Z_FLAG_OPTIONAL, 0, NULL) < 0)
TEST_ERROR;
/* Create the data space */
if ((sid = H5Screate(H5S_NULL)) < 0)
TEST_ERROR;
/* Create dataset with optional filter */
if ((dsid = H5Dcreate2(file, DSET_OPTIONAL_NULL, H5T_NATIVE_INT, sid, H5P_DEFAULT, dcplid, H5P_DEFAULT)) <
0)
TEST_ERROR;
/* Write data to the dataset (no-op for null dataspace) */
if (H5Dwrite(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, NULL) < 0)
TEST_ERROR;
/* Close dataset */
if (H5Dclose(dsid) < 0)
TEST_ERROR;
/* Flush the file (to clear the cache) */
if (H5Fflush(file, H5F_SCOPE_GLOBAL) < 0)
TEST_ERROR;
/* Reopen the dataset */
if ((dsid = H5Dopen2(file, DSET_OPTIONAL_NULL, H5P_DEFAULT)) < 0)
TEST_ERROR;
/* Read data back (no-op for null dataspace) */
if (H5Dread(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, NULL) < 0)
TEST_ERROR;
/* Close dataset */
if (H5Dclose(dsid) < 0)
TEST_ERROR;
/* Close dataspace */
if (H5Sclose(sid) < 0)
TEST_ERROR;
/* Close dataset creation property list */
if (H5Pclose(dcplid) < 0)
TEST_ERROR;
if (count_dnr_calls)
TEST_ERROR;
PASSED();
return SUCCEED;
error:
H5E_BEGIN_TRY
{
H5Dclose(dsid);
H5Sclose(sid);
H5Pclose(dcplid);
}
H5E_END_TRY
return FAIL;
} /* end test_optional_filters_null() */
/*-------------------------------------------------------------------------
* Function: test_can_apply_szip
*
@@ -17752,6 +18048,367 @@ error:
return FAIL;
} /* end test_vds_shared_strings() */
/*-------------------------------------------------------------------------
* Function: test_set_local_updates_cd
*
* Purpose: Tests a set_local callback that updates both cd_nelmts and
* cd_values before executing the filter.
* This test mocks hdf5_blosc and HDF5_BLOSC2, both of which
* rely on set_local to update cd_values before filter is executed.
*
* Return: Success: 0
* Failure: -1
*
*-------------------------------------------------------------------------
*/
static herr_t
test_set_local_updates_cd(hid_t file)
{
hid_t dsid = H5I_INVALID_HID; /* Dataset ID */
hid_t sid = H5I_INVALID_HID; /* Dataspace ID */
hid_t dcpl = H5I_INVALID_HID; /* Dataset creation property list ID */
const hsize_t dims[1] = {1}; /* Dataspace dimensions */
const hsize_t chunk_dims[1] = {1}; /* Chunk dimensions */
const int wdata = 42; /* Write buffer */
int rdata = 0; /* Read buffer */
TESTING("dataset filter 'set local' callback updates cd");
/* Create dcpl with special filter */
if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
TEST_ERROR;
/* Note: filters can only be applied to chunked datasets */
if (H5Pset_chunk(dcpl, 1, chunk_dims) < 0)
TEST_ERROR;
if (H5Zregister(H5Z_SET_LOCAL_UPDATE_CD) < 0)
TEST_ERROR;
if (H5Pset_filter(dcpl, H5Z_FILTER_UPDATE_CD, H5Z_FLAG_MANDATORY, 0, NULL) < 0)
TEST_ERROR;
/* Create the data space */
if ((sid = H5Screate_simple(1, dims, NULL)) < 0)
TEST_ERROR;
/* Create new dataset with fixed-width integer type */
if ((dsid = H5Dcreate2(file, DSET_SET_LOCAL_UPDATES_CD_NAME, H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl,
H5P_DEFAULT)) < 0)
TEST_ERROR;
/* Write data (this triggers set_local and then the filter) */
if (H5Dwrite(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wdata) < 0)
TEST_ERROR;
/* Close dataset */
if (H5Dclose(dsid) < 0)
TEST_ERROR;
/* Flush the file (to clear the cache) */
if (H5Fflush(file, H5F_SCOPE_GLOBAL) < 0)
TEST_ERROR;
/* Re-open dataset */
if ((dsid = H5Dopen2(file, DSET_SET_LOCAL_UPDATES_CD_NAME, H5P_DEFAULT)) < 0)
TEST_ERROR;
/* Read data back */
if (H5Dread(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rdata) < 0)
TEST_ERROR;
/* Compare data */
if (wdata != rdata)
TEST_ERROR;
/* Close dataset */
if (H5Dclose(dsid) < 0)
TEST_ERROR;
/* Close dataspace */
if (H5Sclose(sid) < 0)
TEST_ERROR;
/* Close dataset creation property list */
if (H5Pclose(dcpl) < 0)
TEST_ERROR;
PASSED();
return SUCCEED;
error:
H5E_BEGIN_TRY
{
H5Dclose(dsid);
H5Sclose(sid);
H5Pclose(dcpl);
}
H5E_END_TRY
return FAIL;
} /* end test_set_local_updates_cd() */
/*-------------------------------------------------------------------------
* Function: test_set_local_updates_cd_vlen
*
* Purpose: Tests a set_local callback that updates both cd_nlemnts and
* cd_values before executing the filter.
* This test mocks hdf5_blosc and HDF5_BLOSC2, both of which
* rely on set_local to update cd_values before filter is executed.
*
* This test tests the interaction of the above filter with
* variable-length datatypes.
*
* Return: Success: 0
* Failure: -1
*
*-------------------------------------------------------------------------
*/
static herr_t
test_set_local_updates_cd_vlen(hid_t file)
{
hid_t dsid = H5I_INVALID_HID; /* Dataset ID */
hid_t sid = H5I_INVALID_HID; /* Dataspace ID */
hid_t dcpl = H5I_INVALID_HID; /* Dataset creation property list ID */
hid_t dtype = H5I_INVALID_HID; /* Datatype ID */
const hsize_t dims[1] = {1}; /* Dataspace dimensions */
const hsize_t chunk_dims[1] = {1}; /* Chunk dimensions */
const char *wdata = "test string"; /* Write buffer */
char *rdata = NULL; /* Read buffer */
TESTING("dataset filter 'set local' callback updates cd with variable-length strings");
/* Define variable-length (NULL-terminated) UTF-8 string datatype */
if ((dtype = H5Tcopy(H5T_C_S1)) < 0)
TEST_ERROR;
if (H5Tset_size(dtype, H5T_VARIABLE) < 0)
TEST_ERROR;
/* Optional but recommended: store as UTF-8 strings */
if (H5Tset_cset(dtype, H5T_CSET_UTF8) < 0)
TEST_ERROR;
/* Create dcpl with special filter */
if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
TEST_ERROR;
/* Note: filters can only be applied to chunked datasets */
if (H5Pset_chunk(dcpl, 1, chunk_dims) < 0)
TEST_ERROR;
if (H5Zregister(H5Z_SET_LOCAL_UPDATE_CD) < 0)
TEST_ERROR;
if (H5Pset_filter(dcpl, H5Z_FILTER_UPDATE_CD, H5Z_FLAG_MANDATORY, 0, NULL) < 0)
TEST_ERROR;
/* Create the data space */
if ((sid = H5Screate_simple(1, dims, NULL)) < 0)
TEST_ERROR;
/* Create new dataset */
if ((dsid = H5Dcreate2(file, DSET_SET_LOCAL_UPDATES_CD_VLEN_NAME, dtype, sid, H5P_DEFAULT, dcpl,
H5P_DEFAULT)) < 0)
TEST_ERROR;
/* Write data (this triggers set_local and then the filter) */
if (H5Dwrite(dsid, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wdata) < 0)
TEST_ERROR;
/* Close dataset */
if (H5Dclose(dsid) < 0)
TEST_ERROR;
/* Flush the file (to clear the cache) */
if (H5Fflush(file, H5F_SCOPE_GLOBAL) < 0)
TEST_ERROR;
/* Re-open dataset */
if ((dsid = H5Dopen2(file, DSET_SET_LOCAL_UPDATES_CD_VLEN_NAME, H5P_DEFAULT)) < 0)
TEST_ERROR;
/* Read data back */
if (H5Dread(dsid, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rdata) < 0)
TEST_ERROR;
/* Compare data */
if (strcmp(wdata, rdata) != 0)
TEST_ERROR;
/* Reclaim read buffer memory */
if (H5Treclaim(dtype, sid, H5P_DEFAULT, &rdata) < 0)
TEST_ERROR;
/* Close dataset */
if (H5Dclose(dsid) < 0)
TEST_ERROR;
/* Close dataspace */
if (H5Sclose(sid) < 0)
TEST_ERROR;
/* Close datatype */
if (H5Tclose(dtype) < 0)
TEST_ERROR;
/* Close dataset creation property list */
if (H5Pclose(dcpl) < 0)
TEST_ERROR;
PASSED();
return SUCCEED;
error:
H5E_BEGIN_TRY
{
H5Dclose(dsid);
H5Sclose(sid);
H5Tclose(dtype);
H5Pclose(dcpl);
}
H5E_END_TRY
return FAIL;
} /* end test_set_local_updates_cd_vlen() */
/*-------------------------------------------------------------------------
* Function: test_deflate_vlen
*
* Purpose: Test the deflate filter for vlen dataset
*
* This test tests the interaction of the above filter with
* variable-length datatypes.
*
* Return: Success: 0
* Failure: -1
*
*-------------------------------------------------------------------------
*/
#ifdef H5_HAVE_FILTER_DEFLATE
static herr_t
test_deflate_vlen(hid_t file)
{
hid_t dsid = H5I_INVALID_HID; /* Dataset ID */
hid_t sid = H5I_INVALID_HID; /* Dataspace ID */
hid_t dcpl = H5I_INVALID_HID; /* Dataset creation property list ID */
hid_t dtype = H5I_INVALID_HID; /* Datatype ID */
size_t cd_nelmts = 1;
const unsigned cd_values[1] = {9}; /* Compression level */
const hsize_t dims[1] = {2}; /* Dataspace dimensions */
const hsize_t chunk_dims[1] = {2}; /* Chunk dimensions */
char wdata0[500];
char wdata1[300];
char *wdata[2] = {wdata0, wdata1}; /* Write buffer */
char *rdata[2] = {NULL, NULL}; /* Read buffer */
size_t i;
TESTING("dataset deflate filter with variable-length data");
/* Create dcpl with special filter */
if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
TEST_ERROR;
/* Note: filters can only be applied to chunked datasets */
if (H5Pset_chunk(dcpl, 1, chunk_dims) < 0)
TEST_ERROR;
if (H5Pset_filter(dcpl, H5Z_FILTER_DEFLATE, H5Z_FLAG_MANDATORY, cd_nelmts, cd_values) < 0)
TEST_ERROR;
/* The deflate filter() callback returns failure for chunks where the compressed
output is larger than the uncompressed input, so in production the filter must
must always be optional. Above, we set it as mandatory to make sure that it is
actually being applied; the caveat is that we must use highly compressible
test data.
*/
for (i = 0; i < 499; i++)
wdata0[i] = 'A';
wdata0[499] = '\0';
for (i = 0; i < 299; i++)
wdata1[i] = 'B';
wdata1[299] = '\0';
/* Define variable-length (NULL-terminated) UTF-8 string datatype */
if ((dtype = H5Tcopy(H5T_C_S1)) < 0)
TEST_ERROR;
if (H5Tset_size(dtype, H5T_VARIABLE) < 0)
TEST_ERROR;
/* Optional but recommended: store as UTF-8 strings */
if (H5Tset_cset(dtype, H5T_CSET_UTF8) < 0)
TEST_ERROR;
/* Create the data space */
if ((sid = H5Screate_simple(1, dims, NULL)) < 0)
TEST_ERROR;
/* Create new dataset */
if ((dsid = H5Dcreate2(file, DSET_DEFLATE_VLEN_NAME, dtype, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
TEST_ERROR;
/* Write data (this triggers set_local and then the filter) */
if (H5Dwrite(dsid, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata) < 0)
TEST_ERROR;
/* Close dataset */
if (H5Dclose(dsid) < 0)
TEST_ERROR;
/* Flush the file (to clear the cache) */
if (H5Fflush(file, H5F_SCOPE_GLOBAL) < 0)
TEST_ERROR;
/* Re-open dataset */
if ((dsid = H5Dopen2(file, DSET_DEFLATE_VLEN_NAME, H5P_DEFAULT)) < 0)
TEST_ERROR;
/* Read data back */
if (H5Dread(dsid, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata) < 0)
TEST_ERROR;
/* Compare data */
if (strcmp(wdata[0], rdata[0]) != 0 || strcmp(wdata[1], rdata[1]) != 0)
TEST_ERROR;
/* Reclaim read buffer memory */
if (H5Treclaim(dtype, sid, H5P_DEFAULT, rdata) < 0)
TEST_ERROR;
/* Close dataset */
if (H5Dclose(dsid) < 0)
TEST_ERROR;
/* Close dataspace */
if (H5Sclose(sid) < 0)
TEST_ERROR;
/* Close datatype */
if (H5Tclose(dtype) < 0)
TEST_ERROR;
/* Close dataset creation property list */
if (H5Pclose(dcpl) < 0)
TEST_ERROR;
PASSED();
return SUCCEED;
error:
H5E_BEGIN_TRY
{
H5Dclose(dsid);
H5Sclose(sid);
H5Tclose(dtype);
H5Pclose(dcpl);
}
H5E_END_TRY
return FAIL;
#else /* H5_HAVE_FILTER_DEFLATE */
static herr_t
test_deflate_vlen(hid_t H5_ATTR_UNUSED file)
{
TESTING("dataset deflate filter with variable-length data");
SKIPPED();
return SUCCEED;
#endif /* H5_HAVE_FILTER_DEFLATE */
} /* end test_deflate_vlen() */
/*-------------------------------------------------------------------------
* Function: main
*
@@ -17934,7 +18591,12 @@ main(void)
nerrors += (test_can_apply(file) < 0 ? 1 : 0);
nerrors += (test_can_apply2(file) < 0 ? 1 : 0);
nerrors += (test_optional_filters(file) < 0 ? 1 : 0);
nerrors += (test_optional_filters_scalar(file) < 0 ? 1 : 0);
nerrors += (test_optional_filters_null(file) < 0 ? 1 : 0);
nerrors += (test_set_local(fapl) < 0 ? 1 : 0);
nerrors += (test_set_local_updates_cd(file) < 0 ? 1 : 0);
nerrors += (test_set_local_updates_cd_vlen(file) < 0 ? 1 : 0);
nerrors += (test_deflate_vlen(file) < 0 ? 1 : 0);
nerrors += (test_can_apply_szip(file) < 0 ? 1 : 0);
nerrors += (test_compare_dcpl(file) < 0 ? 1 : 0);
nerrors += (test_copy_dcpl(file, fapl) < 0 ? 1 : 0);