diff --git a/src/H5T.c b/src/H5T.c index cb95e305d7e..20f135e6bda 100644 --- a/src/H5T.c +++ b/src/H5T.c @@ -1311,7 +1311,6 @@ H5T_init(void) status |= H5T__register_int(H5T_PERS_SOFT, "complexbo(opt)", cplx, cplx, H5T__conv_order_opt); status |= H5T__register_int(H5T_PERS_SOFT, "struct(no-opt)", compound, compound, H5T__conv_struct); status |= H5T__register_int(H5T_PERS_SOFT, "struct(opt)", compound, compound, H5T__conv_struct_opt); - /* TODO: complex number struct conversions */ status |= H5T__register_int(H5T_PERS_SOFT, "enum", enum_type, enum_type, H5T__conv_enum); status |= H5T__register_int(H5T_PERS_SOFT, "enum_i", enum_type, fixedpt, H5T__conv_enum_numeric); status |= H5T__register_int(H5T_PERS_SOFT, "enum_f", enum_type, floatpt, H5T__conv_enum_numeric); @@ -1322,6 +1321,10 @@ H5T_init(void) status |= H5T__register_int(H5T_PERS_SOFT, "ref", ref, ref, H5T__conv_ref); status |= H5T__register_int(H5T_PERS_SOFT, "objref_ref", objref, ref, H5T__conv_ref); status |= H5T__register_int(H5T_PERS_SOFT, "regref_ref", regref, ref, H5T__conv_ref); + status |= H5T__register_int(H5T_PERS_SOFT, "complex_array_compat", cplx, array, H5T__conv_complex_compat); + status |= H5T__register_int(H5T_PERS_SOFT, "array_complex_compat", array, cplx, H5T__conv_complex_compat); + status |= H5T__register_int(H5T_PERS_SOFT, "complex_compound_compat", cplx, compound, H5T__conv_complex_compat); + status |= H5T__register_int(H5T_PERS_SOFT, "compound_complex_compat", compound, cplx, H5T__conv_complex_compat); /* * Native conversions should be listed last since we can use hardware to diff --git a/src/H5Tconv_complex.c b/src/H5Tconv_complex.c index a545ed363d5..8d304745772 100644 --- a/src/H5Tconv_complex.c +++ b/src/H5Tconv_complex.c @@ -1385,53 +1385,181 @@ H5T__conv_complex_f_matched(const H5T_t *src_p, const H5T_t *dst_p, const H5T_co } /* end H5T__conv_complex_f_matched() */ /*------------------------------------------------------------------------- - * Function: H5T__conv_complex_struct - * - * Purpose: Convert a complex number type to the equivalent compound - * type representation. A compound type must match one of the - * the following representations exactly to be considered - * equivalent. - * - * H5T_COMPOUND { - * "r"; OFFSET 0 - * "i"; OFFSET SIZEOF("r") - * } - * - * H5T_COMPOUND { - * "re"; OFFSET 0 - * "im"; OFFSET SIZEOF("re") - * } - * - * H5T_COMPOUND { - * "real"; OFFSET 0 - * "imag"; OFFSET SIZEOF("real") - * } - * - * H5T_COMPOUND { - * "real"; OFFSET 0 - * "imaginary"; OFFSET SIZEOF("real") - * } + * Function: H5T__conv_complex_compat + * + * Purpose: Performs a no-op conversion between a complex number type + * and an equivalent datatype. Complex number types are + * considered equivalent to the following: + * + * - An array datatype consisting of two elements where each + * element is of the same floating-point datatype as the + * complex number type's base floating-point datatype + * + * - A compound datatype consisting of two fields where each + * field is of the same floating-point datatype as the + * complex number type's base floating-point datatype. The + * compound datatype must not have any leading or trailing + * structure padding or any padding between its two fields. + * The fields must also have compatible names, must have + * compatible offsets within the datatype and must be in + * the order of "real" part -> "imaginary" part, such that + * the compound datatype matches the following representation: + * + * H5T_COMPOUND { + * "r(e)(a)(l)"; OFFSET 0 + * "i(m)(a)(g)(i)(n)(a)(r)(y)"; OFFSET SIZEOF("r(e)(a)(l)") + * } + * + * where "r(e)(a)(l)" means the field may be named any + * substring of "real", such as "r", or "re" and + * "i(m)(a)(g)(i)(n)(a)(r)(y)" means the field may be named + * any substring of "imaginary", such as "im" or "imag". * * Return: Non-negative on success/Negative on failure * *------------------------------------------------------------------------- */ herr_t -H5T__conv_complex_struct(const H5T_t *src, const H5T_t *dst, H5T_cdata_t *cdata, - const H5T_conv_ctx_t *conv_ctx, size_t nelmts, size_t buf_stride, size_t bkg_stride, - void *_buf, void *bkg) +H5T__conv_complex_compat(const H5T_t *src, const H5T_t *dst, H5T_cdata_t *cdata, + const H5T_conv_ctx_t H5_ATTR_UNUSED *conv_ctx, size_t H5_ATTR_UNUSED nelmts, + size_t H5_ATTR_UNUSED buf_stride, size_t H5_ATTR_UNUSED bkg_stride, + void H5_ATTR_UNUSED *_buf, void H5_ATTR_UNUSED *bkg) { - (void)src; - (void)dst; - (void)cdata; - (void)conv_ctx; - (void)nelmts; - (void)buf_stride; - (void)bkg_stride; - (void)_buf; - (void)bkg; - return FAIL; /* TODO */ -} + H5T_t *compound_copy = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE + + switch (cdata->command) { + case H5T_CONV_INIT: { + const H5T_t *complex_type; + const H5T_t *other_type; + + if (src->shared->type == H5T_COMPLEX) { + if (dst->shared->type != H5T_ARRAY && dst->shared->type != H5T_COMPOUND) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "unsupported destination datatype for conversion"); + complex_type = src; + other_type = dst; + } + else { + if (dst->shared->type != H5T_COMPLEX) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "unsupported destination datatype for conversion"); + complex_type = dst; + other_type = src; + } + + if (!complex_type->shared->u.cplx.homogeneous) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "unsupported form of complex number datatype for conversion"); + if (complex_type->shared->u.cplx.form != H5T_COMPLEX_RECTANGULAR) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "unsupported form of complex number datatype for conversion"); + + if (other_type->shared->type == H5T_ARRAY) { + if (other_type->shared->u.array.nelem != 2) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "array datatype doesn't have the correct number of elements for conversion"); + if (H5T_cmp(other_type->shared->parent, complex_type->shared->parent, false)) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "array datatype's base datatype doesn't match complex number type's base datatype"); + } + else { + H5T_cmemb_t *fields; + size_t name_len; + + assert(other_type->shared->type == H5T_COMPOUND); + + if (other_type->shared->u.compnd.nmembs != 2) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "compound datatype doesn't have the correct number of fields for conversion"); + if (!other_type->shared->u.compnd.packed) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "compound datatype fields aren't packed together"); + if (other_type->shared->u.compnd.memb_size != complex_type->shared->size) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "compound datatype size doesn't match size of complex number datatype"); + + /* Make sure members are unsorted or sorted according to + * their offsets before checking their names + */ + if (other_type->shared->u.compnd.sorted == H5T_SORT_NONE || + other_type->shared->u.compnd.sorted == H5T_SORT_VALUE) + fields = other_type->shared->u.compnd.memb; + else { + /* Make a copy so the sort order of the original type isn't disturbed */ + if (NULL == (compound_copy = H5T_copy(other_type, H5T_COPY_TRANSIENT))) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCOPY, FAIL, "can't copy datatype"); + + H5T__sort_value(compound_copy, NULL); + fields = compound_copy->shared->u.compnd.memb; + } + + /* Check "real" part of compound datatype */ + if (fields[0].offset != 0) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "compound datatype's 'real' field is not at offset 0"); + if (fields[0].size != complex_type->shared->parent->shared->size) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "compound datatype's 'real' field is not the same size as the complex number datatype's base datatype"); + + /* Match up to 5 characters (including the NUL terminator) from the + * field name to a substring of "real". + */ + name_len = strlen(fields[0].name); + if (strncmp(fields[0].name, "real", (name_len < 5) ? name_len : 5)) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "compound datatype's 'real' field name ('%s') didn't match an expected name for conversion", + fields[0].name); + + if (H5T_cmp(fields[0].type, complex_type->shared->parent, false)) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "compound datatype's 'real' field is not the same datatype as the complex number datatype's base datatype"); + + /* Check "imaginary" part of compound datatype */ + if (fields[1].offset != fields[0].size) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, "compound datatype's 'imaginary' field is not at offset 'sizeof(real_field)'"); + if (fields[1].size != complex_type->shared->parent->shared->size) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "compound datatype's 'imaginary' field is not the same size as the complex number datatype's base datatype"); + + /* Match up to 10 characters (including the NUL terminator) from the + * field name to a substring of "imaginary". + */ + name_len = strlen(fields[1].name); + if (strncmp(fields[1].name, "imaginary", (name_len < 10) ? name_len : 10)) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "compound datatype's 'imaginary' field name ('%s') didn't match an expected name for conversion", + fields[1].name); + + if (H5T_cmp(fields[1].type, complex_type->shared->parent, false)) + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, + "compound datatype's 'imaginary' field is not the same datatype as the complex number datatype's base datatype"); + } + + cdata->need_bkg = H5T_BKG_NO; + + break; + } + + case H5T_CONV_FREE: + break; + + case H5T_CONV_CONV: + /* no-op as the types should be equivalent */ + break; + + default: + HGOTO_ERROR(H5E_DATATYPE, H5E_UNSUPPORTED, FAIL, "unknown conversion command"); + } + +done: + if (compound_copy && H5T_close(compound_copy) < 0) + HDONE_ERROR(H5E_DATATYPE, H5E_CANTCLOSEOBJ, FAIL, "can't close datatype"); + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5T__conv_complex_compat() */ #ifdef H5_HAVE_COMPLEX_NUMBERS /*------------------------------------------------------------------------- diff --git a/src/H5Tconv_complex.h b/src/H5Tconv_complex.h index 878ad7cd04d..604f04576cc 100644 --- a/src/H5Tconv_complex.h +++ b/src/H5Tconv_complex.h @@ -41,7 +41,7 @@ H5_DLL herr_t H5T__conv_complex_i(const H5T_t *src_p, const H5T_t *dst_p, H5T_cd H5_DLL herr_t H5T__conv_complex_f(const H5T_t *src_p, const H5T_t *dst_p, H5T_cdata_t *cdata, const H5T_conv_ctx_t *conv_ctx, size_t nelmts, size_t buf_stride, size_t bkg_stride, void *buf, void *bkg); -H5_DLL herr_t H5T__conv_complex_struct(const H5T_t *src, const H5T_t *dst, H5T_cdata_t *cdata, +H5_DLL herr_t H5T__conv_complex_compat(const H5T_t *src, const H5T_t *dst, H5T_cdata_t *cdata, const H5T_conv_ctx_t *conv_ctx, size_t nelmts, size_t buf_stride, size_t bkg_stride, void *_buf, void *bkg); diff --git a/test/dtypes.c b/test/dtypes.c index 7dfc9732441..82de44041af 100644 --- a/test/dtypes.c +++ b/test/dtypes.c @@ -42,6 +42,7 @@ #define H5T_FRIEND /*suppress error about including H5Tpkg */ #include "H5Tpkg.h" #endif +#include "H5Tconv_complex.h" /* Use in version bound test */ #define H5F_FRIEND /*suppress error about including H5Fpkg */ @@ -75,7 +76,8 @@ static const char *FILENAME[] = {"dtypes0", "dtypes1", "dtypes2", "dtypes3", "dtypes4", "dtypes5", "dtypes6", "dtypes7", "dtypes8", "dtypes9", - "dtypes10", "dtypes11", "dtypes12", "dtypes13", NULL}; + "dtypes10", "dtypes11", "dtypes12", "dtypes13", "dtypes14", + "dtypes15", NULL}; #define TESTFILE "bad_compound.h5" @@ -7337,25 +7339,28 @@ test_complex_type(void) } while (0) /*------------------------------------------------------------------------- - * Function: test_complex_type_conv + * Function: test_complex_type_conv_funcs * - * Purpose: Tests complex number datatype conversions. + * Purpose: Checks information about complex number datatype + * conversions. * * Return: Success: 0 * Failure: number of errors *------------------------------------------------------------------------- */ static int -test_complex_type_conv(void) +test_complex_type_conv_funcs(void) { const H5T_path_t *conv_path; H5T_order_t native_order; + const H5T_t *native_float = NULL; + const H5T_t *native_float_complex = NULL; + const H5T_t *native_double_complex = NULL; + const H5T_t *native_ldouble_complex = NULL; + const H5T_t *complex_le = NULL; + const H5T_t *complex_be = NULL; + H5T_t *vlen_type = NULL; hid_t conv_type = H5I_INVALID_HID; - H5T_t *native_float_complex = NULL; - H5T_t *native_double_complex = NULL; - H5T_t *native_ldouble_complex = NULL; - H5T_t *complex_le = NULL; - H5T_t *complex_be = NULL; TESTING("complex number datatype conversions"); @@ -7472,6 +7477,36 @@ test_complex_type_conv(void) goto error; } + /* Check that no conversion path currently exists between complex type and vlen type */ + if (NULL == (native_float = H5I_object_verify(H5T_NATIVE_FLOAT, H5I_DATATYPE))) { + H5_FAILED(); + printf("Can't get H5T_t structure for datatype\n"); + goto error; + } + if (NULL == (vlen_type = H5T__vlen_create(native_float))) { + H5_FAILED(); + printf("Can't create variable-length datatype\n"); + goto error; + } + if (NULL != H5T_path_find(vlen_type, native_float_complex)) { + H5_FAILED(); + AT(); + printf("Found datatype conversion path when there shouldn't have been one\n"); + goto error; + } + if (NULL != H5T_path_find(native_float_complex, vlen_type)) { + H5_FAILED(); + AT(); + printf("Found datatype conversion path when there shouldn't have been one\n"); + goto error; + } + if (H5T_close(vlen_type) < 0) { + H5_FAILED(); + AT(); + printf("Can't close datatype\n"); + goto error; + } + /* Ensure that complex number datatype conversion functions for native * types are library hard conversions */ @@ -7748,6 +7783,805 @@ test_complex_type_conv(void) } #endif /* H5_HAVE_COMPLEX_NUMBERS */ +/*------------------------------------------------------------------------- + * Function: test_complex_array_compat_conv + * + * Purpose: Checks that conversions between a complex number type and + * an array type consisting of two elements of the matching + * floating-point type are no-op conversions covered by + * H5T__conv_complex_compat. + * + * Return: Success: 0 + * Failure: number of errors + *------------------------------------------------------------------------- + */ +static int +test_complex_array_compat_conv(void) +{ + const H5T_path_t *conv_path; + const H5T_t *complex_type = NULL; + const H5T_t *array_type = NULL; + hsize_t dset_dims[] = { 100 }; + hsize_t array_dims[] = { 2 }; + hsize_t array_fail_dims[] = { 4 }; + herr_t status = SUCCEED; + hid_t file_id = H5I_INVALID_HID; + hid_t dset_id = H5I_INVALID_HID; + hid_t array_type_id = H5I_INVALID_HID; + hid_t complex_type_id = H5I_INVALID_HID; + hid_t space_id = H5I_INVALID_HID; + float fake_data[] = { 1.0F, 2.0F, 3.0F, 4.0F }; + void *wdata = NULL; + void *rdata = NULL; + char filename[1024] = {0}; + + TESTING("complex number datatype <-> array datatype compatibility conversions"); + +#ifdef H5_HAVE_COMPLEX_NUMBERS + complex_type_id = H5T_NATIVE_FLOAT_COMPLEX; +#else + if ((complex_type_id = H5Tcomplex_create(H5T_NATIVE_FLOAT)) < 0) { + H5_FAILED(); + printf("Can't create complex number datatype\n"); + goto error; + } +#endif + + /* Test some combinations that should currently fail */ + + /* Test array datatype that consists of too many elements */ + if ((array_type_id = H5Tarray_create2(H5T_NATIVE_FLOAT, 1, array_fail_dims)) < 0) + TEST_ERROR; + H5E_BEGIN_TRY + { + status = H5Tconvert(array_type_id, complex_type_id, 1, fake_data, NULL, H5P_DEFAULT); + } + H5E_END_TRY + if (status >= 0) + TEST_ERROR; + if (H5Tclose(array_type_id) < 0) + TEST_ERROR; + + /* Test array datatype with a base datatype that doesn't match + * the complex number datatype's base datatype + */ + if ((array_type_id = H5Tarray_create2(H5T_NATIVE_INT, 1, array_dims)) < 0) + TEST_ERROR; + H5E_BEGIN_TRY + { + status = H5Tconvert(array_type_id, complex_type_id, 1, fake_data, NULL, H5P_DEFAULT); + } + H5E_END_TRY + if (status >= 0) + TEST_ERROR; + if (H5Tclose(array_type_id) < 0) + TEST_ERROR; + + /* Check that conversions between float complex type and array type are just + * no-op conversions covered by H5T__conv_complex_compat + */ + if ((array_type_id = H5Tarray_create2(H5T_NATIVE_FLOAT, 1, array_dims)) < 0) { + H5_FAILED(); + printf("Can't create array datatype\n"); + goto error; + } + if (NULL == (complex_type = H5I_object_verify(complex_type_id, H5I_DATATYPE))) { + H5_FAILED(); + printf("Can't get H5T_t structure for complex number datatype\n"); + goto error; + } + if (NULL == (array_type = H5I_object_verify(array_type_id, H5I_DATATYPE))) { + H5_FAILED(); + printf("Can't get H5T_t structure for array datatype\n"); + goto error; + } + + if (NULL == (conv_path = H5T_path_find(complex_type, array_type))) { + H5_FAILED(); + printf("Can't find datatype conversion path\n"); + goto error; + } + if (conv_path->is_hard || conv_path->conv.is_app) { + H5_FAILED(); + printf("Invalid conversion path for complex type -> array type\n"); + goto error; + } + if (conv_path->conv.u.lib_func != H5T__conv_complex_compat) { + H5_FAILED(); + printf("Conversion path for complex type -> array type was not a no-op\n"); + goto error; + } + if (NULL == (conv_path = H5T_path_find(array_type, complex_type))) { + H5_FAILED(); + printf("Can't find datatype conversion path\n"); + goto error; + } + if (conv_path->is_hard || conv_path->conv.is_app) { + H5_FAILED(); + printf("Invalid conversion path for array type -> complex type\n"); + goto error; + } + if (conv_path->conv.u.lib_func != H5T__conv_complex_compat) { + H5_FAILED(); + printf("Conversion path for array type -> complex type was not a no-op\n"); + goto error; + } + + h5_fixname(FILENAME[13], H5P_DEFAULT, filename, sizeof(filename)); + if ((file_id = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) { + H5_FAILED(); + printf("Can't create file\n"); + goto error; + } + + if ((space_id = H5Screate_simple(1, dset_dims, NULL)) < 0) { + H5_FAILED(); + printf("Can't create dataspace\n"); + goto error; + } + + /* Create an dataset with an array datatype of 2 float elements, then write + * to it using a matching float complex type. + */ + if ((dset_id = H5Dcreate2(file_id, "DatasetArray", array_type_id, space_id, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) { + H5_FAILED(); + printf("Can't create dataset\n"); + goto error; + } + +#ifdef H5_HAVE_COMPLEX_NUMBERS + if (NULL == (wdata = malloc(dset_dims[0] * sizeof(H5_float_complex)))) + TEST_ERROR; + for (size_t i = 0; i < dset_dims[0]; i++) { + float real = (float)(rand() / (double)RAND_MAX); + float imag = (float)(rand() / (double)RAND_MAX); + ((H5_float_complex *)wdata)[i] = H5_CMPLXF(real, imag); + } +#else + if (NULL == (wdata = malloc(dset_dims[0] * array_dims[0] * sizeof(float)))) + TEST_ERROR; + for (size_t i = 0; i < dset_dims[0]; i++) { + ((float *)wdata)[i * 2] = (float)(rand() / (double)RAND_MAX); + ((float *)wdata)[(i * 2) + 1] = (float)(rand() / (double)RAND_MAX); + } +#endif + + if (NULL == (rdata = calloc(1, dset_dims[0] * array_dims[0] * sizeof(float)))) + TEST_ERROR; + + if (H5Dwrite(dset_id, complex_type_id, H5S_BLOCK, H5S_ALL, H5P_DEFAULT, wdata) < 0) { + H5_FAILED(); + printf("Can't write data\n"); + goto error; + } + + if (H5Dclose(dset_id) < 0) + TEST_ERROR; + + if ((dset_id = H5Dopen2(file_id, "DatasetArray", H5P_DEFAULT)) < 0) { + H5_FAILED(); + printf("Can't open dataset\n"); + goto error; + } + + if (H5Dread(dset_id, complex_type_id, H5S_BLOCK, H5S_ALL, H5P_DEFAULT, rdata) < 0) { + H5_FAILED(); + printf("Can't read data\n"); + goto error; + } + + for (size_t i = 0; i < dset_dims[0]; i++) { + float real1, real2; + float imag1, imag2; + +#ifdef H5_HAVE_COMPLEX_NUMBERS + H5_float_complex fc = ((H5_float_complex *)wdata)[i]; + real1 = crealf(fc); + imag1 = cimagf(fc); +#else + real1 = ((float *)wdata)[i * 2]; + imag1 = ((float *)wdata)[(i * 2) + 1]; +#endif + + real2 = ((float *)rdata)[i * 2]; + imag2 = ((float *)rdata)[(i * 2) + 1]; + + if (!H5_FLT_ABS_EQUAL(real1, real2)) { + H5_FAILED(); + printf("real part of complex numbers didn't match"); + goto error; + } + if (!H5_FLT_ABS_EQUAL(imag1, imag2)) { + H5_FAILED(); + printf("imaginary part of complex numbers didn't match"); + goto error; + } + } + + free(wdata); + wdata = NULL; + free(rdata); + rdata = NULL; + + if (H5Dclose(dset_id) < 0) + TEST_ERROR; + + /* Create an dataset with a float complex datatype, then write + * to it using a matching array datatype of 2 float elements. + */ + if ((dset_id = H5Dcreate2(file_id, "DatasetFloatComplex", complex_type_id, space_id, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) { + H5_FAILED(); + printf("Can't create dataset\n"); + goto error; + } + + if (NULL == (wdata = malloc(dset_dims[0] * array_dims[0] * sizeof(float)))) + TEST_ERROR; + for (size_t i = 0; i < dset_dims[0]; i++) { + ((float *)wdata)[i * 2] = (float)(rand() / (double)RAND_MAX); + ((float *)wdata)[(i * 2) + 1] = (float)(rand() / (double)RAND_MAX); + } + +#ifdef H5_HAVE_COMPLEX_NUMBERS + if (NULL == (rdata = calloc(1, dset_dims[0] * sizeof(H5_float_complex)))) + TEST_ERROR; +#else + if (NULL == (rdata = calloc(1, dset_dims[0] * array_dims[0] * sizeof(float)))) + TEST_ERROR; +#endif + + if (H5Dwrite(dset_id, array_type_id, H5S_BLOCK, H5S_ALL, H5P_DEFAULT, wdata) < 0) { + H5_FAILED(); + printf("Can't write data\n"); + goto error; + } + + if (H5Dclose(dset_id) < 0) + TEST_ERROR; + + if ((dset_id = H5Dopen2(file_id, "DatasetFloatComplex", H5P_DEFAULT)) < 0) { + H5_FAILED(); + printf("Can't open dataset\n"); + goto error; + } + + if (H5Dread(dset_id, array_type_id, H5S_BLOCK, H5S_ALL, H5P_DEFAULT, rdata) < 0) { + H5_FAILED(); + printf("Can't read data\n"); + goto error; + } + + for (size_t i = 0; i < dset_dims[0]; i++) { + float real1, real2; + float imag1, imag2; + + real1 = ((float *)wdata)[i * 2]; + imag1 = ((float *)wdata)[(i * 2) + 1]; + +#ifdef H5_HAVE_COMPLEX_NUMBERS + H5_float_complex fc = ((H5_float_complex *)rdata)[i]; + real2 = crealf(fc); + imag2 = cimagf(fc); +#else + real2 = ((float *)rdata)[i * 2]; + imag2 = ((float *)rdata)[(i * 2) + 1]; +#endif + + if (!H5_FLT_ABS_EQUAL(real1, real2)) { + H5_FAILED(); + printf("real part of complex numbers didn't match"); + goto error; + } + if (!H5_FLT_ABS_EQUAL(imag1, imag2)) { + H5_FAILED(); + printf("imaginary part of complex numbers didn't match"); + goto error; + } + } + + free(wdata); + wdata = NULL; + free(rdata); + rdata = NULL; + +#ifndef H5_HAVE_COMPLEX_NUMBERS + if (H5Tclose(complex_type_id) < 0) + TEST_ERROR; +#endif + if (H5Tclose(array_type_id) < 0) + TEST_ERROR; + if (H5Sclose(space_id) < 0) + TEST_ERROR; + if (H5Dclose(dset_id) < 0) + TEST_ERROR; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + + if (H5Fdelete(filename, H5P_DEFAULT) < 0) { + H5_FAILED(); + printf("Can't delete file\n"); + goto error; + } + + PASSED(); + + return 0; + +error: + H5E_BEGIN_TRY + { +#ifndef H5_HAVE_COMPLEX_NUMBERS + H5Tclose(complex_type_id); +#endif + H5Tclose(array_type_id); + H5Sclose(space_id); + H5Dclose(dset_id); + H5Fclose(file_id); + H5Fdelete(filename, H5P_DEFAULT); + } + H5E_END_TRY + + return 1; +} + +/*------------------------------------------------------------------------- + * Function: test_complex_compound_compat_conv + * + * Purpose: Checks that conversions between a complex number type and + * a compound type consisting of two fields of the matching + * floating-point type are no-op conversions covered by + * H5T__conv_complex_compat. + * + * Return: Success: 0 + * Failure: number of errors + *------------------------------------------------------------------------- + */ +static int +test_complex_compound_compat_conv(void) +{ + const H5T_path_t *conv_path; + const H5T_t *complex_type = NULL; + const H5T_t *compound_type = NULL; + hsize_t dset_dims[] = { 100 }; +#ifndef H5_HAVE_COMPLEX_NUMBERS + hsize_t float_dims[] = { 2 }; +#endif + herr_t status = SUCCEED; + hid_t file_id = H5I_INVALID_HID; + hid_t dset_id = H5I_INVALID_HID; + hid_t compound_type_id = H5I_INVALID_HID; + hid_t complex_type_id = H5I_INVALID_HID; + hid_t space_id = H5I_INVALID_HID; + void *wdata = NULL; + void *rdata = NULL; + char filename[1024] = {0}; + + typedef struct cc_compat { + float real; + float imag; + } cc_compat; + cc_compat fake_data[4] = {{.real = 1.0, .imag = 2.0}, {.real = 3.0, .imag = 4.0}, + {.real = 5.0, .imag = 6.0}, {.real = 7.0, .imag = 8.0}}; + + TESTING("complex number datatype <-> compound datatype compatibility conversions"); + +#ifdef H5_HAVE_COMPLEX_NUMBERS + complex_type_id = H5T_NATIVE_FLOAT_COMPLEX; +#else + if ((complex_type_id = H5Tcomplex_create(H5T_NATIVE_FLOAT)) < 0) { + H5_FAILED(); + printf("Can't create complex number datatype\n"); + goto error; + } +#endif + + /* Test several combinations that should currently fail */ + + /* Test compound datatype that has fields with datatypes that don't + * match the complex number datatype's base datatype + */ + if ((compound_type_id = H5Tcreate(H5T_COMPOUND, 2 * sizeof(int))) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "real", 0, H5T_NATIVE_INT) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "imaginary", sizeof(int), H5T_NATIVE_INT) < 0) + TEST_ERROR; + H5E_BEGIN_TRY + { + status = H5Tconvert(compound_type_id, complex_type_id, 4, fake_data, NULL, H5P_DEFAULT); + } + H5E_END_TRY + if (status >= 0) + TEST_ERROR; + if (H5Tclose(compound_type_id) < 0) + TEST_ERROR; + + /* Test compound datatype that has trailing padding */ + if ((compound_type_id = H5Tcreate(H5T_COMPOUND, (2 * sizeof(float)) + 10)) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "real", 0, H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "imaginary", sizeof(float), H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + H5E_BEGIN_TRY + { + status = H5Tconvert(compound_type_id, complex_type_id, 4, fake_data, NULL, H5P_DEFAULT); + } + H5E_END_TRY + if (status >= 0) + TEST_ERROR; + if (H5Tclose(compound_type_id) < 0) + TEST_ERROR; + + /* Test compound datatype that has leading padding */ + if ((compound_type_id = H5Tcreate(H5T_COMPOUND, (2 * sizeof(float)) + 10)) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "real", 10, H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "imaginary", 10 + sizeof(float), H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + H5E_BEGIN_TRY + { + status = H5Tconvert(compound_type_id, complex_type_id, 4, fake_data, NULL, H5P_DEFAULT); + } + H5E_END_TRY + if (status >= 0) + TEST_ERROR; + if (H5Tclose(compound_type_id) < 0) + TEST_ERROR; + + /* Test compound datatype that has padding between fields */ + if ((compound_type_id = H5Tcreate(H5T_COMPOUND, (2 * sizeof(float)) + 10)) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "real", 0, H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "imaginary", 10 + sizeof(float), H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + H5E_BEGIN_TRY + { + status = H5Tconvert(compound_type_id, complex_type_id, 4, fake_data, NULL, H5P_DEFAULT); + } + H5E_END_TRY + if (status >= 0) + TEST_ERROR; + if (H5Tclose(compound_type_id) < 0) + TEST_ERROR; + + /* Test compound datatype that has incompatible field names */ + if ((compound_type_id = H5Tcreate(H5T_COMPOUND, 2 * sizeof(float))) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "float_mem1", 0, H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "float_mem2", sizeof(float), H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + H5E_BEGIN_TRY + { + status = H5Tconvert(compound_type_id, complex_type_id, 4, fake_data, NULL, H5P_DEFAULT); + } + H5E_END_TRY + if (status >= 0) + TEST_ERROR; + if (H5Tclose(compound_type_id) < 0) + TEST_ERROR; + + /* Test compound datatype that has field names in wrong order */ + if ((compound_type_id = H5Tcreate(H5T_COMPOUND, 2 * sizeof(float))) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "imaginary", 0, H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "real", sizeof(float), H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + H5E_BEGIN_TRY + { + status = H5Tconvert(compound_type_id, complex_type_id, 4, fake_data, NULL, H5P_DEFAULT); + } + H5E_END_TRY + if (status >= 0) + TEST_ERROR; + if (H5Tclose(compound_type_id) < 0) + TEST_ERROR; + + /* Test compound datatype that has too many fields */ + if ((compound_type_id = H5Tcreate(H5T_COMPOUND, 3 * sizeof(float))) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "real", 0, H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "imaginary", sizeof(float), H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + if (H5Tinsert(compound_type_id, "third_mem", 2 * sizeof(float), H5T_NATIVE_FLOAT) < 0) + TEST_ERROR; + H5E_BEGIN_TRY + { + status = H5Tconvert(compound_type_id, complex_type_id, 1, fake_data, NULL, H5P_DEFAULT); + } + H5E_END_TRY + if (status >= 0) + TEST_ERROR; + if (H5Tclose(compound_type_id) < 0) + TEST_ERROR; + + + /* Check that conversions between float complex type and compound type are just + * no-op conversions covered by H5T__conv_complex_compat + */ + if ((compound_type_id = H5Tcreate(H5T_COMPOUND, 2 * sizeof(float))) < 0) { + H5_FAILED(); + printf("Can't create compound datatype\n"); + goto error; + } + if (H5Tinsert(compound_type_id, "real", 0, H5T_NATIVE_FLOAT) < 0) { + H5_FAILED(); + printf("Can't insert 'real' field into compound datatype\n"); + goto error; + } + if (H5Tinsert(compound_type_id, "imaginary", sizeof(float), H5T_NATIVE_FLOAT) < 0) { + H5_FAILED(); + printf("Can't insert 'imaginary' field into compound datatype\n"); + goto error; + } + if (NULL == (complex_type = H5I_object_verify(complex_type_id, H5I_DATATYPE))) { + H5_FAILED(); + printf("Can't get H5T_t structure for complex number datatype\n"); + goto error; + } + if (NULL == (compound_type = H5I_object_verify(compound_type_id, H5I_DATATYPE))) { + H5_FAILED(); + printf("Can't get H5T_t structure for compound datatype\n"); + goto error; + } + + if (NULL == (conv_path = H5T_path_find(complex_type, compound_type))) { + H5_FAILED(); + printf("Can't find datatype conversion path\n"); + goto error; + } + if (conv_path->is_hard || conv_path->conv.is_app) { + H5_FAILED(); + printf("Invalid conversion path for complex type -> compound type\n"); + goto error; + } + if (conv_path->conv.u.lib_func != H5T__conv_complex_compat) { + H5_FAILED(); + printf("Conversion path for complex type -> compound type was not a no-op\n"); + goto error; + } + if (NULL == (conv_path = H5T_path_find(compound_type, complex_type))) { + H5_FAILED(); + printf("Can't find datatype conversion path\n"); + goto error; + } + if (conv_path->is_hard || conv_path->conv.is_app) { + H5_FAILED(); + printf("Invalid conversion path for compound type -> complex type\n"); + goto error; + } + if (conv_path->conv.u.lib_func != H5T__conv_complex_compat) { + H5_FAILED(); + printf("Conversion path for compound type -> complex type was not a no-op\n"); + goto error; + } + + h5_fixname(FILENAME[14], H5P_DEFAULT, filename, sizeof(filename)); + if ((file_id = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) { + H5_FAILED(); + printf("Can't create file\n"); + goto error; + } + + if ((space_id = H5Screate_simple(1, dset_dims, NULL)) < 0) { + H5_FAILED(); + printf("Can't create dataspace\n"); + goto error; + } + + /* Create an dataset with a compound datatype of 2 float fields, then write + * to it using a matching float complex type. + */ + if ((dset_id = H5Dcreate2(file_id, "DatasetCompound", compound_type_id, space_id, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) { + H5_FAILED(); + printf("Can't create dataset\n"); + goto error; + } + +#ifdef H5_HAVE_COMPLEX_NUMBERS + if (NULL == (wdata = malloc(dset_dims[0] * sizeof(H5_float_complex)))) + TEST_ERROR; + for (size_t i = 0; i < dset_dims[0]; i++) { + float real = (float)(rand() / (double)RAND_MAX); + float imag = (float)(rand() / (double)RAND_MAX); + ((H5_float_complex *)wdata)[i] = H5_CMPLXF(real, imag); + } +#else + if (NULL == (wdata = malloc(dset_dims[0] * float_dims[0] * sizeof(float)))) + TEST_ERROR; + for (size_t i = 0; i < dset_dims[0]; i++) { + ((float *)wdata)[i * 2] = (float)(rand() / (double)RAND_MAX); + ((float *)wdata)[(i * 2) + 1] = (float)(rand() / (double)RAND_MAX); + } +#endif + + if (NULL == (rdata = calloc(1, dset_dims[0] * sizeof(cc_compat)))) + TEST_ERROR; + + if (H5Dwrite(dset_id, complex_type_id, H5S_BLOCK, H5S_ALL, H5P_DEFAULT, wdata) < 0) { + H5_FAILED(); + printf("Can't write data\n"); + goto error; + } + + if (H5Dclose(dset_id) < 0) + TEST_ERROR; + + if ((dset_id = H5Dopen2(file_id, "DatasetCompound", H5P_DEFAULT)) < 0) { + H5_FAILED(); + printf("Can't open dataset\n"); + goto error; + } + + if (H5Dread(dset_id, complex_type_id, H5S_BLOCK, H5S_ALL, H5P_DEFAULT, rdata) < 0) { + H5_FAILED(); + printf("Can't read data\n"); + goto error; + } + + for (size_t i = 0; i < dset_dims[0]; i++) { + float real1, real2; + float imag1, imag2; + +#ifdef H5_HAVE_COMPLEX_NUMBERS + H5_float_complex fc = ((H5_float_complex *)wdata)[i]; + real1 = crealf(fc); + imag1 = cimagf(fc); +#else + real1 = ((float *)wdata)[i * 2]; + imag1 = ((float *)wdata)[(i * 2) + 1]; +#endif + + real2 = ((cc_compat *)rdata)[i].real; + imag2 = ((cc_compat *)rdata)[i].imag; + + if (!H5_FLT_ABS_EQUAL(real1, real2)) { + H5_FAILED(); + printf("real part of complex numbers didn't match"); + goto error; + } + if (!H5_FLT_ABS_EQUAL(imag1, imag2)) { + H5_FAILED(); + printf("imaginary part of complex numbers didn't match"); + goto error; + } + } + + free(wdata); + wdata = NULL; + free(rdata); + rdata = NULL; + + if (H5Dclose(dset_id) < 0) + TEST_ERROR; + + /* Create an dataset with a float complex datatype, then write + * to it using a matching compound datatype of 2 float fields. + */ + if ((dset_id = H5Dcreate2(file_id, "DatasetFloatComplex", complex_type_id, space_id, + H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) { + H5_FAILED(); + printf("Can't create dataset\n"); + goto error; + } + + if (NULL == (wdata = malloc(dset_dims[0] * sizeof(cc_compat)))) + TEST_ERROR; + for (size_t i = 0; i < dset_dims[0]; i++) { + ((cc_compat *)wdata)[i].real = (float)(rand() / (double)RAND_MAX); + ((cc_compat *)wdata)[i].imag = (float)(rand() / (double)RAND_MAX); + } + +#ifdef H5_HAVE_COMPLEX_NUMBERS + if (NULL == (rdata = calloc(1, dset_dims[0] * sizeof(H5_float_complex)))) + TEST_ERROR; +#else + if (NULL == (rdata = calloc(1, dset_dims[0] * float_dims[0] * sizeof(float)))) + TEST_ERROR; +#endif + + if (H5Dwrite(dset_id, compound_type_id, H5S_BLOCK, H5S_ALL, H5P_DEFAULT, wdata) < 0) { + H5_FAILED(); + printf("Can't write data\n"); + goto error; + } + + if (H5Dclose(dset_id) < 0) + TEST_ERROR; + + if ((dset_id = H5Dopen2(file_id, "DatasetFloatComplex", H5P_DEFAULT)) < 0) { + H5_FAILED(); + printf("Can't open dataset\n"); + goto error; + } + + if (H5Dread(dset_id, compound_type_id, H5S_BLOCK, H5S_ALL, H5P_DEFAULT, rdata) < 0) { + H5_FAILED(); + printf("Can't read data\n"); + goto error; + } + + for (size_t i = 0; i < dset_dims[0]; i++) { + float real1, real2; + float imag1, imag2; + + real1 = ((cc_compat *)wdata)[i].real; + imag1 = ((cc_compat *)wdata)[i].imag; + +#ifdef H5_HAVE_COMPLEX_NUMBERS + H5_float_complex fc = ((H5_float_complex *)rdata)[i]; + real2 = crealf(fc); + imag2 = cimagf(fc); +#else + real2 = ((float *)rdata)[i * 2]; + imag2 = ((float *)rdata)[(i * 2) + 1]; +#endif + + if (!H5_FLT_ABS_EQUAL(real1, real2)) { + H5_FAILED(); + printf("real part of complex numbers didn't match"); + goto error; + } + if (!H5_FLT_ABS_EQUAL(imag1, imag2)) { + H5_FAILED(); + printf("imaginary part of complex numbers didn't match"); + goto error; + } + } + + free(wdata); + wdata = NULL; + free(rdata); + rdata = NULL; + +#ifndef H5_HAVE_COMPLEX_NUMBERS + if (H5Tclose(complex_type_id) < 0) + TEST_ERROR; +#endif + if (H5Tclose(compound_type_id) < 0) + TEST_ERROR; + if (H5Sclose(space_id) < 0) + TEST_ERROR; + if (H5Dclose(dset_id) < 0) + TEST_ERROR; + if (H5Fclose(file_id) < 0) + TEST_ERROR; + + if (H5Fdelete(filename, H5P_DEFAULT) < 0) { + H5_FAILED(); + printf("Can't delete file\n"); + goto error; + } + + PASSED(); + + return 0; + +error: + H5E_BEGIN_TRY + { +#ifndef H5_HAVE_COMPLEX_NUMBERS + H5Tclose(complex_type_id); +#endif + H5Tclose(compound_type_id); + H5Sclose(space_id); + H5Dclose(dset_id); + H5Fclose(file_id); + H5Fdelete(filename, H5P_DEFAULT); + } + H5E_END_TRY + + return 1; +} + /*------------------------------------------------------------------------- * Function: test_array_cmpd_vl * @@ -7780,7 +8614,7 @@ test_array_cmpd_vl(void) TESTING("array of arrays of compounds with a vlen"); /* Create File */ - h5_fixname(FILENAME[13], H5P_DEFAULT, filename, sizeof filename); + h5_fixname(FILENAME[15], H5P_DEFAULT, filename, sizeof filename); if ((file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) { H5_FAILED(); AT(); @@ -11563,8 +12397,10 @@ main(void) nerrors += test__Float16(); nerrors += test_complex_type(); #ifdef H5_HAVE_COMPLEX_NUMBERS - nerrors += test_complex_type_conv(); + nerrors += test_complex_type_conv_funcs(); #endif + nerrors += test_complex_array_compat_conv(); + nerrors += test_complex_compound_compat_conv(); if (!driver_is_parallel) { nerrors += test_utf_ascii_conv();