diff options
author | Jay Berkenbilt <ejb@ql.org> | 2019-06-20 00:53:22 +0200 |
---|---|---|
committer | Jay Berkenbilt <ejb@ql.org> | 2019-06-21 19:17:21 +0200 |
commit | a66828caff16a4ad64b9d69b5db1c5a5e60418cc (patch) | |
tree | 1f4d5ab4b20668a845d9f7d09b6be5152985c4d3 /include/qpdf | |
parent | bdf29ca33e1b9cd73c46a71019e88a964a1dd91f (diff) | |
download | qpdf-a66828caff16a4ad64b9d69b5db1c5a5e60418cc.tar.zst |
New safe type converters in QIntC
Diffstat (limited to 'include/qpdf')
-rw-r--r-- | include/qpdf/QIntC.hh | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/include/qpdf/QIntC.hh b/include/qpdf/QIntC.hh new file mode 100644 index 00000000..dac85065 --- /dev/null +++ b/include/qpdf/QIntC.hh @@ -0,0 +1,257 @@ +// Copyright (c) 2005-2019 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 <qpdf/DLL.h> +#include <qpdf/Types.h> +#include <stdexcept> +#include <iostream> +#include <limits> +#include <sstream> +#include <cassert> + +// This namespace provides safe integer conversion that detects +// overflows. It uses short, cryptic names for brevity. + +namespace QIntC // QIntC = qpdf Integer Conversion +{ + // Create templates to get the unsigned version of integer types. + // With C++11, we could use std::make_unsigned, but qpdf, at least + // for now, supports pre-c++11 compilers. + template <typename T> + class to_u + { + }; + + template <> + class to_u<char> + { + public: + typedef unsigned char type; + }; + + template <> + class to_u<short> + { + public: + typedef unsigned short type; + }; + + template <> + class to_u<int> + { + public: + typedef unsigned int type; + }; + + template <> + class to_u<long> + { + public: + typedef unsigned long type; + }; + + template <> + class to_u<long long> + { + public: + typedef unsigned long long 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<From>::is_signed, + bool To_signed = std::numeric_limits<To>::is_signed> + class IntConverter + { + }; + + template <typename From, typename To> + class IntConverter<From, To, false, false> + { + public: + static To convert(From const& i) + { + // From and To are both unsigned. + if (i > std::numeric_limits<To>::max()) + { + std::ostringstream msg; + 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<To>(i); + } + }; + + template <typename From, typename To> + class IntConverter<From, To, true, true> + { + public: + static To convert(From const& i) + { + // From and To are both signed. + if ((i < std::numeric_limits<To>::min()) || + (i > std::numeric_limits<To>::max())) + { + std::ostringstream msg; + 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<To>(i); + } + }; + + template <typename From, typename To> + class IntConverter<From, To, true, false> + { + 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. + typename to_u<From>::type ii = + static_cast<typename to_u<From>::type>(i); + if ((i < 0) || (ii > std::numeric_limits<To>::max())) + { + std::ostringstream msg; + 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<To>(i); + } + }; + + template <typename From, typename To> + class IntConverter<From, To, false, true> + { + 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. + typename to_u<To>::type maxval = + static_cast<typename to_u<To>::type>( + std::numeric_limits<To>::max()); + if (i > maxval) + { + std::ostringstream msg; + 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<To>(i); + } + }; + + // Specific converers. The return type of each function must match + // the second template prameter to IntConverter. + template <typename T> + char to_char(T const& i) + { + return IntConverter<T, char>::convert(i); + } + + template <typename T> + unsigned char to_uchar(T const& i) + { + return IntConverter<T, unsigned char>::convert(i); + } + + template <typename T> + short to_short(T const& i) + { + return IntConverter<T, short>::convert(i); + } + + template <typename T> + unsigned short to_ushort(T const& i) + { + return IntConverter<T, unsigned short>::convert(i); + } + + template <typename T> + int to_int(T const& i) + { + return IntConverter<T, int>::convert(i); + } + + template <typename T> + unsigned int to_uint(T const& i) + { + return IntConverter<T, unsigned int>::convert(i); + } + + template <typename T> + size_t to_size(T const& i) + { + return IntConverter<T, size_t>::convert(i); + } + + template <typename T> + qpdf_offset_t to_offset(T const& i) + { + return IntConverter<T, qpdf_offset_t>::convert(i); + } + + template <typename T> + long to_long(T const& i) + { + return IntConverter<T, long >::convert(i); + } + + template <typename T> + unsigned long to_ulong(T const& i) + { + return IntConverter<T, unsigned long >::convert(i); + } + + template <typename T> + long long to_longlong(T const& i) + { + return IntConverter<T, long long>::convert(i); + } + + template <typename T> + unsigned long long to_ulonglong(T const& i) + { + return IntConverter<T, unsigned long long>::convert(i); + } +}; + +#endif // QINTC_HH |