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,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&amp; value) const @@ -806,16 +806,36 @@ QPDFObjectHandle::getValueAsBool(bool&amp; 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&amp; value) const @@ -833,16 +853,18 @@ QPDFObjectHandle::getValueAsInt(long long&amp; 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&amp; value) const @@ -858,12 +880,14 @@ QPDFObjectHandle::getValueAsInt(int&amp; 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&amp; value) const @@ -880,16 +904,18 @@ QPDFObjectHandle::getValueAsUInt(unsigned long long&amp; 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