diff options
Diffstat (limited to 'src/H5Tinit_float.c')
-rw-r--r-- | src/H5Tinit_float.c | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/src/H5Tinit_float.c b/src/H5Tinit_float.c new file mode 100644 index 0000000..bc14e42 --- /dev/null +++ b/src/H5Tinit_float.c @@ -0,0 +1,572 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://www.hdfgroup.org/licenses. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Purpose: Initialize native floating-point datatypes + */ + +/****************/ +/* Module Setup */ +/****************/ + +#include "H5Tmodule.h" /* This source code file is part of the H5T module */ + +/***********/ +/* Headers */ +/***********/ +#include "H5private.h" /* Generic Functions */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5FLprivate.h" /* Free Lists */ +#include "H5Iprivate.h" /* IDs */ +#include "H5Tpkg.h" /* Datatypes */ + +/****************/ +/* Local Macros */ +/****************/ + +/* This could also go in H5private.h, but this is the only place we + * need to turn off the sanitizers and we don't want to encourage + * this. + */ +#if defined(__has_attribute) +#if __has_attribute(no_sanitize) +#define H5_NO_UBSAN __attribute__((no_sanitize("undefined"))) +#else +#define H5_NO_UBSAN +#endif +#else +#define H5_NO_UBSAN +#endif + +/*------------------------------------------------------------------------- + * Function: DETECT_F + * + * Purpose: This macro takes a floating point type like `double' and + * a base name like `natd' and detects byte order, mantissa + * location, exponent location, sign bit location, presence or + * absence of implicit mantissa bit, and exponent bias and + * initializes a detected_t structure with those properties. + *------------------------------------------------------------------------- + */ +#define DETECT_F(TYPE, VAR, INFO) \ + { \ + TYPE _v1, _v2, _v3; \ + unsigned char _buf1[sizeof(TYPE)], _buf3[sizeof(TYPE)]; \ + unsigned char _pad_mask[sizeof(TYPE)]; \ + unsigned char _byte_mask; \ + int _i, _j, _last = (-1); \ + \ + memset(&INFO, 0, sizeof(INFO)); \ + INFO.size = sizeof(TYPE); \ + \ + /* Initialize padding mask */ \ + memset(_pad_mask, 0, sizeof(_pad_mask)); \ + \ + /* Padding bits. Set a variable to 4.0, then flip each bit and see if \ + * the modified variable is equal ("==") to the original. Build a \ + * padding bitmask to indicate which bits in the type are padding (i.e. \ + * have no effect on the value and should be ignored by subsequent \ + * steps). This is necessary because padding bits can change arbitrarily \ + * and interfere with detection of the various properties below unless we \ + * know to ignore them. */ \ + _v1 = (TYPE)4.0L; \ + memcpy(_buf1, (const void *)&_v1, sizeof(TYPE)); \ + for (_i = 0; _i < (int)sizeof(TYPE); _i++) \ + for (_byte_mask = (unsigned char)1; _byte_mask; _byte_mask = (unsigned char)(_byte_mask << 1)) { \ + _buf1[_i] ^= _byte_mask; \ + memcpy((void *)&_v2, (const void *)_buf1, sizeof(TYPE)); \ + H5_GCC_CLANG_DIAG_OFF("float-equal") \ + if (_v1 != _v2) \ + _pad_mask[_i] |= _byte_mask; \ + H5_GCC_CLANG_DIAG_ON("float-equal") \ + _buf1[_i] ^= _byte_mask; \ + } \ + \ + /* Byte Order */ \ + for (_i = 0, _v1 = (TYPE)0.0L, _v2 = (TYPE)1.0L; _i < (int)sizeof(TYPE); _i++) { \ + _v3 = _v1; \ + _v1 += _v2; \ + _v2 /= (TYPE)256.0L; \ + memcpy(_buf1, (const void *)&_v1, sizeof(TYPE)); \ + memcpy(_buf3, (const void *)&_v3, sizeof(TYPE)); \ + _j = H5T__byte_cmp(sizeof(TYPE), _buf3, _buf1, _pad_mask); \ + if (_j >= 0) { \ + INFO.perm[_i] = _j; \ + _last = _i; \ + } \ + } \ + if (H5T__fix_order(sizeof(TYPE), _last, INFO.perm, &INFO.order) < 0) \ + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "failed to detect byte order"); \ + \ + /* Implicit mantissa bit */ \ + _v1 = (TYPE)0.5L; \ + _v2 = (TYPE)1.0L; \ + if (H5T__imp_bit(sizeof(TYPE), INFO.perm, &_v1, &_v2, _pad_mask, &(INFO.imp)) < 0) \ + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "failed to determine implicit bit"); \ + INFO.norm = INFO.imp ? H5T_NORM_IMPLIED : H5T_NORM_NONE; \ + \ + /* Sign bit */ \ + _v1 = (TYPE)1.0L; \ + _v2 = (TYPE)-1.0L; \ + if (H5T__bit_cmp(sizeof(TYPE), INFO.perm, &_v1, &_v2, _pad_mask, &(INFO.sign)) < 0) \ + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "failed to detect byte order"); \ + \ + /* Mantissa */ \ + INFO.mpos = 0; \ + \ + _v1 = (TYPE)1.0L; \ + _v2 = (TYPE)1.5L; \ + if (H5T__bit_cmp(sizeof(TYPE), INFO.perm, &_v1, &_v2, _pad_mask, &(INFO.msize)) < 0) \ + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "failed to detect byte order"); \ + INFO.msize += 1 + (unsigned)(INFO.imp ? 0 : 1) - INFO.mpos; \ + \ + /* Exponent */ \ + INFO.epos = INFO.mpos + INFO.msize; \ + \ + INFO.esize = INFO.sign - INFO.epos; \ + \ + _v1 = (TYPE)1.0L; \ + INFO.ebias = H5T__find_bias(INFO.epos, INFO.esize, INFO.perm, &_v1); \ + H5T__set_precision(&(INFO)); \ + COMP_ALIGNMENT(TYPE, INFO.comp_align); \ + } + +/* Detect alignment for C structure */ +#define COMP_ALIGNMENT(TYPE, COMP_ALIGN) \ + { \ + struct { \ + char c; \ + TYPE x; \ + } s; \ + \ + COMP_ALIGN = (unsigned)((char *)(&(s.x)) - (char *)(&s)); \ + } + +/******************/ +/* Local Typedefs */ +/******************/ + +/* Holds detected information about a native floating-point type */ +typedef struct H5T_fpoint_det_t { + unsigned size; /* Total byte size */ + unsigned prec; /* Meaningful bits */ + unsigned offset; /* Bit offset to meaningful bits */ + int perm[32]; /* For detection of byte order */ + H5T_order_t order; /* byte order */ + unsigned sign; /* Location of sign bit */ + unsigned mpos, msize, imp; /* Information about mantissa */ + H5T_norm_t norm; /* Information about mantissa */ + unsigned epos, esize; /* Information about exponent */ + unsigned long ebias; /* Exponent bias for floating point */ + unsigned comp_align; /* Alignment for structure */ +} H5T_fpoint_det_t; + +/********************/ +/* Package Typedefs */ +/********************/ + +/********************/ +/* Local Prototypes */ +/********************/ + +/********************/ +/* Public Variables */ +/********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*********************/ +/* Package Variables */ +/*********************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +/* Functions used in the DETECT_F() macro */ +static int H5T__byte_cmp(int, const void *, const void *, const unsigned char *); +static herr_t H5T__bit_cmp(unsigned, int *, void *, void *, const unsigned char *, unsigned *); +static herr_t H5T__fix_order(int, int, int *, H5T_order_t *); +static herr_t H5T__imp_bit(unsigned, int *, void *, void *, const unsigned char *, unsigned *); +static unsigned H5T__find_bias(unsigned, unsigned, int *, void *); +static void H5T__set_precision(H5T_fpoint_det_t *); + +/*------------------------------------------------------------------------- + * Function: H5T__byte_cmp + * + * Purpose: Compares two chunks of memory A and B and returns the + * byte index into those arrays of the first byte that + * differs between A and B. Ignores differences where the + * corresponding bit in pad_mask is set to 0. + * + * Return: Success: Index of differing byte. + * Failure: -1 if all bytes are the same. + *------------------------------------------------------------------------- + */ +static int +H5T__byte_cmp(int n, const void *_a, const void *_b, const unsigned char *pad_mask) +{ + const unsigned char *a = (const unsigned char *)_a; + const unsigned char *b = (const unsigned char *)_b; + int ret_value = -1; + + FUNC_ENTER_PACKAGE_NOERR + + for (int i = 0; i < n; i++) + if ((a[i] & pad_mask[i]) != (b[i] & pad_mask[i])) + HGOTO_DONE(i); + +done: + FUNC_LEAVE_NOAPI(ret_value) +} + +/*------------------------------------------------------------------------- + * Function: H5T__bit_cmp + * + * Purpose: Compares two bit vectors and returns the index for the + * first bit that differs between the two vectors. The + * size of the vector is NBYTES. PERM is a mapping from + * actual order to little endian. Ignores differences where + * the corresponding bit in pad_mask is set to 0. + * + * Sets `first` to the index of the first differing bit + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5T__bit_cmp(unsigned nbytes, int *perm, void *_a, void *_b, const unsigned char *pad_mask, unsigned *first) +{ + unsigned char *a = (unsigned char *)_a; + unsigned char *b = (unsigned char *)_b; + unsigned char aa, bb; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE + + *first = 0; + + for (unsigned i = 0; i < nbytes; i++) { + if (perm[i] >= (int)nbytes) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "failure in bit comparison"); + if ((aa = (unsigned char)(a[perm[i]] & pad_mask[perm[i]])) != + (bb = (unsigned char)(b[perm[i]] & pad_mask[perm[i]]))) { + + for (unsigned j = 0; j < 8; j++, aa >>= 1, bb >>= 1) { + if ((aa & 1) != (bb & 1)) { + *first = i * 8 + j; + HGOTO_DONE(SUCCEED); + } + } + } + } + + /* If we got here and didn't set a value, error out */ + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "didn't find a value for `first`"); + +done: + FUNC_LEAVE_NOAPI(ret_value) +} + +/*------------------------------------------------------------------------- + * Function: H5T__fix_order + * + * Purpose: Given an array PERM with elements FIRST through LAST + * initialized with zero origin byte numbers, this function + * creates a permutation vector that maps the actual order + * of a floating point number to little-endian. + * + * This function assumes that the mantissa byte ordering + * implies the total ordering. + * + * Return: SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5T__fix_order(int n, int last, int *perm, H5T_order_t *order) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE + + if (last <= 0) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "failed to detect byte order"); + + /* We have at least three points to consider */ + if (perm[last] < perm[last - 1] && perm[last - 1] < perm[last - 2]) { + /* Little endian */ + *order = H5T_ORDER_LE; + for (int i = 0; i < n; i++) + perm[i] = i; + } + else if (perm[last] > perm[last - 1] && perm[last - 1] > perm[last - 2]) { + /* Big endian */ + *order = H5T_ORDER_BE; + for (int i = 0; i < n; i++) + perm[i] = (n - 1) - i; + } + else { + /* Undetermined endianness - defaults to 'VAX' for historical + * reasons, but there are other mixed-endian systems (like ARM + * in rare cases) + */ + if (0 != n % 2) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "n is not a power of 2"); + + *order = H5T_ORDER_VAX; + for (int i = 0; i < n; i += 2) { + perm[i] = (n - 2) - i; + perm[i + 1] = (n - 1) - i; + } + } + +done: + FUNC_LEAVE_NOAPI(ret_value) +} + +/*------------------------------------------------------------------------- + * Function: H5T__imp_bit + * + * Purpose: Looks for an implicit bit in the mantissa. The value + * of _A should be 1.0 and the value of _B should be 0.5. + * Some floating-point formats discard the most significant + * bit of the mantissa after normalizing since it will always + * be a one (except for 0.0). If this is true for the native + * floating point values stored in _A and _B then the function + * returns non-zero. + * + * This function assumes that the exponent occupies higher + * order bits than the mantissa and that the most significant + * bit of the mantissa is next to the least significant bit + * of the exponent. + * + * + * Return: imp_bit will be set to 1 if the most significant bit + * of the mantissa is discarded (ie, the mantissa has an + * implicit `one' as the most significant bit). Otherwise, + * imp_bit will be set to zero zero. + * + * SUCCEED/FAIL + *------------------------------------------------------------------------- + */ +static herr_t +H5T__imp_bit(unsigned n, int *perm, void *_a, void *_b, const unsigned char *pad_mask, unsigned *imp_bit) +{ + unsigned char *a = (unsigned char *)_a; + unsigned char *b = (unsigned char *)_b; + unsigned changed; + unsigned major; + unsigned minor; + unsigned msmb; /* Most significant mantissa bit */ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE + + /* Look for the least significant bit that has changed between + * A and B. This is the least significant bit of the exponent. + */ + if (H5T__bit_cmp(n, perm, a, b, pad_mask, &changed) < 0) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "couldn't find LSB"); + + /* The bit to the right (less significant) of the changed bit should + * be the most significant bit of the mantissa. If it is non-zero + * then the format does not remove the leading `1' of the mantissa. + */ + msmb = changed - 1; + major = msmb / 8; + minor = msmb % 8; + + *imp_bit = (a[perm[major]] >> minor) & 0x01 ? 0 : 1; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} + +/*------------------------------------------------------------------------- + * Function: find_bias + * + * Purpose: Determines the bias of the exponent. This function should + * be called with _A having a value of `1'. + * + * Return: The exponent bias + *------------------------------------------------------------------------- + */ +H5_ATTR_PURE static unsigned +H5T__find_bias(unsigned epos, unsigned esize, int *perm, void *_a) +{ + unsigned char *a = (unsigned char *)_a; + unsigned char mask; + unsigned b, shift = 0, nbits, bias = 0; + + FUNC_ENTER_PACKAGE_NOERR + + while (esize > 0) { + nbits = MIN(esize, (8 - epos % 8)); + mask = (unsigned char)((1 << nbits) - 1); + b = (unsigned)(a[perm[epos / 8]] >> (epos % 8)) & mask; + bias |= b << shift; + + shift += nbits; + esize -= nbits; + epos += nbits; + } + + FUNC_LEAVE_NOAPI(bias) +} + +/*------------------------------------------------------------------------- + * Function: H5T__set_precision + * + * Purpose: Determine the precision and offset + * + * Return: void + *------------------------------------------------------------------------- + */ +static void +H5T__set_precision(H5T_fpoint_det_t *d) +{ + FUNC_ENTER_PACKAGE_NOERR + + d->offset = MIN3(d->mpos, d->epos, d->sign); + d->prec = d->msize + d->esize + 1; + + FUNC_LEAVE_NOAPI_VOID +} + +/*------------------------------------------------------------------------- + * Function: H5T__init_native_float_types + * + * Purpose: Initialize native floating-point datatypes + * + * Return: Success: non-negative + * Failure: negative + *------------------------------------------------------------------------- + */ +herr_t H5_NO_UBSAN +H5T__init_native_float_types(void) +{ + H5T_fpoint_det_t det; + H5T_t *dt = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE + + /* H5T_NATIVE_FLOAT */ + + /* Get the type's characteristics */ + memset(&det, 0, sizeof(H5T_fpoint_det_t)); + DETECT_F(float, FLOAT, det); + + /* Allocate and fill type structure */ + if (NULL == (dt = H5T__alloc())) + HGOTO_ERROR(H5E_DATATYPE, H5E_NOSPACE, FAIL, "datatype allocation failed"); + dt->shared->state = H5T_STATE_IMMUTABLE; + dt->shared->type = H5T_FLOAT; + dt->shared->size = det.size; + dt->shared->u.atomic.order = det.order; + dt->shared->u.atomic.offset = det.offset; + dt->shared->u.atomic.prec = det.prec; + dt->shared->u.atomic.lsb_pad = H5T_PAD_ZERO; + dt->shared->u.atomic.msb_pad = H5T_PAD_ZERO; + dt->shared->u.atomic.u.f.sign = det.sign; + dt->shared->u.atomic.u.f.epos = det.epos; + dt->shared->u.atomic.u.f.esize = det.esize; + dt->shared->u.atomic.u.f.ebias = det.ebias; + dt->shared->u.atomic.u.f.mpos = det.mpos; + dt->shared->u.atomic.u.f.msize = det.msize; + dt->shared->u.atomic.u.f.norm = det.norm; + dt->shared->u.atomic.u.f.pad = H5T_PAD_ZERO; + + /* Register the type and set global variables */ + if ((H5T_NATIVE_FLOAT_g = H5I_register(H5I_DATATYPE, dt, FALSE)) < 0) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "can't register ID for built-in datatype"); + H5T_NATIVE_FLOAT_ALIGN_g = det.comp_align; + + /* H5T_NATIVE_DOUBLE */ + + /* Get the type's characteristics */ + memset(&det, 0, sizeof(H5T_fpoint_det_t)); + DETECT_F(double, DOUBLE, det); + + /* Allocate and fill type structure */ + if (NULL == (dt = H5T__alloc())) + HGOTO_ERROR(H5E_DATATYPE, H5E_NOSPACE, FAIL, "datatype allocation failed"); + dt->shared->state = H5T_STATE_IMMUTABLE; + dt->shared->type = H5T_FLOAT; + dt->shared->size = det.size; + dt->shared->u.atomic.order = det.order; + dt->shared->u.atomic.offset = det.offset; + dt->shared->u.atomic.prec = det.prec; + dt->shared->u.atomic.lsb_pad = H5T_PAD_ZERO; + dt->shared->u.atomic.msb_pad = H5T_PAD_ZERO; + dt->shared->u.atomic.u.f.sign = det.sign; + dt->shared->u.atomic.u.f.epos = det.epos; + dt->shared->u.atomic.u.f.esize = det.esize; + dt->shared->u.atomic.u.f.ebias = det.ebias; + dt->shared->u.atomic.u.f.mpos = det.mpos; + dt->shared->u.atomic.u.f.msize = det.msize; + dt->shared->u.atomic.u.f.norm = det.norm; + dt->shared->u.atomic.u.f.pad = H5T_PAD_ZERO; + + /* Register the type and set global variables */ + if ((H5T_NATIVE_DOUBLE_g = H5I_register(H5I_DATATYPE, dt, FALSE)) < 0) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "can't register ID for built-in datatype"); + H5T_NATIVE_DOUBLE_ALIGN_g = det.comp_align; + + /* H5T_NATIVE_LDOUBLE */ + + /* Get the type's characteristics */ + memset(&det, 0, sizeof(H5T_fpoint_det_t)); + DETECT_F(long double, LDOUBLE, det); + + /* Allocate and fill type structure */ + if (NULL == (dt = H5T__alloc())) + HGOTO_ERROR(H5E_DATATYPE, H5E_NOSPACE, FAIL, "datatype allocation failed"); + dt->shared->state = H5T_STATE_IMMUTABLE; + dt->shared->type = H5T_FLOAT; + dt->shared->size = det.size; + dt->shared->u.atomic.order = det.order; + dt->shared->u.atomic.offset = det.offset; + dt->shared->u.atomic.prec = det.prec; + dt->shared->u.atomic.lsb_pad = H5T_PAD_ZERO; + dt->shared->u.atomic.msb_pad = H5T_PAD_ZERO; + dt->shared->u.atomic.u.f.sign = det.sign; + dt->shared->u.atomic.u.f.epos = det.epos; + dt->shared->u.atomic.u.f.esize = det.esize; + dt->shared->u.atomic.u.f.ebias = det.ebias; + dt->shared->u.atomic.u.f.mpos = det.mpos; + dt->shared->u.atomic.u.f.msize = det.msize; + dt->shared->u.atomic.u.f.norm = det.norm; + dt->shared->u.atomic.u.f.pad = H5T_PAD_ZERO; + + /* Register the type and set global variables */ + if ((H5T_NATIVE_LDOUBLE_g = H5I_register(H5I_DATATYPE, dt, FALSE)) < 0) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "can't register ID for built-in datatype"); + H5T_NATIVE_LDOUBLE_ALIGN_g = det.comp_align; + + /* Set the platform's alignment (assumes long double's order + * is true for all types) + */ + H5T_native_order_g = det.order; + +done: + if (ret_value < 0) { + if (dt != NULL) { + dt->shared = H5FL_FREE(H5T_shared_t, dt->shared); + dt = H5FL_FREE(H5T_t, dt); + } + } + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5T__init_native_float_types() */ |