1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
#ifndef RITSUKO_HDF5_FORBID_LARGE_INTEGERS_HPP
#define RITSUKO_HDF5_FORBID_LARGE_INTEGERS_HPP
#include "H5Cpp.h"
#include <stdexcept>
/**
* @file exceeds_limit.hpp
* @brief Check for larger-than-expected types in HDF5 datasets.
*/
namespace ritsuko {
namespace hdf5 {
/**
* Check if a HDF5 datatype could hold values beyond the range of a limiting integer type.
* This is used by validators to ensure that a dataset can be represented in memory by the limiting type.
*
* @param itype HDF5 integer datatype.
* @param precision Number of bits in the limiting integer type, assuming 2's complement.
* @param is_signed Whether the limiting integer type is signed.
*
* @return Whether the datatype cannot be represented by the limiting integer type.
* `true` is also returned for non-integer datasets.
*/
inline bool exceeds_integer_limit(const H5::IntType& itype, size_t precision, bool is_signed) {
if (itype.getSign() == H5T_SGN_NONE) {
if (is_signed) {
return (itype.getPrecision() >= precision); // equality, as one bit of the limiting type is used for the sign.
} else {
return (itype.getPrecision() > precision);
}
} else {
if (is_signed) {
return (itype.getPrecision() > precision);
} else {
return true;
}
}
}
/**
* Overload of `exceeds_integer_limit()` that accepts a HDF5 dataset handle.
*
* @param handle Handle for a HDF5 dataset.
* @param precision Number of bits in the limiting integer type, assuming 2's complement.
* @param is_signed Whether the limiting integer type is signed.
*
* @return Whether the dataset uses a datatype than cannot be represented by the limiting integer type.
*/
inline bool exceeds_integer_limit(const H5::DataSet& handle, size_t precision, bool is_signed) {
if (handle.getTypeClass() != H5T_INTEGER) {
return true;
}
H5::IntType itype(handle);
return exceeds_integer_limit(itype, precision, is_signed);
}
/**
* Overload of `exceeds_integer_limit()` that accepts a HDF5 attribute handle.
*
* @param handle Handle for a HDF5 attribute.
* @param precision Number of bits in the limiting integer type, assuming 2's complement.
* @param is_signed Whether the limiting integer type is signed.
*
* @return Whether the attribute uses a datatype than cannot be represented by the limiting integer type.
*/
inline bool exceeds_integer_limit(const H5::Attribute& handle, size_t precision, bool is_signed) {
if (handle.getTypeClass() != H5T_INTEGER) {
return true;
}
return exceeds_integer_limit(handle.getIntType(), precision, is_signed);
}
/**
* @cond
*/
inline bool exceeds_float_limit_by_integer(const H5::IntType& itype, size_t precision) {
if (precision >= 64) {
return exceeds_integer_limit(itype, 52, true);
} else if (precision >= 32) {
return exceeds_integer_limit(itype, 24, true);
} else {
return true;
}
}
inline bool exceeds_float_limit_by_float(const H5::FloatType& ftype, size_t precision) {
// Only considering IEEE-compatible types here.
if (precision >= 64) {
return !(
ftype == H5::PredType::IEEE_F64LE ||
ftype == H5::PredType::IEEE_F64BE ||
ftype == H5::PredType::IEEE_F32LE ||
ftype == H5::PredType::IEEE_F32BE
);
} else if (precision >= 32) {
return !(
ftype == H5::PredType::IEEE_F32LE ||
ftype == H5::PredType::IEEE_F32BE
);
} else {
return true;
}
}
/**
* @endcond
*/
/**
* Check if a HDF5 datatype could hold values beyond the range of a limiting (IEEE754-compliant) float type.
* This is used by validators to ensure that a dataset can be represented in memory by the limiting type.
*
* Note that the limiting float type is assumed to be IEEE754-compliant.
* If the HDF5 datatype is not also IEEE754-compliant, it will be considered out-of-range regardless of its precision.
* This is necessary as non-IEEE754 floats could have an arbitrary split of bits between the exponent and significand,
* such that two float datatypes with the same number of bits could represent a different set of numbers.
* (Though this seems unlikely in practice, as all CPU-specific predefined float types in later HDF5 versions are already aliases of the IEEE types.)
*
* @param handle Handle for a HDF5 dataset.
* @param precision Number of bits in the limiting float type.
*
* @return Whether the dataset uses a datatype than cannot be represented by the limiting float type.
* `true` is also returned for non-numeric datasets.
*/
inline bool exceeds_float_limit(const H5::DataSet& handle, size_t precision) {
auto tclass = handle.getTypeClass();
if (tclass == H5T_INTEGER) {
return exceeds_float_limit_by_integer(H5::IntType(handle), precision);
} else if (tclass == H5T_FLOAT) {
return exceeds_float_limit_by_float(H5::FloatType(handle), precision);
} else {
return true;
}
}
/**
* Overload of `exceeds_float_limit()` that accepts a HDF5 attribute handle.
*
* @param handle Handle for a HDF5 attribute.
* @param precision Number of bits in the limiting float type.
*
* @return Whether the attribute uses a datatype than cannot be represented by the limiting integer type.
* `true` is also returned for non-numeric attributes.
*/
inline bool exceeds_float_limit(const H5::Attribute& handle, size_t precision) {
auto tclass = handle.getTypeClass();
if (tclass == H5T_INTEGER) {
return exceeds_float_limit_by_integer(handle.getIntType(), precision);
} else if (tclass == H5T_FLOAT) {
return exceeds_float_limit_by_float(handle.getFloatType(), precision);
} else {
return true;
}
}
}
}
#endif
|