// Copyright (c) 2005-2022 Jay Berkenbilt // // This file is part of qpdf. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Versions of qpdf prior to version 7 were released under the terms // of version 2.0 of the Artistic License. At your option, you may // continue to consider qpdf to be licensed under those terms. Please // see the manual for additional information. #ifndef QINTC_HH #define QINTC_HH #include #include #include #include #include #include #include #include // This namespace provides safe integer conversion that detects // overflows. It uses short, cryptic names for brevity. namespace QIntC // QIntC = qpdf Integer Conversion { // to_u is here for backward-compatibility from before we required // C++-11. template class to_u { public: typedef typename std::make_unsigned::type type; }; // Basic IntConverter class, which converts an integer from the // From class to one of the To class if it can be done safely and // throws a range_error otherwise. This class is specialized for // each permutation of signed/unsigned for the From and To // classes. template < typename From, typename To, bool From_signed = std::numeric_limits::is_signed, bool To_signed = std::numeric_limits::is_signed> class IntConverter { }; template class IntConverter { public: static To convert(From const& i) { // From and To are both unsigned. if (i > std::numeric_limits::max()) { std::ostringstream msg; msg.imbue(std::locale::classic()); msg << "integer out of range converting " << i << " from a " << sizeof(From) << "-byte unsigned type to a " << sizeof(To) << "-byte unsigned type"; throw std::range_error(msg.str()); } return static_cast(i); } }; template class IntConverter { public: static To convert(From const& i) { // From and To are both signed. if ((i < std::numeric_limits::min()) || (i > std::numeric_limits::max())) { std::ostringstream msg; msg.imbue(std::locale::classic()); msg << "integer out of range converting " << i << " from a " << sizeof(From) << "-byte signed type to a " << sizeof(To) << "-byte signed type"; throw std::range_error(msg.str()); } return static_cast(i); } }; template class IntConverter { public: static To convert(From const& i) { // From is signed, and To is unsigned. If i > 0, it's safe to // convert it to the corresponding unsigned type and to // compare with To's max. auto ii = static_cast::type>(i); if ((i < 0) || (ii > std::numeric_limits::max())) { std::ostringstream msg; msg.imbue(std::locale::classic()); msg << "integer out of range converting " << i << " from a " << sizeof(From) << "-byte signed type to a " << sizeof(To) << "-byte unsigned type"; throw std::range_error(msg.str()); } return static_cast(i); } }; template class IntConverter { public: static To convert(From const& i) { // From is unsigned, and to is signed. Convert To's max to the // unsigned version of To and compare i against that. auto maxval = static_cast::type>( std::numeric_limits::max()); if (i > maxval) { std::ostringstream msg; msg.imbue(std::locale::classic()); msg << "integer out of range converting " << i << " from a " << sizeof(From) << "-byte unsigned type to a " << sizeof(To) << "-byte signed type"; throw std::range_error(msg.str()); } return static_cast(i); } }; // Specific converters. The return type of each function must match // the second template parameter to IntConverter. template char to_char(T const& i) { return IntConverter::convert(i); } template unsigned char to_uchar(T const& i) { return IntConverter::convert(i); } template short to_short(T const& i) { return IntConverter::convert(i); } template unsigned short to_ushort(T const& i) { return IntConverter::convert(i); } template int to_int(T const& i) { return IntConverter::convert(i); } template unsigned int to_uint(T const& i) { return IntConverter::convert(i); } template size_t to_size(T const& i) { return IntConverter::convert(i); } template qpdf_offset_t to_offset(T const& i) { return IntConverter::convert(i); } template long to_long(T const& i) { return IntConverter::convert(i); } template unsigned long to_ulong(T const& i) { return IntConverter::convert(i); } template long long to_longlong(T const& i) { return IntConverter::convert(i); } template unsigned long long to_ulonglong(T const& i) { return IntConverter::convert(i); } template void range_check(T const& cur, T const& delta) { if ((delta > 0) != (cur > 0)) { return; } if ((delta > 0) && ((std::numeric_limits::max() - cur) < delta)) { std::ostringstream msg; msg.imbue(std::locale::classic()); msg << "adding " << delta << " to " << cur << " would cause an integer overflow"; throw std::range_error(msg.str()); } else if ( (delta < 0) && ((std::numeric_limits::min() - cur) > delta)) { std::ostringstream msg; msg.imbue(std::locale::classic()); msg << "adding " << delta << " to " << cur << " would cause an integer underflow"; throw std::range_error(msg.str()); } } template void range_check_substract(T const& cur, T const& delta) { if ((delta >= 0) == (cur >= 0)) { return; } if ((delta > 0) && ((std::numeric_limits::min() + delta) > cur)) { std::ostringstream msg; msg.imbue(std::locale::classic()); msg << "subtracting " << delta << " from " << cur << " would cause an integer underflow"; throw std::range_error(msg.str()); } else if ( (delta < 0) && ((std::numeric_limits::max() + delta) < cur)) { std::ostringstream msg; msg.imbue(std::locale::classic()); msg << "subtracting " << delta << " from " << cur << " would cause an integer overflow"; throw std::range_error(msg.str()); } } }; // namespace QIntC #endif // QINTC_HH