Commit f8e39253be9d1806ccdb88d8117b7418f2045da1

Authored by Jay Berkenbilt
Committed by GitHub
2 parents 40e4d1f9 0b53b648

Merge pull request #863 from m-holger/array

Refactor QPDF_Array
include/qpdf/QPDFObjectHandle.hh
@@ -1496,11 +1496,10 @@ class QPDFObjectHandle @@ -1496,11 +1496,10 @@ class QPDFObjectHandle
1496 { 1496 {
1497 friend class QPDF_Dictionary; 1497 friend class QPDF_Dictionary;
1498 friend class QPDF_Stream; 1498 friend class QPDF_Stream;
1499 - friend class SparseOHArray;  
1500 1499
1501 private: 1500 private:
1502 static void 1501 static void
1503 - disconnect(QPDFObjectHandle& o) 1502 + disconnect(QPDFObjectHandle o)
1504 { 1503 {
1505 o.disconnect(); 1504 o.disconnect();
1506 } 1505 }
@@ -1577,6 +1576,11 @@ class QPDFObjectHandle @@ -1577,6 +1576,11 @@ class QPDFObjectHandle
1577 { 1576 {
1578 return obj; 1577 return obj;
1579 } 1578 }
  1579 + std::shared_ptr<QPDFObject>
  1580 + getObj() const
  1581 + {
  1582 + return obj;
  1583 + }
1580 QPDFObject* 1584 QPDFObject*
1581 getObjectPtr() 1585 getObjectPtr()
1582 { 1586 {
libqpdf/CMakeLists.txt
@@ -115,7 +115,6 @@ set(libqpdf_SOURCES @@ -115,7 +115,6 @@ set(libqpdf_SOURCES
115 ResourceFinder.cc 115 ResourceFinder.cc
116 SecureRandomDataProvider.cc 116 SecureRandomDataProvider.cc
117 SF_FlateLzwDecode.cc 117 SF_FlateLzwDecode.cc
118 - SparseOHArray.cc  
119 qpdf-c.cc 118 qpdf-c.cc
120 qpdfjob-c.cc 119 qpdfjob-c.cc
121 qpdflogger-c.cc) 120 qpdflogger-c.cc)
libqpdf/QPDFObjectHandle.cc
@@ -23,7 +23,6 @@ @@ -23,7 +23,6 @@
23 #include <qpdf/QPDF_Stream.hh> 23 #include <qpdf/QPDF_Stream.hh>
24 #include <qpdf/QPDF_String.hh> 24 #include <qpdf/QPDF_String.hh>
25 #include <qpdf/QPDF_Unresolved.hh> 25 #include <qpdf/QPDF_Unresolved.hh>
26 -#include <qpdf/SparseOHArray.hh>  
27 26
28 #include <qpdf/QIntC.hh> 27 #include <qpdf/QIntC.hh>
29 #include <qpdf/QTC.hh> 28 #include <qpdf/QTC.hh>
@@ -789,9 +788,8 @@ QPDFObjectHandle::aitems() @@ -789,9 +788,8 @@ QPDFObjectHandle::aitems()
789 int 788 int
790 QPDFObjectHandle::getArrayNItems() 789 QPDFObjectHandle::getArrayNItems()
791 { 790 {
792 - auto array = asArray();  
793 - if (array) {  
794 - return array->getNItems(); 791 + if (auto array = asArray()) {
  792 + return array->size();
795 } else { 793 } else {
796 typeWarning("array", "treating as empty"); 794 typeWarning("array", "treating as empty");
797 QTC::TC("qpdf", "QPDFObjectHandle array treating as empty"); 795 QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
@@ -802,104 +800,101 @@ QPDFObjectHandle::getArrayNItems() @@ -802,104 +800,101 @@ QPDFObjectHandle::getArrayNItems()
802 QPDFObjectHandle 800 QPDFObjectHandle
803 QPDFObjectHandle::getArrayItem(int n) 801 QPDFObjectHandle::getArrayItem(int n)
804 { 802 {
805 - auto array = asArray();  
806 - if (array && (n < array->getNItems()) && (n >= 0)) {  
807 - return array->getItem(n);  
808 - } else {  
809 - if (array) { 803 + if (auto array = asArray()) {
  804 + if (auto result = array->at(n); result.obj != nullptr) {
  805 + return result;
  806 + } else {
810 objectWarning("returning null for out of bounds array access"); 807 objectWarning("returning null for out of bounds array access");
811 QTC::TC("qpdf", "QPDFObjectHandle array bounds"); 808 QTC::TC("qpdf", "QPDFObjectHandle array bounds");
812 - } else {  
813 - typeWarning("array", "returning null");  
814 - QTC::TC("qpdf", "QPDFObjectHandle array null for non-array");  
815 } 809 }
816 - static auto constexpr msg =  
817 - " -> null returned from invalid array access"sv;  
818 - return QPDF_Null::create(obj, msg, ""); 810 + } else {
  811 + typeWarning("array", "returning null");
  812 + QTC::TC("qpdf", "QPDFObjectHandle array null for non-array");
819 } 813 }
  814 + static auto constexpr msg = " -> null returned from invalid array access"sv;
  815 + return QPDF_Null::create(obj, msg, "");
820 } 816 }
821 817
822 bool 818 bool
823 QPDFObjectHandle::isRectangle() 819 QPDFObjectHandle::isRectangle()
824 { 820 {
825 - auto array = asArray();  
826 - if ((array == nullptr) || (array->getNItems() != 4)) {  
827 - return false;  
828 - }  
829 - for (int i = 0; i < 4; ++i) {  
830 - if (!array->getItem(i).isNumber()) {  
831 - return false; 821 + if (auto array = asArray()) {
  822 + for (int i = 0; i < 4; ++i) {
  823 + if (auto item = array->at(i); !(item.obj && item.isNumber())) {
  824 + return false;
  825 + }
832 } 826 }
  827 + return array->size() == 4;
833 } 828 }
834 - return true; 829 + return false;
835 } 830 }
836 831
837 bool 832 bool
838 QPDFObjectHandle::isMatrix() 833 QPDFObjectHandle::isMatrix()
839 { 834 {
840 - auto array = asArray();  
841 - if ((array == nullptr) || (array->getNItems() != 6)) {  
842 - return false;  
843 - }  
844 - for (int i = 0; i < 6; ++i) {  
845 - if (!array->getItem(i).isNumber()) {  
846 - return false; 835 + if (auto array = asArray()) {
  836 + for (int i = 0; i < 6; ++i) {
  837 + if (auto item = array->at(i); !(item.obj && item.isNumber())) {
  838 + return false;
  839 + }
847 } 840 }
  841 + return array->size() == 6;
848 } 842 }
849 - return true; 843 + return false;
850 } 844 }
851 845
852 QPDFObjectHandle::Rectangle 846 QPDFObjectHandle::Rectangle
853 QPDFObjectHandle::getArrayAsRectangle() 847 QPDFObjectHandle::getArrayAsRectangle()
854 { 848 {
855 - Rectangle result;  
856 - if (isRectangle()) {  
857 - auto array = asArray();  
858 - // Rectangle coordinates are always supposed to be llx, lly,  
859 - // urx, ury, but files have been found in the wild where  
860 - // llx > urx or lly > ury.  
861 - double i0 = array->getItem(0).getNumericValue();  
862 - double i1 = array->getItem(1).getNumericValue();  
863 - double i2 = array->getItem(2).getNumericValue();  
864 - double i3 = array->getItem(3).getNumericValue();  
865 - result = Rectangle(  
866 - std::min(i0, i2),  
867 - std::min(i1, i3),  
868 - std::max(i0, i2),  
869 - std::max(i1, i3)); 849 + if (auto array = asArray()) {
  850 + if (array->size() != 4) {
  851 + return {};
  852 + }
  853 + double items[4];
  854 + for (int i = 0; i < 4; ++i) {
  855 + if (!array->at(i).getValueAsNumber(items[i])) {
  856 + return {};
  857 + }
  858 + }
  859 + return Rectangle(
  860 + std::min(items[0], items[2]),
  861 + std::min(items[1], items[3]),
  862 + std::max(items[0], items[2]),
  863 + std::max(items[1], items[3]));
870 } 864 }
871 - return result; 865 + return {};
872 } 866 }
873 867
874 QPDFObjectHandle::Matrix 868 QPDFObjectHandle::Matrix
875 QPDFObjectHandle::getArrayAsMatrix() 869 QPDFObjectHandle::getArrayAsMatrix()
876 { 870 {
877 - Matrix result;  
878 - if (isMatrix()) {  
879 - auto array = asArray();  
880 - result = Matrix(  
881 - array->getItem(0).getNumericValue(),  
882 - array->getItem(1).getNumericValue(),  
883 - array->getItem(2).getNumericValue(),  
884 - array->getItem(3).getNumericValue(),  
885 - array->getItem(4).getNumericValue(),  
886 - array->getItem(5).getNumericValue()); 871 + if (auto array = asArray()) {
  872 + if (array->size() != 6) {
  873 + return {};
  874 + }
  875 + double items[6];
  876 + for (int i = 0; i < 6; ++i) {
  877 + if (!array->at(i).getValueAsNumber(items[i])) {
  878 + return {};
  879 + }
  880 + }
  881 + return Matrix(
  882 + items[0], items[1], items[2], items[3], items[4], items[5]);
887 } 883 }
888 - return result; 884 + return {};
889 } 885 }
890 886
891 std::vector<QPDFObjectHandle> 887 std::vector<QPDFObjectHandle>
892 QPDFObjectHandle::getArrayAsVector() 888 QPDFObjectHandle::getArrayAsVector()
893 { 889 {
894 - std::vector<QPDFObjectHandle> result;  
895 auto array = asArray(); 890 auto array = asArray();
896 if (array) { 891 if (array) {
897 - array->getAsVector(result); 892 + return array->getAsVector();
898 } else { 893 } else {
899 typeWarning("array", "treating as empty"); 894 typeWarning("array", "treating as empty");
900 QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector"); 895 QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector");
901 } 896 }
902 - return result; 897 + return {};
903 } 898 }
904 899
905 // Array mutators 900 // Array mutators
@@ -907,24 +902,20 @@ QPDFObjectHandle::getArrayAsVector() @@ -907,24 +902,20 @@ QPDFObjectHandle::getArrayAsVector()
907 void 902 void
908 QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) 903 QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
909 { 904 {
910 - auto array = asArray();  
911 - if (array) {  
912 - checkOwnership(item);  
913 - array->setItem(n, item); 905 + if (auto array = asArray()) {
  906 + if (!array->setAt(n, item)) {
  907 + objectWarning("ignoring attempt to set out of bounds array item");
  908 + QTC::TC("qpdf", "QPDFObjectHandle set array bounds");
  909 + }
914 } else { 910 } else {
915 typeWarning("array", "ignoring attempt to set item"); 911 typeWarning("array", "ignoring attempt to set item");
916 QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item"); 912 QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item");
917 } 913 }
918 } 914 }
919 -  
920 void 915 void
921 QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) 916 QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items)
922 { 917 {
923 - auto array = asArray();  
924 - if (array) {  
925 - for (auto const& item: items) {  
926 - checkOwnership(item);  
927 - } 918 + if (auto array = asArray()) {
928 array->setFromVector(items); 919 array->setFromVector(items);
929 } else { 920 } else {
930 typeWarning("array", "ignoring attempt to replace items"); 921 typeWarning("array", "ignoring attempt to replace items");
@@ -935,9 +926,12 @@ QPDFObjectHandle::setArrayFromVector(std::vector&lt;QPDFObjectHandle&gt; const&amp; items) @@ -935,9 +926,12 @@ QPDFObjectHandle::setArrayFromVector(std::vector&lt;QPDFObjectHandle&gt; const&amp; items)
935 void 926 void
936 QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) 927 QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item)
937 { 928 {
938 - auto array = asArray();  
939 - if (array) {  
940 - array->insertItem(at, item); 929 + if (auto array = asArray()) {
  930 + if (!array->insert(at, item)) {
  931 + objectWarning(
  932 + "ignoring attempt to insert out of bounds array item");
  933 + QTC::TC("qpdf", "QPDFObjectHandle insert array bounds");
  934 + }
941 } else { 935 } else {
942 typeWarning("array", "ignoring attempt to insert item"); 936 typeWarning("array", "ignoring attempt to insert item");
943 QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item"); 937 QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item");
@@ -954,10 +948,8 @@ QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const&amp; item) @@ -954,10 +948,8 @@ QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const&amp; item)
954 void 948 void
955 QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) 949 QPDFObjectHandle::appendItem(QPDFObjectHandle const& item)
956 { 950 {
957 - auto array = asArray();  
958 - if (array) {  
959 - checkOwnership(item);  
960 - array->appendItem(item); 951 + if (auto array = asArray()) {
  952 + array->push_back(item);
961 } else { 953 } else {
962 typeWarning("array", "ignoring attempt to append item"); 954 typeWarning("array", "ignoring attempt to append item");
963 QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item"); 955 QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item");
@@ -974,28 +966,23 @@ QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const&amp; item) @@ -974,28 +966,23 @@ QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const&amp; item)
974 void 966 void
975 QPDFObjectHandle::eraseItem(int at) 967 QPDFObjectHandle::eraseItem(int at)
976 { 968 {
977 - auto array = asArray();  
978 - if (array && (at < array->getNItems()) && (at >= 0)) {  
979 - array->eraseItem(at);  
980 - } else {  
981 - if (array) { 969 + if (auto array = asArray()) {
  970 + if (!array->erase(at)) {
982 objectWarning("ignoring attempt to erase out of bounds array item"); 971 objectWarning("ignoring attempt to erase out of bounds array item");
983 QTC::TC("qpdf", "QPDFObjectHandle erase array bounds"); 972 QTC::TC("qpdf", "QPDFObjectHandle erase array bounds");
984 - } else {  
985 - typeWarning("array", "ignoring attempt to erase item");  
986 - QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item");  
987 } 973 }
  974 + } else {
  975 + typeWarning("array", "ignoring attempt to erase item");
  976 + QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item");
988 } 977 }
989 } 978 }
990 979
991 QPDFObjectHandle 980 QPDFObjectHandle
992 QPDFObjectHandle::eraseItemAndGetOld(int at) 981 QPDFObjectHandle::eraseItemAndGetOld(int at)
993 { 982 {
994 - auto result = QPDFObjectHandle::newNull();  
995 auto array = asArray(); 983 auto array = asArray();
996 - if (array && (at < array->getNItems()) && (at >= 0)) {  
997 - result = array->getItem(at);  
998 - } 984 + auto result =
  985 + (array && at < array->size() && at >= 0) ? array->at(at) : newNull();
999 eraseItem(at); 986 eraseItem(at);
1000 return result; 987 return result;
1001 } 988 }
@@ -1515,11 +1502,10 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( @@ -1515,11 +1502,10 @@ QPDFObjectHandle::arrayOrStreamToStreamArray(
1515 { 1502 {
1516 all_description = description; 1503 all_description = description;
1517 std::vector<QPDFObjectHandle> result; 1504 std::vector<QPDFObjectHandle> result;
1518 - auto array = asArray();  
1519 - if (array) {  
1520 - int n_items = array->getNItems(); 1505 + if (auto array = asArray()) {
  1506 + int n_items = array->size();
1521 for (int i = 0; i < n_items; ++i) { 1507 for (int i = 0; i < n_items; ++i) {
1522 - QPDFObjectHandle item = array->getItem(i); 1508 + QPDFObjectHandle item = array->at(i);
1523 if (item.isStream()) { 1509 if (item.isStream()) {
1524 result.push_back(item); 1510 result.push_back(item);
1525 } else { 1511 } else {
@@ -2217,9 +2203,9 @@ QPDFObjectHandle::makeDirect( @@ -2217,9 +2203,9 @@ QPDFObjectHandle::makeDirect(
2217 } else if (isArray()) { 2203 } else if (isArray()) {
2218 std::vector<QPDFObjectHandle> items; 2204 std::vector<QPDFObjectHandle> items;
2219 auto array = asArray(); 2205 auto array = asArray();
2220 - int n = array->getNItems(); 2206 + int n = array->size();
2221 for (int i = 0; i < n; ++i) { 2207 for (int i = 0; i < n; ++i) {
2222 - items.push_back(array->getItem(i)); 2208 + items.push_back(array->at(i));
2223 items.back().makeDirect(visited, stop_at_streams); 2209 items.back().makeDirect(visited, stop_at_streams);
2224 } 2210 }
2225 this->obj = QPDF_Array::create(items); 2211 this->obj = QPDF_Array::create(items);
libqpdf/QPDFParser.cc
@@ -27,16 +27,15 @@ namespace @@ -27,16 +27,15 @@ namespace
27 struct StackFrame 27 struct StackFrame
28 { 28 {
29 StackFrame(std::shared_ptr<InputSource> input) : 29 StackFrame(std::shared_ptr<InputSource> input) :
30 - offset(input->tell()),  
31 - contents_string(""),  
32 - contents_offset(-1) 30 + offset(input->tell())
33 { 31 {
34 } 32 }
35 33
36 std::vector<std::shared_ptr<QPDFObject>> olist; 34 std::vector<std::shared_ptr<QPDFObject>> olist;
37 qpdf_offset_t offset; 35 qpdf_offset_t offset;
38 - std::string contents_string;  
39 - qpdf_offset_t contents_offset; 36 + std::string contents_string{""};
  37 + qpdf_offset_t contents_offset{-1};
  38 + int null_count{0};
40 }; 39 };
41 } // namespace 40 } // namespace
42 41
@@ -50,6 +49,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream) @@ -50,6 +49,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
50 // this, it will cause a logic error to be thrown from 49 // this, it will cause a logic error to be thrown from
51 // QPDF::inParse(). 50 // QPDF::inParse().
52 51
  52 + const static std::shared_ptr<QPDFObject> null_oh = QPDF_Null::create();
53 QPDF::ParseGuard pg(context); 53 QPDF::ParseGuard pg(context);
54 54
55 empty = false; 55 empty = false;
@@ -67,7 +67,6 @@ QPDFParser::parse(bool&amp; empty, bool content_stream) @@ -67,7 +67,6 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
67 int good_count = 0; 67 int good_count = 0;
68 bool b_contents = false; 68 bool b_contents = false;
69 bool is_null = false; 69 bool is_null = false;
70 - auto null_oh = QPDF_Null::create();  
71 70
72 while (!done) { 71 while (!done) {
73 bool bad = false; 72 bool bad = false;
@@ -156,6 +155,8 @@ QPDFParser::parse(bool&amp; empty, bool content_stream) @@ -156,6 +155,8 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
156 155
157 case QPDFTokenizer::tt_null: 156 case QPDFTokenizer::tt_null:
158 is_null = true; 157 is_null = true;
  158 + ++frame.null_count;
  159 +
159 break; 160 break;
160 161
161 case QPDFTokenizer::tt_integer: 162 case QPDFTokenizer::tt_integer:
@@ -301,9 +302,11 @@ QPDFParser::parse(bool&amp; empty, bool content_stream) @@ -301,9 +302,11 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
301 302
302 case st_dictionary: 303 case st_dictionary:
303 case st_array: 304 case st_array:
304 - if (!indirect_ref && !is_null) {  
305 - // No need to set description for direct nulls - they will  
306 - // become implicit. 305 + if (is_null) {
  306 + object = null_oh;
  307 + // No need to set description for direct nulls - they probably
  308 + // will become implicit.
  309 + } else if (!indirect_ref) {
307 setDescription(object, input->getLastOffset()); 310 setDescription(object, input->getLastOffset());
308 } 311 }
309 set_offset = true; 312 set_offset = true;
@@ -326,7 +329,8 @@ QPDFParser::parse(bool&amp; empty, bool content_stream) @@ -326,7 +329,8 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
326 parser_state_e old_state = state_stack.back(); 329 parser_state_e old_state = state_stack.back();
327 state_stack.pop_back(); 330 state_stack.pop_back();
328 if (old_state == st_array) { 331 if (old_state == st_array) {
329 - object = QPDF_Array::create(std::move(olist)); 332 + object = QPDF_Array::create(
  333 + std::move(olist), frame.null_count > 100);
330 setDescription(object, offset - 1); 334 setDescription(object, offset - 1);
331 // The `offset` points to the next of "[". Set the rewind 335 // The `offset` points to the next of "[". Set the rewind
332 // offset to point to the beginning of "[". This has been 336 // offset to point to the beginning of "[". This has been
@@ -381,7 +385,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream) @@ -381,7 +385,7 @@ QPDFParser::parse(bool&amp; empty, bool content_stream)
381 // Calculate value. 385 // Calculate value.
382 std::shared_ptr<QPDFObject> val; 386 std::shared_ptr<QPDFObject> val;
383 if (iter != olist.end()) { 387 if (iter != olist.end()) {
384 - val = *iter ? *iter : QPDF_Null::create(); 388 + val = *iter;
385 ++iter; 389 ++iter;
386 } else { 390 } else {
387 QTC::TC("qpdf", "QPDFParser no val for last key"); 391 QTC::TC("qpdf", "QPDFParser no val for last key");
libqpdf/QPDF_Array.cc
1 #include <qpdf/QPDF_Array.hh> 1 #include <qpdf/QPDF_Array.hh>
2 2
3 -#include <qpdf/QIntC.hh> 3 +#include <qpdf/QPDFObjectHandle.hh>
4 #include <qpdf/QPDFObject_private.hh> 4 #include <qpdf/QPDFObject_private.hh>
5 -#include <qpdf/QUtil.hh>  
6 -#include <stdexcept>  
7 5
8 -QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& v) :  
9 - QPDFValue(::ot_array, "array") 6 +static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull();
  7 +
  8 +inline void
  9 +QPDF_Array::checkOwnership(QPDFObjectHandle const& item) const
10 { 10 {
11 - setFromVector(v); 11 + if (auto obj = item.getObjectPtr()) {
  12 + if (qpdf) {
  13 + if (auto item_qpdf = obj->getQPDF()) {
  14 + if (qpdf != item_qpdf) {
  15 + throw std::logic_error(
  16 + "Attempting to add an object from a different QPDF. "
  17 + "Use QPDF::copyForeignObject to add objects from "
  18 + "another file.");
  19 + }
  20 + }
  21 + }
  22 + } else {
  23 + throw std::logic_error(
  24 + "Attempting to add an uninitialized object to a QPDF_Array.");
  25 + }
12 } 26 }
13 27
14 -QPDF_Array::QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& v) : 28 +QPDF_Array::QPDF_Array() :
15 QPDFValue(::ot_array, "array") 29 QPDFValue(::ot_array, "array")
16 { 30 {
17 - setFromVector(std::move(v));  
18 } 31 }
19 32
20 -QPDF_Array::QPDF_Array(SparseOHArray const& items) : 33 +QPDF_Array::QPDF_Array(QPDF_Array const& other) :
21 QPDFValue(::ot_array, "array"), 34 QPDFValue(::ot_array, "array"),
22 - elements(items) 35 + sparse(other.sparse),
  36 + sp_size(other.sp_size),
  37 + sp_elements(other.sp_elements),
  38 + elements(other.elements)
23 { 39 {
24 } 40 }
25 41
26 -std::shared_ptr<QPDFObject>  
27 -QPDF_Array::create(std::vector<QPDFObjectHandle> const& items) 42 +QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& v) :
  43 + QPDFValue(::ot_array, "array")
28 { 44 {
29 - return do_create(new QPDF_Array(items)); 45 + setFromVector(v);
30 } 46 }
31 47
32 -std::shared_ptr<QPDFObject>  
33 -QPDF_Array::create(std::vector<std::shared_ptr<QPDFObject>>&& items) 48 +QPDF_Array::QPDF_Array(
  49 + std::vector<std::shared_ptr<QPDFObject>>&& v, bool sparse) :
  50 + QPDFValue(::ot_array, "array"),
  51 + sparse(sparse)
34 { 52 {
35 - return do_create(new QPDF_Array(std::move(items))); 53 + if (sparse) {
  54 + for (auto&& item: v) {
  55 + if (item->getTypeCode() != ::ot_null ||
  56 + item->getObjGen().isIndirect()) {
  57 + sp_elements[sp_size] = std::move(item);
  58 + }
  59 + ++sp_size;
  60 + }
  61 + } else {
  62 + elements = std::move(v);
  63 + }
36 } 64 }
37 65
38 std::shared_ptr<QPDFObject> 66 std::shared_ptr<QPDFObject>
39 -QPDF_Array::create(SparseOHArray const& items) 67 +QPDF_Array::create(std::vector<QPDFObjectHandle> const& items)
40 { 68 {
41 return do_create(new QPDF_Array(items)); 69 return do_create(new QPDF_Array(items));
42 } 70 }
43 71
44 std::shared_ptr<QPDFObject> 72 std::shared_ptr<QPDFObject>
  73 +QPDF_Array::create(
  74 + std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse)
  75 +{
  76 + return do_create(new QPDF_Array(std::move(items), sparse));
  77 +}
  78 +
  79 +std::shared_ptr<QPDFObject>
45 QPDF_Array::copy(bool shallow) 80 QPDF_Array::copy(bool shallow)
46 { 81 {
47 - return create(shallow ? elements : elements.copy()); 82 + if (shallow) {
  83 + return do_create(new QPDF_Array(*this));
  84 + } else {
  85 + if (sparse) {
  86 + QPDF_Array* result = new QPDF_Array();
  87 + result->sp_size = sp_size;
  88 + for (auto const& element: sp_elements) {
  89 + auto const& obj = element.second;
  90 + result->sp_elements[element.first] =
  91 + obj->getObjGen().isIndirect() ? obj : obj->copy();
  92 + }
  93 + return do_create(result);
  94 + } else {
  95 + std::vector<std::shared_ptr<QPDFObject>> result;
  96 + result.reserve(elements.size());
  97 + for (auto const& element: elements) {
  98 + result.push_back(
  99 + element
  100 + ? (element->getObjGen().isIndirect() ? element
  101 + : element->copy())
  102 + : element);
  103 + }
  104 + return create(std::move(result), false);
  105 + }
  106 + }
48 } 107 }
49 108
50 void 109 void
51 QPDF_Array::disconnect() 110 QPDF_Array::disconnect()
52 { 111 {
53 - elements.disconnect(); 112 + if (sparse) {
  113 + for (auto& item: sp_elements) {
  114 + auto& obj = item.second;
  115 + if (!obj->getObjGen().isIndirect()) {
  116 + obj->disconnect();
  117 + }
  118 + }
  119 + } else {
  120 + for (auto& obj: elements) {
  121 + if (!obj->getObjGen().isIndirect()) {
  122 + obj->disconnect();
  123 + }
  124 + }
  125 + }
54 } 126 }
55 127
56 std::string 128 std::string
57 QPDF_Array::unparse() 129 QPDF_Array::unparse()
58 { 130 {
59 std::string result = "[ "; 131 std::string result = "[ ";
60 - size_t size = this->elements.size();  
61 - for (size_t i = 0; i < size; ++i) {  
62 - result += this->elements.at(i).unparse();  
63 - result += " "; 132 + if (sparse) {
  133 + int next = 0;
  134 + for (auto& item: sp_elements) {
  135 + int key = item.first;
  136 + for (int j = next; j < key; ++j) {
  137 + result += "null ";
  138 + }
  139 + item.second->resolve();
  140 + auto og = item.second->getObjGen();
  141 + result += og.isIndirect() ? og.unparse(' ') + " R "
  142 + : item.second->unparse() + " ";
  143 + next = ++key;
  144 + }
  145 + for (int j = next; j < sp_size; ++j) {
  146 + result += "null ";
  147 + }
  148 + } else {
  149 + for (auto const& item: elements) {
  150 + item->resolve();
  151 + auto og = item->getObjGen();
  152 + result += og.isIndirect() ? og.unparse(' ') + " R "
  153 + : item->unparse() + " ";
  154 + }
64 } 155 }
65 result += "]"; 156 result += "]";
66 return result; 157 return result;
@@ -69,96 +160,157 @@ QPDF_Array::unparse() @@ -69,96 +160,157 @@ QPDF_Array::unparse()
69 JSON 160 JSON
70 QPDF_Array::getJSON(int json_version) 161 QPDF_Array::getJSON(int json_version)
71 { 162 {
72 - JSON j = JSON::makeArray();  
73 - size_t size = this->elements.size();  
74 - for (size_t i = 0; i < size; ++i) {  
75 - j.addArrayElement(this->elements.at(i).getJSON(json_version)); 163 + static const JSON j_null = JSON::makeNull();
  164 + JSON j_array = JSON::makeArray();
  165 + if (sparse) {
  166 + int next = 0;
  167 + for (auto& item: sp_elements) {
  168 + int key = item.first;
  169 + for (int j = next; j < key; ++j) {
  170 + j_array.addArrayElement(j_null);
  171 + }
  172 + auto og = item.second->getObjGen();
  173 + j_array.addArrayElement(
  174 + og.isIndirect() ? JSON::makeString(og.unparse(' ') + " R")
  175 + : item.second->getJSON(json_version));
  176 + next = ++key;
  177 + }
  178 + for (int j = next; j < sp_size; ++j) {
  179 + j_array.addArrayElement(j_null);
  180 + }
  181 + } else {
  182 + for (auto const& item: elements) {
  183 + auto og = item->getObjGen();
  184 + j_array.addArrayElement(
  185 + og.isIndirect() ? JSON::makeString(og.unparse(' ') + " R")
  186 + : item->getJSON(json_version));
  187 + }
76 } 188 }
77 - return j;  
78 -}  
79 -  
80 -int  
81 -QPDF_Array::getNItems() const  
82 -{  
83 - // This should really return a size_t, but changing it would break  
84 - // a lot of code.  
85 - return QIntC::to_int(this->elements.size()); 189 + return j_array;
86 } 190 }
87 191
88 QPDFObjectHandle 192 QPDFObjectHandle
89 -QPDF_Array::getItem(int n) const 193 +QPDF_Array::at(int n) const noexcept
90 { 194 {
91 - if ((n < 0) || (n >= QIntC::to_int(elements.size()))) {  
92 - throw std::logic_error(  
93 - "INTERNAL ERROR: bounds error accessing QPDF_Array element"); 195 + if (n < 0 || n >= size()) {
  196 + return {};
  197 + } else if (sparse) {
  198 + auto const& iter = sp_elements.find(n);
  199 + return iter == sp_elements.end() ? null_oh : (*iter).second;
  200 + } else {
  201 + return elements[size_t(n)];
94 } 202 }
95 - return this->elements.at(QIntC::to_size(n));  
96 } 203 }
97 204
98 -void  
99 -QPDF_Array::getAsVector(std::vector<QPDFObjectHandle>& v) const 205 +std::vector<QPDFObjectHandle>
  206 +QPDF_Array::getAsVector() const
100 { 207 {
101 - size_t size = this->elements.size();  
102 - for (size_t i = 0; i < size; ++i) {  
103 - v.push_back(this->elements.at(i)); 208 + if (sparse) {
  209 + std::vector<QPDFObjectHandle> v;
  210 + v.reserve(size_t(size()));
  211 + for (auto const& item: sp_elements) {
  212 + v.resize(size_t(item.first), null_oh);
  213 + v.push_back(item.second);
  214 + }
  215 + v.resize(size_t(size()), null_oh);
  216 + return v;
  217 + } else {
  218 + return {elements.cbegin(), elements.cend()};
104 } 219 }
105 } 220 }
106 221
107 -void  
108 -QPDF_Array::setItem(int n, QPDFObjectHandle const& oh) 222 +bool
  223 +QPDF_Array::setAt(int at, QPDFObjectHandle const& oh)
109 { 224 {
110 - this->elements.setAt(QIntC::to_size(n), oh); 225 + if (at < 0 || at >= size()) {
  226 + return false;
  227 + }
  228 + checkOwnership(oh);
  229 + if (sparse) {
  230 + sp_elements[at] = oh.getObj();
  231 + } else {
  232 + elements[size_t(at)] = oh.getObj();
  233 + }
  234 + return true;
111 } 235 }
112 236
113 void 237 void
114 QPDF_Array::setFromVector(std::vector<QPDFObjectHandle> const& v) 238 QPDF_Array::setFromVector(std::vector<QPDFObjectHandle> const& v)
115 { 239 {
116 - this->elements = SparseOHArray();  
117 - for (auto const& iter: v) {  
118 - this->elements.append(iter); 240 + elements.resize(0);
  241 + elements.reserve(v.size());
  242 + for (auto const& item: v) {
  243 + checkOwnership(item);
  244 + elements.push_back(item.getObj());
119 } 245 }
120 } 246 }
121 247
122 -void  
123 -QPDF_Array::setFromVector(std::vector<std::shared_ptr<QPDFObject>>&& v) 248 +bool
  249 +QPDF_Array::insert(int at, QPDFObjectHandle const& item)
124 { 250 {
125 - this->elements = SparseOHArray();  
126 - for (auto&& item: v) {  
127 - if (item) {  
128 - this->elements.append(item); 251 + int sz = size();
  252 + if (at < 0 || at > sz) {
  253 + // As special case, also allow insert beyond the end
  254 + return false;
  255 + } else if (at == sz) {
  256 + push_back(item);
  257 + } else {
  258 + checkOwnership(item);
  259 + if (sparse) {
  260 + auto iter = sp_elements.crbegin();
  261 + while (iter != sp_elements.crend()) {
  262 + auto key = (iter++)->first;
  263 + if (key >= at) {
  264 + auto nh = sp_elements.extract(key);
  265 + ++nh.key();
  266 + sp_elements.insert(std::move(nh));
  267 + } else {
  268 + break;
  269 + }
  270 + }
  271 + sp_elements[at] = item.getObj();
  272 + ++sp_size;
129 } else { 273 } else {
130 - ++this->elements.n_elements; 274 + elements.insert(elements.cbegin() + at, item.getObj());
131 } 275 }
132 } 276 }
  277 + return true;
133 } 278 }
134 279
135 void 280 void
136 -QPDF_Array::insertItem(int at, QPDFObjectHandle const& item) 281 +QPDF_Array::push_back(QPDFObjectHandle const& item)
137 { 282 {
138 - // As special case, also allow insert beyond the end  
139 - if ((at < 0) || (at > QIntC::to_int(this->elements.size()))) {  
140 - throw std::logic_error(  
141 - "INTERNAL ERROR: bounds error accessing QPDF_Array element"); 283 + checkOwnership(item);
  284 + if (sparse) {
  285 + sp_elements[sp_size++] = item.getObj();
  286 + } else {
  287 + elements.push_back(item.getObj());
142 } 288 }
143 - this->elements.insert(QIntC::to_size(at), item);  
144 } 289 }
145 290
146 -void  
147 -QPDF_Array::appendItem(QPDFObjectHandle const& item) 291 +bool
  292 +QPDF_Array::erase(int at)
148 { 293 {
149 - this->elements.append(item);  
150 -}  
151 -  
152 -void  
153 -QPDF_Array::eraseItem(int at)  
154 -{  
155 - this->elements.erase(QIntC::to_size(at));  
156 -} 294 + if (at < 0 || at >= size()) {
  295 + return false;
  296 + }
  297 + if (sparse) {
  298 + auto end = sp_elements.end();
  299 + if (auto iter = sp_elements.lower_bound(at); iter != end) {
  300 + if (iter->first == at) {
  301 + iter++;
  302 + sp_elements.erase(at);
  303 + }
157 304
158 -void  
159 -QPDF_Array::addExplicitElementsToList(std::list<QPDFObjectHandle>& l) const  
160 -{  
161 - for (auto const& iter: this->elements) {  
162 - l.push_back(iter.second); 305 + while (iter != end) {
  306 + auto nh = sp_elements.extract(iter++);
  307 + --nh.key();
  308 + sp_elements.insert(std::move(nh));
  309 + }
  310 + }
  311 + --sp_size;
  312 + } else {
  313 + elements.erase(elements.cbegin() + at);
163 } 314 }
  315 + return true;
164 } 316 }
libqpdf/QPDF_Null.cc
@@ -50,5 +50,6 @@ QPDF_Null::unparse() @@ -50,5 +50,6 @@ QPDF_Null::unparse()
50 JSON 50 JSON
51 QPDF_Null::getJSON(int json_version) 51 QPDF_Null::getJSON(int json_version)
52 { 52 {
  53 + // If this is updated, QPDF_Array::getJSON must also be updated.
53 return JSON::makeNull(); 54 return JSON::makeNull();
54 } 55 }
libqpdf/SparseOHArray.cc deleted
1 -#include <qpdf/SparseOHArray.hh>  
2 -  
3 -#include <qpdf/QPDFObjectHandle.hh>  
4 -#include <qpdf/QPDFObject_private.hh>  
5 -  
6 -#include <stdexcept>  
7 -  
8 -SparseOHArray::SparseOHArray() :  
9 - n_elements(0)  
10 -{  
11 -}  
12 -  
13 -size_t  
14 -SparseOHArray::size() const  
15 -{  
16 - return this->n_elements;  
17 -}  
18 -  
19 -void  
20 -SparseOHArray::append(QPDFObjectHandle oh)  
21 -{  
22 - if (!oh.isDirectNull()) {  
23 - this->elements[this->n_elements] = oh;  
24 - }  
25 - ++this->n_elements;  
26 -}  
27 -  
28 -void  
29 -SparseOHArray::append(std::shared_ptr<QPDFObject>&& obj)  
30 -{  
31 - if (obj->getTypeCode() != ::ot_null || !obj->getObjGen().isIndirect()) {  
32 - this->elements[this->n_elements] = std::move(obj);  
33 - }  
34 - ++this->n_elements;  
35 -}  
36 -  
37 -QPDFObjectHandle  
38 -SparseOHArray::at(size_t idx) const  
39 -{  
40 - if (idx >= this->n_elements) {  
41 - throw std::logic_error(  
42 - "INTERNAL ERROR: bounds error accessing SparseOHArray element");  
43 - }  
44 - auto const& iter = this->elements.find(idx);  
45 - if (iter == this->elements.end()) {  
46 - return QPDFObjectHandle::newNull();  
47 - } else {  
48 - return (*iter).second;  
49 - }  
50 -}  
51 -  
52 -void  
53 -SparseOHArray::remove_last()  
54 -{  
55 - if (this->n_elements == 0) {  
56 - throw std::logic_error("INTERNAL ERROR: attempt to remove"  
57 - " last item from empty SparseOHArray");  
58 - }  
59 - --this->n_elements;  
60 - this->elements.erase(this->n_elements);  
61 -}  
62 -  
63 -void  
64 -SparseOHArray::disconnect()  
65 -{  
66 - for (auto& iter: this->elements) {  
67 - QPDFObjectHandle::DisconnectAccess::disconnect(iter.second);  
68 - }  
69 -}  
70 -  
71 -void  
72 -SparseOHArray::setAt(size_t idx, QPDFObjectHandle oh)  
73 -{  
74 - if (idx >= this->n_elements) {  
75 - throw std::logic_error("bounds error setting item in SparseOHArray");  
76 - }  
77 - if (oh.isDirectNull()) {  
78 - this->elements.erase(idx);  
79 - } else {  
80 - this->elements[idx] = oh;  
81 - }  
82 -}  
83 -  
84 -void  
85 -SparseOHArray::erase(size_t idx)  
86 -{  
87 - if (idx >= this->n_elements) {  
88 - throw std::logic_error("bounds error erasing item from SparseOHArray");  
89 - }  
90 - decltype(this->elements) dest;  
91 - for (auto const& iter: this->elements) {  
92 - if (iter.first < idx) {  
93 - dest.insert(iter);  
94 - } else if (iter.first > idx) {  
95 - dest[iter.first - 1] = iter.second;  
96 - }  
97 - }  
98 - this->elements = dest;  
99 - --this->n_elements;  
100 -}  
101 -  
102 -void  
103 -SparseOHArray::insert(size_t idx, QPDFObjectHandle oh)  
104 -{  
105 - if (idx > this->n_elements) {  
106 - throw std::logic_error("bounds error inserting item to SparseOHArray");  
107 - } else if (idx == this->n_elements) {  
108 - // Allow inserting to the last position  
109 - append(oh);  
110 - } else {  
111 - decltype(this->elements) dest;  
112 - for (auto const& iter: this->elements) {  
113 - if (iter.first < idx) {  
114 - dest.insert(iter);  
115 - } else {  
116 - dest[iter.first + 1] = iter.second;  
117 - }  
118 - }  
119 - this->elements = dest;  
120 - this->elements[idx] = oh;  
121 - ++this->n_elements;  
122 - }  
123 -}  
124 -  
125 -SparseOHArray  
126 -SparseOHArray::copy()  
127 -{  
128 - SparseOHArray result;  
129 - result.n_elements = this->n_elements;  
130 - for (auto const& element: this->elements) {  
131 - auto value = element.second;  
132 - result.elements[element.first] =  
133 - value.isIndirect() ? value : value.shallowCopy();  
134 - }  
135 - return result;  
136 -}  
137 -  
138 -SparseOHArray::const_iterator  
139 -SparseOHArray::begin() const  
140 -{  
141 - return this->elements.begin();  
142 -}  
143 -  
144 -SparseOHArray::const_iterator  
145 -SparseOHArray::end() const  
146 -{  
147 - return this->elements.end();  
148 -}  
libqpdf/qpdf/QPDF_Array.hh
@@ -3,8 +3,7 @@ @@ -3,8 +3,7 @@
3 3
4 #include <qpdf/QPDFValue.hh> 4 #include <qpdf/QPDFValue.hh>
5 5
6 -#include <qpdf/SparseOHArray.hh>  
7 -#include <list> 6 +#include <map>
8 #include <vector> 7 #include <vector>
9 8
10 class QPDF_Array: public QPDFValue 9 class QPDF_Array: public QPDFValue
@@ -14,34 +13,37 @@ class QPDF_Array: public QPDFValue @@ -14,34 +13,37 @@ class QPDF_Array: public QPDFValue
14 static std::shared_ptr<QPDFObject> 13 static std::shared_ptr<QPDFObject>
15 create(std::vector<QPDFObjectHandle> const& items); 14 create(std::vector<QPDFObjectHandle> const& items);
16 static std::shared_ptr<QPDFObject> 15 static std::shared_ptr<QPDFObject>
17 - create(std::vector<std::shared_ptr<QPDFObject>>&& items);  
18 - static std::shared_ptr<QPDFObject> create(SparseOHArray const& items); 16 + create(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse);
19 virtual std::shared_ptr<QPDFObject> copy(bool shallow = false); 17 virtual std::shared_ptr<QPDFObject> copy(bool shallow = false);
20 virtual std::string unparse(); 18 virtual std::string unparse();
21 virtual JSON getJSON(int json_version); 19 virtual JSON getJSON(int json_version);
22 virtual void disconnect(); 20 virtual void disconnect();
23 21
24 - int getNItems() const;  
25 - QPDFObjectHandle getItem(int n) const;  
26 - void getAsVector(std::vector<QPDFObjectHandle>&) const;  
27 -  
28 - void setItem(int, QPDFObjectHandle const&); 22 + int
  23 + size() const noexcept
  24 + {
  25 + return sparse ? sp_size : int(elements.size());
  26 + }
  27 + QPDFObjectHandle at(int n) const noexcept;
  28 + bool setAt(int n, QPDFObjectHandle const& oh);
  29 + std::vector<QPDFObjectHandle> getAsVector() const;
29 void setFromVector(std::vector<QPDFObjectHandle> const& items); 30 void setFromVector(std::vector<QPDFObjectHandle> const& items);
30 - void setFromVector(std::vector<std::shared_ptr<QPDFObject>>&& items);  
31 - void insertItem(int at, QPDFObjectHandle const& item);  
32 - void appendItem(QPDFObjectHandle const& item);  
33 - void eraseItem(int at);  
34 -  
35 - // Helper methods for QPDF and QPDFObjectHandle -- these are  
36 - // public methods since the whole class is not part of the public  
37 - // API. Otherwise, these would be wrapped in accessor classes.  
38 - void addExplicitElementsToList(std::list<QPDFObjectHandle>&) const; 31 + bool insert(int at, QPDFObjectHandle const& item);
  32 + void push_back(QPDFObjectHandle const& item);
  33 + bool erase(int at);
39 34
40 private: 35 private:
  36 + QPDF_Array();
  37 + QPDF_Array(QPDF_Array const&);
41 QPDF_Array(std::vector<QPDFObjectHandle> const& items); 38 QPDF_Array(std::vector<QPDFObjectHandle> const& items);
42 - QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items);  
43 - QPDF_Array(SparseOHArray const& items);  
44 - SparseOHArray elements; 39 + QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse);
  40 +
  41 + void checkOwnership(QPDFObjectHandle const& item) const;
  42 +
  43 + bool sparse{false};
  44 + int sp_size{0};
  45 + std::map<int, std::shared_ptr<QPDFObject>> sp_elements;
  46 + std::vector<std::shared_ptr<QPDFObject>> elements;
45 }; 47 };
46 48
47 #endif // QPDF_ARRAY_HH 49 #endif // QPDF_ARRAY_HH
libqpdf/qpdf/SparseOHArray.hh deleted
1 -#ifndef QPDF_SPARSEOHARRAY_HH  
2 -#define QPDF_SPARSEOHARRAY_HH  
3 -  
4 -#include <qpdf/QPDFObjectHandle.hh>  
5 -#include <unordered_map>  
6 -  
7 -class QPDF_Array;  
8 -  
9 -class SparseOHArray  
10 -{  
11 - public:  
12 - SparseOHArray();  
13 - size_t size() const;  
14 - void append(QPDFObjectHandle oh);  
15 - void append(std::shared_ptr<QPDFObject>&& obj);  
16 - QPDFObjectHandle at(size_t idx) const;  
17 - void remove_last();  
18 - void setAt(size_t idx, QPDFObjectHandle oh);  
19 - void erase(size_t idx);  
20 - void insert(size_t idx, QPDFObjectHandle oh);  
21 - SparseOHArray copy();  
22 - void disconnect();  
23 -  
24 - typedef std::unordered_map<size_t, QPDFObjectHandle>::const_iterator  
25 - const_iterator;  
26 - const_iterator begin() const;  
27 - const_iterator end() const;  
28 -  
29 - private:  
30 - friend class QPDF_Array;  
31 - std::unordered_map<size_t, QPDFObjectHandle> elements;  
32 - size_t n_elements;  
33 -};  
34 -  
35 -#endif // QPDF_SPARSEOHARRAY_HH  
libtests/sparse_array.cc
1 #include <qpdf/assert_test.h> 1 #include <qpdf/assert_test.h>
2 2
3 -#include <qpdf/SparseOHArray.hh> 3 +#include <qpdf/QPDFObjectHandle.hh>
  4 +#include <qpdf/QPDFObject_private.hh>
  5 +#include <qpdf/QPDF_Array.hh>
4 #include <iostream> 6 #include <iostream>
5 7
6 int 8 int
7 main() 9 main()
8 { 10 {
9 - SparseOHArray a; 11 + auto obj = QPDF_Array::create({}, true);
  12 + QPDF_Array& a = *obj->as<QPDF_Array>();
  13 +
10 assert(a.size() == 0); 14 assert(a.size() == 0);
11 15
12 - a.append(QPDFObjectHandle::parse("1"));  
13 - a.append(QPDFObjectHandle::parse("(potato)"));  
14 - a.append(QPDFObjectHandle::parse("null"));  
15 - a.append(QPDFObjectHandle::parse("null"));  
16 - a.append(QPDFObjectHandle::parse("/Quack")); 16 + a.push_back(QPDFObjectHandle::parse("1"));
  17 + a.push_back(QPDFObjectHandle::parse("(potato)"));
  18 + a.push_back(QPDFObjectHandle::parse("null"));
  19 + a.push_back(QPDFObjectHandle::parse("null"));
  20 + a.push_back(QPDFObjectHandle::parse("/Quack"));
17 assert(a.size() == 5); 21 assert(a.size() == 5);
18 assert(a.at(0).isInteger() && (a.at(0).getIntValue() == 1)); 22 assert(a.at(0).isInteger() && (a.at(0).getIntValue() == 1));
19 assert(a.at(1).isString() && (a.at(1).getStringValue() == "potato")); 23 assert(a.at(1).isString() && (a.at(1).getStringValue() == "potato"));
@@ -60,20 +64,20 @@ main() @@ -60,20 +64,20 @@ main()
60 a.setAt(4, QPDFObjectHandle::newNull()); 64 a.setAt(4, QPDFObjectHandle::newNull());
61 assert(a.at(4).isNull()); 65 assert(a.at(4).isNull());
62 66
63 - a.remove_last(); 67 + a.erase(a.size() - 1);
64 assert(a.size() == 5); 68 assert(a.size() == 5);
65 assert(a.at(0).isName() && (a.at(0).getName() == "/First")); 69 assert(a.at(0).isName() && (a.at(0).getName() == "/First"));
66 assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); 70 assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1));
67 assert(a.at(3).isName() && (a.at(3).getName() == "/Third")); 71 assert(a.at(3).isName() && (a.at(3).getName() == "/Third"));
68 assert(a.at(4).isNull()); 72 assert(a.at(4).isNull());
69 73
70 - a.remove_last(); 74 + a.erase(a.size() - 1);
71 assert(a.size() == 4); 75 assert(a.size() == 4);
72 assert(a.at(0).isName() && (a.at(0).getName() == "/First")); 76 assert(a.at(0).isName() && (a.at(0).getName() == "/First"));
73 assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); 77 assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1));
74 assert(a.at(3).isName() && (a.at(3).getName() == "/Third")); 78 assert(a.at(3).isName() && (a.at(3).getName() == "/Third"));
75 79
76 - a.remove_last(); 80 + a.erase(a.size() - 1);
77 assert(a.size() == 3); 81 assert(a.size() == 3);
78 assert(a.at(0).isName() && (a.at(0).getName() == "/First")); 82 assert(a.at(0).isName() && (a.at(0).getName() == "/First"));
79 assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); 83 assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1));
qpdf/qpdf.testcov
@@ -303,8 +303,10 @@ QPDFObjectHandle array treating as empty 0 @@ -303,8 +303,10 @@ QPDFObjectHandle array treating as empty 0
303 QPDFObjectHandle array null for non-array 0 303 QPDFObjectHandle array null for non-array 0
304 QPDFObjectHandle array treating as empty vector 0 304 QPDFObjectHandle array treating as empty vector 0
305 QPDFObjectHandle array ignoring set item 0 305 QPDFObjectHandle array ignoring set item 0
  306 +QPDFObjectHandle set array bounds 0
306 QPDFObjectHandle array ignoring replace items 0 307 QPDFObjectHandle array ignoring replace items 0
307 QPDFObjectHandle array ignoring insert item 0 308 QPDFObjectHandle array ignoring insert item 0
  309 +QPDFObjectHandle insert array bounds 0
308 QPDFObjectHandle array ignoring append item 0 310 QPDFObjectHandle array ignoring append item 0
309 QPDFObjectHandle array ignoring erase item 0 311 QPDFObjectHandle array ignoring erase item 0
310 QPDFObjectHandle dictionary false for hasKey 0 312 QPDFObjectHandle dictionary false for hasKey 0
qpdf/qtest/qpdf/object-types-os.out
@@ -5,6 +5,8 @@ WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operatio @@ -5,6 +5,8 @@ WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operatio
5 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to append item 5 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to append item
6 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to erase out of bounds array item 6 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to erase out of bounds array item
7 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to erase out of bounds array item 7 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to erase out of bounds array item
  8 +WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to insert out of bounds array item
  9 +WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to set out of bounds array item
8 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to erase item 10 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to erase item
9 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to insert item 11 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to insert item
10 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to replace items 12 WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to replace items
qpdf/qtest/qpdf/object-types.out
@@ -5,6 +5,8 @@ WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempt @@ -5,6 +5,8 @@ WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempt
5 WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to append item 5 WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to append item
6 WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to erase out of bounds array item 6 WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to erase out of bounds array item
7 WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to erase out of bounds array item 7 WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to erase out of bounds array item
  8 +WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to insert out of bounds array item
  9 +WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to set out of bounds array item
8 WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to erase item 10 WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to erase item
9 WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to insert item 11 WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to insert item
10 WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to replace items 12 WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to replace items
qpdf/qtest/qpdf/test88.out
1 WARNING: test array: ignoring attempt to erase out of bounds array item 1 WARNING: test array: ignoring attempt to erase out of bounds array item
  2 +WARNING: minimal.pdf, object 1 0 at offset 19: operation for array attempted on object of type dictionary: ignoring attempt to erase item
2 test 88 done 3 test 88 done
qpdf/test_driver.cc
@@ -1506,6 +1506,8 @@ test_42(QPDF&amp; pdf, char const* arg2) @@ -1506,6 +1506,8 @@ test_42(QPDF&amp; pdf, char const* arg2)
1506 integer.appendItem(null); 1506 integer.appendItem(null);
1507 array.eraseItem(-1); 1507 array.eraseItem(-1);
1508 array.eraseItem(16059); 1508 array.eraseItem(16059);
  1509 + array.insertItem(42, "/Dontpanic"_qpdf);
  1510 + array.setArrayItem(42, "/Dontpanic"_qpdf);
1509 integer.eraseItem(0); 1511 integer.eraseItem(0);
1510 integer.insertItem(0, null); 1512 integer.insertItem(0, null);
1511 integer.setArrayFromVector(std::vector<QPDFObjectHandle>()); 1513 integer.setArrayFromVector(std::vector<QPDFObjectHandle>());
@@ -3282,6 +3284,7 @@ test_88(QPDF&amp; pdf, char const* arg2) @@ -3282,6 +3284,7 @@ test_88(QPDF&amp; pdf, char const* arg2)
3282 auto arr2 = pdf.getRoot().replaceKeyAndGetNew("/QTest", "[1 2]"_qpdf); 3284 auto arr2 = pdf.getRoot().replaceKeyAndGetNew("/QTest", "[1 2]"_qpdf);
3283 arr2.setObjectDescription(&pdf, "test array"); 3285 arr2.setObjectDescription(&pdf, "test array");
3284 assert(arr2.eraseItemAndGetOld(50).isNull()); 3286 assert(arr2.eraseItemAndGetOld(50).isNull());
  3287 + assert(pdf.getRoot().eraseItemAndGetOld(0).isNull());
3285 } 3288 }
3286 3289
3287 static void 3290 static void