Commit 7e9c6c74bb3831b0f3ccf8fd0468d5a04cef68bb

Authored by m-holger
Committed by GitHub
2 parents db288584 e3ce9e17

Merge pull request #1523 from m-holger/oh_array

Refactor private API class Array
include/qpdf/ObjectHandle.hh
... ... @@ -74,7 +74,6 @@ namespace qpdf
74 74 }
75 75  
76 76 QPDFObjectHandle operator[](size_t n) const;
77   -
78 77 QPDFObjectHandle operator[](int n) const;
79 78  
80 79 std::shared_ptr<QPDFObject> copy(bool shallow = false) const;
... ... @@ -104,11 +103,18 @@ namespace qpdf
104 103 BaseHandle& operator=(BaseHandle const&) = default;
105 104 BaseHandle(BaseHandle&&) = default;
106 105 BaseHandle& operator=(BaseHandle&&) = default;
  106 +
  107 + inline BaseHandle(QPDFObjectHandle const& oh);
  108 + inline BaseHandle(QPDFObjectHandle&& oh);
  109 +
107 110 ~BaseHandle() = default;
108 111  
109 112 template <typename T>
110 113 T* as() const;
111 114  
  115 + inline void assign(qpdf_object_type_e required, BaseHandle const& other);
  116 + inline void assign(qpdf_object_type_e required, BaseHandle&& other);
  117 +
112 118 std::string description() const;
113 119 std::runtime_error type_error(char const* expected_type) const;
114 120 QPDFExc type_error(char const* expected_type, std::string const& message) const;
... ...
libqpdf/NNTree.cc
... ... @@ -11,6 +11,8 @@
11 11 #include <exception>
12 12 #include <utility>
13 13  
  14 +using namespace qpdf;
  15 +
14 16 static std::string
15 17 get_description(QPDFObjectHandle const& node)
16 18 {
... ... @@ -66,7 +68,7 @@ NNTreeIterator::updateIValue(bool allow_invalid)
66 68 ivalue.second = QPDFObjectHandle();
67 69 return;
68 70 }
69   - auto items = node.getKey(impl.details.itemsKey());
  71 + Array items = node.getKey(impl.details.itemsKey());
70 72 if (!std::cmp_less(item_number + 1, items.size())) {
71 73 impl.error(node, "update ivalue: items array is too short");
72 74 }
... ... @@ -85,7 +87,7 @@ NNTreeIterator::getNextKid(PathElement&amp; pe, bool backward)
85 87 {
86 88 while (true) {
87 89 pe.kid_number += backward ? -1 : 1;
88   - auto kids = pe.node.getKey("/Kids");
  90 + Array kids = pe.node.getKey("/Kids");
89 91 if (pe.kid_number >= 0 && std::cmp_less(pe.kid_number, kids.size())) {
90 92 auto result = kids[pe.kid_number];
91 93 if (result.isDictionary() &&
... ... @@ -117,7 +119,7 @@ NNTreeIterator::increment(bool backward)
117 119  
118 120 while (valid()) {
119 121 item_number += backward ? -2 : 2;
120   - auto items = node.getKey(impl.details.itemsKey());
  122 + Array items = node.getKey(impl.details.itemsKey());
121 123 if (item_number < 0 || std::cmp_greater_equal(item_number, items.size())) {
122 124 bool found = false;
123 125 setItemNumber(QPDFObjectHandle(), -1);
... ... @@ -152,9 +154,9 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
152 154 a_node.removeKey("/Limits");
153 155 break;
154 156 }
155   - auto kids = a_node.getKey("/Kids");
156   - size_t nkids = kids.isArray() ? kids.size() : 0;
157   - auto items = a_node.getKey(impl.details.itemsKey());
  157 + Array kids = a_node.getKey("/Kids");
  158 + size_t nkids = kids.size();
  159 + Array items = a_node.getKey(impl.details.itemsKey());
158 160 size_t nitems = items.size();
159 161  
160 162 bool changed = true;
... ... @@ -167,8 +169,8 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
167 169 auto first_kid = kids[0];
168 170 auto last_kid = kids[nkids - 1u];
169 171 if (first_kid.isDictionary() && last_kid.isDictionary()) {
170   - auto first_limits = first_kid.getKey("/Limits");
171   - auto last_limits = last_kid.getKey("/Limits");
  172 + Array first_limits = first_kid.getKey("/Limits");
  173 + Array last_limits = last_kid.getKey("/Limits");
172 174 if (first_limits.size() >= 2 && last_limits.size() >= 2) {
173 175 first = first_limits[0];
174 176 last = last_limits[1];
... ... @@ -176,16 +178,14 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
176 178 }
177 179 }
178 180 if (first && last) {
179   - auto limits = QPDFObjectHandle::newArray();
180   - limits.appendItem(first);
181   - limits.appendItem(last);
182   - auto olimits = a_node.getKey("/Limits");
  181 + Array limits({first, last});
  182 + Array olimits = a_node.getKey("/Limits");
183 183 if (olimits.size() == 2) {
184 184 auto ofirst = olimits[0];
185 185 auto olast = olimits[1];
186 186 if (impl.details.keyValid(ofirst) && impl.details.keyValid(olast) &&
187   - (impl.details.compareKeys(first, ofirst) == 0) &&
188   - (impl.details.compareKeys(last, olast) == 0)) {
  187 + impl.details.compareKeys(first, ofirst) == 0 &&
  188 + impl.details.compareKeys(last, olast) == 0) {
189 189 changed = false;
190 190 }
191 191 }
... ... @@ -240,24 +240,23 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
240 240 }
241 241  
242 242 // Find the array we actually need to split, which is either this node's kids or items.
243   - auto kids = to_split.getKey("/Kids");
244   - int nkids = kids.isArray() ? static_cast<int>(kids.size()) : 0;
245   - auto items = to_split.getKey(impl.details.itemsKey());
246   - int nitems = items.isArray() ? static_cast<int>(items.size()) : 0;
  243 + Array kids = to_split.getKey("/Kids");
  244 + size_t nkids = kids.size();
  245 + Array items = to_split.getKey(impl.details.itemsKey());
  246 + size_t nitems = items.size();
247 247  
248   - QPDFObjectHandle first_half;
249   - int n = 0;
  248 + Array first_half;
  249 + size_t n = 0;
250 250 std::string key;
251   - int threshold = 0;
  251 + size_t threshold = static_cast<size_t>(impl.split_threshold);
252 252 if (nkids > 0) {
253 253 first_half = kids;
254 254 n = nkids;
255   - threshold = impl.split_threshold;
256 255 key = "/Kids";
257 256 } else if (nitems > 0) {
258 257 first_half = items;
259 258 n = nitems;
260   - threshold = 2 * impl.split_threshold;
  259 + threshold *= 2;
261 260 key = impl.details.itemsKey();
262 261 } else {
263 262 throw std::logic_error("NNTreeIterator::split called on invalid node");
... ... @@ -289,8 +288,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
289 288  
290 289 auto first_node = impl.qpdf.makeIndirectObject(QPDFObjectHandle::newDictionary());
291 290 first_node.replaceKey(key, first_half);
292   - QPDFObjectHandle new_kids = QPDFObjectHandle::newArray();
293   - new_kids.appendItem(first_node);
  291 + Array new_kids;
  292 + new_kids.push_back(first_node);
294 293 to_split.removeKey("/Limits"); // already shouldn't be there for root
295 294 to_split.removeKey(impl.details.itemsKey());
296 295 to_split.replaceKey("/Kids", new_kids);
... ... @@ -310,11 +309,11 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
310 309  
311 310 // Create a second half array, and transfer the second half of the items into the second half
312 311 // array.
313   - QPDFObjectHandle second_half = QPDFObjectHandle::newArray();
314   - int start_idx = ((n / 2) & ~1);
  312 + Array second_half;
  313 + auto start_idx = static_cast<int>((n / 2) & ~1u);
315 314 while (std::cmp_greater(first_half.size(), start_idx)) {
316   - second_half.appendItem(first_half[start_idx]);
317   - first_half.eraseItem(start_idx);
  315 + second_half.push_back(first_half[start_idx]);
  316 + first_half.erase(start_idx);
318 317 }
319 318 resetLimits(to_split, parent);
320 319  
... ... @@ -331,8 +330,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
331 330 // kid_number to traverse through it. We need to update to_split's path element, or the node if
332 331 // this is a leaf, so that the kid/item number points to the right place.
333 332  
334   - auto parent_kids = parent->node.getKey("/Kids");
335   - parent_kids.insertItem(parent->kid_number + 1, second_node);
  333 + Array parent_kids = parent->node.getKey("/Kids");
  334 + parent_kids.insert(parent->kid_number + 1, second_node);
336 335 auto cur_elem = parent;
337 336 ++cur_elem; // points to end() for leaf nodes
338 337 int old_idx = (is_leaf ? item_number : cur_elem->kid_number);
... ... @@ -367,22 +366,21 @@ void
367 366 NNTreeIterator::insertAfter(QPDFObjectHandle const& key, QPDFObjectHandle const& value)
368 367 {
369 368 if (!valid()) {
370   - QTC::TC("qpdf", "NNTree insertAfter inserts first");
371 369 impl.insertFirst(key, value);
372 370 deepen(impl.oh, true, false);
373 371 return;
374 372 }
375 373  
376   - auto items = node.getKey(impl.details.itemsKey());
  374 + Array items = node.getKey(impl.details.itemsKey());
  375 + if (!items) {
  376 + impl.error(node, "node contains no items array");
  377 + }
377 378  
378 379 if (std::cmp_less(items.size(), item_number + 2)) {
379   - if (!items.isArray()) {
380   - impl.error(node, "node contains no items array");
381   - }
382 380 impl.error(node, "insert: items array is too short");
383 381 }
384   - items.insertItem(item_number + 2, key);
385   - items.insertItem(item_number + 3, value);
  382 + items.insert(item_number + 2, key);
  383 + items.insert(item_number + 3, value);
386 384 resetLimits(node, lastPathElement());
387 385 split(node, lastPathElement());
388 386 increment(false);
... ... @@ -396,14 +394,14 @@ NNTreeIterator::remove()
396 394 if (!valid()) {
397 395 throw std::logic_error("attempt made to remove an invalid iterator");
398 396 }
399   - auto items = node.getKey(impl.details.itemsKey());
  397 + Array items = node.getKey(impl.details.itemsKey());
400 398 int nitems = static_cast<int>(items.size());
401 399 if (std::cmp_greater(item_number + 2, nitems)) {
402 400 impl.error(node, "found short items array while removing an item");
403 401 }
404 402  
405   - items.eraseItem(item_number);
406   - items.eraseItem(item_number);
  403 + items.erase(item_number);
  404 + items.erase(item_number);
407 405 nitems -= 2;
408 406  
409 407 if (nitems > 0) {
... ... @@ -443,8 +441,8 @@ NNTreeIterator::remove()
443 441 auto element = lastPathElement();
444 442 auto parent = element;
445 443 --parent;
446   - auto kids = element->node.getKey("/Kids");
447   - kids.eraseItem(element->kid_number);
  444 + Array kids = element->node.getKey("/Kids");
  445 + kids.erase(element->kid_number);
448 446 auto nkids = kids.size();
449 447 if (nkids > 0) {
450 448 // The logic here is similar to the items case.
... ... @@ -462,7 +460,7 @@ NNTreeIterator::remove()
462 460 }
463 461 } else {
464 462 // Next kid is in deleted kid's position
465   - deepen(kids.getArrayItem(element->kid_number), true, true);
  463 + deepen(kids.get(element->kid_number), true, true);
466 464 }
467 465 return;
468 466 }
... ... @@ -470,7 +468,7 @@ NNTreeIterator::remove()
470 468 if (parent == path.end()) {
471 469 // We erased the very last item. Convert the root to an empty items array.
472 470 element->node.removeKey("/Kids");
473   - element->node.replaceKey(impl.details.itemsKey(), QPDFObjectHandle::newArray());
  471 + element->node.replaceKey(impl.details.itemsKey(), Array());
474 472 path.clear();
475 473 setItemNumber(impl.oh, -1);
476 474 return;
... ... @@ -527,10 +525,7 @@ NNTreeIterator::operator==(NNTreeIterator const&amp; other) const
527 525 ++tpi;
528 526 ++opi;
529 527 }
530   - if (item_number != other.item_number) {
531   - return false;
532   - }
533   - return true;
  528 + return item_number == other.item_number;
534 529 }
535 530  
536 531 void
... ... @@ -574,27 +569,30 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
574 569 return fail(a_node, "non-dictionary node while traversing name/number tree");
575 570 }
576 571  
577   - auto items = a_node.getKey(impl.details.itemsKey());
  572 + Array items = a_node.getKey(impl.details.itemsKey());
578 573 int nitems = static_cast<int>(items.size());
579 574 if (nitems > 1) {
580 575 setItemNumber(a_node, first ? 0 : nitems - 2);
581 576 break;
582 577 }
583 578  
584   - auto kids = a_node.getKey("/Kids");
585   - int nkids = kids.isArray() ? static_cast<int>(kids.size()) : 0;
  579 + Array kids = a_node.getKey("/Kids");
  580 + int nkids = static_cast<int>(kids.size());
586 581 if (nkids > 0) {
587 582 int kid_number = first ? 0 : nkids - 1;
588 583 addPathElement(a_node, kid_number);
589 584 auto next = kids[kid_number];
590   - if (!next.isIndirect()) {
  585 + if (!next) {
  586 + return fail(a_node, "kid number " + std::to_string(kid_number) + " is invalid");
  587 + }
  588 + if (!next.indirect()) {
591 589 if (impl.auto_repair) {
592 590 impl.warn(
593 591 a_node,
594 592 "converting kid number " + std::to_string(kid_number) +
595 593 " to an indirect object");
596 594 next = impl.qpdf.makeIndirectObject(next);
597   - kids.setArrayItem(kid_number, next);
  595 + kids.set(kid_number, next);
598 596 } else {
599 597 impl.warn(
600 598 a_node,
... ... @@ -602,7 +600,7 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
602 600 }
603 601 }
604 602 a_node = next;
605   - } else if (allow_empty && items.isArray()) {
  603 + } else if (allow_empty && items) {
606 604 setItemNumber(a_node, -1);
607 605 break;
608 606 } else {
... ... @@ -655,8 +653,8 @@ NNTreeImpl::last()
655 653 int
656 654 NNTreeImpl::withinLimits(QPDFObjectHandle const& key, QPDFObjectHandle const& node)
657 655 {
658   - auto limits = node.getKey("/Limits");
659   - if (!(limits.size() >= 2 && details.keyValid(limits[0]) && details.keyValid(limits[1]))) {
  656 + Array limits = node.getKey("/Limits");
  657 + if (!(details.keyValid(limits[0]) && details.keyValid(limits[1]))) {
660 658 error(node, "node is missing /Limits");
661 659 }
662 660 if (details.compareKeys(key, limits[0]) < 0) {
... ... @@ -734,7 +732,7 @@ void
734 732 NNTreeImpl::repair()
735 733 {
736 734 auto new_node = QPDFObjectHandle::newDictionary();
737   - new_node.replaceKey(details.itemsKey(), QPDFObjectHandle::newArray());
  735 + new_node.replaceKey(details.itemsKey(), Array());
738 736 NNTreeImpl repl(details, qpdf, new_node, false);
739 737 for (auto const& i: *this) {
740 738 repl.insert(i.first, i.second);
... ... @@ -750,7 +748,6 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found)
750 748 return findInternal(key, return_prev_if_not_found);
751 749 } catch (QPDFExc& e) {
752 750 if (auto_repair) {
753   - QTC::TC("qpdf", "NNTree repair");
754 751 warn(oh, std::string("attempting to repair after error: ") + e.what());
755 752 repair();
756 753 return findInternal(key, return_prev_if_not_found);
... ... @@ -784,7 +781,7 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
784 781 error(node, "loop detected in find");
785 782 }
786 783  
787   - auto items = node.getKey(details.itemsKey());
  784 + Array items = node.getKey(details.itemsKey());
788 785 size_t nitems = items.size();
789 786 if (nitems > 1) {
790 787 int idx = binarySearch(
... ... @@ -795,8 +792,8 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
795 792 return result;
796 793 }
797 794  
798   - auto kids = node.getKey("/Kids");
799   - size_t nkids = kids.isArray() ? kids.size() : 0;
  795 + Array kids = node.getKey("/Kids");
  796 + size_t nkids = kids.size();
800 797 if (nkids > 0) {
801 798 int idx = binarySearch(key, kids, nkids, true, &NNTreeImpl::compareKeyKid);
802 799 if (idx == -1) {
... ... @@ -814,15 +811,15 @@ NNTreeImpl::iterator
814 811 NNTreeImpl::insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value)
815 812 {
816 813 auto iter = begin();
817   - QPDFObjectHandle items;
  814 + Array items(nullptr);
818 815 if (iter.node.isDictionary()) {
819 816 items = iter.node.getKey(details.itemsKey());
820 817 }
821   - if (!items.isArray()) {
  818 + if (!items) {
822 819 error(oh, "unable to find a valid items node");
823 820 }
824   - items.insertItem(0, key);
825   - items.insertItem(1, value);
  821 + items.insert(0, key);
  822 + items.insert(1, value);
826 823 iter.setItemNumber(iter.node, 0);
827 824 iter.resetLimits(iter.node, iter.lastPathElement());
828 825 iter.split(iter.node, iter.lastPathElement());
... ... @@ -836,8 +833,8 @@ NNTreeImpl::insert(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; value)
836 833 if (!iter.valid()) {
837 834 return insertFirst(key, value);
838 835 } else if (details.compareKeys(key, iter->first) == 0) {
839   - auto items = iter.node.getKey(details.itemsKey());
840   - items.setArrayItem(iter.item_number + 1, value);
  836 + Array items = iter.node.getKey(details.itemsKey());
  837 + items.set(iter.item_number + 1, value);
841 838 iter.updateIValue();
842 839 } else {
843 840 iter.insertAfter(key, value);
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -486,7 +486,7 @@ BaseHandle::write_json(int json_version, JSON::Writer&amp; p) const
486 486 } else {
487 487 for (auto const& item: a.elements) {
488 488 p.writeNext();
489   - auto item_og = item.getObj()->getObjGen();
  489 + auto item_og = item.id_gen();
490 490 if (item_og.isIndirect()) {
491 491 p << "\"" << item_og.unparse(' ') << " R\"";
492 492 } else {
... ... @@ -1234,11 +1234,10 @@ QPDFObjectHandle::arrayOrStreamToStreamArray(
1234 1234 if (auto array = as_array(strict)) {
1235 1235 int n_items = static_cast<int>(array.size());
1236 1236 for (int i = 0; i < n_items; ++i) {
1237   - QPDFObjectHandle item = array.at(i).second;
  1237 + QPDFObjectHandle item = array[i];
1238 1238 if (item.isStream()) {
1239 1239 result.emplace_back(item);
1240 1240 } else {
1241   - QTC::TC("qpdf", "QPDFObjectHandle non-stream in stream array");
1242 1241 item.warn(
1243 1242 {qpdf_e_damaged_pdf,
1244 1243 "",
... ...
libqpdf/QPDF_Array.cc
... ... @@ -2,6 +2,7 @@
2 2  
3 3 #include <qpdf/QTC.hh>
4 4  
  5 +#include <array>
5 6 #include <utility>
6 7  
7 8 using namespace std::literals;
... ... @@ -60,6 +61,21 @@ Array::array() const
60 61 return nullptr; // unreachable
61 62 }
62 63  
  64 +Array::Array(bool empty) :
  65 + BaseHandle(empty ? QPDFObject::create<QPDF_Array>() : nullptr)
  66 +{
  67 +}
  68 +
  69 +Array::Array(std::vector<QPDFObjectHandle> const& items) :
  70 + BaseHandle(QPDFObject::create<QPDF_Array>(items))
  71 +{
  72 +}
  73 +
  74 +Array::Array(std::vector<QPDFObjectHandle>&& items) :
  75 + BaseHandle(QPDFObject::create<QPDF_Array>(std::move(items)))
  76 +{
  77 +}
  78 +
63 79 Array::iterator
64 80 Array::begin()
65 81 {
... ... @@ -159,22 +175,58 @@ Array::null() const
159 175 size_t
160 176 Array::size() const
161 177 {
162   - auto a = array();
163   - return a->sp ? a->sp->size : a->elements.size();
  178 + if (auto a = as<QPDF_Array>()) {
  179 + return a->sp ? a->sp->size : a->elements.size();
  180 + }
  181 + return 0;
164 182 }
165 183  
166   -std::pair<bool, QPDFObjectHandle>
167   -Array::at(int n) const
  184 +QPDFObjectHandle const&
  185 +Array::operator[](size_t n) const
168 186 {
169   - auto a = array();
170   - if (n < 0 || std::cmp_greater_equal(n, size())) {
171   - return {false, {}};
  187 + static const QPDFObjectHandle null_obj;
  188 + auto a = as<QPDF_Array>();
  189 + if (!a) {
  190 + return null_obj;
  191 + }
  192 + if (a->sp) {
  193 + auto const& iter = a->sp->elements.find(n);
  194 + return iter == a->sp->elements.end() ? null_obj : iter->second;
172 195 }
  196 + return n >= a->elements.size() ? null_obj : a->elements[n];
  197 +}
  198 +
  199 +QPDFObjectHandle const&
  200 +Array::operator[](int n) const
  201 +{
  202 + static const QPDFObjectHandle null_obj;
  203 + if (n < 0) {
  204 + return null_obj;
  205 + }
  206 + return (*this)[static_cast<size_t>(n)];
  207 +}
  208 +
  209 +QPDFObjectHandle
  210 +Array::get(size_t n) const
  211 +{
  212 + if (n >= size()) {
  213 + return {};
  214 + }
  215 + auto a = array();
173 216 if (!a->sp) {
174   - return {true, a->elements[to_s(n)]};
  217 + return a->elements[n];
175 218 }
176   - auto const& iter = a->sp->elements.find(to_s(n));
177   - return {true, iter == a->sp->elements.end() ? null() : iter->second};
  219 + auto const& iter = a->sp->elements.find(n);
  220 + return iter == a->sp->elements.end() ? null() : iter->second;
  221 +}
  222 +
  223 +QPDFObjectHandle
  224 +Array::get(int n) const
  225 +{
  226 + if (n < 0) {
  227 + return {};
  228 + }
  229 + return get(to_s(n));
178 230 }
179 231  
180 232 std::vector<QPDFObjectHandle>
... ... @@ -196,21 +248,30 @@ Array::getAsVector() const
196 248 }
197 249  
198 250 bool
199   -Array::setAt(int at, QPDFObjectHandle const& oh)
  251 +Array::set(size_t at, QPDFObjectHandle const& oh)
200 252 {
201   - if (at < 0 || std::cmp_greater_equal(at, size())) {
  253 + if (at >= size()) {
202 254 return false;
203 255 }
204 256 auto a = array();
205 257 checkOwnership(oh);
206 258 if (a->sp) {
207   - a->sp->elements[to_s(at)] = oh;
  259 + a->sp->elements[at] = oh;
208 260 } else {
209   - a->elements[to_s(at)] = oh;
  261 + a->elements[at] = oh;
210 262 }
211 263 return true;
212 264 }
213 265  
  266 +bool
  267 +Array::set(int at, QPDFObjectHandle const& oh)
  268 +{
  269 + if (at < 0) {
  270 + return false;
  271 + }
  272 + return set(to_s(at), oh);
  273 +}
  274 +
214 275 void
215 276 Array::setFromVector(std::vector<QPDFObjectHandle> const& v)
216 277 {
... ... @@ -224,43 +285,48 @@ Array::setFromVector(std::vector&lt;QPDFObjectHandle&gt; const&amp; v)
224 285 }
225 286  
226 287 bool
227   -Array::insert(int at_i, QPDFObjectHandle const& item)
  288 +Array::insert(size_t at, QPDFObjectHandle const& item)
228 289 {
229 290 auto a = array();
230 291 size_t sz = size();
231   - if (at_i < 0) {
232   - return false;
233   - }
234   - size_t at = to_s(at_i);
235 292 if (at > sz) {
236 293 return false;
237 294 }
  295 + checkOwnership(item);
238 296 if (at == sz) {
239 297 // As special case, also allow insert beyond the end
240 298 push_back(item);
241 299 return true;
242 300 }
243   - checkOwnership(item);
244   - if (a->sp) {
245   - auto iter = a->sp->elements.crbegin();
246   - while (iter != a->sp->elements.crend()) {
247   - auto key = (iter++)->first;
248   - if (key >= at) {
249   - auto nh = a->sp->elements.extract(key);
250   - ++nh.key();
251   - a->sp->elements.insert(std::move(nh));
252   - } else {
253   - break;
254   - }
  301 + if (!a->sp) {
  302 + a->elements.insert(a->elements.cbegin() + to_i(at), item);
  303 + return true;
  304 + }
  305 + auto iter = a->sp->elements.crbegin();
  306 + while (iter != a->sp->elements.crend()) {
  307 + auto key = (iter++)->first;
  308 + if (key >= at) {
  309 + auto nh = a->sp->elements.extract(key);
  310 + ++nh.key();
  311 + a->sp->elements.insert(std::move(nh));
  312 + } else {
  313 + break;
255 314 }
256   - a->sp->elements[at] = item.getObj();
257   - ++a->sp->size;
258   - } else {
259   - a->elements.insert(a->elements.cbegin() + at_i, item.getObj());
260 315 }
  316 + a->sp->elements[at] = item;
  317 + ++a->sp->size;
261 318 return true;
262 319 }
263 320  
  321 +bool
  322 +Array::insert(int at_i, QPDFObjectHandle const& item)
  323 +{
  324 + if (at_i < 0) {
  325 + return false;
  326 + }
  327 + return insert(to_s(at_i), item);
  328 +}
  329 +
264 330 void
265 331 Array::push_back(QPDFObjectHandle const& item)
266 332 {
... ... @@ -274,61 +340,68 @@ Array::push_back(QPDFObjectHandle const&amp; item)
274 340 }
275 341  
276 342 bool
277   -Array::erase(int at_i)
  343 +Array::erase(size_t at)
278 344 {
279 345 auto a = array();
280   - if (at_i < 0) {
281   - return false;
282   - }
283   - size_t at = to_s(at_i);
284 346 if (at >= size()) {
285 347 return false;
286 348 }
287   - if (a->sp) {
288   - auto end = a->sp->elements.end();
289   - if (auto iter = a->sp->elements.lower_bound(at); iter != end) {
290   - if (iter->first == at) {
291   - iter++;
292   - a->sp->elements.erase(at);
293   - }
  349 + if (!a->sp) {
  350 + a->elements.erase(a->elements.cbegin() + to_i(at));
  351 + return true;
  352 + }
  353 + auto end = a->sp->elements.end();
  354 + if (auto iter = a->sp->elements.lower_bound(at); iter != end) {
  355 + if (iter->first == at) {
  356 + iter++;
  357 + a->sp->elements.erase(at);
  358 + }
294 359  
295   - while (iter != end) {
296   - auto nh = a->sp->elements.extract(iter++);
297   - --nh.key();
298   - a->sp->elements.insert(std::move(nh));
299   - }
  360 + while (iter != end) {
  361 + auto nh = a->sp->elements.extract(iter++);
  362 + --nh.key();
  363 + a->sp->elements.insert(std::move(nh));
300 364 }
301   - --(a->sp->size);
302   - } else {
303   - a->elements.erase(a->elements.cbegin() + at_i);
304 365 }
  366 + --(a->sp->size);
305 367 return true;
306 368 }
307 369  
  370 +bool
  371 +Array::erase(int at_i)
  372 +{
  373 + if (at_i < 0) {
  374 + return false;
  375 + }
  376 + return erase(to_s(at_i));
  377 +}
  378 +
308 379 int
309 380 QPDFObjectHandle::getArrayNItems() const
310 381 {
311   - if (auto array = as_array(strict)) {
312   - return to_i(array.size());
  382 + auto s = size();
  383 + if (s > 1 || isArray()) {
  384 + return to_i(s);
313 385 }
314 386 typeWarning("array", "treating as empty");
315   - QTC::TC("qpdf", "QPDFObjectHandle array treating as empty");
316 387 return 0;
317 388 }
318 389  
319 390 QPDFObjectHandle
320 391 QPDFObjectHandle::getArrayItem(int n) const
321 392 {
322   - if (auto array = as_array(strict)) {
323   - if (auto const [success, oh] = array.at(n); success) {
324   - return oh;
325   - } else {
326   - objectWarning("returning null for out of bounds array access");
327   - QTC::TC("qpdf", "QPDFObjectHandle array bounds");
  393 + if (auto array = Array(*this)) {
  394 + if (auto result = array[n]) {
  395 + return result;
328 396 }
  397 + if (n >= 0 && std::cmp_less(n, array.size())) {
  398 + // sparse array null
  399 + return newNull();
  400 + }
  401 + objectWarning("returning null for out of bounds array access");
  402 +
329 403 } else {
330 404 typeWarning("array", "returning null");
331   - QTC::TC("qpdf", "QPDFObjectHandle array null for non-array");
332 405 }
333 406 static auto constexpr msg = " -> null returned from invalid array access"sv;
334 407 return QPDF_Null::create(obj, msg, "");
... ... @@ -337,69 +410,61 @@ QPDFObjectHandle::getArrayItem(int n) const
337 410 bool
338 411 QPDFObjectHandle::isRectangle() const
339 412 {
340   - if (auto array = as_array(strict)) {
341   - for (int i = 0; i < 4; ++i) {
342   - if (auto item = array.at(i).second; !item.isNumber()) {
343   - return false;
344   - }
  413 + Array array(*this);
  414 + for (auto const& oh: array) {
  415 + if (!oh.isNumber()) {
  416 + return false;
345 417 }
346   - return array.size() == 4;
347 418 }
348   - return false;
  419 + return array.size() == 4;
349 420 }
350 421  
351 422 bool
352 423 QPDFObjectHandle::isMatrix() const
353 424 {
354   - if (auto array = as_array(strict)) {
355   - for (int i = 0; i < 6; ++i) {
356   - if (auto item = array.at(i).second; !item.isNumber()) {
357   - return false;
358   - }
  425 + Array array(*this);
  426 + for (auto const& oh: array) {
  427 + if (!oh.isNumber()) {
  428 + return false;
359 429 }
360   - return array.size() == 6;
361 430 }
362   - return false;
  431 + return array.size() == 6;
363 432 }
364 433  
365 434 QPDFObjectHandle::Rectangle
366 435 QPDFObjectHandle::getArrayAsRectangle() const
367 436 {
368   - if (auto array = as_array(strict)) {
369   - if (array.size() != 4) {
  437 + Array array(*this);
  438 + if (array.size() != 4) {
  439 + return {};
  440 + }
  441 + std::array<double, 4> items;
  442 + for (size_t i = 0; i < 4; ++i) {
  443 + if (!array[i].getValueAsNumber(items[i])) {
370 444 return {};
371 445 }
372   - double items[4];
373   - for (int i = 0; i < 4; ++i) {
374   - if (auto item = array.at(i).second; !item.getValueAsNumber(items[i])) {
375   - return {};
376   - }
377   - }
378   - return {
379   - std::min(items[0], items[2]),
380   - std::min(items[1], items[3]),
381   - std::max(items[0], items[2]),
382   - std::max(items[1], items[3])};
383 446 }
384   - return {};
  447 + return {
  448 + std::min(items[0], items[2]),
  449 + std::min(items[1], items[3]),
  450 + std::max(items[0], items[2]),
  451 + std::max(items[1], items[3])};
385 452 }
386 453  
387 454 QPDFObjectHandle::Matrix
388 455 QPDFObjectHandle::getArrayAsMatrix() const
389 456 {
390   - if (auto array = as_array(strict)) {
391   - if (array.size() != 6) {
  457 + Array array(*this);
  458 + if (array.size() != 6) {
  459 + return {};
  460 + }
  461 + std::array<double, 6> items;
  462 + for (size_t i = 0; i < 6; ++i) {
  463 + if (!array[i].getValueAsNumber(items[i])) {
392 464 return {};
393 465 }
394   - double items[6];
395   - for (int i = 0; i < 6; ++i) {
396   - if (auto item = array.at(i).second; !item.getValueAsNumber(items[i])) {
397   - return {};
398   - }
399   - }
400   - return {items[0], items[1], items[2], items[3], items[4], items[5]};
401 466 }
402   - return {};
  467 + return {items[0], items[1], items[2], items[3], items[4], items[5]};
403 468 }
404 469  
405 470 std::vector<QPDFObjectHandle>
... ... @@ -417,13 +482,11 @@ void
417 482 QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item)
418 483 {
419 484 if (auto array = as_array(strict)) {
420   - if (!array.setAt(n, item)) {
  485 + if (!array.set(n, item)) {
421 486 objectWarning("ignoring attempt to set out of bounds array item");
422   - QTC::TC("qpdf", "QPDFObjectHandle set array bounds");
423 487 }
424 488 } else {
425 489 typeWarning("array", "ignoring attempt to set item");
426   - QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item");
427 490 }
428 491 }
429 492 void
... ... @@ -493,11 +556,9 @@ QPDFObjectHandle::eraseItem(int at)
493 556 QPDFObjectHandle
494 557 QPDFObjectHandle::eraseItemAndGetOld(int at)
495 558 {
496   - auto array = as_array(strict);
497   - auto result =
498   - (array && std::cmp_less(at, array.size()) && at >= 0) ? array.at(at).second : newNull();
  559 + auto result = Array(*this)[at];
499 560 eraseItem(at);
500   - return result;
  561 + return result ? result : newNull();
501 562 }
502 563  
503 564 size_t
... ... @@ -532,36 +593,13 @@ BaseHandle::size() const
532 593 QPDFObjectHandle
533 594 BaseHandle::operator[](size_t n) const
534 595 {
535   - switch (resolved_type_code()) {
536   - case ::ot_array:
537   - {
538   - auto a = as<QPDF_Array>();
539   - if (n >= a->size()) {
540   - return {};
541   - }
542   - return Array(obj).at(static_cast<int>(n)).second;
543   - }
544   - case ::ot_uninitialized:
545   - case ::ot_reserved:
546   - case ::ot_null:
547   - case ::ot_destroyed:
548   - case ::ot_unresolved:
549   - case ::ot_reference:
550   - return {};
551   - case ::ot_boolean:
552   - case ::ot_integer:
553   - case ::ot_real:
554   - case ::ot_string:
555   - case ::ot_name:
556   - case ::ot_dictionary:
557   - case ::ot_stream:
558   - case ::ot_inlineimage:
559   - case ::ot_operator:
560   - return {obj};
561   - default:
562   - throw std::logic_error("Unexpected type code in size"); // unreachable
563   - return {}; // unreachable
  596 + if (resolved_type_code() == ::ot_array) {
  597 + return Array(obj)[n];
564 598 }
  599 + if (n < size()) {
  600 + return *this;
  601 + }
  602 + return {};
565 603 }
566 604  
567 605 QPDFObjectHandle
... ...
libqpdf/QPDF_Stream.cc
... ... @@ -442,7 +442,7 @@ Stream::filterable(
442 442  
443 443 int i = -1;
444 444 for (auto& filter: filters) {
445   - auto d_obj = decode_array.at(++i).second;
  445 + auto d_obj = decode_array.get(++i);
446 446 if (!can_filter(decode_level, *filter, d_obj)) {
447 447 return false;
448 448 }
... ...
libqpdf/qpdf/QPDFObjectHandle_private.hh
... ... @@ -12,6 +12,30 @@ namespace qpdf
12 12 class Array final: public BaseHandle
13 13 {
14 14 public:
  15 + // Create an empty Array. If 'empty' is false, create a null Array.
  16 + Array(bool empty = true);
  17 +
  18 + Array(std::vector<QPDFObjectHandle> const& items);
  19 +
  20 + Array(std::vector<QPDFObjectHandle>&& items);
  21 +
  22 + Array(Array const& other) :
  23 + BaseHandle(other.obj)
  24 + {
  25 + }
  26 +
  27 + Array&
  28 + operator=(Array const& other)
  29 + {
  30 + if (obj != other.obj) {
  31 + obj = other.obj;
  32 + }
  33 + return *this;
  34 + }
  35 +
  36 + Array(Array&&) = default;
  37 + Array& operator=(Array&&) = default;
  38 +
15 39 explicit Array(std::shared_ptr<QPDFObject> const& obj) :
16 40 BaseHandle(obj)
17 41 {
... ... @@ -22,6 +46,34 @@ namespace qpdf
22 46 {
23 47 }
24 48  
  49 + Array(QPDFObjectHandle const& oh) :
  50 + BaseHandle(oh.resolved_type_code() == ::ot_array ? oh : QPDFObjectHandle())
  51 + {
  52 + }
  53 +
  54 + Array&
  55 + operator=(QPDFObjectHandle const& oh)
  56 + {
  57 + assign(::ot_array, oh);
  58 + return *this;
  59 + }
  60 +
  61 + Array(QPDFObjectHandle&& oh) :
  62 + BaseHandle(oh.resolved_type_code() == ::ot_array ? std::move(oh) : QPDFObjectHandle())
  63 + {
  64 + }
  65 +
  66 + Array&
  67 + operator=(QPDFObjectHandle&& oh)
  68 + {
  69 + assign(::ot_array, std::move(oh));
  70 + return *this;
  71 + }
  72 +
  73 + QPDFObjectHandle const& operator[](size_t n) const;
  74 +
  75 + QPDFObjectHandle const& operator[](int n) const;
  76 +
25 77 using iterator = std::vector<QPDFObjectHandle>::iterator;
26 78 using const_iterator = std::vector<QPDFObjectHandle>::const_iterator;
27 79 using const_reverse_iterator = std::vector<QPDFObjectHandle>::const_reverse_iterator;
... ... @@ -38,11 +90,16 @@ namespace qpdf
38 90  
39 91 const_reverse_iterator crend();
40 92  
  93 + // Return the number of elements in the array. Return 0 if the object is not an array.
41 94 size_t size() const;
42   - std::pair<bool, QPDFObjectHandle> at(int n) const;
43   - bool setAt(int at, QPDFObjectHandle const& oh);
  95 + QPDFObjectHandle get(size_t n) const;
  96 + QPDFObjectHandle get(int n) const;
  97 + bool set(size_t at, QPDFObjectHandle const& oh);
  98 + bool set(int at, QPDFObjectHandle const& oh);
  99 + bool insert(size_t at, QPDFObjectHandle const& item);
44 100 bool insert(int at, QPDFObjectHandle const& item);
45 101 void push_back(QPDFObjectHandle const& item);
  102 + bool erase(size_t at);
46 103 bool erase(int at);
47 104  
48 105 std::vector<QPDFObjectHandle> getAsVector() const;
... ... @@ -342,6 +399,32 @@ namespace qpdf
342 399 return nullptr;
343 400 }
344 401  
  402 + inline BaseHandle::BaseHandle(QPDFObjectHandle const& oh) :
  403 + obj(oh.obj)
  404 + {
  405 + }
  406 +
  407 + inline BaseHandle::BaseHandle(QPDFObjectHandle&& oh) :
  408 + obj(std::move(oh.obj))
  409 + {
  410 + }
  411 +
  412 + inline void
  413 + BaseHandle::assign(qpdf_object_type_e required, BaseHandle const& other)
  414 + {
  415 + if (obj != other.obj) {
  416 + obj = other.resolved_type_code() == required ? other.obj : nullptr;
  417 + }
  418 + }
  419 +
  420 + inline void
  421 + BaseHandle::assign(qpdf_object_type_e required, BaseHandle&& other)
  422 + {
  423 + if (obj != other.obj) {
  424 + obj = other.resolved_type_code() == required ? std::move(other.obj) : nullptr;
  425 + }
  426 + }
  427 +
345 428 inline QPDFObjGen
346 429 BaseHandle::id_gen() const
347 430 {
... ...
libtests/sparse_array.cc
... ... @@ -26,69 +26,69 @@ main()
26 26 a.push_back(QPDFObjectHandle::parse("null"));
27 27 a.push_back(QPDFObjectHandle::parse("/Quack"));
28 28 assert(a.size() == 5);
29   - assert(a.at(0).second.isInteger() && (a.at(0).second.getIntValue() == 1));
30   - assert(a.at(1).second.isString() && (a.at(1).second.getStringValue() == "potato"));
31   - assert(a.at(2).second.isNull());
32   - assert(a.at(3).second.isNull());
33   - assert(a.at(4).second.isName() && (a.at(4).second.getName() == "/Quack"));
  29 + assert(a[0].isInteger() && (a[0].getIntValue() == 1));
  30 + assert(a[1].isString() && (a[1].getStringValue() == "potato"));
  31 + assert(a[2].isNull());
  32 + assert(a[3].isNull());
  33 + assert(a[4].isName() && (a[4].getName() == "/Quack"));
34 34  
35 35 a.insert(4, QPDFObjectHandle::parse("/BeforeQuack"));
36 36 assert(a.size() == 6);
37   - assert(a.at(0).second.isInteger() && (a.at(0).second.getIntValue() == 1));
38   - assert(a.at(4).second.isName() && (a.at(4).second.getName() == "/BeforeQuack"));
39   - assert(a.at(5).second.isName() && (a.at(5).second.getName() == "/Quack"));
  37 + assert(a[0].isInteger() && (a[0].getIntValue() == 1));
  38 + assert(a[4].isName() && (a[4].getName() == "/BeforeQuack"));
  39 + assert(a[5].isName() && (a[5].getName() == "/Quack"));
40 40  
41 41 a.insert(2, QPDFObjectHandle::parse("/Third"));
42 42 assert(a.size() == 7);
43   - assert(a.at(1).second.isString() && (a.at(1).second.getStringValue() == "potato"));
44   - assert(a.at(2).second.isName() && (a.at(2).second.getName() == "/Third"));
45   - assert(a.at(3).second.isNull());
46   - assert(a.at(6).second.isName() && (a.at(6).second.getName() == "/Quack"));
  43 + assert(a[1].isString() && (a[1].getStringValue() == "potato"));
  44 + assert(a[2].isName() && (a[2].getName() == "/Third"));
  45 + assert(a[3].isNull());
  46 + assert(a[6].isName() && (a[6].getName() == "/Quack"));
47 47  
48 48 a.insert(0, QPDFObjectHandle::parse("/First"));
49 49 assert(a.size() == 8);
50   - assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
51   - assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
52   - assert(a.at(7).second.isName() && (a.at(7).second.getName() == "/Quack"));
  50 + assert(a[0].isName() && (a[0].getName() == "/First"));
  51 + assert(a[1].isInteger() && (a[1].getIntValue() == 1));
  52 + assert(a[7].isName() && (a[7].getName() == "/Quack"));
53 53  
54 54 a.erase(6);
55 55 assert(a.size() == 7);
56   - assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
57   - assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
58   - assert(a.at(5).second.isNull());
59   - assert(a.at(6).second.isName() && (a.at(6).second.getName() == "/Quack"));
  56 + assert(a[0].isName() && (a[0].getName() == "/First"));
  57 + assert(a[1].isInteger() && (a[1].getIntValue() == 1));
  58 + assert(a[5].isNull());
  59 + assert(a[6].isName() && (a[6].getName() == "/Quack"));
60 60  
61 61 a.erase(6);
62 62 assert(a.size() == 6);
63   - assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
64   - assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
65   - assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third"));
66   - assert(a.at(4).second.isNull());
67   - assert(a.at(5).second.isNull());
  63 + assert(a[0].isName() && (a[0].getName() == "/First"));
  64 + assert(a[1].isInteger() && (a[1].getIntValue() == 1));
  65 + assert(a[3].isName() && (a[3].getName() == "/Third"));
  66 + assert(a[4].isNull());
  67 + assert(a[5].isNull());
68 68  
69   - a.setAt(4, QPDFObjectHandle::parse("12"));
70   - assert(a.at(4).second.isInteger() && (a.at(4).second.getIntValue() == 12));
71   - a.setAt(4, QPDFObjectHandle::newNull());
72   - assert(a.at(4).second.isNull());
  69 + a.set(4, QPDFObjectHandle::parse("12"));
  70 + assert(a[4].isInteger() && (a[4].getIntValue() == 12));
  71 + a.set(4, QPDFObjectHandle::newNull());
  72 + assert(a[4].isNull());
73 73  
74 74 a.erase(to_i(a.size()) - 1);
75 75 assert(a.size() == 5);
76   - assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
77   - assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
78   - assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third"));
79   - assert(a.at(4).second.isNull());
  76 + assert(a[0].isName() && (a[0].getName() == "/First"));
  77 + assert(a[1].isInteger() && (a[1].getIntValue() == 1));
  78 + assert(a[3].isName() && (a[3].getName() == "/Third"));
  79 + assert(a[4].isNull());
80 80  
81 81 a.erase(to_i(a.size()) - 1);
82 82 assert(a.size() == 4);
83   - assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
84   - assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
85   - assert(a.at(3).second.isName() && (a.at(3).second.getName() == "/Third"));
  83 + assert(a[0].isName() && (a[0].getName() == "/First"));
  84 + assert(a[1].isInteger() && (a[1].getIntValue() == 1));
  85 + assert(a[3].isName() && (a[3].getName() == "/Third"));
86 86  
87 87 a.erase(to_i(a.size()) - 1);
88 88 assert(a.size() == 3);
89   - assert(a.at(0).second.isName() && (a.at(0).second.getName() == "/First"));
90   - assert(a.at(1).second.isInteger() && (a.at(1).second.getIntValue() == 1));
91   - assert(a.at(2).second.isString() && (a.at(2).second.getStringValue() == "potato"));
  89 + assert(a[0].isName() && (a[0].getName() == "/First"));
  90 + assert(a[1].isInteger() && (a[1].getIntValue() == 1));
  91 + assert(a[2].isString() && (a[2].getStringValue() == "potato"));
92 92  
93 93 QPDF pdf;
94 94 pdf.emptyPDF();
... ... @@ -96,22 +96,22 @@ main()
96 96 obj = QPDFObject::create<QPDF_Array>(
97 97 std::vector<QPDFObjectHandle>{10, "null"_qpdf.getObj()}, true);
98 98 auto b = qpdf::Array(obj);
99   - b.setAt(5, pdf.newIndirectNull());
100   - b.setAt(7, "[0 1 2 3]"_qpdf);
101   - assert(b.at(3).second.isNull());
102   - assert(b.at(8).second.isNull());
103   - assert(b.at(5).second.isIndirect());
  99 + b.set(5, pdf.newIndirectNull());
  100 + b.set(7, "[0 1 2 3]"_qpdf);
  101 + assert(b[3].null());
  102 + assert(b[8].null());
  103 + assert(b[5].indirect());
104 104 assert(
105 105 QPDFObjectHandle(obj).unparse() ==
106 106 "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]");
107 107 auto c = QPDFObjectHandle(obj).unsafeShallowCopy();
108 108 auto d = QPDFObjectHandle(obj).shallowCopy();
109   - b.at(7).second.setArrayItem(2, "42"_qpdf);
  109 + b.get(7).setArrayItem(2, "42"_qpdf);
110 110 assert(c.unparse() == "[ null null null null null 3 0 R null [ 0 1 42 3 ] null null ]");
111 111 assert(d.unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]");
112 112  
113 113 try {
114   - b.setAt(3, {});
  114 + b.set(3, {});
115 115 std::cout << "inserted uninitialized object\n";
116 116 } catch (std::logic_error&) {
117 117 }
... ... @@ -119,7 +119,7 @@ main()
119 119 pdf2.emptyPDF();
120 120 try {
121 121 pdf.makeIndirectObject(obj);
122   - b.setAt(3, pdf2.getObject(1, 0));
  122 + b.set(3, pdf2.getObject(1, 0));
123 123 std::cout << "inserted uninitialized object\n";
124 124 } catch (std::logic_error&) {
125 125 }
... ...
qpdf/qpdf.testcov
... ... @@ -282,7 +282,6 @@ qpdf-c called qpdf_set_newline_before_endstream 0
282 282 SF_FlateLzwDecode TIFF predictor 0
283 283 QPDFTokenizer inline image at EOF 0
284 284 Pl_QPDFTokenizer found ID 0
285   -QPDFObjectHandle non-stream in stream array 0
286 285 QPDFObjectHandle coalesce called on stream 0
287 286 QPDFObjectHandle coalesce provide stream data 0
288 287 QPDF_Stream bad token at end during normalize 0
... ... @@ -290,7 +289,6 @@ QPDFParser bad token in parse 0
290 289 QPDFParser bad token in parseRemainder 0
291 290 QPDFParser eof in parse 0
292 291 QPDFParser eof in parseRemainder 0
293   -QPDFObjectHandle array bounds 0
294 292 QPDFObjectHandle boolean returning false 0
295 293 QPDFObjectHandle integer returning 0 0
296 294 QPDFObjectHandle real returning 0.0 0
... ... @@ -299,11 +297,7 @@ QPDFObjectHandle string returning empty string 0
299 297 QPDFObjectHandle string returning empty utf8 0
300 298 QPDFObjectHandle operator returning fake value 0
301 299 QPDFObjectHandle inlineimage returning empty data 0
302   -QPDFObjectHandle array treating as empty 0
303   -QPDFObjectHandle array null for non-array 0
304 300 QPDFObjectHandle array treating as empty vector 0
305   -QPDFObjectHandle array ignoring set item 0
306   -QPDFObjectHandle set array bounds 0
307 301 QPDFObjectHandle array ignoring replace items 0
308 302 QPDFObjectHandle array ignoring insert item 0
309 303 QPDFObjectHandle insert array bounds 0
... ... @@ -519,8 +513,6 @@ qpdf-c called qpdf_oh_unparse_resolved 0
519 513 qpdf-c called qpdf_oh_unparse_binary 0
520 514 QPDFWriter getFilterOnWrite false 0
521 515 QPDFPageObjectHelper::forEachXObject 3
522   -NNTree repair 0
523   -NNTree insertAfter inserts first 0
524 516 NNTree erased last kid/item in tree 1
525 517 QPDFPageObjectHelper unresolved names 0
526 518 QPDFPageObjectHelper resolving unresolved 0
... ...