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 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);
... ...
qpdf/qpdf.testcov
... ... @@ -513,8 +513,6 @@ qpdf-c called qpdf_oh_unparse_resolved 0
513 513 qpdf-c called qpdf_oh_unparse_binary 0
514 514 QPDFWriter getFilterOnWrite false 0
515 515 QPDFPageObjectHelper::forEachXObject 3
516   -NNTree repair 0
517   -NNTree insertAfter inserts first 0
518 516 NNTree erased last kid/item in tree 1
519 517 QPDFPageObjectHelper unresolved names 0
520 518 QPDFPageObjectHelper resolving unresolved 0
... ...