From 39641acf3bae1314224d91df1c6556c69dbe2e3a Mon Sep 17 00:00:00 2001 From: Eugen Betke Date: Tue, 8 Oct 2024 17:09:44 +0000 Subject: [PATCH] ECC-1930: Fix overflow using compile-time code --- src/NumericLimits.h | 70 ++++++++++++++++++++++ src/accessor/grib_accessor_class_signed.cc | 5 +- 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 src/NumericLimits.h diff --git a/src/NumericLimits.h b/src/NumericLimits.h new file mode 100644 index 000000000..b7cc9c7c1 --- /dev/null +++ b/src/NumericLimits.h @@ -0,0 +1,70 @@ +/* + * (C) Copyright 2005- ECMWF. + * + * This software is licensed under the terms of the Apache Licence Version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * + * In applying this licence, ECMWF does not waive the privileges and immunities granted to it by + * virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction. + */ + +#pragma once + +#include + +template +struct NumericLimits +{ + static_assert(std::is_integral::value, "ValueType must be an integral type"); + +private: + using S = std::make_signed_t; + using U = std::make_unsigned_t; + static constexpr int MaxN = 8 * sizeof(ValueType); + + static constexpr std::array max_ = []() { + if constexpr (std::is_signed::value) { + std::array max{}; + for (int i = 1; i < MaxN; ++i) { + auto ones = ~(static_cast(0)); + max[i] = static_cast(ones >> (MaxN - i)); + } + return max; + } + else { + std::array max{}; + for (int i = 1; i < MaxN; ++i) { + auto ones = ~(static_cast(0)); + max[i] = ones >> (MaxN - i - 1); + } + return max; + } + }(); + + static constexpr std::array min_ = []() { + if constexpr (std::is_signed::value) { + std::array min{}; + for (int i = 1; i < MaxN; ++i) { + min[i] = ~max_[i]; + } + return min; + } + else { + return std::array{}; + } + }(); + +public: + static constexpr ValueType min(int nbits) + { + if constexpr (std::is_signed::value) + return min_[nbits - 1]; + else + return 0; + } + + static constexpr ValueType max(int nbits) + { + return max_[nbits - 1]; + } +}; diff --git a/src/accessor/grib_accessor_class_signed.cc b/src/accessor/grib_accessor_class_signed.cc index 4d6004c33..1b88fac12 100644 --- a/src/accessor/grib_accessor_class_signed.cc +++ b/src/accessor/grib_accessor_class_signed.cc @@ -10,6 +10,7 @@ */ #include "grib_accessor_class_signed.h" +#include "NumericLimits.h" grib_accessor_signed_t _grib_accessor_signed{}; grib_accessor* grib_accessor_signed = &_grib_accessor_signed; @@ -118,8 +119,8 @@ int grib_accessor_signed_t::pack_long(const long* val, size_t* len) else { // ECC-1605: Check overflow/underflow const int nbits = nbytes_ * 8; - const long minval = -(1L << (nbits - 1)) + 1; - const long maxval = (1L << (nbits - 1)) - 1; + const long minval = NumericLimits::min(nbits); + const long maxval = NumericLimits::max(nbits); // printf(" key=%s: v=%ld (minval=%ld maxval=%ld)\n", name_ , v, minval, maxval); if (v > maxval || v < minval) { grib_context_log(context_, GRIB_LOG_ERROR,