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 | 27 | #include <qpdf/JSON.hh> |
| 28 | 28 | #include <qpdf/QPDFExc.hh> |
| 29 | 29 | #include <qpdf/QPDFObjGen.hh> |
| 30 | -#include <qpdf/Types.h> | |
| 31 | 30 | |
| 32 | 31 | #include <cstdint> |
| 33 | 32 | #include <memory> |
| ... | ... | @@ -42,6 +41,7 @@ namespace qpdf |
| 42 | 41 | class Array; |
| 43 | 42 | class BaseDictionary; |
| 44 | 43 | class Dictionary; |
| 44 | + class Integer; | |
| 45 | 45 | class Stream; |
| 46 | 46 | |
| 47 | 47 | enum typed : std::uint8_t { strict = 0, any_flag = 1, optional = 2, any = 3, error = 4 }; |
| ... | ... | @@ -127,6 +127,7 @@ namespace qpdf |
| 127 | 127 | inline void assign(qpdf_object_type_e required, BaseHandle&& other); |
| 128 | 128 | |
| 129 | 129 | std::string description() const; |
| 130 | + std::invalid_argument invalid_error(std::string const& method) const; | |
| 130 | 131 | std::runtime_error type_error(char const* expected_type) const; |
| 131 | 132 | QPDFExc type_error(char const* expected_type, std::string const& message) const; |
| 132 | 133 | char const* type_name() const; | ... | ... |
libqpdf/QPDFObjectHandle.cc
| ... | ... | @@ -806,16 +806,36 @@ QPDFObjectHandle::getValueAsBool(bool& value) const |
| 806 | 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 | 832 | long long |
| 812 | 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 | 837 | } else { |
| 817 | 838 | typeWarning("integer", "returning 0"); |
| 818 | - QTC::TC("qpdf", "QPDFObjectHandle integer returning 0"); | |
| 819 | 839 | return 0; |
| 820 | 840 | } |
| 821 | 841 | } |
| ... | ... | @@ -823,8 +843,8 @@ QPDFObjectHandle::getIntValue() const |
| 823 | 843 | bool |
| 824 | 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 | 848 | return true; |
| 829 | 849 | } |
| 830 | 850 | return false; |
| ... | ... | @@ -833,16 +853,18 @@ QPDFObjectHandle::getValueAsInt(long long& value) const |
| 833 | 853 | int |
| 834 | 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 | 859 | warn("requested value of integer is too small; returning INT_MIN"); |
| 839 | 860 | return INT_MIN; |
| 840 | - } | |
| 841 | - if (v > INT_MAX) { | |
| 861 | + } catch (std::overflow_error&) { | |
| 842 | 862 | warn("requested value of integer is too big; returning INT_MAX"); |
| 843 | 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 | 870 | bool |
| ... | ... | @@ -858,12 +880,14 @@ QPDFObjectHandle::getValueAsInt(int& value) const |
| 858 | 880 | unsigned long long |
| 859 | 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 | 886 | warn("unsigned value request for negative number; returning 0"); |
| 864 | 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 | 904 | unsigned int |
| 881 | 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 | 910 | warn("unsigned integer value request for negative number; returning 0"); |
| 886 | 911 | return 0; |
| 887 | - } | |
| 888 | - if (v > UINT_MAX) { | |
| 912 | + } catch (std::overflow_error&) { | |
| 889 | 913 | warn("requested value of unsigned integer is too big; returning UINT_MAX"); |
| 890 | 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 | 921 | bool |
| ... | ... | @@ -1329,7 +1355,7 @@ QPDFObjectHandle::rotatePage(int angle, bool relative) |
| 1329 | 1355 | } |
| 1330 | 1356 | new_angle = (new_angle + 360) % 360; |
| 1331 | 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 | 1361 | void |
| ... | ... | @@ -1595,12 +1621,11 @@ QPDFObjectHandle::parseContentStream_data( |
| 1595 | 1621 | QTC::TC("qpdf", "QPDFObjectHandle EOF in inline image"); |
| 1596 | 1622 | warn( |
| 1597 | 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 | 1629 | } else { |
| 1605 | 1630 | QTC::TC("qpdf", "QPDFObjectHandle inline image token"); |
| 1606 | 1631 | if (callbacks) { |
| ... | ... | @@ -1658,12 +1683,6 @@ QPDFObjectHandle::newNull() |
| 1658 | 1683 | } |
| 1659 | 1684 | |
| 1660 | 1685 | QPDFObjectHandle |
| 1661 | -QPDFObjectHandle::newInteger(long long value) | |
| 1662 | -{ | |
| 1663 | - return {QPDFObject::create<QPDF_Integer>(value)}; | |
| 1664 | -} | |
| 1665 | - | |
| 1666 | -QPDFObjectHandle | |
| 1667 | 1686 | QPDFObjectHandle::newReal(std::string const& value) |
| 1668 | 1687 | { |
| 1669 | 1688 | return {QPDFObject::create<QPDF_Real>(value)}; |
| ... | ... | @@ -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 | 1959 | std::runtime_error |
| 1936 | 1960 | BaseHandle::type_error(char const* expected_type) const |
| 1937 | 1961 | { |
| ... | ... | @@ -1976,9 +2000,7 @@ void |
| 1976 | 2000 | QPDFObjectHandle::assertType(char const* type_name, bool istype) const |
| 1977 | 2001 | { |
| 1978 | 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 | 7 | #include <qpdf/QPDF_private.hh> |
| 8 | 8 | #include <qpdf/QUtil.hh> |
| 9 | 9 | |
| 10 | +#include <concepts> | |
| 11 | +#include <utility> | |
| 12 | + | |
| 13 | +using namespace std::literals; | |
| 14 | + | |
| 10 | 15 | namespace qpdf |
| 11 | 16 | { |
| 12 | 17 | class Array final: public BaseHandle |
| ... | ... | @@ -285,6 +290,97 @@ namespace qpdf |
| 285 | 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 | 384 | class Name final: public BaseHandle |
| 289 | 385 | { |
| 290 | 386 | public: | ... | ... |
libqpdf/qpdf/QPDFObject_private.hh
| ... | ... | @@ -27,6 +27,7 @@ namespace qpdf |
| 27 | 27 | class Array; |
| 28 | 28 | class BaseDictionary; |
| 29 | 29 | class Dictionary; |
| 30 | + class Integer; | |
| 30 | 31 | class Stream; |
| 31 | 32 | } // namespace qpdf |
| 32 | 33 | |
| ... | ... | @@ -123,6 +124,7 @@ class QPDF_Integer final |
| 123 | 124 | { |
| 124 | 125 | friend class QPDFObject; |
| 125 | 126 | friend class qpdf::BaseHandle; |
| 127 | + friend class qpdf::Integer; | |
| 126 | 128 | friend class QPDFObjectHandle; |
| 127 | 129 | |
| 128 | 130 | QPDF_Integer(long long val) : | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -277,7 +277,6 @@ QPDFParser bad token in parseRemainder 0 |
| 277 | 277 | QPDFParser eof in parse 0 |
| 278 | 278 | QPDFParser eof in parseRemainder 0 |
| 279 | 279 | QPDFObjectHandle boolean returning false 0 |
| 280 | -QPDFObjectHandle integer returning 0 0 | |
| 281 | 280 | QPDFObjectHandle real returning 0.0 0 |
| 282 | 281 | QPDFObjectHandle name returning dummy name 0 |
| 283 | 282 | QPDFObjectHandle string returning empty string 0 | ... | ... |