Commit e3ce9e173c1a70ed02a5d749b262ebae4e0fd663
1 parent
14f445b1
Refactor `NNTree` and `NNTreeImpl` to use `Array` API, simplify logic, and remove redundant traces.
Showing
2 changed files
with
65 additions
and
70 deletions
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& 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<PathElement>::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<PathElement>::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<PathElement>::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<PathElement>::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<PathElement>::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<PathElement>::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<PathElement>::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& 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& 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& 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& key, QPDFObjectHandle const& 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 | ... | ... |