Commit e3ce9e173c1a70ed02a5d749b262ebae4e0fd663

Authored by m-holger
1 parent 14f445b1

Refactor `NNTree` and `NNTreeImpl` to use `Array` API, simplify logic, and remove redundant traces.

libqpdf/NNTree.cc
@@ -11,6 +11,8 @@ @@ -11,6 +11,8 @@
11 #include <exception> 11 #include <exception>
12 #include <utility> 12 #include <utility>
13 13
  14 +using namespace qpdf;
  15 +
14 static std::string 16 static std::string
15 get_description(QPDFObjectHandle const& node) 17 get_description(QPDFObjectHandle const& node)
16 { 18 {
@@ -66,7 +68,7 @@ NNTreeIterator::updateIValue(bool allow_invalid) @@ -66,7 +68,7 @@ NNTreeIterator::updateIValue(bool allow_invalid)
66 ivalue.second = QPDFObjectHandle(); 68 ivalue.second = QPDFObjectHandle();
67 return; 69 return;
68 } 70 }
69 - auto items = node.getKey(impl.details.itemsKey()); 71 + Array items = node.getKey(impl.details.itemsKey());
70 if (!std::cmp_less(item_number + 1, items.size())) { 72 if (!std::cmp_less(item_number + 1, items.size())) {
71 impl.error(node, "update ivalue: items array is too short"); 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,7 +87,7 @@ NNTreeIterator::getNextKid(PathElement&amp; pe, bool backward)
85 { 87 {
86 while (true) { 88 while (true) {
87 pe.kid_number += backward ? -1 : 1; 89 pe.kid_number += backward ? -1 : 1;
88 - auto kids = pe.node.getKey("/Kids"); 90 + Array kids = pe.node.getKey("/Kids");
89 if (pe.kid_number >= 0 && std::cmp_less(pe.kid_number, kids.size())) { 91 if (pe.kid_number >= 0 && std::cmp_less(pe.kid_number, kids.size())) {
90 auto result = kids[pe.kid_number]; 92 auto result = kids[pe.kid_number];
91 if (result.isDictionary() && 93 if (result.isDictionary() &&
@@ -117,7 +119,7 @@ NNTreeIterator::increment(bool backward) @@ -117,7 +119,7 @@ NNTreeIterator::increment(bool backward)
117 119
118 while (valid()) { 120 while (valid()) {
119 item_number += backward ? -2 : 2; 121 item_number += backward ? -2 : 2;
120 - auto items = node.getKey(impl.details.itemsKey()); 122 + Array items = node.getKey(impl.details.itemsKey());
121 if (item_number < 0 || std::cmp_greater_equal(item_number, items.size())) { 123 if (item_number < 0 || std::cmp_greater_equal(item_number, items.size())) {
122 bool found = false; 124 bool found = false;
123 setItemNumber(QPDFObjectHandle(), -1); 125 setItemNumber(QPDFObjectHandle(), -1);
@@ -152,9 +154,9 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite @@ -152,9 +154,9 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
152 a_node.removeKey("/Limits"); 154 a_node.removeKey("/Limits");
153 break; 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 size_t nitems = items.size(); 160 size_t nitems = items.size();
159 161
160 bool changed = true; 162 bool changed = true;
@@ -167,8 +169,8 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite @@ -167,8 +169,8 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
167 auto first_kid = kids[0]; 169 auto first_kid = kids[0];
168 auto last_kid = kids[nkids - 1u]; 170 auto last_kid = kids[nkids - 1u];
169 if (first_kid.isDictionary() && last_kid.isDictionary()) { 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 if (first_limits.size() >= 2 && last_limits.size() >= 2) { 174 if (first_limits.size() >= 2 && last_limits.size() >= 2) {
173 first = first_limits[0]; 175 first = first_limits[0];
174 last = last_limits[1]; 176 last = last_limits[1];
@@ -176,16 +178,14 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite @@ -176,16 +178,14 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
176 } 178 }
177 } 179 }
178 if (first && last) { 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 if (olimits.size() == 2) { 183 if (olimits.size() == 2) {
184 auto ofirst = olimits[0]; 184 auto ofirst = olimits[0];
185 auto olast = olimits[1]; 185 auto olast = olimits[1];
186 if (impl.details.keyValid(ofirst) && impl.details.keyValid(olast) && 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 changed = false; 189 changed = false;
190 } 190 }
191 } 191 }
@@ -240,24 +240,23 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -240,24 +240,23 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
240 } 240 }
241 241
242 // Find the array we actually need to split, which is either this node's kids or items. 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 std::string key; 250 std::string key;
251 - int threshold = 0; 251 + size_t threshold = static_cast<size_t>(impl.split_threshold);
252 if (nkids > 0) { 252 if (nkids > 0) {
253 first_half = kids; 253 first_half = kids;
254 n = nkids; 254 n = nkids;
255 - threshold = impl.split_threshold;  
256 key = "/Kids"; 255 key = "/Kids";
257 } else if (nitems > 0) { 256 } else if (nitems > 0) {
258 first_half = items; 257 first_half = items;
259 n = nitems; 258 n = nitems;
260 - threshold = 2 * impl.split_threshold; 259 + threshold *= 2;
261 key = impl.details.itemsKey(); 260 key = impl.details.itemsKey();
262 } else { 261 } else {
263 throw std::logic_error("NNTreeIterator::split called on invalid node"); 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,8 +288,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
289 288
290 auto first_node = impl.qpdf.makeIndirectObject(QPDFObjectHandle::newDictionary()); 289 auto first_node = impl.qpdf.makeIndirectObject(QPDFObjectHandle::newDictionary());
291 first_node.replaceKey(key, first_half); 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 to_split.removeKey("/Limits"); // already shouldn't be there for root 293 to_split.removeKey("/Limits"); // already shouldn't be there for root
295 to_split.removeKey(impl.details.itemsKey()); 294 to_split.removeKey(impl.details.itemsKey());
296 to_split.replaceKey("/Kids", new_kids); 295 to_split.replaceKey("/Kids", new_kids);
@@ -310,11 +309,11 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -310,11 +309,11 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
310 309
311 // Create a second half array, and transfer the second half of the items into the second half 310 // Create a second half array, and transfer the second half of the items into the second half
312 // array. 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 while (std::cmp_greater(first_half.size(), start_idx)) { 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 resetLimits(to_split, parent); 318 resetLimits(to_split, parent);
320 319
@@ -331,8 +330,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -331,8 +330,8 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
331 // kid_number to traverse through it. We need to update to_split's path element, or the node if 330 // kid_number to traverse through it. We need to update to_split's path element, or the node if
332 // this is a leaf, so that the kid/item number points to the right place. 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 auto cur_elem = parent; 335 auto cur_elem = parent;
337 ++cur_elem; // points to end() for leaf nodes 336 ++cur_elem; // points to end() for leaf nodes
338 int old_idx = (is_leaf ? item_number : cur_elem->kid_number); 337 int old_idx = (is_leaf ? item_number : cur_elem->kid_number);
@@ -367,22 +366,21 @@ void @@ -367,22 +366,21 @@ void
367 NNTreeIterator::insertAfter(QPDFObjectHandle const& key, QPDFObjectHandle const& value) 366 NNTreeIterator::insertAfter(QPDFObjectHandle const& key, QPDFObjectHandle const& value)
368 { 367 {
369 if (!valid()) { 368 if (!valid()) {
370 - QTC::TC("qpdf", "NNTree insertAfter inserts first");  
371 impl.insertFirst(key, value); 369 impl.insertFirst(key, value);
372 deepen(impl.oh, true, false); 370 deepen(impl.oh, true, false);
373 return; 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 if (std::cmp_less(items.size(), item_number + 2)) { 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 impl.error(node, "insert: items array is too short"); 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 resetLimits(node, lastPathElement()); 384 resetLimits(node, lastPathElement());
387 split(node, lastPathElement()); 385 split(node, lastPathElement());
388 increment(false); 386 increment(false);
@@ -396,14 +394,14 @@ NNTreeIterator::remove() @@ -396,14 +394,14 @@ NNTreeIterator::remove()
396 if (!valid()) { 394 if (!valid()) {
397 throw std::logic_error("attempt made to remove an invalid iterator"); 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 int nitems = static_cast<int>(items.size()); 398 int nitems = static_cast<int>(items.size());
401 if (std::cmp_greater(item_number + 2, nitems)) { 399 if (std::cmp_greater(item_number + 2, nitems)) {
402 impl.error(node, "found short items array while removing an item"); 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 nitems -= 2; 405 nitems -= 2;
408 406
409 if (nitems > 0) { 407 if (nitems > 0) {
@@ -443,8 +441,8 @@ NNTreeIterator::remove() @@ -443,8 +441,8 @@ NNTreeIterator::remove()
443 auto element = lastPathElement(); 441 auto element = lastPathElement();
444 auto parent = element; 442 auto parent = element;
445 --parent; 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 auto nkids = kids.size(); 446 auto nkids = kids.size();
449 if (nkids > 0) { 447 if (nkids > 0) {
450 // The logic here is similar to the items case. 448 // The logic here is similar to the items case.
@@ -462,7 +460,7 @@ NNTreeIterator::remove() @@ -462,7 +460,7 @@ NNTreeIterator::remove()
462 } 460 }
463 } else { 461 } else {
464 // Next kid is in deleted kid's position 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 return; 465 return;
468 } 466 }
@@ -470,7 +468,7 @@ NNTreeIterator::remove() @@ -470,7 +468,7 @@ NNTreeIterator::remove()
470 if (parent == path.end()) { 468 if (parent == path.end()) {
471 // We erased the very last item. Convert the root to an empty items array. 469 // We erased the very last item. Convert the root to an empty items array.
472 element->node.removeKey("/Kids"); 470 element->node.removeKey("/Kids");
473 - element->node.replaceKey(impl.details.itemsKey(), QPDFObjectHandle::newArray()); 471 + element->node.replaceKey(impl.details.itemsKey(), Array());
474 path.clear(); 472 path.clear();
475 setItemNumber(impl.oh, -1); 473 setItemNumber(impl.oh, -1);
476 return; 474 return;
@@ -527,10 +525,7 @@ NNTreeIterator::operator==(NNTreeIterator const&amp; other) const @@ -527,10 +525,7 @@ NNTreeIterator::operator==(NNTreeIterator const&amp; other) const
527 ++tpi; 525 ++tpi;
528 ++opi; 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 void 531 void
@@ -574,27 +569,30 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) @@ -574,27 +569,30 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
574 return fail(a_node, "non-dictionary node while traversing name/number tree"); 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 int nitems = static_cast<int>(items.size()); 573 int nitems = static_cast<int>(items.size());
579 if (nitems > 1) { 574 if (nitems > 1) {
580 setItemNumber(a_node, first ? 0 : nitems - 2); 575 setItemNumber(a_node, first ? 0 : nitems - 2);
581 break; 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 if (nkids > 0) { 581 if (nkids > 0) {
587 int kid_number = first ? 0 : nkids - 1; 582 int kid_number = first ? 0 : nkids - 1;
588 addPathElement(a_node, kid_number); 583 addPathElement(a_node, kid_number);
589 auto next = kids[kid_number]; 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 if (impl.auto_repair) { 589 if (impl.auto_repair) {
592 impl.warn( 590 impl.warn(
593 a_node, 591 a_node,
594 "converting kid number " + std::to_string(kid_number) + 592 "converting kid number " + std::to_string(kid_number) +
595 " to an indirect object"); 593 " to an indirect object");
596 next = impl.qpdf.makeIndirectObject(next); 594 next = impl.qpdf.makeIndirectObject(next);
597 - kids.setArrayItem(kid_number, next); 595 + kids.set(kid_number, next);
598 } else { 596 } else {
599 impl.warn( 597 impl.warn(
600 a_node, 598 a_node,
@@ -602,7 +600,7 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) @@ -602,7 +600,7 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
602 } 600 }
603 } 601 }
604 a_node = next; 602 a_node = next;
605 - } else if (allow_empty && items.isArray()) { 603 + } else if (allow_empty && items) {
606 setItemNumber(a_node, -1); 604 setItemNumber(a_node, -1);
607 break; 605 break;
608 } else { 606 } else {
@@ -655,8 +653,8 @@ NNTreeImpl::last() @@ -655,8 +653,8 @@ NNTreeImpl::last()
655 int 653 int
656 NNTreeImpl::withinLimits(QPDFObjectHandle const& key, QPDFObjectHandle const& node) 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 error(node, "node is missing /Limits"); 658 error(node, "node is missing /Limits");
661 } 659 }
662 if (details.compareKeys(key, limits[0]) < 0) { 660 if (details.compareKeys(key, limits[0]) < 0) {
@@ -734,7 +732,7 @@ void @@ -734,7 +732,7 @@ void
734 NNTreeImpl::repair() 732 NNTreeImpl::repair()
735 { 733 {
736 auto new_node = QPDFObjectHandle::newDictionary(); 734 auto new_node = QPDFObjectHandle::newDictionary();
737 - new_node.replaceKey(details.itemsKey(), QPDFObjectHandle::newArray()); 735 + new_node.replaceKey(details.itemsKey(), Array());
738 NNTreeImpl repl(details, qpdf, new_node, false); 736 NNTreeImpl repl(details, qpdf, new_node, false);
739 for (auto const& i: *this) { 737 for (auto const& i: *this) {
740 repl.insert(i.first, i.second); 738 repl.insert(i.first, i.second);
@@ -750,7 +748,6 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found) @@ -750,7 +748,6 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found)
750 return findInternal(key, return_prev_if_not_found); 748 return findInternal(key, return_prev_if_not_found);
751 } catch (QPDFExc& e) { 749 } catch (QPDFExc& e) {
752 if (auto_repair) { 750 if (auto_repair) {
753 - QTC::TC("qpdf", "NNTree repair");  
754 warn(oh, std::string("attempting to repair after error: ") + e.what()); 751 warn(oh, std::string("attempting to repair after error: ") + e.what());
755 repair(); 752 repair();
756 return findInternal(key, return_prev_if_not_found); 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,7 +781,7 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
784 error(node, "loop detected in find"); 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 size_t nitems = items.size(); 785 size_t nitems = items.size();
789 if (nitems > 1) { 786 if (nitems > 1) {
790 int idx = binarySearch( 787 int idx = binarySearch(
@@ -795,8 +792,8 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo @@ -795,8 +792,8 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
795 return result; 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 if (nkids > 0) { 797 if (nkids > 0) {
801 int idx = binarySearch(key, kids, nkids, true, &NNTreeImpl::compareKeyKid); 798 int idx = binarySearch(key, kids, nkids, true, &NNTreeImpl::compareKeyKid);
802 if (idx == -1) { 799 if (idx == -1) {
@@ -814,15 +811,15 @@ NNTreeImpl::iterator @@ -814,15 +811,15 @@ NNTreeImpl::iterator
814 NNTreeImpl::insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value) 811 NNTreeImpl::insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value)
815 { 812 {
816 auto iter = begin(); 813 auto iter = begin();
817 - QPDFObjectHandle items; 814 + Array items(nullptr);
818 if (iter.node.isDictionary()) { 815 if (iter.node.isDictionary()) {
819 items = iter.node.getKey(details.itemsKey()); 816 items = iter.node.getKey(details.itemsKey());
820 } 817 }
821 - if (!items.isArray()) { 818 + if (!items) {
822 error(oh, "unable to find a valid items node"); 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 iter.setItemNumber(iter.node, 0); 823 iter.setItemNumber(iter.node, 0);
827 iter.resetLimits(iter.node, iter.lastPathElement()); 824 iter.resetLimits(iter.node, iter.lastPathElement());
828 iter.split(iter.node, iter.lastPathElement()); 825 iter.split(iter.node, iter.lastPathElement());
@@ -836,8 +833,8 @@ NNTreeImpl::insert(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; value) @@ -836,8 +833,8 @@ NNTreeImpl::insert(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; value)
836 if (!iter.valid()) { 833 if (!iter.valid()) {
837 return insertFirst(key, value); 834 return insertFirst(key, value);
838 } else if (details.compareKeys(key, iter->first) == 0) { 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 iter.updateIValue(); 838 iter.updateIValue();
842 } else { 839 } else {
843 iter.insertAfter(key, value); 840 iter.insertAfter(key, value);
qpdf/qpdf.testcov
@@ -513,8 +513,6 @@ qpdf-c called qpdf_oh_unparse_resolved 0 @@ -513,8 +513,6 @@ qpdf-c called qpdf_oh_unparse_resolved 0
513 qpdf-c called qpdf_oh_unparse_binary 0 513 qpdf-c called qpdf_oh_unparse_binary 0
514 QPDFWriter getFilterOnWrite false 0 514 QPDFWriter getFilterOnWrite false 0
515 QPDFPageObjectHelper::forEachXObject 3 515 QPDFPageObjectHelper::forEachXObject 3
516 -NNTree repair 0  
517 -NNTree insertAfter inserts first 0  
518 NNTree erased last kid/item in tree 1 516 NNTree erased last kid/item in tree 1
519 QPDFPageObjectHelper unresolved names 0 517 QPDFPageObjectHelper unresolved names 0
520 QPDFPageObjectHelper resolving unresolved 0 518 QPDFPageObjectHelper resolving unresolved 0