Commit f49c5588ec5d4242a2e96714e3ebe0b7cf041e95
1 parent
e50ee3b6
Refactor: introduce private-API `Integer` class and update integer handling in `QPDFObjectHandle`.
Showing
5 changed files
with
158 additions
and
38 deletions
include/qpdf/ObjectHandle.hh
| @@ -27,7 +27,6 @@ | @@ -27,7 +27,6 @@ | ||
| 27 | #include <qpdf/JSON.hh> | 27 | #include <qpdf/JSON.hh> |
| 28 | #include <qpdf/QPDFExc.hh> | 28 | #include <qpdf/QPDFExc.hh> |
| 29 | #include <qpdf/QPDFObjGen.hh> | 29 | #include <qpdf/QPDFObjGen.hh> |
| 30 | -#include <qpdf/Types.h> | ||
| 31 | 30 | ||
| 32 | #include <cstdint> | 31 | #include <cstdint> |
| 33 | #include <memory> | 32 | #include <memory> |
| @@ -42,6 +41,7 @@ namespace qpdf | @@ -42,6 +41,7 @@ namespace qpdf | ||
| 42 | class Array; | 41 | class Array; |
| 43 | class BaseDictionary; | 42 | class BaseDictionary; |
| 44 | class Dictionary; | 43 | class Dictionary; |
| 44 | + class Integer; | ||
| 45 | class Stream; | 45 | class Stream; |
| 46 | 46 | ||
| 47 | enum typed : std::uint8_t { strict = 0, any_flag = 1, optional = 2, any = 3, error = 4 }; | 47 | enum typed : std::uint8_t { strict = 0, any_flag = 1, optional = 2, any = 3, error = 4 }; |
| @@ -127,6 +127,7 @@ namespace qpdf | @@ -127,6 +127,7 @@ namespace qpdf | ||
| 127 | inline void assign(qpdf_object_type_e required, BaseHandle&& other); | 127 | inline void assign(qpdf_object_type_e required, BaseHandle&& other); |
| 128 | 128 | ||
| 129 | std::string description() const; | 129 | std::string description() const; |
| 130 | + std::invalid_argument invalid_error(std::string const& method) const; | ||
| 130 | std::runtime_error type_error(char const* expected_type) const; | 131 | std::runtime_error type_error(char const* expected_type) const; |
| 131 | QPDFExc type_error(char const* expected_type, std::string const& message) const; | 132 | QPDFExc type_error(char const* expected_type, std::string const& message) const; |
| 132 | char const* type_name() const; | 133 | char const* type_name() const; |
libqpdf/QPDFObjectHandle.cc
| @@ -806,16 +806,36 @@ QPDFObjectHandle::getValueAsBool(bool& value) const | @@ -806,16 +806,36 @@ QPDFObjectHandle::getValueAsBool(bool& value) const | ||
| 806 | return false; | 806 | return false; |
| 807 | } | 807 | } |
| 808 | 808 | ||
| 809 | -// Integer accessors | 809 | +// Integer methods |
| 810 | + | ||
| 811 | +Integer::Integer(long long value) : | ||
| 812 | + BaseHandle(QPDFObject::create<QPDF_Integer>(value)) | ||
| 813 | +{ | ||
| 814 | +} | ||
| 815 | + | ||
| 816 | +QPDFObjectHandle | ||
| 817 | +QPDFObjectHandle::newInteger(long long value) | ||
| 818 | +{ | ||
| 819 | + return {QPDFObject::create<QPDF_Integer>(value)}; | ||
| 820 | +} | ||
| 821 | + | ||
| 822 | +int64_t | ||
| 823 | +Integer::value() const | ||
| 824 | +{ | ||
| 825 | + auto* i = as<QPDF_Integer>(); | ||
| 826 | + if (!i) { | ||
| 827 | + throw invalid_error("Integer"); | ||
| 828 | + } | ||
| 829 | + return i->val; | ||
| 830 | +} | ||
| 810 | 831 | ||
| 811 | long long | 832 | long long |
| 812 | QPDFObjectHandle::getIntValue() const | 833 | QPDFObjectHandle::getIntValue() const |
| 813 | { | 834 | { |
| 814 | - if (auto integer = as<QPDF_Integer>()) { | ||
| 815 | - return integer->val; | 835 | + if (auto const integer = Integer(*this)) { |
| 836 | + return integer; | ||
| 816 | } else { | 837 | } else { |
| 817 | typeWarning("integer", "returning 0"); | 838 | typeWarning("integer", "returning 0"); |
| 818 | - QTC::TC("qpdf", "QPDFObjectHandle integer returning 0"); | ||
| 819 | return 0; | 839 | return 0; |
| 820 | } | 840 | } |
| 821 | } | 841 | } |
| @@ -823,8 +843,8 @@ QPDFObjectHandle::getIntValue() const | @@ -823,8 +843,8 @@ QPDFObjectHandle::getIntValue() const | ||
| 823 | bool | 843 | bool |
| 824 | QPDFObjectHandle::getValueAsInt(long long& value) const | 844 | QPDFObjectHandle::getValueAsInt(long long& value) const |
| 825 | { | 845 | { |
| 826 | - if (auto integer = as<QPDF_Integer>()) { | ||
| 827 | - value = integer->val; | 846 | + if (auto const integer = Integer(*this)) { |
| 847 | + value = integer; | ||
| 828 | return true; | 848 | return true; |
| 829 | } | 849 | } |
| 830 | return false; | 850 | return false; |
| @@ -833,16 +853,18 @@ QPDFObjectHandle::getValueAsInt(long long& value) const | @@ -833,16 +853,18 @@ QPDFObjectHandle::getValueAsInt(long long& value) const | ||
| 833 | int | 853 | int |
| 834 | QPDFObjectHandle::getIntValueAsInt() const | 854 | QPDFObjectHandle::getIntValueAsInt() const |
| 835 | { | 855 | { |
| 836 | - long long v = getIntValue(); | ||
| 837 | - if (v < INT_MIN) { | 856 | + try { |
| 857 | + return Integer(*this); | ||
| 858 | + } catch (std::underflow_error&) { | ||
| 838 | warn("requested value of integer is too small; returning INT_MIN"); | 859 | warn("requested value of integer is too small; returning INT_MIN"); |
| 839 | return INT_MIN; | 860 | return INT_MIN; |
| 840 | - } | ||
| 841 | - if (v > INT_MAX) { | 861 | + } catch (std::overflow_error&) { |
| 842 | warn("requested value of integer is too big; returning INT_MAX"); | 862 | warn("requested value of integer is too big; returning INT_MAX"); |
| 843 | return INT_MAX; | 863 | return INT_MAX; |
| 864 | + } catch (std::invalid_argument&) { | ||
| 865 | + typeWarning("integer", "returning 0"); | ||
| 866 | + return 0; | ||
| 844 | } | 867 | } |
| 845 | - return static_cast<int>(v); | ||
| 846 | } | 868 | } |
| 847 | 869 | ||
| 848 | bool | 870 | bool |
| @@ -858,12 +880,14 @@ QPDFObjectHandle::getValueAsInt(int& value) const | @@ -858,12 +880,14 @@ QPDFObjectHandle::getValueAsInt(int& value) const | ||
| 858 | unsigned long long | 880 | unsigned long long |
| 859 | QPDFObjectHandle::getUIntValue() const | 881 | QPDFObjectHandle::getUIntValue() const |
| 860 | { | 882 | { |
| 861 | - long long v = getIntValue(); | ||
| 862 | - if (v < 0) { | 883 | + try { |
| 884 | + return Integer(*this); | ||
| 885 | + } catch (std::underflow_error&) { | ||
| 863 | warn("unsigned value request for negative number; returning 0"); | 886 | warn("unsigned value request for negative number; returning 0"); |
| 864 | return 0; | 887 | return 0; |
| 865 | - } else { | ||
| 866 | - return static_cast<unsigned long long>(v); | 888 | + } catch (std::invalid_argument&) { |
| 889 | + typeWarning("integer", "returning 0"); | ||
| 890 | + return 0; | ||
| 867 | } | 891 | } |
| 868 | } | 892 | } |
| 869 | 893 | ||
| @@ -880,16 +904,18 @@ QPDFObjectHandle::getValueAsUInt(unsigned long long& value) const | @@ -880,16 +904,18 @@ QPDFObjectHandle::getValueAsUInt(unsigned long long& value) const | ||
| 880 | unsigned int | 904 | unsigned int |
| 881 | QPDFObjectHandle::getUIntValueAsUInt() const | 905 | QPDFObjectHandle::getUIntValueAsUInt() const |
| 882 | { | 906 | { |
| 883 | - long long v = getIntValue(); | ||
| 884 | - if (v < 0) { | 907 | + try { |
| 908 | + return Integer(*this); | ||
| 909 | + } catch (std::underflow_error&) { | ||
| 885 | warn("unsigned integer value request for negative number; returning 0"); | 910 | warn("unsigned integer value request for negative number; returning 0"); |
| 886 | return 0; | 911 | return 0; |
| 887 | - } | ||
| 888 | - if (v > UINT_MAX) { | 912 | + } catch (std::overflow_error&) { |
| 889 | warn("requested value of unsigned integer is too big; returning UINT_MAX"); | 913 | warn("requested value of unsigned integer is too big; returning UINT_MAX"); |
| 890 | return UINT_MAX; | 914 | return UINT_MAX; |
| 915 | + } catch (std::invalid_argument&) { | ||
| 916 | + typeWarning("integer", "returning 0"); | ||
| 917 | + return 0; | ||
| 891 | } | 918 | } |
| 892 | - return static_cast<unsigned int>(v); | ||
| 893 | } | 919 | } |
| 894 | 920 | ||
| 895 | bool | 921 | bool |
| @@ -1329,7 +1355,7 @@ QPDFObjectHandle::rotatePage(int angle, bool relative) | @@ -1329,7 +1355,7 @@ QPDFObjectHandle::rotatePage(int angle, bool relative) | ||
| 1329 | } | 1355 | } |
| 1330 | new_angle = (new_angle + 360) % 360; | 1356 | new_angle = (new_angle + 360) % 360; |
| 1331 | // Make this explicit even with new_angle == 0 since /Rotate can be inherited. | 1357 | // Make this explicit even with new_angle == 0 since /Rotate can be inherited. |
| 1332 | - replaceKey("/Rotate", QPDFObjectHandle::newInteger(new_angle)); | 1358 | + replaceKey("/Rotate", Integer(new_angle)); |
| 1333 | } | 1359 | } |
| 1334 | 1360 | ||
| 1335 | void | 1361 | void |
| @@ -1595,12 +1621,11 @@ QPDFObjectHandle::parseContentStream_data( | @@ -1595,12 +1621,11 @@ QPDFObjectHandle::parseContentStream_data( | ||
| 1595 | QTC::TC("qpdf", "QPDFObjectHandle EOF in inline image"); | 1621 | QTC::TC("qpdf", "QPDFObjectHandle EOF in inline image"); |
| 1596 | warn( | 1622 | warn( |
| 1597 | context, | 1623 | context, |
| 1598 | - QPDFExc( | ||
| 1599 | - qpdf_e_damaged_pdf, | ||
| 1600 | - description, | ||
| 1601 | - "stream data", | ||
| 1602 | - input.tell(), | ||
| 1603 | - "EOF found while reading inline image")); | 1624 | + {qpdf_e_damaged_pdf, |
| 1625 | + description, | ||
| 1626 | + "stream data", | ||
| 1627 | + input.tell(), | ||
| 1628 | + "EOF found while reading inline image"}); | ||
| 1604 | } else { | 1629 | } else { |
| 1605 | QTC::TC("qpdf", "QPDFObjectHandle inline image token"); | 1630 | QTC::TC("qpdf", "QPDFObjectHandle inline image token"); |
| 1606 | if (callbacks) { | 1631 | if (callbacks) { |
| @@ -1658,12 +1683,6 @@ QPDFObjectHandle::newNull() | @@ -1658,12 +1683,6 @@ QPDFObjectHandle::newNull() | ||
| 1658 | } | 1683 | } |
| 1659 | 1684 | ||
| 1660 | QPDFObjectHandle | 1685 | QPDFObjectHandle |
| 1661 | -QPDFObjectHandle::newInteger(long long value) | ||
| 1662 | -{ | ||
| 1663 | - return {QPDFObject::create<QPDF_Integer>(value)}; | ||
| 1664 | -} | ||
| 1665 | - | ||
| 1666 | -QPDFObjectHandle | ||
| 1667 | QPDFObjectHandle::newReal(std::string const& value) | 1686 | QPDFObjectHandle::newReal(std::string const& value) |
| 1668 | { | 1687 | { |
| 1669 | return {QPDFObject::create<QPDF_Real>(value)}; | 1688 | return {QPDFObject::create<QPDF_Real>(value)}; |
| @@ -1932,6 +1951,11 @@ QPDFObjectHandle::assertInitialized() const | @@ -1932,6 +1951,11 @@ QPDFObjectHandle::assertInitialized() const | ||
| 1932 | } | 1951 | } |
| 1933 | } | 1952 | } |
| 1934 | 1953 | ||
| 1954 | +std::invalid_argument | ||
| 1955 | +BaseHandle::invalid_error(std::string const& method) const | ||
| 1956 | +{ | ||
| 1957 | + return std::invalid_argument(method + " operation attempted on invalid object"); | ||
| 1958 | +} | ||
| 1935 | std::runtime_error | 1959 | std::runtime_error |
| 1936 | BaseHandle::type_error(char const* expected_type) const | 1960 | BaseHandle::type_error(char const* expected_type) const |
| 1937 | { | 1961 | { |
| @@ -1976,9 +2000,7 @@ void | @@ -1976,9 +2000,7 @@ void | ||
| 1976 | QPDFObjectHandle::assertType(char const* type_name, bool istype) const | 2000 | QPDFObjectHandle::assertType(char const* type_name, bool istype) const |
| 1977 | { | 2001 | { |
| 1978 | if (!istype) { | 2002 | if (!istype) { |
| 1979 | - throw std::runtime_error( | ||
| 1980 | - std::string("operation for ") + type_name + " attempted on object of type " + | ||
| 1981 | - QPDFObjectHandle(*this).getTypeName()); | 2003 | + throw type_error(type_name); |
| 1982 | } | 2004 | } |
| 1983 | } | 2005 | } |
| 1984 | 2006 |
libqpdf/qpdf/QPDFObjectHandle_private.hh
| @@ -7,6 +7,11 @@ | @@ -7,6 +7,11 @@ | ||
| 7 | #include <qpdf/QPDF_private.hh> | 7 | #include <qpdf/QPDF_private.hh> |
| 8 | #include <qpdf/QUtil.hh> | 8 | #include <qpdf/QUtil.hh> |
| 9 | 9 | ||
| 10 | +#include <concepts> | ||
| 11 | +#include <utility> | ||
| 12 | + | ||
| 13 | +using namespace std::literals; | ||
| 14 | + | ||
| 10 | namespace qpdf | 15 | namespace qpdf |
| 11 | { | 16 | { |
| 12 | class Array final: public BaseHandle | 17 | class Array final: public BaseHandle |
| @@ -285,6 +290,97 @@ namespace qpdf | @@ -285,6 +290,97 @@ namespace qpdf | ||
| 285 | ~Dictionary() = default; | 290 | ~Dictionary() = default; |
| 286 | }; | 291 | }; |
| 287 | 292 | ||
| 293 | + class Integer final: public BaseHandle | ||
| 294 | + { | ||
| 295 | + public: | ||
| 296 | + Integer() = default; | ||
| 297 | + Integer(Integer const&) = default; | ||
| 298 | + Integer(Integer&&) = default; | ||
| 299 | + Integer& operator=(Integer const&) = default; | ||
| 300 | + Integer& operator=(Integer&&) = default; | ||
| 301 | + ~Integer() = default; | ||
| 302 | + | ||
| 303 | + explicit Integer(long long value); | ||
| 304 | + | ||
| 305 | + explicit Integer(std::integral auto value) : | ||
| 306 | + Integer(static_cast<long long>(value)) | ||
| 307 | + { | ||
| 308 | + if constexpr ( | ||
| 309 | + std::numeric_limits<decltype(value)>::max() > | ||
| 310 | + std::numeric_limits<long long>::max()) { | ||
| 311 | + if (value > std::numeric_limits<long long>::max()) { | ||
| 312 | + throw std::overflow_error("overflow constructing Integer"); | ||
| 313 | + } | ||
| 314 | + } | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + Integer(QPDFObjectHandle const& oh) : | ||
| 318 | + BaseHandle(oh.type_code() == ::ot_integer ? oh : QPDFObjectHandle()) | ||
| 319 | + { | ||
| 320 | + } | ||
| 321 | + | ||
| 322 | + Integer(QPDFObjectHandle&& oh) : | ||
| 323 | + BaseHandle(oh.type_code() == ::ot_integer ? std::move(oh) : QPDFObjectHandle()) | ||
| 324 | + { | ||
| 325 | + } | ||
| 326 | + | ||
| 327 | + // Return the integer value. If the object is not a valid integer, throw a | ||
| 328 | + // std::invalid_argument exception. If the object is out of range for the target type, | ||
| 329 | + // throw a std::overflow_error or std::underflow_error exception. | ||
| 330 | + template <std::integral T> | ||
| 331 | + operator T() const | ||
| 332 | + { | ||
| 333 | + auto v = value(); | ||
| 334 | + | ||
| 335 | + if (std::cmp_greater(v, std::numeric_limits<T>::max())) { | ||
| 336 | + throw std::overflow_error("Integer conversion overflow"); | ||
| 337 | + } | ||
| 338 | + if (std::cmp_less(v, std::numeric_limits<T>::min())) { | ||
| 339 | + throw std::underflow_error("Integer conversion underflow"); | ||
| 340 | + } | ||
| 341 | + return static_cast<T>(v); | ||
| 342 | + } | ||
| 343 | + | ||
| 344 | + // Return the integer value. If the object is not a valid integer, throw a | ||
| 345 | + // std::invalid_argument exception. | ||
| 346 | + int64_t value() const; | ||
| 347 | + | ||
| 348 | + // Return true if object value is equal to the 'rhs' value. Return false if the object is | ||
| 349 | + // not a valid Integer. | ||
| 350 | + friend bool | ||
| 351 | + operator==(Integer const& lhs, std::integral auto rhs) | ||
| 352 | + { | ||
| 353 | + return lhs && std::cmp_equal(lhs.value(), rhs); | ||
| 354 | + } | ||
| 355 | + | ||
| 356 | + // Compare the object value to the 'rhs' value. Throw a std::invalid_argument exception if | ||
| 357 | + // the object is not a valid Integer. | ||
| 358 | + friend std::strong_ordering | ||
| 359 | + operator<=>(Integer const& lhs, std::integral auto rhs) | ||
| 360 | + { | ||
| 361 | + if (!lhs) { | ||
| 362 | + throw lhs.invalid_error("Integer"); | ||
| 363 | + } | ||
| 364 | + if (std::cmp_less(lhs.value(), rhs)) { | ||
| 365 | + return std::strong_ordering::less; | ||
| 366 | + } | ||
| 367 | + return std::cmp_greater(lhs.value(), rhs) ? std::strong_ordering::greater | ||
| 368 | + : std::strong_ordering::equal; | ||
| 369 | + } | ||
| 370 | + }; | ||
| 371 | + | ||
| 372 | + bool | ||
| 373 | + operator==(std::integral auto lhs, Integer const& rhs) | ||
| 374 | + { | ||
| 375 | + return rhs == lhs; | ||
| 376 | + } | ||
| 377 | + | ||
| 378 | + std::strong_ordering | ||
| 379 | + operator<=>(std::integral auto lhs, Integer const& rhs) | ||
| 380 | + { | ||
| 381 | + return rhs <=> lhs; | ||
| 382 | + } | ||
| 383 | + | ||
| 288 | class Name final: public BaseHandle | 384 | class Name final: public BaseHandle |
| 289 | { | 385 | { |
| 290 | public: | 386 | public: |
libqpdf/qpdf/QPDFObject_private.hh
| @@ -27,6 +27,7 @@ namespace qpdf | @@ -27,6 +27,7 @@ namespace qpdf | ||
| 27 | class Array; | 27 | class Array; |
| 28 | class BaseDictionary; | 28 | class BaseDictionary; |
| 29 | class Dictionary; | 29 | class Dictionary; |
| 30 | + class Integer; | ||
| 30 | class Stream; | 31 | class Stream; |
| 31 | } // namespace qpdf | 32 | } // namespace qpdf |
| 32 | 33 | ||
| @@ -123,6 +124,7 @@ class QPDF_Integer final | @@ -123,6 +124,7 @@ class QPDF_Integer final | ||
| 123 | { | 124 | { |
| 124 | friend class QPDFObject; | 125 | friend class QPDFObject; |
| 125 | friend class qpdf::BaseHandle; | 126 | friend class qpdf::BaseHandle; |
| 127 | + friend class qpdf::Integer; | ||
| 126 | friend class QPDFObjectHandle; | 128 | friend class QPDFObjectHandle; |
| 127 | 129 | ||
| 128 | QPDF_Integer(long long val) : | 130 | QPDF_Integer(long long val) : |
qpdf/qpdf.testcov
| @@ -277,7 +277,6 @@ QPDFParser bad token in parseRemainder 0 | @@ -277,7 +277,6 @@ QPDFParser bad token in parseRemainder 0 | ||
| 277 | QPDFParser eof in parse 0 | 277 | QPDFParser eof in parse 0 |
| 278 | QPDFParser eof in parseRemainder 0 | 278 | QPDFParser eof in parseRemainder 0 |
| 279 | QPDFObjectHandle boolean returning false 0 | 279 | QPDFObjectHandle boolean returning false 0 |
| 280 | -QPDFObjectHandle integer returning 0 0 | ||
| 281 | QPDFObjectHandle real returning 0.0 0 | 280 | QPDFObjectHandle real returning 0.0 0 |
| 282 | QPDFObjectHandle name returning dummy name 0 | 281 | QPDFObjectHandle name returning dummy name 0 |
| 283 | QPDFObjectHandle string returning empty string 0 | 282 | QPDFObjectHandle string returning empty string 0 |