From 88c7fc9162d2e9660949f366d17f8324860485b1 Mon Sep 17 00:00:00 2001 From: Jordan Henderson Date: Tue, 18 Jun 2024 16:28:26 -0500 Subject: [PATCH] Refactor special floating-point value handling --- src/H5Tbit.c | 2 +- src/H5Tconv_bitfield.c | 4 +- src/H5Tconv_float.c | 225 +++++++++++++++++++---------------------- src/H5Tconv_float.h | 15 +++ src/H5Tconv_integer.c | 9 +- src/H5Tpkg.h | 2 +- 6 files changed, 129 insertions(+), 128 deletions(-) diff --git a/src/H5Tbit.c b/src/H5Tbit.c index c8300b85cde..503f298ae9e 100644 --- a/src/H5Tbit.c +++ b/src/H5Tbit.c @@ -231,7 +231,7 @@ H5T__bit_shift(uint8_t *buf, ssize_t shift_dist, size_t offset, size_t size) *------------------------------------------------------------------------- */ uint64_t -H5T__bit_get_d(uint8_t *buf, size_t offset, size_t size) +H5T__bit_get_d(const uint8_t *buf, size_t offset, size_t size) { uint64_t val = 0; size_t i, hs; diff --git a/src/H5Tconv_bitfield.c b/src/H5Tconv_bitfield.c index 486036ad260..3046a01d9e1 100644 --- a/src/H5Tconv_bitfield.c +++ b/src/H5Tconv_bitfield.c @@ -105,7 +105,9 @@ H5T__conv_b_b(const H5T_t *src, const H5T_t *dst, H5T_cdata_t *cdata, const H5T_ } /* Allocate space for order-reversed source buffer */ - src_rev = (uint8_t *)H5MM_calloc(src->shared->size); + if (conv_ctx->u.conv.cb_struct.func) + if (NULL == (src_rev = H5MM_calloc(src->shared->size))) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "unable to allocate temporary buffer"); /* The conversion loop */ H5_CHECK_OVERFLOW(buf_stride, size_t, ssize_t); diff --git a/src/H5Tconv_float.c b/src/H5Tconv_float.c index eacbe4c7a82..762ec86fb84 100644 --- a/src/H5Tconv_float.c +++ b/src/H5Tconv_float.c @@ -177,23 +177,24 @@ H5T__conv_f_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t dst_delta = (ssize_t)direction * (ssize_t)(buf_stride ? buf_stride : dst_p->shared->size); /* Allocate space for order-reversed source buffer */ - if (NULL == (src_rev = H5MM_calloc(src_p->shared->size))) - HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer"); + if (conv_ctx->u.conv.cb_struct.func) + if (NULL == (src_rev = H5MM_calloc(src_p->shared->size))) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer"); /* The conversion loop */ for (size_t elmtno = 0; elmtno < nelmts; elmtno++) { - H5T_conv_ret_t except_ret = H5T_CONV_UNHANDLED; /* return of conversion exception callback function */ - uint64_t sign; /* source sign bit value */ - ssize_t bitno = 0; /* bit number */ - int64_t expo; /* exponent */ - uint8_t dbuf[TEMP_FLOAT_CONV_BUFFER_SIZE] = {0}; /* temp destination buffer */ - size_t implied; /* destination implied bits */ - size_t mpos; /* offset to useful mant in src */ - size_t msize = 0; /* useful size of mantissa in src */ - size_t mrsh; /* amount to right shift mantissa */ - bool reverse = true; /* if reversed the order of destination */ - bool denormalized = false; /* is either source or destination denormalized? */ - bool carry = false; /* carry after rounding mantissa */ + H5T_conv_float_specval_t specval_type; /* floating-point value type (regular, +/-Inf, +/-0, NaN) */ + H5T_conv_ret_t except_ret = H5T_CONV_UNHANDLED; /* return of conversion exception callback function */ + ssize_t bitno = 0; /* bit number */ + int64_t expo; /* exponent */ + uint8_t dbuf[TEMP_FLOAT_CONV_BUFFER_SIZE] = {0}; /* temp destination buffer */ + size_t implied; /* destination implied bits */ + size_t mpos; /* offset to useful mant in src */ + size_t msize = 0; /* useful size of mantissa in src */ + size_t mrsh; /* amount to right shift mantissa */ + bool reverse = true; /* if reversed the order of destination */ + bool denormalized = false; /* is either source or destination denormalized? */ + bool carry = false; /* carry after rounding mantissa */ /* * If the source and destination buffers overlap then use a @@ -263,70 +264,21 @@ H5T__conv_f_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t "VAX byte ordering is unsupported for complex number type conversions"); } - /* Find the sign bit value of the source. */ - sign = H5T__bit_get_d(s, src_atomic.u.f.sign, (size_t)1); - /* Check for special cases: +0, -0, +Inf, -Inf, NaN */ - if (H5T__bit_find(s, src_atomic.u.f.mpos, src_atomic.u.f.msize, H5T_BIT_LSB, true) < 0) { - if (H5T__bit_find(s, src_atomic.u.f.epos, src_atomic.u.f.esize, H5T_BIT_LSB, true) < 0) { - /* +0 or -0 */ - H5T__bit_copy(d, dst_atomic.u.f.sign, s, src_atomic.u.f.sign, (size_t)1); - H5T__bit_set(d, dst_atomic.u.f.epos, dst_atomic.u.f.esize, false); - H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, false); - goto padding; - } - else if (H5T__bit_find(s, src_atomic.u.f.epos, src_atomic.u.f.esize, H5T_BIT_LSB, false) < 0) { - /* +Inf or -Inf */ - if (conv_ctx->u.conv.cb_struct.func) { /* If user's exception handler is present, use it */ - H5T_conv_except_t except_type; - - /* reverse order first */ - if (H5T_FLOAT == src_p->shared->type) - H5T__reverse_order(src_rev, s, src_p->shared->size, src_atomic.order); - else { /* reverse real and imaginary parts of complex number independently */ - size_t part_size = src_p->shared->size / 2; - H5T__reverse_order(src_rev, s, part_size, src_atomic.order); - H5T__reverse_order(src_rev + part_size, s + part_size, part_size, src_atomic.order); - } - - except_type = sign ? H5T_CONV_EXCEPT_NINF : H5T_CONV_EXCEPT_PINF; - except_ret = (conv_ctx->u.conv.cb_struct.func)(except_type, conv_ctx->u.conv.src_type_id, - conv_ctx->u.conv.dst_type_id, src_rev, d, - conv_ctx->u.conv.cb_struct.user_data); - } - - if (except_ret == H5T_CONV_UNHANDLED) { - H5T__bit_copy(d, dst_atomic.u.f.sign, s, src_atomic.u.f.sign, (size_t)1); - H5T__bit_set(d, dst_atomic.u.f.epos, dst_atomic.u.f.esize, true); - H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, false); - /* If the destination has no implied mantissa bit, we'll need to set - * the 1st bit of mantissa to 1. The Intel-Linux "long double" is - * this case. */ - if (H5T_NORM_NONE == dst_atomic.u.f.norm) - H5T__bit_set(d, dst_atomic.u.f.mpos + dst_atomic.u.f.msize - 1, (size_t)1, true); - } - else if (except_ret == H5T_CONV_HANDLED) { - /* No need to reverse the order of destination because user handles it */ - reverse = false; - goto next; - } - else if (except_ret == H5T_CONV_ABORT) - HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCONVERT, FAIL, "can't handle conversion exception"); - - goto padding; - } + specval_type = H5T__conv_float_find_special(s, &src_atomic); + if (specval_type == H5T_CONV_FLOAT_SPECVAL_POSZERO || specval_type == H5T_CONV_FLOAT_SPECVAL_NEGZERO) { + H5T__bit_copy(d, dst_atomic.u.f.sign, s, src_atomic.u.f.sign, (size_t)1); + H5T__bit_set(d, dst_atomic.u.f.epos, dst_atomic.u.f.esize, false); + H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, false); + goto padding; } - else if (H5T_NORM_NONE == src_atomic.u.f.norm && - H5T__bit_find(s, src_atomic.u.f.mpos, src_atomic.u.f.msize - 1, H5T_BIT_LSB, true) < 0 && - H5T__bit_find(s, src_atomic.u.f.epos, src_atomic.u.f.esize, H5T_BIT_LSB, false) < 0) { - /* This is a special case for the source of no implied mantissa bit. - * If the exponent bits are all 1s and only the 1st bit of mantissa - * is set to 1. It's infinity. The Intel-Linux "long double" is this case. */ - /* +Inf or -Inf */ - if (conv_ctx->u.conv.cb_struct.func) { /* If user's exception handler is present, use it */ - H5T_conv_except_t except_type; + else if (specval_type != H5T_CONV_FLOAT_SPECVAL_REGULAR) { + /* If user's exception handler is present, use it */ + if (conv_ctx->u.conv.cb_struct.func) { + H5T_conv_except_t except_type; /* type of conversion exception that occurred */ - /* reverse order first */ + /* reverse source buffer order first */ + /* TODO: make this transparent for complex vs float types */ if (H5T_FLOAT == src_p->shared->type) H5T__reverse_order(src_rev, s, src_p->shared->size, src_atomic.order); else { /* reverse real and imaginary parts of complex number independently */ @@ -335,8 +287,14 @@ H5T__conv_f_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t H5T__reverse_order(src_rev + part_size, s + part_size, part_size, src_atomic.order); } - except_type = sign ? H5T_CONV_EXCEPT_NINF : H5T_CONV_EXCEPT_PINF; - except_ret = (conv_ctx->u.conv.cb_struct.func)(except_type, conv_ctx->u.conv.src_type_id, + if (specval_type == H5T_CONV_FLOAT_SPECVAL_POSINF) + except_type = H5T_CONV_EXCEPT_PINF; + else if (specval_type == H5T_CONV_FLOAT_SPECVAL_NEGINF) + except_type = H5T_CONV_EXCEPT_NINF; + else + except_type = H5T_CONV_EXCEPT_NAN; + + except_ret = (conv_ctx->u.conv.cb_struct.func)(except_type, conv_ctx->u.conv.src_type_id, conv_ctx->u.conv.dst_type_id, src_rev, d, conv_ctx->u.conv.cb_struct.user_data); } @@ -344,50 +302,18 @@ H5T__conv_f_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t if (except_ret == H5T_CONV_UNHANDLED) { H5T__bit_copy(d, dst_atomic.u.f.sign, s, src_atomic.u.f.sign, (size_t)1); H5T__bit_set(d, dst_atomic.u.f.epos, dst_atomic.u.f.esize, true); - H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, false); - /* If the destination has no implied mantissa bit, we'll need to set - * the 1st bit of mantissa to 1. The Intel-Linux "long double" is - * this case. */ - if (H5T_NORM_NONE == dst_atomic.u.f.norm) - H5T__bit_set(d, dst_atomic.u.f.mpos + dst_atomic.u.f.msize - 1, (size_t)1, true); - } - else if (except_ret == H5T_CONV_HANDLED) { - /* No need to reverse the order of destination because user handles it */ - reverse = false; - goto next; - } - else if (except_ret == H5T_CONV_ABORT) - HGOTO_ERROR(H5E_DATATYPE, H5E_CANTCONVERT, FAIL, "can't handle conversion exception"); - - goto padding; - /* Temporary solution to handle VAX special values. - * Note that even though we don't support VAX anymore, we - * still need to handle legacy VAX files so this code must - * remain in place. - */ - } - else if (H5T__bit_find(s, src_atomic.u.f.epos, src_atomic.u.f.esize, H5T_BIT_LSB, false) < 0) { - /* NaN */ - if (conv_ctx->u.conv.cb_struct.func) { /* If user's exception handler is present, use it */ - /* reverse order first */ - if (H5T_FLOAT == src_p->shared->type) - H5T__reverse_order(src_rev, s, src_p->shared->size, src_atomic.order); - else { /* reverse real and imaginary parts of complex number independently */ - size_t part_size = src_p->shared->size / 2; - H5T__reverse_order(src_rev, s, part_size, src_atomic.order); - H5T__reverse_order(src_rev + part_size, s + part_size, part_size, src_atomic.order); + if (specval_type == H5T_CONV_FLOAT_SPECVAL_NAN) + /* There are many NaN values, so we just set all bits of the significand. */ + H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, true); + else { + /* +/-Inf */ + H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, false); + /* If the destination has no implied mantissa bit, we'll need to set + * the 1st bit of mantissa to 1. The Intel-Linux "long double" is + * this case. */ + if (H5T_NORM_NONE == dst_atomic.u.f.norm) + H5T__bit_set(d, dst_atomic.u.f.mpos + dst_atomic.u.f.msize - 1, (size_t)1, true); } - - except_ret = (conv_ctx->u.conv.cb_struct.func)( - H5T_CONV_EXCEPT_NAN, conv_ctx->u.conv.src_type_id, conv_ctx->u.conv.dst_type_id, src_rev, - d, conv_ctx->u.conv.cb_struct.user_data); - } - - if (except_ret == H5T_CONV_UNHANDLED) { - /* There are many NaN values, so we just set all bits of the significand. */ - H5T__bit_copy(d, dst_atomic.u.f.sign, s, src_atomic.u.f.sign, (size_t)1); - H5T__bit_set(d, dst_atomic.u.f.epos, dst_atomic.u.f.esize, true); - H5T__bit_set(d, dst_atomic.u.f.mpos, dst_atomic.u.f.msize, true); } else if (except_ret == H5T_CONV_HANDLED) { /* No need to reverse the order of destination because user handles it */ @@ -713,6 +639,59 @@ H5T__conv_f_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t FUNC_LEAVE_NOAPI(ret_value) } /* end H5T__conv_f_f_loop() */ +/*------------------------------------------------------------------------- + * Function: H5T__conv_float_find_special + * + * Purpose: Helper function to inspect the bits of a floating-point + * value during data conversions and determine if that value + * is a special value (+/-Inf, +/-0, NaN). + * + * Return: Non-negative on success/Negative on failure + * + *------------------------------------------------------------------------- + */ +H5T_conv_float_specval_t +H5T__conv_float_find_special(const uint8_t *src_buf, const H5T_atomic_t *src_atomic) +{ + uint64_t sign; /* sign bit value */ + H5T_conv_float_specval_t ret_value = H5T_CONV_FLOAT_SPECVAL_REGULAR; + + FUNC_ENTER_PACKAGE_NOERR + + assert(src_buf); + assert(src_atomic); + + /* Find the sign bit value of the source. */ + sign = H5T__bit_get_d(src_buf, src_atomic->u.f.sign, (size_t)1); + + /* Is the mantissa all 0 bits? */ + if (H5T__bit_find(src_buf, src_atomic->u.f.mpos, src_atomic->u.f.msize, H5T_BIT_LSB, true) < 0) { + /* Is the exponent all 0 bits? */ + if (H5T__bit_find(src_buf, src_atomic->u.f.epos, src_atomic->u.f.esize, H5T_BIT_LSB, true) < 0) + /* +0 or -0 */ + ret_value = sign ? H5T_CONV_FLOAT_SPECVAL_NEGZERO : H5T_CONV_FLOAT_SPECVAL_POSZERO; + /* Is the exponent all 1 bits? */ + else if (H5T__bit_find(src_buf, src_atomic->u.f.epos, src_atomic->u.f.esize, H5T_BIT_LSB, false) < 0) + /* +Inf or -Inf */ + ret_value = sign ? H5T_CONV_FLOAT_SPECVAL_NEGINF : H5T_CONV_FLOAT_SPECVAL_POSINF; + } + else { + bool exp_all_ones = (H5T__bit_find(src_buf, src_atomic->u.f.epos, src_atomic->u.f.esize, H5T_BIT_LSB, false) < 0); + + /* For a source value with no implied mantissa bit, if the exponent bits + * are all 1s and only the 1st bit of the mantissa is set to 1, the value + * is infinity. The Intel-Linux "long double" is this case. + */ + if (H5T_NORM_NONE == src_atomic->u.f.norm && exp_all_ones && + H5T__bit_find(src_buf, src_atomic->u.f.mpos, src_atomic->u.f.msize - 1, H5T_BIT_LSB, true) < 0) + ret_value = sign ? H5T_CONV_FLOAT_SPECVAL_NEGINF : H5T_CONV_FLOAT_SPECVAL_POSINF; + else if (exp_all_ones) + ret_value = H5T_CONV_FLOAT_SPECVAL_NAN; + } + + FUNC_LEAVE_NOAPI(ret_value); +} /* end H5T__conv_float_find_special() */ + /*------------------------------------------------------------------------- * Function: H5T__conv_f_i * @@ -868,8 +847,9 @@ H5T__conv_f_i_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer"); /* Allocate space for order-reversed source buffer */ - if (NULL == (src_rev = H5MM_calloc(src_p->shared->size))) - HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer"); + if (conv_ctx->u.conv.cb_struct.func) + if (NULL == (src_rev = H5MM_calloc(src_p->shared->size))) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer"); /* The conversion loop */ for (size_t elmtno = 0; elmtno < nelmts; elmtno++) { @@ -958,6 +938,7 @@ H5T__conv_f_i_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t sign = (hssize_t)H5T__bit_get_d(s, src_atomic.u.f.sign, (size_t)1); /* Check for special cases: +0, -0, +Inf, -Inf, NaN */ + /* TODO: refactor with new function */ if (H5T__bit_find(s, src_atomic.u.f.mpos, src_atomic.u.f.msize, H5T_BIT_LSB, true) < 0) { if (H5T__bit_find(s, src_atomic.u.f.epos, src_atomic.u.f.esize, H5T_BIT_LSB, true) < 0) { /* +0 or -0 */ diff --git a/src/H5Tconv_float.h b/src/H5Tconv_float.h index 13b81c9b96d..a6cdbd25b8e 100644 --- a/src/H5Tconv_float.h +++ b/src/H5Tconv_float.h @@ -22,6 +22,19 @@ #define TEMP_FLOAT_CONV_BUFFER_SIZE 64 +/****************************/ +/* Library Private Typedefs */ +/****************************/ + +typedef enum { + H5T_CONV_FLOAT_SPECVAL_REGULAR, + H5T_CONV_FLOAT_SPECVAL_POSZERO, + H5T_CONV_FLOAT_SPECVAL_NEGZERO, + H5T_CONV_FLOAT_SPECVAL_POSINF, + H5T_CONV_FLOAT_SPECVAL_NEGINF, + H5T_CONV_FLOAT_SPECVAL_NAN, +} H5T_conv_float_specval_t; + /***********************/ /* Function Prototypes */ /***********************/ @@ -32,6 +45,8 @@ H5_DLL herr_t H5T__conv_f_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H H5_DLL herr_t H5T__conv_f_i_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t *conv_ctx, size_t nelmts, size_t buf_stride, void *buf); +H5_DLL H5T_conv_float_specval_t H5T__conv_float_find_special(const uint8_t *src_buf, const H5T_atomic_t *src_atomic); + /****************************************/ /* Soft (emulated) conversion functions */ /****************************************/ diff --git a/src/H5Tconv_integer.c b/src/H5Tconv_integer.c index 7cfe46b8aa0..6b53ab22e1c 100644 --- a/src/H5Tconv_integer.c +++ b/src/H5Tconv_integer.c @@ -118,7 +118,9 @@ H5T__conv_i_i(const H5T_t *src, const H5T_t *dst, H5T_cdata_t *cdata, const H5T_ dst_delta = (ssize_t)direction * (ssize_t)(buf_stride ? buf_stride : dst->shared->size); /* Allocate space for order-reversed source buffer */ - src_rev = (uint8_t *)H5MM_calloc(src->shared->size); + if (conv_ctx->u.conv.cb_struct.func) + if (NULL == (src_rev = H5MM_calloc(src->shared->size))) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "unable to allocate temporary buffer"); /* The conversion loop */ for (elmtno = 0; elmtno < nelmts; elmtno++) { @@ -559,8 +561,9 @@ H5T__conv_i_f_loop(const H5T_t *src_p, const H5T_t *dst_p, const H5T_conv_ctx_t HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer"); /* Allocate space for order-reversed source buffer */ - if (NULL == (src_rev = H5MM_calloc(src_p->shared->size))) - HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer"); + if (conv_ctx->u.conv.cb_struct.func) + if (NULL == (src_rev = H5MM_calloc(src_p->shared->size))) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTALLOC, FAIL, "couldn't allocate temporary buffer"); /* The conversion loop */ for (size_t elmtno = 0; elmtno < nelmts; elmtno++) { diff --git a/src/H5Tpkg.h b/src/H5Tpkg.h index 59e16c7ff5b..12b80ec859b 100644 --- a/src/H5Tpkg.h +++ b/src/H5Tpkg.h @@ -484,7 +484,7 @@ H5_DLL void H5T__bit_copy(uint8_t *dst, size_t dst_offset, const uint8_t *sr size_t size); H5_DLL herr_t H5T__bit_shift(uint8_t *buf, ssize_t shift_dist, size_t offset, size_t size); H5_DLL void H5T__bit_set(uint8_t *buf, size_t offset, size_t size, bool value); -H5_DLL uint64_t H5T__bit_get_d(uint8_t *buf, size_t offset, size_t size); +H5_DLL uint64_t H5T__bit_get_d(const uint8_t *buf, size_t offset, size_t size); H5_DLL void H5T__bit_set_d(uint8_t *buf, size_t offset, size_t size, uint64_t val); H5_DLL ssize_t H5T__bit_find(const uint8_t *buf, size_t offset, size_t size, H5T_sdir_t direction, bool value);