Commit f49c5588ec5d4242a2e96714e3ebe0b7cf041e95

Authored by m-holger
1 parent e50ee3b6

Refactor: introduce private-API `Integer` class and update integer handling in `QPDFObjectHandle`.

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&amp; 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&amp; 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&amp; 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&amp; 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
... ...