Commit 306f0efa56b3227e9015bdf3b0d84b68bb9687a3

Authored by m-holger
1 parent a03c6863

Refactor QPDF_Array

Move all array-specific methods to new class qpdf::Array.
include/qpdf/ObjectHandle.hh
@@ -26,6 +26,7 @@ @@ -26,6 +26,7 @@
26 #include <qpdf/Types.h> 26 #include <qpdf/Types.h>
27 27
28 #include <cstdint> 28 #include <cstdint>
  29 +#include <memory>
29 30
30 class QPDF_Dictionary; 31 class QPDF_Dictionary;
31 class QPDFObject; 32 class QPDFObject;
@@ -33,6 +34,7 @@ class QPDFObjectHandle; @@ -33,6 +34,7 @@ class QPDFObjectHandle;
33 34
34 namespace qpdf 35 namespace qpdf
35 { 36 {
  37 + class Array;
36 class Dictionary; 38 class Dictionary;
37 class BaseDictionary; 39 class BaseDictionary;
38 40
include/qpdf/QPDFObjectHandle.hh
@@ -1355,6 +1355,7 @@ class QPDFObjectHandle final: public qpdf::BaseHandle @@ -1355,6 +1355,7 @@ class QPDFObjectHandle final: public qpdf::BaseHandle
1355 1355
1356 void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false) const; 1356 void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false) const;
1357 1357
  1358 + inline qpdf::Array as_array(qpdf::typed options = qpdf::typed::any) const;
1358 inline qpdf::Dictionary as_dictionary(qpdf::typed options = qpdf::typed::any) const; 1359 inline qpdf::Dictionary as_dictionary(qpdf::typed options = qpdf::typed::any) const;
1359 1360
1360 private: 1361 private:
libqpdf/QPDFObjectHandle.cc
@@ -777,22 +777,20 @@ QPDFObjectHandle::aitems() @@ -777,22 +777,20 @@ QPDFObjectHandle::aitems()
777 int 777 int
778 QPDFObjectHandle::getArrayNItems() const 778 QPDFObjectHandle::getArrayNItems() const
779 { 779 {
780 - if (auto array = asArray()) {  
781 - return array->size();  
782 - } else {  
783 - typeWarning("array", "treating as empty");  
784 - QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");  
785 - return 0; 780 + if (auto array = as_array(strict)) {
  781 + return array.size();
786 } 782 }
  783 + typeWarning("array", "treating as empty");
  784 + QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
  785 + return 0;
787 } 786 }
788 787
789 QPDFObjectHandle 788 QPDFObjectHandle
790 QPDFObjectHandle::getArrayItem(int n) const 789 QPDFObjectHandle::getArrayItem(int n) const
791 { 790 {
792 - if (auto array = asArray()) {  
793 - auto result = array->at(n);  
794 - if (result.first) {  
795 - return result.second; 791 + if (auto array = as_array(strict)) {
  792 + if (auto const [success, oh] = array.at(n); success) {
  793 + return oh;
796 } else { 794 } else {
797 objectWarning("returning null for out of bounds array access"); 795 objectWarning("returning null for out of bounds array access");
798 QTC::TC("qpdf", "QPDFObjectHandle array bounds"); 796 QTC::TC("qpdf", "QPDFObjectHandle array bounds");
@@ -808,13 +806,13 @@ QPDFObjectHandle::getArrayItem(int n) const @@ -808,13 +806,13 @@ QPDFObjectHandle::getArrayItem(int n) const
808 bool 806 bool
809 QPDFObjectHandle::isRectangle() const 807 QPDFObjectHandle::isRectangle() const
810 { 808 {
811 - if (auto array = asArray()) { 809 + if (auto array = as_array(strict)) {
812 for (int i = 0; i < 4; ++i) { 810 for (int i = 0; i < 4; ++i) {
813 - if (auto item = array->at(i).second; !item.isNumber()) { 811 + if (auto item = array.at(i).second; !item.isNumber()) {
814 return false; 812 return false;
815 } 813 }
816 } 814 }
817 - return array->size() == 4; 815 + return array.size() == 4;
818 } 816 }
819 return false; 817 return false;
820 } 818 }
@@ -822,13 +820,13 @@ QPDFObjectHandle::isRectangle() const @@ -822,13 +820,13 @@ QPDFObjectHandle::isRectangle() const
822 bool 820 bool
823 QPDFObjectHandle::isMatrix() const 821 QPDFObjectHandle::isMatrix() const
824 { 822 {
825 - if (auto array = asArray()) { 823 + if (auto array = as_array(strict)) {
826 for (int i = 0; i < 6; ++i) { 824 for (int i = 0; i < 6; ++i) {
827 - if (auto item = array->at(i).second; !item.isNumber()) { 825 + if (auto item = array.at(i).second; !item.isNumber()) {
828 return false; 826 return false;
829 } 827 }
830 } 828 }
831 - return array->size() == 6; 829 + return array.size() == 6;
832 } 830 }
833 return false; 831 return false;
834 } 832 }
@@ -836,13 +834,13 @@ QPDFObjectHandle::isMatrix() const @@ -836,13 +834,13 @@ QPDFObjectHandle::isMatrix() const
836 QPDFObjectHandle::Rectangle 834 QPDFObjectHandle::Rectangle
837 QPDFObjectHandle::getArrayAsRectangle() const 835 QPDFObjectHandle::getArrayAsRectangle() const
838 { 836 {
839 - if (auto array = asArray()) {  
840 - if (array->size() != 4) { 837 + if (auto array = as_array(strict)) {
  838 + if (array.size() != 4) {
841 return {}; 839 return {};
842 } 840 }
843 double items[4]; 841 double items[4];
844 for (int i = 0; i < 4; ++i) { 842 for (int i = 0; i < 4; ++i) {
845 - if (auto item = array->at(i).second; !item.getValueAsNumber(items[i])) { 843 + if (auto item = array.at(i).second; !item.getValueAsNumber(items[i])) {
846 return {}; 844 return {};
847 } 845 }
848 } 846 }
@@ -858,13 +856,13 @@ QPDFObjectHandle::getArrayAsRectangle() const @@ -858,13 +856,13 @@ QPDFObjectHandle::getArrayAsRectangle() const
858 QPDFObjectHandle::Matrix 856 QPDFObjectHandle::Matrix
859 QPDFObjectHandle::getArrayAsMatrix() const 857 QPDFObjectHandle::getArrayAsMatrix() const
860 { 858 {
861 - if (auto array = asArray()) {  
862 - if (array->size() != 6) { 859 + if (auto array = as_array(strict)) {
  860 + if (array.size() != 6) {
863 return {}; 861 return {};
864 } 862 }
865 double items[6]; 863 double items[6];
866 for (int i = 0; i < 6; ++i) { 864 for (int i = 0; i < 6; ++i) {
867 - if (auto item = array->at(i).second; !item.getValueAsNumber(items[i])) { 865 + if (auto item = array.at(i).second; !item.getValueAsNumber(items[i])) {
868 return {}; 866 return {};
869 } 867 }
870 } 868 }
@@ -876,13 +874,11 @@ QPDFObjectHandle::getArrayAsMatrix() const @@ -876,13 +874,11 @@ QPDFObjectHandle::getArrayAsMatrix() const
876 std::vector<QPDFObjectHandle> 874 std::vector<QPDFObjectHandle>
877 QPDFObjectHandle::getArrayAsVector() const 875 QPDFObjectHandle::getArrayAsVector() const
878 { 876 {
879 - auto array = asArray();  
880 - if (array) {  
881 - return array->getAsVector();  
882 - } else {  
883 - typeWarning("array", "treating as empty");  
884 - QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector"); 877 + if (auto array = as_array(strict)) {
  878 + return array.getAsVector();
885 } 879 }
  880 + typeWarning("array", "treating as empty");
  881 + QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector");
886 return {}; 882 return {};
887 } 883 }
888 884
@@ -891,8 +887,8 @@ QPDFObjectHandle::getArrayAsVector() const @@ -891,8 +887,8 @@ QPDFObjectHandle::getArrayAsVector() const
891 void 887 void
892 QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) 888 QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
893 { 889 {
894 - if (auto array = asArray()) {  
895 - if (!array->setAt(n, item)) { 890 + if (auto array = as_array(strict)) {
  891 + if (!array.setAt(n, item)) {
896 objectWarning("ignoring attempt to set out of bounds array item"); 892 objectWarning("ignoring attempt to set out of bounds array item");
897 QTC::TC("qpdf", "QPDFObjectHandle set array bounds"); 893 QTC::TC("qpdf", "QPDFObjectHandle set array bounds");
898 } 894 }
@@ -904,8 +900,8 @@ QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const&amp; item) @@ -904,8 +900,8 @@ QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const&amp; item)
904 void 900 void
905 QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) 901 QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items)
906 { 902 {
907 - if (auto array = asArray()) {  
908 - array->setFromVector(items); 903 + if (auto array = as_array(strict)) {
  904 + array.setFromVector(items);
909 } else { 905 } else {
910 typeWarning("array", "ignoring attempt to replace items"); 906 typeWarning("array", "ignoring attempt to replace items");
911 QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items"); 907 QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items");
@@ -915,8 +911,8 @@ QPDFObjectHandle::setArrayFromVector(std::vector&lt;QPDFObjectHandle&gt; const&amp; items) @@ -915,8 +911,8 @@ QPDFObjectHandle::setArrayFromVector(std::vector&lt;QPDFObjectHandle&gt; const&amp; items)
915 void 911 void
916 QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) 912 QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item)
917 { 913 {
918 - if (auto array = asArray()) {  
919 - if (!array->insert(at, item)) { 914 + if (auto array = as_array(strict)) {
  915 + if (!array.insert(at, item)) {
920 objectWarning("ignoring attempt to insert out of bounds array item"); 916 objectWarning("ignoring attempt to insert out of bounds array item");
921 QTC::TC("qpdf", "QPDFObjectHandle insert array bounds"); 917 QTC::TC("qpdf", "QPDFObjectHandle insert array bounds");
922 } 918 }
@@ -936,8 +932,8 @@ QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const&amp; item) @@ -936,8 +932,8 @@ QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const&amp; item)
936 void 932 void
937 QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) 933 QPDFObjectHandle::appendItem(QPDFObjectHandle const& item)
938 { 934 {
939 - if (auto array = asArray()) {  
940 - array->push_back(item); 935 + if (auto array = as_array(strict)) {
  936 + array.push_back(item);
941 } else { 937 } else {
942 typeWarning("array", "ignoring attempt to append item"); 938 typeWarning("array", "ignoring attempt to append item");
943 QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item"); 939 QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item");
@@ -954,8 +950,8 @@ QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const&amp; item) @@ -954,8 +950,8 @@ QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const&amp; item)
954 void 950 void
955 QPDFObjectHandle::eraseItem(int at) 951 QPDFObjectHandle::eraseItem(int at)
956 { 952 {
957 - if (auto array = asArray()) {  
958 - if (!array->erase(at)) { 953 + if (auto array = as_array(strict)) {
  954 + if (!array.erase(at)) {
959 objectWarning("ignoring attempt to erase out of bounds array item"); 955 objectWarning("ignoring attempt to erase out of bounds array item");
960 QTC::TC("qpdf", "QPDFObjectHandle erase array bounds"); 956 QTC::TC("qpdf", "QPDFObjectHandle erase array bounds");
961 } 957 }
@@ -968,8 +964,8 @@ QPDFObjectHandle::eraseItem(int at) @@ -968,8 +964,8 @@ QPDFObjectHandle::eraseItem(int at)
968 QPDFObjectHandle 964 QPDFObjectHandle
969 QPDFObjectHandle::eraseItemAndGetOld(int at) 965 QPDFObjectHandle::eraseItemAndGetOld(int at)
970 { 966 {
971 - auto array = asArray();  
972 - auto result = (array && at < array->size() && at >= 0) ? array->at(at).second : newNull(); 967 + auto array = as_array(strict);
  968 + auto result = (array && at < array.size() && at >= 0) ? array.at(at).second : newNull();
973 eraseItem(at); 969 eraseItem(at);
974 return result; 970 return result;
975 } 971 }
@@ -1344,12 +1340,12 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( @@ -1344,12 +1340,12 @@ QPDFObjectHandle::arrayOrStreamToStreamArray(
1344 { 1340 {
1345 all_description = description; 1341 all_description = description;
1346 std::vector<QPDFObjectHandle> result; 1342 std::vector<QPDFObjectHandle> result;
1347 - if (auto array = asArray()) {  
1348 - int n_items = array->size(); 1343 + if (auto array = as_array(strict)) {
  1344 + int n_items = array.size();
1349 for (int i = 0; i < n_items; ++i) { 1345 for (int i = 0; i < n_items; ++i) {
1350 - QPDFObjectHandle item = array->at(i).second; 1346 + QPDFObjectHandle item = array.at(i).second;
1351 if (item.isStream()) { 1347 if (item.isStream()) {
1352 - result.push_back(item); 1348 + result.emplace_back(item);
1353 } else { 1349 } else {
1354 QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array"); 1350 QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array");
1355 warn( 1351 warn(
@@ -1363,7 +1359,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( @@ -1363,7 +1359,7 @@ QPDFObjectHandle::arrayOrStreamToStreamArray(
1363 } 1359 }
1364 } 1360 }
1365 } else if (isStream()) { 1361 } else if (isStream()) {
1366 - result.push_back(*this); 1362 + result.emplace_back(*this);
1367 } else if (!isNull()) { 1363 } else if (!isNull()) {
1368 warn( 1364 warn(
1369 getOwningQPDF(), 1365 getOwningQPDF(),
@@ -1993,10 +1989,10 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set&amp; visited, bool stop_at_streams) @@ -1993,10 +1989,10 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set&amp; visited, bool stop_at_streams)
1993 this->obj = obj->copy(true); 1989 this->obj = obj->copy(true);
1994 } else if (isArray()) { 1990 } else if (isArray()) {
1995 std::vector<QPDFObjectHandle> items; 1991 std::vector<QPDFObjectHandle> items;
1996 - auto array = asArray();  
1997 - int n = array->size(); 1992 + auto array = as_array(strict);
  1993 + int n = array.size();
1998 for (int i = 0; i < n; ++i) { 1994 for (int i = 0; i < n; ++i) {
1999 - items.push_back(array->at(i).second); 1995 + items.emplace_back(array.at(i).second);
2000 items.back().makeDirect(visited, stop_at_streams); 1996 items.back().makeDirect(visited, stop_at_streams);
2001 } 1997 }
2002 this->obj = QPDF_Array::create(items); 1998 this->obj = QPDF_Array::create(items);
libqpdf/QPDF_Array.cc
1 #include <qpdf/QPDF_Array.hh> 1 #include <qpdf/QPDF_Array.hh>
2 2
3 #include <qpdf/JSON_writer.hh> 3 #include <qpdf/JSON_writer.hh>
4 -#include <qpdf/QPDFObjectHandle.hh> 4 +#include <qpdf/QPDFObjectHandle_private.hh>
5 #include <qpdf/QPDFObject_private.hh> 5 #include <qpdf/QPDFObject_private.hh>
6 #include <qpdf/QTC.hh> 6 #include <qpdf/QTC.hh>
7 7
  8 +using namespace qpdf;
  9 +
8 static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull(); 10 static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull();
9 11
10 inline void 12 inline void
11 QPDF_Array::checkOwnership(QPDFObjectHandle const& item) const 13 QPDF_Array::checkOwnership(QPDFObjectHandle const& item) const
12 { 14 {
13 - if (auto obj = item.getObjectPtr()) {  
14 - if (qpdf) {  
15 - if (auto item_qpdf = obj->getQPDF()) {  
16 - if (qpdf != item_qpdf) { 15 + // This is only called from QPDF_Array::setFromVector, which in turn is only called from create.
  16 + // At his point qpdf is a nullptr and therefore the ownership check reduces to an uninitialized
  17 + // check
  18 + if (!item.getObjectPtr()) {
  19 + throw std::logic_error("Attempting to add an uninitialized object to a QPDF_Array.");
  20 + }
  21 +}
  22 +
  23 +inline void
  24 +Array::checkOwnership(QPDFObjectHandle const& item) const
  25 +{
  26 + if (auto o = item.getObjectPtr()) {
  27 + if (auto pdf = obj->getQPDF()) {
  28 + if (auto item_qpdf = o->getQPDF()) {
  29 + if (pdf != item_qpdf) {
17 throw std::logic_error( 30 throw std::logic_error(
18 "Attempting to add an object from a different QPDF. Use " 31 "Attempting to add an object from a different QPDF. Use "
19 "QPDF::copyForeignObject to add objects from another file."); 32 "QPDF::copyForeignObject to add objects from another file.");
@@ -184,47 +197,75 @@ QPDF_Array::writeJSON(int json_version, JSON::Writer&amp; p) @@ -184,47 +197,75 @@ QPDF_Array::writeJSON(int json_version, JSON::Writer&amp; p)
184 p.writeEnd(']'); 197 p.writeEnd(']');
185 } 198 }
186 199
  200 +QPDF_Array*
  201 +Array::array() const
  202 +{
  203 + if (obj) {
  204 + if (auto a = obj->as<QPDF_Array>()) {
  205 + return a;
  206 + }
  207 + }
  208 + throw std::runtime_error("Expected an array but found a non-array object");
  209 + return nullptr; // unreachable
  210 +}
  211 +
  212 +QPDFObjectHandle
  213 +Array::null() const
  214 +{
  215 + return null_oh;
  216 +}
  217 +
  218 +int
  219 +Array::size() const
  220 +{
  221 + auto a = array();
  222 + return a->sp ? a->sp->size : int(a->elements.size());
  223 +}
  224 +
187 std::pair<bool, QPDFObjectHandle> 225 std::pair<bool, QPDFObjectHandle>
188 -QPDF_Array::at(int n) const noexcept 226 +Array::at(int n) const
189 { 227 {
  228 + auto a = array();
190 if (n < 0 || n >= size()) { 229 if (n < 0 || n >= size()) {
191 return {false, {}}; 230 return {false, {}};
192 - } else if (sp) {  
193 - auto const& iter = sp->elements.find(n);  
194 - return {true, iter == sp->elements.end() ? null_oh : (*iter).second};  
195 - } else {  
196 - return {true, elements[size_t(n)]};  
197 } 231 }
  232 + if (!a->sp) {
  233 + return {true, a->elements[size_t(n)]};
  234 + }
  235 + auto const& iter = a->sp->elements.find(n);
  236 + return {true, iter == a->sp->elements.end() ? null() : iter->second};
198 } 237 }
199 238
200 std::vector<QPDFObjectHandle> 239 std::vector<QPDFObjectHandle>
201 -QPDF_Array::getAsVector() const 240 +Array::getAsVector() const
202 { 241 {
203 - if (sp) { 242 + auto a = array();
  243 + if (a->sp) {
204 std::vector<QPDFObjectHandle> v; 244 std::vector<QPDFObjectHandle> v;
205 v.reserve(size_t(size())); 245 v.reserve(size_t(size()));
206 - for (auto const& item: sp->elements) { 246 + for (auto const& item: a->sp->elements) {
207 v.resize(size_t(item.first), null_oh); 247 v.resize(size_t(item.first), null_oh);
208 v.emplace_back(item.second); 248 v.emplace_back(item.second);
209 } 249 }
210 v.resize(size_t(size()), null_oh); 250 v.resize(size_t(size()), null_oh);
211 return v; 251 return v;
212 } else { 252 } else {
213 - return {elements.cbegin(), elements.cend()}; 253 + return {a->elements.cbegin(), a->elements.cend()};
214 } 254 }
215 } 255 }
216 256
217 bool 257 bool
218 -QPDF_Array::setAt(int at, QPDFObjectHandle const& oh) 258 +Array::setAt(int at, QPDFObjectHandle const& oh)
219 { 259 {
220 if (at < 0 || at >= size()) { 260 if (at < 0 || at >= size()) {
221 return false; 261 return false;
222 } 262 }
  263 + auto a = array();
223 checkOwnership(oh); 264 checkOwnership(oh);
224 - if (sp) {  
225 - sp->elements[at] = oh.getObj(); 265 + if (a->sp) {
  266 + a->sp->elements[at] = oh.getObj();
226 } else { 267 } else {
227 - elements[size_t(at)] = oh.getObj(); 268 + a->elements[size_t(at)] = oh.getObj();
228 } 269 }
229 return true; 270 return true;
230 } 271 }
@@ -240,9 +281,22 @@ QPDF_Array::setFromVector(std::vector&lt;QPDFObjectHandle&gt; const&amp; v) @@ -240,9 +281,22 @@ QPDF_Array::setFromVector(std::vector&lt;QPDFObjectHandle&gt; const&amp; v)
240 } 281 }
241 } 282 }
242 283
  284 +void
  285 +Array::setFromVector(std::vector<QPDFObjectHandle> const& v)
  286 +{
  287 + auto a = array();
  288 + a->elements.resize(0);
  289 + a->elements.reserve(v.size());
  290 + for (auto const& item: v) {
  291 + checkOwnership(item);
  292 + a->elements.push_back(item.getObj());
  293 + }
  294 +}
  295 +
243 bool 296 bool
244 -QPDF_Array::insert(int at, QPDFObjectHandle const& item) 297 +Array::insert(int at, QPDFObjectHandle const& item)
245 { 298 {
  299 + auto a = array();
246 int sz = size(); 300 int sz = size();
247 if (at < 0 || at > sz) { 301 if (at < 0 || at > sz) {
248 // As special case, also allow insert beyond the end 302 // As special case, also allow insert beyond the end
@@ -251,61 +305,63 @@ QPDF_Array::insert(int at, QPDFObjectHandle const&amp; item) @@ -251,61 +305,63 @@ QPDF_Array::insert(int at, QPDFObjectHandle const&amp; item)
251 push_back(item); 305 push_back(item);
252 } else { 306 } else {
253 checkOwnership(item); 307 checkOwnership(item);
254 - if (sp) {  
255 - auto iter = sp->elements.crbegin();  
256 - while (iter != sp->elements.crend()) { 308 + if (a->sp) {
  309 + auto iter = a->sp->elements.crbegin();
  310 + while (iter != a->sp->elements.crend()) {
257 auto key = (iter++)->first; 311 auto key = (iter++)->first;
258 if (key >= at) { 312 if (key >= at) {
259 - auto nh = sp->elements.extract(key); 313 + auto nh = a->sp->elements.extract(key);
260 ++nh.key(); 314 ++nh.key();
261 - sp->elements.insert(std::move(nh)); 315 + a->sp->elements.insert(std::move(nh));
262 } else { 316 } else {
263 break; 317 break;
264 } 318 }
265 } 319 }
266 - sp->elements[at] = item.getObj();  
267 - ++sp->size; 320 + a->sp->elements[at] = item.getObj();
  321 + ++a->sp->size;
268 } else { 322 } else {
269 - elements.insert(elements.cbegin() + at, item.getObj()); 323 + a->elements.insert(a->elements.cbegin() + at, item.getObj());
270 } 324 }
271 } 325 }
272 return true; 326 return true;
273 } 327 }
274 328
275 void 329 void
276 -QPDF_Array::push_back(QPDFObjectHandle const& item) 330 +Array::push_back(QPDFObjectHandle const& item)
277 { 331 {
  332 + auto a = array();
278 checkOwnership(item); 333 checkOwnership(item);
279 - if (sp) {  
280 - sp->elements[(sp->size)++] = item.getObj(); 334 + if (a->sp) {
  335 + a->sp->elements[(a->sp->size)++] = item.getObj();
281 } else { 336 } else {
282 - elements.push_back(item.getObj()); 337 + a->elements.push_back(item.getObj());
283 } 338 }
284 } 339 }
285 340
286 bool 341 bool
287 -QPDF_Array::erase(int at) 342 +Array::erase(int at)
288 { 343 {
  344 + auto a = array();
289 if (at < 0 || at >= size()) { 345 if (at < 0 || at >= size()) {
290 return false; 346 return false;
291 } 347 }
292 - if (sp) {  
293 - auto end = sp->elements.end();  
294 - if (auto iter = sp->elements.lower_bound(at); iter != end) { 348 + if (a->sp) {
  349 + auto end = a->sp->elements.end();
  350 + if (auto iter = a->sp->elements.lower_bound(at); iter != end) {
295 if (iter->first == at) { 351 if (iter->first == at) {
296 iter++; 352 iter++;
297 - sp->elements.erase(at); 353 + a->sp->elements.erase(at);
298 } 354 }
299 355
300 while (iter != end) { 356 while (iter != end) {
301 - auto nh = sp->elements.extract(iter++); 357 + auto nh = a->sp->elements.extract(iter++);
302 --nh.key(); 358 --nh.key();
303 - sp->elements.insert(std::move(nh)); 359 + a->sp->elements.insert(std::move(nh));
304 } 360 }
305 } 361 }
306 - --(sp->size); 362 + --(a->sp->size);
307 } else { 363 } else {
308 - elements.erase(elements.cbegin() + at); 364 + a->elements.erase(a->elements.cbegin() + at);
309 } 365 }
310 return true; 366 return true;
311 } 367 }
libqpdf/qpdf/QPDFObjectHandle_private.hh
@@ -4,10 +4,40 @@ @@ -4,10 +4,40 @@
4 #include <qpdf/QPDFObjectHandle.hh> 4 #include <qpdf/QPDFObjectHandle.hh>
5 5
6 #include <qpdf/QPDFObject_private.hh> 6 #include <qpdf/QPDFObject_private.hh>
  7 +#include <qpdf/QPDF_Array.hh>
7 #include <qpdf/QPDF_Dictionary.hh> 8 #include <qpdf/QPDF_Dictionary.hh>
8 9
9 namespace qpdf 10 namespace qpdf
10 { 11 {
  12 + class Array final: public BaseHandle
  13 + {
  14 + public:
  15 + explicit Array(std::shared_ptr<QPDFObject> const& obj) :
  16 + BaseHandle(obj)
  17 + {
  18 + }
  19 +
  20 + explicit Array(std::shared_ptr<QPDFObject>&& obj) :
  21 + BaseHandle(std::move(obj))
  22 + {
  23 + }
  24 +
  25 + int size() const;
  26 + std::pair<bool, QPDFObjectHandle> at(int n) const;
  27 + bool setAt(int at, QPDFObjectHandle const& oh);
  28 + bool insert(int at, QPDFObjectHandle const& item);
  29 + void push_back(QPDFObjectHandle const& item);
  30 + bool erase(int at);
  31 +
  32 + std::vector<QPDFObjectHandle> getAsVector() const;
  33 + void setFromVector(std::vector<QPDFObjectHandle> const& items);
  34 +
  35 + private:
  36 + QPDF_Array* array() const;
  37 + void checkOwnership(QPDFObjectHandle const& item) const;
  38 + QPDFObjectHandle null() const;
  39 + };
  40 +
11 // BaseDictionary is only used as a base class. It does not contain any methods exposed in the 41 // BaseDictionary is only used as a base class. It does not contain any methods exposed in the
12 // public API. 42 // public API.
13 class BaseDictionary: public BaseHandle 43 class BaseDictionary: public BaseHandle
@@ -58,6 +88,19 @@ namespace qpdf @@ -58,6 +88,19 @@ namespace qpdf
58 88
59 } // namespace qpdf 89 } // namespace qpdf
60 90
  91 +inline qpdf::Array
  92 +QPDFObjectHandle::as_array(qpdf::typed options) const
  93 +{
  94 + if (options & qpdf::error) {
  95 + assertType("array", false);
  96 + }
  97 + if (options & qpdf::any_flag || type_code() == ::ot_array ||
  98 + (options & qpdf::optional && type_code() == ::ot_null)) {
  99 + return qpdf::Array(obj);
  100 + }
  101 + return qpdf::Array({});
  102 +}
  103 +
61 inline qpdf::Dictionary 104 inline qpdf::Dictionary
62 QPDFObjectHandle::as_dictionary(qpdf::typed options) const 105 QPDFObjectHandle::as_dictionary(qpdf::typed options) const
63 { 106 {
libqpdf/qpdf/QPDF_Array.hh
1 #ifndef QPDF_ARRAY_HH 1 #ifndef QPDF_ARRAY_HH
2 #define QPDF_ARRAY_HH 2 #define QPDF_ARRAY_HH
3 3
  4 +#include <qpdf/QPDFObjectHandle.hh>
  5 +#include <qpdf/QPDFObject_private.hh>
4 #include <qpdf/QPDFValue.hh> 6 #include <qpdf/QPDFValue.hh>
5 7
6 #include <map> 8 #include <map>
@@ -25,25 +27,20 @@ class QPDF_Array: public QPDFValue @@ -25,25 +27,20 @@ class QPDF_Array: public QPDFValue
25 void writeJSON(int json_version, JSON::Writer& p) override; 27 void writeJSON(int json_version, JSON::Writer& p) override;
26 void disconnect() override; 28 void disconnect() override;
27 29
28 - int  
29 - size() const noexcept  
30 - {  
31 - return sp ? sp->size : int(elements.size());  
32 - }  
33 - std::pair<bool, QPDFObjectHandle> at(int n) const noexcept;  
34 - bool setAt(int n, QPDFObjectHandle const& oh);  
35 - std::vector<QPDFObjectHandle> getAsVector() const;  
36 - void setFromVector(std::vector<QPDFObjectHandle> const& items);  
37 - bool insert(int at, QPDFObjectHandle const& item);  
38 - void push_back(QPDFObjectHandle const& item);  
39 - bool erase(int at);  
40 -  
41 private: 30 private:
  31 + friend class qpdf::Array;
42 QPDF_Array(); 32 QPDF_Array();
43 QPDF_Array(QPDF_Array const&); 33 QPDF_Array(QPDF_Array const&);
44 QPDF_Array(std::vector<QPDFObjectHandle> const& items); 34 QPDF_Array(std::vector<QPDFObjectHandle> const& items);
45 QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse); 35 QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse);
46 36
  37 + int
  38 + size() const
  39 + {
  40 + return sp ? sp->size : int(elements.size());
  41 + }
  42 + void setFromVector(std::vector<QPDFObjectHandle> const& items);
  43 +
47 void checkOwnership(QPDFObjectHandle const& item) const; 44 void checkOwnership(QPDFObjectHandle const& item) const;
48 45
49 std::unique_ptr<Sparse> sp; 46 std::unique_ptr<Sparse> sp;
libtests/sparse_array.cc
1 #include <qpdf/assert_test.h> 1 #include <qpdf/assert_test.h>
2 2
3 #include <qpdf/QPDF.hh> 3 #include <qpdf/QPDF.hh>
4 -#include <qpdf/QPDFObjectHandle.hh> 4 +#include <qpdf/QPDFObjectHandle_private.hh>
5 #include <qpdf/QPDFObject_private.hh> 5 #include <qpdf/QPDFObject_private.hh>
6 #include <qpdf/QPDF_Array.hh> 6 #include <qpdf/QPDF_Array.hh>
7 7
@@ -11,7 +11,7 @@ int @@ -11,7 +11,7 @@ int
11 main() 11 main()
12 { 12 {
13 auto obj = QPDF_Array::create({}, true); 13 auto obj = QPDF_Array::create({}, true);
14 - QPDF_Array& a = *obj->as<QPDF_Array>(); 14 + auto a = qpdf::Array(obj);
15 15
16 assert(a.size() == 0); 16 assert(a.size() == 0);
17 17
@@ -89,15 +89,15 @@ main() @@ -89,15 +89,15 @@ main()
89 pdf.emptyPDF(); 89 pdf.emptyPDF();
90 90
91 obj = QPDF_Array::create({10, "null"_qpdf.getObj()}, true); 91 obj = QPDF_Array::create({10, "null"_qpdf.getObj()}, true);
92 - QPDF_Array& b = *obj->as<QPDF_Array>(); 92 + auto b = qpdf::Array(obj);
93 b.setAt(5, pdf.newIndirectNull()); 93 b.setAt(5, pdf.newIndirectNull());
94 b.setAt(7, "[0 1 2 3]"_qpdf); 94 b.setAt(7, "[0 1 2 3]"_qpdf);
95 assert(b.at(3).second.isNull()); 95 assert(b.at(3).second.isNull());
96 assert(b.at(8).second.isNull()); 96 assert(b.at(8).second.isNull());
97 assert(b.at(5).second.isIndirect()); 97 assert(b.at(5).second.isIndirect());
98 - assert(b.unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]");  
99 - auto c = b.copy(true);  
100 - auto d = b.copy(false); 98 + assert(obj->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]");
  99 + auto c = obj->copy(true);
  100 + auto d = obj->copy(false);
101 b.at(7).second.setArrayItem(2, "42"_qpdf); 101 b.at(7).second.setArrayItem(2, "42"_qpdf);
102 assert(c->unparse() == "[ null null null null null 3 0 R null [ 0 1 42 3 ] null null ]"); 102 assert(c->unparse() == "[ null null null null null 3 0 R null [ 0 1 42 3 ] null null ]");
103 assert(d->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]"); 103 assert(d->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]");