Commit 979dc51b57eb9207a5cc72737fefd4d579b9e983

Authored by m-holger
Committed by GitHub
2 parents 5e7b37e1 97198c27

Merge pull request #1531 from m-holger/nnt

Refactor NNTree
include/qpdf/QPDFNameTreeObjectHelper.hh
@@ -176,23 +176,7 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper @@ -176,23 +176,7 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper
176 void setSplitThreshold(int); 176 void setSplitThreshold(int);
177 177
178 private: 178 private:
179 - class QPDF_DLL_PRIVATE Members  
180 - {  
181 - friend class QPDFNameTreeObjectHelper;  
182 -  
183 - public:  
184 - ~Members() = default;  
185 -  
186 - private:  
187 - Members(  
188 - QPDFObjectHandle& oh,  
189 - QPDF&,  
190 - std::function<bool(QPDFObjectHandle const&)> value_validator,  
191 - bool auto_repair);  
192 - Members(Members const&) = delete;  
193 -  
194 - std::shared_ptr<NNTreeImpl> impl;  
195 - }; 179 + class QPDF_DLL_PRIVATE Members;
196 180
197 std::shared_ptr<Members> m; 181 std::shared_ptr<Members> m;
198 }; 182 };
include/qpdf/QPDFNumberTreeObjectHelper.hh
@@ -192,24 +192,7 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper @@ -192,24 +192,7 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper
192 void setSplitThreshold(int); 192 void setSplitThreshold(int);
193 193
194 private: 194 private:
195 - class QPDF_DLL_PRIVATE Members  
196 - {  
197 - friend class QPDFNumberTreeObjectHelper;  
198 - typedef QPDFNumberTreeObjectHelper::numtree_number numtree_number;  
199 -  
200 - public:  
201 - ~Members() = default;  
202 -  
203 - private:  
204 - Members(  
205 - QPDFObjectHandle& oh,  
206 - QPDF&,  
207 - std::function<bool(QPDFObjectHandle const&)> value_validator,  
208 - bool auto_repair);  
209 - Members(Members const&) = delete;  
210 -  
211 - std::shared_ptr<NNTreeImpl> impl;  
212 - }; 195 + class QPDF_DLL_PRIVATE Members;
213 196
214 std::shared_ptr<Members> m; 197 std::shared_ptr<Members> m;
215 }; 198 };
libqpdf/CMakeLists.txt
@@ -71,8 +71,6 @@ set(libqpdf_SOURCES @@ -71,8 +71,6 @@ set(libqpdf_SOURCES
71 QPDFJob_json.cc 71 QPDFJob_json.cc
72 QPDFLogger.cc 72 QPDFLogger.cc
73 QPDFMatrix.cc 73 QPDFMatrix.cc
74 - QPDFNameTreeObjectHelper.cc  
75 - QPDFNumberTreeObjectHelper.cc  
76 QPDFObject.cc 74 QPDFObject.cc
77 QPDFObjectHandle.cc 75 QPDFObjectHandle.cc
78 QPDFObjectHelper.cc 76 QPDFObjectHelper.cc
libqpdf/NNTree.cc
@@ -2,6 +2,9 @@ @@ -2,6 +2,9 @@
2 2
3 #include <qpdf/NNTree.hh> 3 #include <qpdf/NNTree.hh>
4 4
  5 +#include <qpdf/QPDFNameTreeObjectHelper.hh>
  6 +#include <qpdf/QPDFNumberTreeObjectHelper.hh>
  7 +
5 #include <qpdf/QPDFObjectHandle_private.hh> 8 #include <qpdf/QPDFObjectHandle_private.hh>
6 #include <qpdf/QPDF_private.hh> 9 #include <qpdf/QPDF_private.hh>
7 #include <qpdf/QTC.hh> 10 #include <qpdf/QTC.hh>
@@ -33,16 +36,11 @@ NNTreeImpl::warn(QPDFObjectHandle const&amp; node, std::string const&amp; msg) @@ -33,16 +36,11 @@ NNTreeImpl::warn(QPDFObjectHandle const&amp; node, std::string const&amp; msg)
33 } 36 }
34 37
35 void 38 void
36 -NNTreeImpl::error(QPDFObjectHandle const& node, std::string const& msg) 39 +NNTreeImpl::error(QPDFObjectHandle const& node, std::string const& msg) const
37 { 40 {
38 throw QPDFExc(qpdf_e_damaged_pdf, qpdf.getFilename(), get_description(node), 0, msg); 41 throw QPDFExc(qpdf_e_damaged_pdf, qpdf.getFilename(), get_description(node), 0, msg);
39 } 42 }
40 43
41 -NNTreeIterator::NNTreeIterator(NNTreeImpl& impl) :  
42 - impl(impl)  
43 -{  
44 -}  
45 -  
46 void 44 void
47 NNTreeIterator::updateIValue(bool allow_invalid) 45 NNTreeIterator::updateIValue(bool allow_invalid)
48 { 46 {
@@ -68,7 +66,7 @@ NNTreeIterator::updateIValue(bool allow_invalid) @@ -68,7 +66,7 @@ NNTreeIterator::updateIValue(bool allow_invalid)
68 ivalue.second = QPDFObjectHandle(); 66 ivalue.second = QPDFObjectHandle();
69 return; 67 return;
70 } 68 }
71 - Array items = node.getKey(impl.details.itemsKey()); 69 + Array items = node.getKey(impl.itemsKey());
72 if (!std::cmp_less(item_number + 1, items.size())) { 70 if (!std::cmp_less(item_number + 1, items.size())) {
73 impl.error(node, "update ivalue: items array is too short"); 71 impl.error(node, "update ivalue: items array is too short");
74 } 72 }
@@ -91,7 +89,7 @@ NNTreeIterator::getNextKid(PathElement&amp; pe, bool backward) @@ -91,7 +89,7 @@ NNTreeIterator::getNextKid(PathElement&amp; pe, bool backward)
91 if (pe.kid_number >= 0 && std::cmp_less(pe.kid_number, kids.size())) { 89 if (pe.kid_number >= 0 && std::cmp_less(pe.kid_number, kids.size())) {
92 auto result = kids[pe.kid_number]; 90 auto result = kids[pe.kid_number];
93 if (result.isDictionary() && 91 if (result.isDictionary() &&
94 - (result.hasKey("/Kids") || result.hasKey(impl.details.itemsKey()))) { 92 + (result.hasKey("/Kids") || result.hasKey(impl.itemsKey()))) {
95 return result; 93 return result;
96 } else { 94 } else {
97 impl.warn( 95 impl.warn(
@@ -102,15 +100,6 @@ NNTreeIterator::getNextKid(PathElement&amp; pe, bool backward) @@ -102,15 +100,6 @@ NNTreeIterator::getNextKid(PathElement&amp; pe, bool backward)
102 } 100 }
103 } 101 }
104 } 102 }
105 -  
106 -// iterator can be incremented or decremented, or dereferenced. This does not imply that it points  
107 -// to a valid item.  
108 -bool  
109 -NNTreeIterator::valid() const  
110 -{  
111 - return item_number >= 0;  
112 -}  
113 -  
114 void 103 void
115 NNTreeIterator::increment(bool backward) 104 NNTreeIterator::increment(bool backward)
116 { 105 {
@@ -121,7 +110,7 @@ NNTreeIterator::increment(bool backward) @@ -121,7 +110,7 @@ NNTreeIterator::increment(bool backward)
121 110
122 while (valid()) { 111 while (valid()) {
123 item_number += backward ? -2 : 2; 112 item_number += backward ? -2 : 2;
124 - Array items = node.getKey(impl.details.itemsKey()); 113 + Array items = node.getKey(impl.itemsKey());
125 if (item_number < 0 || std::cmp_greater_equal(item_number, items.size())) { 114 if (item_number < 0 || std::cmp_greater_equal(item_number, items.size())) {
126 bool found = false; 115 bool found = false;
127 setItemNumber(QPDFObjectHandle(), -1); 116 setItemNumber(QPDFObjectHandle(), -1);
@@ -136,10 +125,10 @@ NNTreeIterator::increment(bool backward) @@ -136,10 +125,10 @@ NNTreeIterator::increment(bool backward)
136 } 125 }
137 } 126 }
138 if (item_number >= 0) { 127 if (item_number >= 0) {
139 - items = node.getKey(impl.details.itemsKey()); 128 + items = node.getKey(impl.itemsKey());
140 if (std::cmp_greater_equal(item_number + 1, items.size())) { 129 if (std::cmp_greater_equal(item_number + 1, items.size())) {
141 impl.warn(node, "items array doesn't have enough elements"); 130 impl.warn(node, "items array doesn't have enough elements");
142 - } else if (!impl.details.keyValid(items[item_number])) { 131 + } else if (!impl.keyValid(items[item_number])) {
143 impl.warn(node, ("item " + std::to_string(item_number) + " has the wrong type")); 132 impl.warn(node, ("item " + std::to_string(item_number) + " has the wrong type"));
144 } else if (!impl.value_valid(items[item_number + 1])) { 133 } else if (!impl.value_valid(items[item_number + 1])) {
145 impl.warn(node, "item " + std::to_string(item_number + 1) + " is invalid"); 134 impl.warn(node, "item " + std::to_string(item_number + 1) + " is invalid");
@@ -160,7 +149,7 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite @@ -160,7 +149,7 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
160 } 149 }
161 Array kids = a_node.getKey("/Kids"); 150 Array kids = a_node.getKey("/Kids");
162 size_t nkids = kids.size(); 151 size_t nkids = kids.size();
163 - Array items = a_node.getKey(impl.details.itemsKey()); 152 + Array items = a_node.getKey(impl.itemsKey());
164 size_t nitems = items.size(); 153 size_t nitems = items.size();
165 154
166 bool changed = true; 155 bool changed = true;
@@ -187,9 +176,8 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite @@ -187,9 +176,8 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
187 if (olimits.size() == 2) { 176 if (olimits.size() == 2) {
188 auto ofirst = olimits[0]; 177 auto ofirst = olimits[0];
189 auto olast = olimits[1]; 178 auto olast = olimits[1];
190 - if (impl.details.keyValid(ofirst) && impl.details.keyValid(olast) &&  
191 - impl.details.compareKeys(first, ofirst) == 0 &&  
192 - impl.details.compareKeys(last, olast) == 0) { 179 + if (impl.keyValid(ofirst) && impl.keyValid(olast) &&
  180 + impl.compareKeys(first, ofirst) == 0 && impl.compareKeys(last, olast) == 0) {
193 changed = false; 181 changed = false;
194 } 182 }
195 } 183 }
@@ -246,7 +234,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -246,7 +234,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
246 // Find the array we actually need to split, which is either this node's kids or items. 234 // Find the array we actually need to split, which is either this node's kids or items.
247 Array kids = to_split.getKey("/Kids"); 235 Array kids = to_split.getKey("/Kids");
248 size_t nkids = kids.size(); 236 size_t nkids = kids.size();
249 - Array items = to_split.getKey(impl.details.itemsKey()); 237 + Array items = to_split.getKey(impl.itemsKey());
250 size_t nitems = items.size(); 238 size_t nitems = items.size();
251 239
252 Array first_half; 240 Array first_half;
@@ -261,7 +249,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -261,7 +249,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
261 first_half = items; 249 first_half = items;
262 n = nitems; 250 n = nitems;
263 threshold *= 2; 251 threshold *= 2;
264 - key = impl.details.itemsKey(); 252 + key = impl.itemsKey();
265 } else { 253 } else {
266 throw std::logic_error("NNTreeIterator::split called on invalid node"); 254 throw std::logic_error("NNTreeIterator::split called on invalid node");
267 } 255 }
@@ -295,7 +283,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato @@ -295,7 +283,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
295 Array new_kids; 283 Array new_kids;
296 new_kids.push_back(first_node); 284 new_kids.push_back(first_node);
297 to_split.removeKey("/Limits"); // already shouldn't be there for root 285 to_split.removeKey("/Limits"); // already shouldn't be there for root
298 - to_split.removeKey(impl.details.itemsKey()); 286 + to_split.removeKey(impl.itemsKey());
299 to_split.replaceKey("/Kids", new_kids); 287 to_split.replaceKey("/Kids", new_kids);
300 if (is_leaf) { 288 if (is_leaf) {
301 node = first_node; 289 node = first_node;
@@ -375,7 +363,7 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; @@ -375,7 +363,7 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp;
375 return; 363 return;
376 } 364 }
377 365
378 - Array items = node.getKey(impl.details.itemsKey()); 366 + Array items = node.getKey(impl.itemsKey());
379 if (!items) { 367 if (!items) {
380 impl.error(node, "node contains no items array"); 368 impl.error(node, "node contains no items array");
381 } 369 }
@@ -404,7 +392,7 @@ NNTreeIterator::remove() @@ -404,7 +392,7 @@ NNTreeIterator::remove()
404 if (!valid()) { 392 if (!valid()) {
405 throw std::logic_error("attempt made to remove an invalid iterator"); 393 throw std::logic_error("attempt made to remove an invalid iterator");
406 } 394 }
407 - Array items = node.getKey(impl.details.itemsKey()); 395 + Array items = node.getKey(impl.itemsKey());
408 int nitems = static_cast<int>(items.size()); 396 int nitems = static_cast<int>(items.size());
409 if (std::cmp_greater(item_number + 2, nitems)) { 397 if (std::cmp_greater(item_number + 2, nitems)) {
410 impl.error(node, "found short items array while removing an item"); 398 impl.error(node, "found short items array while removing an item");
@@ -478,7 +466,7 @@ NNTreeIterator::remove() @@ -478,7 +466,7 @@ NNTreeIterator::remove()
478 if (parent == path.end()) { 466 if (parent == path.end()) {
479 // We erased the very last item. Convert the root to an empty items array. 467 // We erased the very last item. Convert the root to an empty items array.
480 element->node.removeKey("/Kids"); 468 element->node.removeKey("/Kids");
481 - element->node.replaceKey(impl.details.itemsKey(), Array()); 469 + element->node.replaceKey(impl.itemsKey(), Array());
482 path.clear(); 470 path.clear();
483 setItemNumber(impl.oh, -1); 471 setItemNumber(impl.oh, -1);
484 return; 472 return;
@@ -489,34 +477,6 @@ NNTreeIterator::remove() @@ -489,34 +477,6 @@ NNTreeIterator::remove()
489 } 477 }
490 } 478 }
491 479
492 -NNTreeIterator&  
493 -NNTreeIterator::operator++()  
494 -{  
495 - increment(false);  
496 - return *this;  
497 -}  
498 -  
499 -NNTreeIterator&  
500 -NNTreeIterator::operator--()  
501 -{  
502 - increment(true);  
503 - return *this;  
504 -}  
505 -  
506 -NNTreeIterator::reference  
507 -NNTreeIterator::operator*()  
508 -{  
509 - updateIValue(false);  
510 - return ivalue;  
511 -}  
512 -  
513 -NNTreeIterator::pointer  
514 -NNTreeIterator::operator->()  
515 -{  
516 - updateIValue(false);  
517 - return &ivalue;  
518 -}  
519 -  
520 bool 480 bool
521 NNTreeIterator::operator==(NNTreeIterator const& other) const 481 NNTreeIterator::operator==(NNTreeIterator const& other) const
522 { 482 {
@@ -538,20 +498,6 @@ NNTreeIterator::operator==(NNTreeIterator const&amp; other) const @@ -538,20 +498,6 @@ NNTreeIterator::operator==(NNTreeIterator const&amp; other) const
538 return item_number == other.item_number; 498 return item_number == other.item_number;
539 } 499 }
540 500
541 -void  
542 -NNTreeIterator::setItemNumber(QPDFObjectHandle const& a_node, int n)  
543 -{  
544 - node = a_node;  
545 - item_number = n;  
546 - updateIValue();  
547 -}  
548 -  
549 -void  
550 -NNTreeIterator::addPathElement(QPDFObjectHandle const& a_node, int kid_number)  
551 -{  
552 - path.emplace_back(a_node, kid_number);  
553 -}  
554 -  
555 bool 501 bool
556 NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) 502 NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
557 { 503 {
@@ -579,7 +525,7 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) @@ -579,7 +525,7 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
579 return fail(a_node, "non-dictionary node while traversing name/number tree"); 525 return fail(a_node, "non-dictionary node while traversing name/number tree");
580 } 526 }
581 527
582 - Array items = a_node.getKey(impl.details.itemsKey()); 528 + Array items = a_node.getKey(impl.itemsKey());
583 int nitems = static_cast<int>(items.size()); 529 int nitems = static_cast<int>(items.size());
584 if (nitems > 1) { 530 if (nitems > 1) {
585 setItemNumber(a_node, first ? 0 : nitems - 2); 531 setItemNumber(a_node, first ? 0 : nitems - 2);
@@ -616,33 +562,12 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty) @@ -616,33 +562,12 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
616 } else { 562 } else {
617 return fail( 563 return fail(
618 a_node, 564 a_node,
619 - "name/number tree node has neither non-empty " + impl.details.itemsKey() +  
620 - " nor /Kids"); 565 + "name/number tree node has neither non-empty " + impl.itemsKey() + " nor /Kids");
621 } 566 }
622 } 567 }
623 return true; 568 return true;
624 } 569 }
625 570
626 -NNTreeImpl::NNTreeImpl(  
627 - NNTreeDetails const& details,  
628 - QPDF& qpdf,  
629 - QPDFObjectHandle& oh,  
630 - std::function<bool(QPDFObjectHandle const&)> value_validator,  
631 - bool auto_repair) :  
632 - details(details),  
633 - qpdf(qpdf),  
634 - oh(oh),  
635 - value_valid(value_validator),  
636 - auto_repair(auto_repair)  
637 -{  
638 -}  
639 -  
640 -void  
641 -NNTreeImpl::setSplitThreshold(int threshold)  
642 -{  
643 - split_threshold = threshold;  
644 -}  
645 -  
646 NNTreeImpl::iterator 571 NNTreeImpl::iterator
647 NNTreeImpl::begin() 572 NNTreeImpl::begin()
648 { 573 {
@@ -652,12 +577,6 @@ NNTreeImpl::begin() @@ -652,12 +577,6 @@ NNTreeImpl::begin()
652 } 577 }
653 578
654 NNTreeImpl::iterator 579 NNTreeImpl::iterator
655 -NNTreeImpl::end()  
656 -{  
657 - return {*this};  
658 -}  
659 -  
660 -NNTreeImpl::iterator  
661 NNTreeImpl::last() 580 NNTreeImpl::last()
662 { 581 {
663 iterator result(*this); 582 iterator result(*this);
@@ -666,28 +585,28 @@ NNTreeImpl::last() @@ -666,28 +585,28 @@ NNTreeImpl::last()
666 } 585 }
667 586
668 int 587 int
669 -NNTreeImpl::withinLimits(QPDFObjectHandle const& key, QPDFObjectHandle const& node) 588 +NNTreeImpl::compareKeys(QPDFObjectHandle a, QPDFObjectHandle b) const
670 { 589 {
671 - Array limits = node.getKey("/Limits");  
672 - if (!(details.keyValid(limits[0]) && details.keyValid(limits[1]))) {  
673 - error(node, "node is missing /Limits");  
674 - }  
675 - if (details.compareKeys(key, limits[0]) < 0) {  
676 - return -1; 590 + // We don't call this without calling keyValid first
  591 + qpdf_assert_debug(keyValid(a));
  592 + qpdf_assert_debug(keyValid(b));
  593 + if (key_type == ::ot_string) {
  594 + auto as = a.getUTF8Value();
  595 + auto bs = b.getUTF8Value();
  596 + return as < bs ? -1 : (as > bs ? 1 : 0);
677 } 597 }
678 - if (details.compareKeys(key, limits[1]) > 0) {  
679 - return 1;  
680 - }  
681 - return 0; 598 + auto as = a.getIntValue();
  599 + auto bs = b.getIntValue();
  600 + return as < bs ? -1 : (as > bs ? 1 : 0);
682 } 601 }
683 602
684 int 603 int
685 NNTreeImpl::binarySearch( 604 NNTreeImpl::binarySearch(
686 - QPDFObjectHandle key,  
687 - QPDFObjectHandle items, 605 + QPDFObjectHandle const& key,
  606 + Array const& items,
688 size_t num_items, 607 size_t num_items,
689 bool return_prev_if_not_found, 608 bool return_prev_if_not_found,
690 - int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item)) 609 + int (NNTreeImpl::*compare)(QPDFObjectHandle const& key, Array const& arr, int item) const) const
691 { 610 {
692 size_t max_idx = std::bit_ceil(num_items); 611 size_t max_idx = std::bit_ceil(num_items);
693 612
@@ -726,36 +645,46 @@ NNTreeImpl::binarySearch( @@ -726,36 +645,46 @@ NNTreeImpl::binarySearch(
726 } 645 }
727 646
728 int 647 int
729 -NNTreeImpl::compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx) 648 +NNTreeImpl::compareKeyItem(QPDFObjectHandle const& key, Array const& items, int idx) const
730 { 649 {
731 - if (!(std::cmp_greater(items.size(), 2 * idx) && details.keyValid(items[2 * idx]))) { 650 + if (!keyValid(items[2 * idx])) {
732 error(oh, ("item at index " + std::to_string(2 * idx) + " is not the right type")); 651 error(oh, ("item at index " + std::to_string(2 * idx) + " is not the right type"));
733 } 652 }
734 - return details.compareKeys(key, items[2 * idx]); 653 + return compareKeys(key, items[2 * idx]);
735 } 654 }
736 655
737 int 656 int
738 -NNTreeImpl::compareKeyKid(QPDFObjectHandle& key, QPDFObjectHandle& kids, int idx) 657 +NNTreeImpl::compareKeyKid(QPDFObjectHandle const& key, Array const& kids, int idx) const
739 { 658 {
740 - if (!(std::cmp_less(idx, kids.size()) && kids[idx].isDictionary())) { 659 + if (!kids[idx].isDictionary()) {
741 error(oh, "invalid kid at index " + std::to_string(idx)); 660 error(oh, "invalid kid at index " + std::to_string(idx));
742 } 661 }
743 - return withinLimits(key, kids[idx]); 662 + Array limits = kids[idx].getKey("/Limits");
  663 + if (!(keyValid(limits[0]) && keyValid(limits[1]))) {
  664 + error(kids[idx], "node is missing /Limits");
  665 + }
  666 + if (compareKeys(key, limits[0]) < 0) {
  667 + return -1;
  668 + }
  669 + if (compareKeys(key, limits[1]) > 0) {
  670 + return 1;
  671 + }
  672 + return 0;
744 } 673 }
745 674
746 void 675 void
747 NNTreeImpl::repair() 676 NNTreeImpl::repair()
748 { 677 {
749 auto new_node = QPDFObjectHandle::newDictionary(); 678 auto new_node = QPDFObjectHandle::newDictionary();
750 - new_node.replaceKey(details.itemsKey(), Array());  
751 - NNTreeImpl repl(details, qpdf, new_node, value_valid, false); 679 + new_node.replaceKey(itemsKey(), Array());
  680 + NNTreeImpl repl(qpdf, new_node, key_type, value_valid, false);
752 for (auto const& [key, value]: *this) { 681 for (auto const& [key, value]: *this) {
753 if (key && value) { 682 if (key && value) {
754 repl.insert(key, value); 683 repl.insert(key, value);
755 } 684 }
756 } 685 }
757 oh.replaceKey("/Kids", new_node.getKey("/Kids")); 686 oh.replaceKey("/Kids", new_node.getKey("/Kids"));
758 - oh.replaceKey(details.itemsKey(), new_node.getKey(details.itemsKey())); 687 + oh.replaceKey(itemsKey(), new_node.getKey(itemsKey()));
759 } 688 }
760 689
761 NNTreeImpl::iterator 690 NNTreeImpl::iterator
@@ -783,13 +712,13 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo @@ -783,13 +712,13 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
783 return end(); 712 return end();
784 } 713 }
785 if (first_item.valid()) { 714 if (first_item.valid()) {
786 - if (!details.keyValid(first_item->first)) { 715 + if (!keyValid(first_item->first)) {
787 error(oh, "encountered invalid key in find"); 716 error(oh, "encountered invalid key in find");
788 } 717 }
789 if (!value_valid(first_item->second)) { 718 if (!value_valid(first_item->second)) {
790 error(oh, "encountered invalid value in find"); 719 error(oh, "encountered invalid value in find");
791 } 720 }
792 - if (details.compareKeys(key, first_item->first) < 0) { 721 + if (compareKeys(key, first_item->first) < 0) {
793 // Before the first key 722 // Before the first key
794 return end(); 723 return end();
795 } 724 }
@@ -805,14 +734,14 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo @@ -805,14 +734,14 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
805 error(node, "loop detected in find"); 734 error(node, "loop detected in find");
806 } 735 }
807 736
808 - Array items = node.getKey(details.itemsKey()); 737 + Array items = node.getKey(itemsKey());
809 size_t nitems = items.size(); 738 size_t nitems = items.size();
810 if (nitems > 1) { 739 if (nitems > 1) {
811 int idx = binarySearch( 740 int idx = binarySearch(
812 key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem); 741 key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem);
813 if (idx >= 0) { 742 if (idx >= 0) {
814 result.setItemNumber(node, 2 * idx); 743 result.setItemNumber(node, 2 * idx);
815 - if (!result.impl.details.keyValid(result.ivalue.first)) { 744 + if (!result.impl.keyValid(result.ivalue.first)) {
816 error(node, "encountered invalid key in find"); 745 error(node, "encountered invalid key in find");
817 } 746 }
818 if (!result.impl.value_valid(result.ivalue.second)) { 747 if (!result.impl.value_valid(result.ivalue.second)) {
@@ -843,7 +772,7 @@ NNTreeImpl::insertFirst(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; val @@ -843,7 +772,7 @@ NNTreeImpl::insertFirst(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; val
843 auto iter = begin(); 772 auto iter = begin();
844 Array items(nullptr); 773 Array items(nullptr);
845 if (iter.node.isDictionary()) { 774 if (iter.node.isDictionary()) {
846 - items = iter.node.getKey(details.itemsKey()); 775 + items = iter.node.getKey(itemsKey());
847 } 776 }
848 if (!items) { 777 if (!items) {
849 error(oh, "unable to find a valid items node"); 778 error(oh, "unable to find a valid items node");
@@ -868,8 +797,8 @@ NNTreeImpl::insert(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; value) @@ -868,8 +797,8 @@ NNTreeImpl::insert(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; value)
868 auto iter = find(key, true); 797 auto iter = find(key, true);
869 if (!iter.valid()) { 798 if (!iter.valid()) {
870 return insertFirst(key, value); 799 return insertFirst(key, value);
871 - } else if (details.compareKeys(key, iter->first) == 0) {  
872 - Array items = iter.node.getKey(details.itemsKey()); 800 + } else if (compareKeys(key, iter->first) == 0) {
  801 + Array items = iter.node.getKey(itemsKey());
873 items.set(iter.item_number + 1, value); 802 items.set(iter.item_number + 1, value);
874 iter.updateIValue(); 803 iter.updateIValue();
875 } else { 804 } else {
@@ -899,7 +828,7 @@ NNTreeImpl::validate(bool a_repair) @@ -899,7 +828,7 @@ NNTreeImpl::validate(bool a_repair)
899 QPDFObjectHandle last_key; 828 QPDFObjectHandle last_key;
900 try { 829 try {
901 for (auto const& [key, value]: *this) { 830 for (auto const& [key, value]: *this) {
902 - if (!details.keyValid(key)) { 831 + if (!keyValid(key)) {
903 error(oh, "invalid key in validate"); 832 error(oh, "invalid key in validate");
904 } 833 }
905 if (!value_valid(value)) { 834 if (!value_valid(value)) {
@@ -907,7 +836,7 @@ NNTreeImpl::validate(bool a_repair) @@ -907,7 +836,7 @@ NNTreeImpl::validate(bool a_repair)
907 } 836 }
908 if (first) { 837 if (first) {
909 first = false; 838 first = false;
910 - } else if (last_key && details.compareKeys(last_key, key) != -1) { 839 + } else if (last_key && compareKeys(last_key, key) != -1) {
911 error(oh, "keys are not sorted in validate"); 840 error(oh, "keys are not sorted in validate");
912 } 841 }
913 last_key = key; 842 last_key = key;
@@ -921,3 +850,426 @@ NNTreeImpl::validate(bool a_repair) @@ -921,3 +850,426 @@ NNTreeImpl::validate(bool a_repair)
921 } 850 }
922 return true; 851 return true;
923 } 852 }
  853 +
  854 +class QPDFNameTreeObjectHelper::Members
  855 +{
  856 + public:
  857 + Members(
  858 + QPDFObjectHandle& oh,
  859 + QPDF& q,
  860 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  861 + bool auto_repair) :
  862 + impl(q, oh, ::ot_string, value_validator, auto_repair)
  863 + {
  864 + }
  865 + Members(Members const&) = delete;
  866 + ~Members() = default;
  867 +
  868 + NNTreeImpl impl;
  869 +};
  870 +
  871 +// Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer. For this specific
  872 +// class, see github issue #745.
  873 +QPDFNameTreeObjectHelper::~QPDFNameTreeObjectHelper() = default;
  874 +
  875 +QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(QPDFObjectHandle oh, QPDF& q, bool auto_repair) :
  876 + QPDFNameTreeObjectHelper(
  877 + oh, q, [](QPDFObjectHandle const& o) -> bool { return static_cast<bool>(o); }, auto_repair)
  878 +{
  879 +}
  880 +
  881 +QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(
  882 + QPDFObjectHandle oh,
  883 + QPDF& q,
  884 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  885 + bool auto_repair) :
  886 + QPDFObjectHelper(oh),
  887 + m(std::make_shared<Members>(oh, q, value_validator, auto_repair))
  888 +{
  889 +}
  890 +
  891 +QPDFNameTreeObjectHelper
  892 +QPDFNameTreeObjectHelper::newEmpty(QPDF& qpdf, bool auto_repair)
  893 +{
  894 + return {qpdf.makeIndirectObject("<< /Names [] >>"_qpdf), qpdf, auto_repair};
  895 +}
  896 +
  897 +QPDFNameTreeObjectHelper::iterator::iterator(std::shared_ptr<NNTreeIterator> const& i) :
  898 + impl(i)
  899 +{
  900 +}
  901 +
  902 +bool
  903 +QPDFNameTreeObjectHelper::iterator::valid() const
  904 +{
  905 + return impl->valid();
  906 +}
  907 +
  908 +QPDFNameTreeObjectHelper::iterator&
  909 +QPDFNameTreeObjectHelper::iterator::operator++()
  910 +{
  911 + ++(*impl);
  912 + updateIValue();
  913 + return *this;
  914 +}
  915 +
  916 +QPDFNameTreeObjectHelper::iterator&
  917 +QPDFNameTreeObjectHelper::iterator::operator--()
  918 +{
  919 + --(*impl);
  920 + updateIValue();
  921 + return *this;
  922 +}
  923 +
  924 +void
  925 +QPDFNameTreeObjectHelper::iterator::updateIValue()
  926 +{
  927 + if (impl->valid()) {
  928 + auto p = *impl;
  929 + ivalue.first = p->first.getUTF8Value();
  930 + ivalue.second = p->second;
  931 + } else {
  932 + ivalue.first = "";
  933 + ivalue.second = QPDFObjectHandle();
  934 + }
  935 +}
  936 +
  937 +QPDFNameTreeObjectHelper::iterator::reference
  938 +QPDFNameTreeObjectHelper::iterator::operator*()
  939 +{
  940 + updateIValue();
  941 + return ivalue;
  942 +}
  943 +
  944 +QPDFNameTreeObjectHelper::iterator::pointer
  945 +QPDFNameTreeObjectHelper::iterator::operator->()
  946 +{
  947 + updateIValue();
  948 + return &ivalue;
  949 +}
  950 +
  951 +bool
  952 +QPDFNameTreeObjectHelper::iterator::operator==(iterator const& other) const
  953 +{
  954 + return *(impl) == *(other.impl);
  955 +}
  956 +
  957 +void
  958 +QPDFNameTreeObjectHelper::iterator::insertAfter(std::string const& key, QPDFObjectHandle value)
  959 +{
  960 + impl->insertAfter(QPDFObjectHandle::newUnicodeString(key), value);
  961 + updateIValue();
  962 +}
  963 +
  964 +void
  965 +QPDFNameTreeObjectHelper::iterator::remove()
  966 +{
  967 + impl->remove();
  968 + updateIValue();
  969 +}
  970 +
  971 +QPDFNameTreeObjectHelper::iterator
  972 +QPDFNameTreeObjectHelper::begin() const
  973 +{
  974 + return {std::make_shared<NNTreeIterator>(m->impl.begin())};
  975 +}
  976 +
  977 +QPDFNameTreeObjectHelper::iterator
  978 +QPDFNameTreeObjectHelper::end() const
  979 +{
  980 + return {std::make_shared<NNTreeIterator>(m->impl.end())};
  981 +}
  982 +
  983 +QPDFNameTreeObjectHelper::iterator
  984 +QPDFNameTreeObjectHelper::last() const
  985 +{
  986 + return {std::make_shared<NNTreeIterator>(m->impl.last())};
  987 +}
  988 +
  989 +QPDFNameTreeObjectHelper::iterator
  990 +QPDFNameTreeObjectHelper::find(std::string const& key, bool return_prev_if_not_found)
  991 +{
  992 + auto i = m->impl.find(QPDFObjectHandle::newUnicodeString(key), return_prev_if_not_found);
  993 + return {std::make_shared<NNTreeIterator>(i)};
  994 +}
  995 +
  996 +QPDFNameTreeObjectHelper::iterator
  997 +QPDFNameTreeObjectHelper::insert(std::string const& key, QPDFObjectHandle value)
  998 +{
  999 + auto i = m->impl.insert(QPDFObjectHandle::newUnicodeString(key), value);
  1000 + return {std::make_shared<NNTreeIterator>(i)};
  1001 +}
  1002 +
  1003 +bool
  1004 +QPDFNameTreeObjectHelper::remove(std::string const& key, QPDFObjectHandle* value)
  1005 +{
  1006 + return m->impl.remove(QPDFObjectHandle::newUnicodeString(key), value);
  1007 +}
  1008 +
  1009 +bool
  1010 +QPDFNameTreeObjectHelper::hasName(std::string const& name)
  1011 +{
  1012 + auto i = find(name);
  1013 + return (i != end());
  1014 +}
  1015 +
  1016 +bool
  1017 +QPDFNameTreeObjectHelper::findObject(std::string const& name, QPDFObjectHandle& oh)
  1018 +{
  1019 + auto i = find(name);
  1020 + if (i == end()) {
  1021 + return false;
  1022 + }
  1023 + oh = i->second;
  1024 + return true;
  1025 +}
  1026 +
  1027 +void
  1028 +QPDFNameTreeObjectHelper::setSplitThreshold(int t)
  1029 +{
  1030 + m->impl.setSplitThreshold(t);
  1031 +}
  1032 +
  1033 +std::map<std::string, QPDFObjectHandle>
  1034 +QPDFNameTreeObjectHelper::getAsMap() const
  1035 +{
  1036 + std::map<std::string, QPDFObjectHandle> result;
  1037 + result.insert(begin(), end());
  1038 + return result;
  1039 +}
  1040 +
  1041 +bool
  1042 +QPDFNameTreeObjectHelper::validate(bool repair)
  1043 +{
  1044 + return m->impl.validate(repair);
  1045 +}
  1046 +
  1047 +class QPDFNumberTreeObjectHelper::Members
  1048 +{
  1049 + typedef QPDFNumberTreeObjectHelper::numtree_number numtree_number;
  1050 +
  1051 + public:
  1052 + Members(
  1053 + QPDFObjectHandle& oh,
  1054 + QPDF& q,
  1055 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  1056 + bool auto_repair) :
  1057 + impl(q, oh, ::ot_integer, value_validator, auto_repair)
  1058 + {
  1059 + }
  1060 + Members(Members const&) = delete;
  1061 + ~Members() = default;
  1062 +
  1063 + NNTreeImpl impl;
  1064 +};
  1065 +
  1066 +// Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer. For this specific
  1067 +// class, see github issue #745.
  1068 +QPDFNumberTreeObjectHelper::~QPDFNumberTreeObjectHelper() = default;
  1069 +
  1070 +QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper(
  1071 + QPDFObjectHandle oh, QPDF& q, bool auto_repair) :
  1072 + QPDFNumberTreeObjectHelper(
  1073 + oh, q, [](QPDFObjectHandle const& o) -> bool { return static_cast<bool>(o); }, auto_repair)
  1074 +{
  1075 +}
  1076 +
  1077 +QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper(
  1078 + QPDFObjectHandle oh,
  1079 + QPDF& q,
  1080 + std::function<bool(QPDFObjectHandle const&)> value_validator,
  1081 + bool auto_repair) :
  1082 + QPDFObjectHelper(oh),
  1083 + m(std::make_shared<Members>(oh, q, value_validator, auto_repair))
  1084 +{
  1085 +}
  1086 +
  1087 +QPDFNumberTreeObjectHelper
  1088 +QPDFNumberTreeObjectHelper::newEmpty(QPDF& qpdf, bool auto_repair)
  1089 +{
  1090 + return {qpdf.makeIndirectObject("<< /Nums [] >>"_qpdf), qpdf, auto_repair};
  1091 +}
  1092 +
  1093 +QPDFNumberTreeObjectHelper::iterator::iterator(std::shared_ptr<NNTreeIterator> const& i) :
  1094 + impl(i)
  1095 +{
  1096 +}
  1097 +
  1098 +bool
  1099 +QPDFNumberTreeObjectHelper::iterator::valid() const
  1100 +{
  1101 + return impl->valid();
  1102 +}
  1103 +
  1104 +QPDFNumberTreeObjectHelper::iterator&
  1105 +QPDFNumberTreeObjectHelper::iterator::operator++()
  1106 +{
  1107 + ++(*impl);
  1108 + updateIValue();
  1109 + return *this;
  1110 +}
  1111 +
  1112 +QPDFNumberTreeObjectHelper::iterator&
  1113 +QPDFNumberTreeObjectHelper::iterator::operator--()
  1114 +{
  1115 + --(*impl);
  1116 + updateIValue();
  1117 + return *this;
  1118 +}
  1119 +
  1120 +void
  1121 +QPDFNumberTreeObjectHelper::iterator::updateIValue()
  1122 +{
  1123 + if (impl->valid()) {
  1124 + auto p = *impl;
  1125 + this->ivalue.first = p->first.getIntValue();
  1126 + this->ivalue.second = p->second;
  1127 + } else {
  1128 + this->ivalue.first = 0;
  1129 + this->ivalue.second = QPDFObjectHandle();
  1130 + }
  1131 +}
  1132 +
  1133 +QPDFNumberTreeObjectHelper::iterator::reference
  1134 +QPDFNumberTreeObjectHelper::iterator::operator*()
  1135 +{
  1136 + updateIValue();
  1137 + return this->ivalue;
  1138 +}
  1139 +
  1140 +QPDFNumberTreeObjectHelper::iterator::pointer
  1141 +QPDFNumberTreeObjectHelper::iterator::operator->()
  1142 +{
  1143 + updateIValue();
  1144 + return &this->ivalue;
  1145 +}
  1146 +
  1147 +bool
  1148 +QPDFNumberTreeObjectHelper::iterator::operator==(iterator const& other) const
  1149 +{
  1150 + return *(impl) == *(other.impl);
  1151 +}
  1152 +
  1153 +void
  1154 +QPDFNumberTreeObjectHelper::iterator::insertAfter(numtree_number key, QPDFObjectHandle value)
  1155 +{
  1156 + impl->insertAfter(QPDFObjectHandle::newInteger(key), value);
  1157 + updateIValue();
  1158 +}
  1159 +
  1160 +void
  1161 +QPDFNumberTreeObjectHelper::iterator::remove()
  1162 +{
  1163 + impl->remove();
  1164 + updateIValue();
  1165 +}
  1166 +
  1167 +QPDFNumberTreeObjectHelper::iterator
  1168 +QPDFNumberTreeObjectHelper::begin() const
  1169 +{
  1170 + return {std::make_shared<NNTreeIterator>(m->impl.begin())};
  1171 +}
  1172 +
  1173 +QPDFNumberTreeObjectHelper::iterator
  1174 +QPDFNumberTreeObjectHelper::end() const
  1175 +{
  1176 + return {std::make_shared<NNTreeIterator>(m->impl.end())};
  1177 +}
  1178 +
  1179 +QPDFNumberTreeObjectHelper::iterator
  1180 +QPDFNumberTreeObjectHelper::last() const
  1181 +{
  1182 + return {std::make_shared<NNTreeIterator>(m->impl.last())};
  1183 +}
  1184 +
  1185 +QPDFNumberTreeObjectHelper::iterator
  1186 +QPDFNumberTreeObjectHelper::find(numtree_number key, bool return_prev_if_not_found)
  1187 +{
  1188 + auto i = m->impl.find(QPDFObjectHandle::newInteger(key), return_prev_if_not_found);
  1189 + return {std::make_shared<NNTreeIterator>(i)};
  1190 +}
  1191 +
  1192 +QPDFNumberTreeObjectHelper::iterator
  1193 +QPDFNumberTreeObjectHelper::insert(numtree_number key, QPDFObjectHandle value)
  1194 +{
  1195 + auto i = m->impl.insert(QPDFObjectHandle::newInteger(key), value);
  1196 + return {std::make_shared<NNTreeIterator>(i)};
  1197 +}
  1198 +
  1199 +bool
  1200 +QPDFNumberTreeObjectHelper::remove(numtree_number key, QPDFObjectHandle* value)
  1201 +{
  1202 + return m->impl.remove(QPDFObjectHandle::newInteger(key), value);
  1203 +}
  1204 +
  1205 +QPDFNumberTreeObjectHelper::numtree_number
  1206 +QPDFNumberTreeObjectHelper::getMin()
  1207 +{
  1208 + auto i = begin();
  1209 + if (i == end()) {
  1210 + return 0;
  1211 + }
  1212 + return i->first;
  1213 +}
  1214 +
  1215 +QPDFNumberTreeObjectHelper::numtree_number
  1216 +QPDFNumberTreeObjectHelper::getMax()
  1217 +{
  1218 + auto i = last();
  1219 + if (i == end()) {
  1220 + return 0;
  1221 + }
  1222 + return i->first;
  1223 +}
  1224 +
  1225 +bool
  1226 +QPDFNumberTreeObjectHelper::hasIndex(numtree_number idx)
  1227 +{
  1228 + auto i = find(idx);
  1229 + return (i != this->end());
  1230 +}
  1231 +
  1232 +bool
  1233 +QPDFNumberTreeObjectHelper::findObject(numtree_number idx, QPDFObjectHandle& oh)
  1234 +{
  1235 + auto i = find(idx);
  1236 + if (i == end()) {
  1237 + return false;
  1238 + }
  1239 + oh = i->second;
  1240 + return true;
  1241 +}
  1242 +
  1243 +bool
  1244 +QPDFNumberTreeObjectHelper::findObjectAtOrBelow(
  1245 + numtree_number idx, QPDFObjectHandle& oh, numtree_number& offset)
  1246 +{
  1247 + auto i = find(idx, true);
  1248 + if (i == end()) {
  1249 + return false;
  1250 + }
  1251 + oh = i->second;
  1252 + QIntC::range_check_subtract(idx, i->first);
  1253 + offset = idx - i->first;
  1254 + return true;
  1255 +}
  1256 +
  1257 +void
  1258 +QPDFNumberTreeObjectHelper::setSplitThreshold(int t)
  1259 +{
  1260 + m->impl.setSplitThreshold(t);
  1261 +}
  1262 +
  1263 +std::map<QPDFNumberTreeObjectHelper::numtree_number, QPDFObjectHandle>
  1264 +QPDFNumberTreeObjectHelper::getAsMap() const
  1265 +{
  1266 + std::map<numtree_number, QPDFObjectHandle> result;
  1267 + result.insert(begin(), end());
  1268 + return result;
  1269 +}
  1270 +
  1271 +bool
  1272 +QPDFNumberTreeObjectHelper::validate(bool repair)
  1273 +{
  1274 + return m->impl.validate(repair);
  1275 +}
libqpdf/QPDFNameTreeObjectHelper.cc
1 -#include <qpdf/QPDFNameTreeObjectHelper.hh>  
2 -  
3 -#include <qpdf/NNTree.hh>  
4 -  
5 -namespace  
6 -{  
7 - class NameTreeDetails: public NNTreeDetails  
8 - {  
9 - public:  
10 - std::string const&  
11 - itemsKey() const override  
12 - {  
13 - static std::string k("/Names");  
14 - return k;  
15 - }  
16 - bool  
17 - keyValid(QPDFObjectHandle oh) const override  
18 - {  
19 - return oh.isString();  
20 - }  
21 - int  
22 - compareKeys(QPDFObjectHandle a, QPDFObjectHandle b) const override  
23 - {  
24 - if (!(keyValid(a) && keyValid(b))) {  
25 - // We don't call this without calling keyValid first  
26 - throw std::logic_error("comparing invalid keys");  
27 - }  
28 - auto as = a.getUTF8Value();  
29 - auto bs = b.getUTF8Value();  
30 - return ((as < bs) ? -1 : (as > bs) ? 1 : 0);  
31 - }  
32 - };  
33 -} // namespace  
34 -  
35 -static NameTreeDetails name_tree_details;  
36 -  
37 -QPDFNameTreeObjectHelper::~QPDFNameTreeObjectHelper() // NOLINT (modernize-use-equals-default)  
38 -{  
39 - // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer. For this specific  
40 - // class, see github issue #745.  
41 -}  
42 -  
43 -QPDFNameTreeObjectHelper::Members::Members(  
44 - QPDFObjectHandle& oh,  
45 - QPDF& q,  
46 - std::function<bool(QPDFObjectHandle const&)> value_validator,  
47 - bool auto_repair) :  
48 - impl(std::make_shared<NNTreeImpl>(name_tree_details, q, oh, value_validator, auto_repair))  
49 -{  
50 -}  
51 -  
52 -QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(QPDFObjectHandle oh, QPDF& q, bool auto_repair) :  
53 - QPDFObjectHelper(oh),  
54 - m(new Members(  
55 - oh, q, [](QPDFObjectHandle const& o) -> bool { return static_cast<bool>(o); }, auto_repair))  
56 -{  
57 -}  
58 -  
59 -QPDFNameTreeObjectHelper::QPDFNameTreeObjectHelper(  
60 - QPDFObjectHandle oh,  
61 - QPDF& q,  
62 - std::function<bool(QPDFObjectHandle const&)> value_validator,  
63 - bool auto_repair) :  
64 - QPDFObjectHelper(oh),  
65 - m(new Members(oh, q, value_validator, auto_repair))  
66 -{  
67 -}  
68 -  
69 -QPDFNameTreeObjectHelper  
70 -QPDFNameTreeObjectHelper::newEmpty(QPDF& qpdf, bool auto_repair)  
71 -{  
72 - return {qpdf.makeIndirectObject("<< /Names [] >>"_qpdf), qpdf, auto_repair};  
73 -}  
74 -  
75 -QPDFNameTreeObjectHelper::iterator::iterator(std::shared_ptr<NNTreeIterator> const& i) :  
76 - impl(i)  
77 -{  
78 -}  
79 -  
80 -bool  
81 -QPDFNameTreeObjectHelper::iterator::valid() const  
82 -{  
83 - return impl->valid();  
84 -}  
85 -  
86 -QPDFNameTreeObjectHelper::iterator&  
87 -QPDFNameTreeObjectHelper::iterator::operator++()  
88 -{  
89 - ++(*impl);  
90 - updateIValue();  
91 - return *this;  
92 -}  
93 -  
94 -QPDFNameTreeObjectHelper::iterator&  
95 -QPDFNameTreeObjectHelper::iterator::operator--()  
96 -{  
97 - --(*impl);  
98 - updateIValue();  
99 - return *this;  
100 -}  
101 -  
102 -void  
103 -QPDFNameTreeObjectHelper::iterator::updateIValue()  
104 -{  
105 - if (impl->valid()) {  
106 - auto p = *impl;  
107 - ivalue.first = p->first.getUTF8Value();  
108 - ivalue.second = p->second;  
109 - } else {  
110 - ivalue.first = "";  
111 - ivalue.second = QPDFObjectHandle();  
112 - }  
113 -}  
114 -  
115 -QPDFNameTreeObjectHelper::iterator::reference  
116 -QPDFNameTreeObjectHelper::iterator::operator*()  
117 -{  
118 - updateIValue();  
119 - return ivalue;  
120 -}  
121 -  
122 -QPDFNameTreeObjectHelper::iterator::pointer  
123 -QPDFNameTreeObjectHelper::iterator::operator->()  
124 -{  
125 - updateIValue();  
126 - return &ivalue;  
127 -}  
128 -  
129 -bool  
130 -QPDFNameTreeObjectHelper::iterator::operator==(iterator const& other) const  
131 -{  
132 - return *(impl) == *(other.impl);  
133 -}  
134 -  
135 -void  
136 -QPDFNameTreeObjectHelper::iterator::insertAfter(std::string const& key, QPDFObjectHandle value)  
137 -{  
138 - impl->insertAfter(QPDFObjectHandle::newUnicodeString(key), value);  
139 - updateIValue();  
140 -}  
141 -  
142 -void  
143 -QPDFNameTreeObjectHelper::iterator::remove()  
144 -{  
145 - impl->remove();  
146 - updateIValue();  
147 -}  
148 -  
149 -QPDFNameTreeObjectHelper::iterator  
150 -QPDFNameTreeObjectHelper::begin() const  
151 -{  
152 - return {std::make_shared<NNTreeIterator>(m->impl->begin())};  
153 -}  
154 -  
155 -QPDFNameTreeObjectHelper::iterator  
156 -QPDFNameTreeObjectHelper::end() const  
157 -{  
158 - return {std::make_shared<NNTreeIterator>(m->impl->end())};  
159 -}  
160 -  
161 -QPDFNameTreeObjectHelper::iterator  
162 -QPDFNameTreeObjectHelper::last() const  
163 -{  
164 - return {std::make_shared<NNTreeIterator>(m->impl->last())};  
165 -}  
166 -  
167 -QPDFNameTreeObjectHelper::iterator  
168 -QPDFNameTreeObjectHelper::find(std::string const& key, bool return_prev_if_not_found)  
169 -{  
170 - auto i = m->impl->find(QPDFObjectHandle::newUnicodeString(key), return_prev_if_not_found);  
171 - return {std::make_shared<NNTreeIterator>(i)};  
172 -}  
173 -  
174 -QPDFNameTreeObjectHelper::iterator  
175 -QPDFNameTreeObjectHelper::insert(std::string const& key, QPDFObjectHandle value)  
176 -{  
177 - auto i = m->impl->insert(QPDFObjectHandle::newUnicodeString(key), value);  
178 - return {std::make_shared<NNTreeIterator>(i)};  
179 -}  
180 -  
181 -bool  
182 -QPDFNameTreeObjectHelper::remove(std::string const& key, QPDFObjectHandle* value)  
183 -{  
184 - return m->impl->remove(QPDFObjectHandle::newUnicodeString(key), value);  
185 -}  
186 -  
187 -bool  
188 -QPDFNameTreeObjectHelper::hasName(std::string const& name)  
189 -{  
190 - auto i = find(name);  
191 - return (i != end());  
192 -}  
193 -  
194 -bool  
195 -QPDFNameTreeObjectHelper::findObject(std::string const& name, QPDFObjectHandle& oh)  
196 -{  
197 - auto i = find(name);  
198 - if (i == end()) {  
199 - return false;  
200 - }  
201 - oh = i->second;  
202 - return true;  
203 -}  
204 -  
205 -void  
206 -QPDFNameTreeObjectHelper::setSplitThreshold(int t)  
207 -{  
208 - m->impl->setSplitThreshold(t);  
209 -}  
210 -  
211 -std::map<std::string, QPDFObjectHandle>  
212 -QPDFNameTreeObjectHelper::getAsMap() const  
213 -{  
214 - std::map<std::string, QPDFObjectHandle> result;  
215 - result.insert(begin(), end());  
216 - return result;  
217 -}  
218 -  
219 -bool  
220 -QPDFNameTreeObjectHelper::validate(bool repair)  
221 -{  
222 - return m->impl->validate(repair);  
223 -}  
libqpdf/QPDFNumberTreeObjectHelper.cc
1 -#include <qpdf/QPDFNumberTreeObjectHelper.hh>  
2 -  
3 -#include <qpdf/NNTree.hh>  
4 -#include <qpdf/QIntC.hh>  
5 -  
6 -namespace  
7 -{  
8 - class NumberTreeDetails: public NNTreeDetails  
9 - {  
10 - public:  
11 - std::string const&  
12 - itemsKey() const override  
13 - {  
14 - static std::string k("/Nums");  
15 - return k;  
16 - }  
17 - bool  
18 - keyValid(QPDFObjectHandle oh) const override  
19 - {  
20 - return oh.isInteger();  
21 - }  
22 - int  
23 - compareKeys(QPDFObjectHandle a, QPDFObjectHandle b) const override  
24 - {  
25 - if (!(keyValid(a) && keyValid(b))) {  
26 - // We don't call this without calling keyValid first  
27 - throw std::logic_error("comparing invalid keys");  
28 - }  
29 - auto as = a.getIntValue();  
30 - auto bs = b.getIntValue();  
31 - return ((as < bs) ? -1 : (as > bs) ? 1 : 0);  
32 - }  
33 - };  
34 -} // namespace  
35 -  
36 -static NumberTreeDetails number_tree_details;  
37 -  
38 -QPDFNumberTreeObjectHelper::~QPDFNumberTreeObjectHelper() // NOLINT (modernize-use-equals-default)  
39 -{  
40 - // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer. For this specific  
41 - // class, see github issue #745.  
42 -}  
43 -  
44 -QPDFNumberTreeObjectHelper::Members::Members(  
45 - QPDFObjectHandle& oh,  
46 - QPDF& q,  
47 - std::function<bool(QPDFObjectHandle const&)> value_validator,  
48 - bool auto_repair) :  
49 - impl(std::make_shared<NNTreeImpl>(number_tree_details, q, oh, value_validator, auto_repair))  
50 -{  
51 -}  
52 -  
53 -QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper(  
54 - QPDFObjectHandle oh, QPDF& q, bool auto_repair) :  
55 - QPDFObjectHelper(oh),  
56 - m(new Members(  
57 - oh, q, [](QPDFObjectHandle const& o) -> bool { return static_cast<bool>(o); }, auto_repair))  
58 -{  
59 -}  
60 -  
61 -QPDFNumberTreeObjectHelper::QPDFNumberTreeObjectHelper(  
62 - QPDFObjectHandle oh,  
63 - QPDF& q,  
64 - std::function<bool(QPDFObjectHandle const&)> value_validator,  
65 - bool auto_repair) :  
66 - QPDFObjectHelper(oh),  
67 - m(new Members(oh, q, value_validator, auto_repair))  
68 -{  
69 -}  
70 -  
71 -QPDFNumberTreeObjectHelper  
72 -QPDFNumberTreeObjectHelper::newEmpty(QPDF& qpdf, bool auto_repair)  
73 -{  
74 - return {qpdf.makeIndirectObject("<< /Nums [] >>"_qpdf), qpdf, auto_repair};  
75 -}  
76 -  
77 -QPDFNumberTreeObjectHelper::iterator::iterator(std::shared_ptr<NNTreeIterator> const& i) :  
78 - impl(i)  
79 -{  
80 -}  
81 -  
82 -bool  
83 -QPDFNumberTreeObjectHelper::iterator::valid() const  
84 -{  
85 - return impl->valid();  
86 -}  
87 -  
88 -QPDFNumberTreeObjectHelper::iterator&  
89 -QPDFNumberTreeObjectHelper::iterator::operator++()  
90 -{  
91 - ++(*impl);  
92 - updateIValue();  
93 - return *this;  
94 -}  
95 -  
96 -QPDFNumberTreeObjectHelper::iterator&  
97 -QPDFNumberTreeObjectHelper::iterator::operator--()  
98 -{  
99 - --(*impl);  
100 - updateIValue();  
101 - return *this;  
102 -}  
103 -  
104 -void  
105 -QPDFNumberTreeObjectHelper::iterator::updateIValue()  
106 -{  
107 - if (impl->valid()) {  
108 - auto p = *impl;  
109 - this->ivalue.first = p->first.getIntValue();  
110 - this->ivalue.second = p->second;  
111 - } else {  
112 - this->ivalue.first = 0;  
113 - this->ivalue.second = QPDFObjectHandle();  
114 - }  
115 -}  
116 -  
117 -QPDFNumberTreeObjectHelper::iterator::reference  
118 -QPDFNumberTreeObjectHelper::iterator::operator*()  
119 -{  
120 - updateIValue();  
121 - return this->ivalue;  
122 -}  
123 -  
124 -QPDFNumberTreeObjectHelper::iterator::pointer  
125 -QPDFNumberTreeObjectHelper::iterator::operator->()  
126 -{  
127 - updateIValue();  
128 - return &this->ivalue;  
129 -}  
130 -  
131 -bool  
132 -QPDFNumberTreeObjectHelper::iterator::operator==(iterator const& other) const  
133 -{  
134 - return *(impl) == *(other.impl);  
135 -}  
136 -  
137 -void  
138 -QPDFNumberTreeObjectHelper::iterator::insertAfter(numtree_number key, QPDFObjectHandle value)  
139 -{  
140 - impl->insertAfter(QPDFObjectHandle::newInteger(key), value);  
141 - updateIValue();  
142 -}  
143 -  
144 -void  
145 -QPDFNumberTreeObjectHelper::iterator::remove()  
146 -{  
147 - impl->remove();  
148 - updateIValue();  
149 -}  
150 -  
151 -QPDFNumberTreeObjectHelper::iterator  
152 -QPDFNumberTreeObjectHelper::begin() const  
153 -{  
154 - return {std::make_shared<NNTreeIterator>(m->impl->begin())};  
155 -}  
156 -  
157 -QPDFNumberTreeObjectHelper::iterator  
158 -QPDFNumberTreeObjectHelper::end() const  
159 -{  
160 - return {std::make_shared<NNTreeIterator>(m->impl->end())};  
161 -}  
162 -  
163 -QPDFNumberTreeObjectHelper::iterator  
164 -QPDFNumberTreeObjectHelper::last() const  
165 -{  
166 - return {std::make_shared<NNTreeIterator>(m->impl->last())};  
167 -}  
168 -  
169 -QPDFNumberTreeObjectHelper::iterator  
170 -QPDFNumberTreeObjectHelper::find(numtree_number key, bool return_prev_if_not_found)  
171 -{  
172 - auto i = m->impl->find(QPDFObjectHandle::newInteger(key), return_prev_if_not_found);  
173 - return {std::make_shared<NNTreeIterator>(i)};  
174 -}  
175 -  
176 -QPDFNumberTreeObjectHelper::iterator  
177 -QPDFNumberTreeObjectHelper::insert(numtree_number key, QPDFObjectHandle value)  
178 -{  
179 - auto i = m->impl->insert(QPDFObjectHandle::newInteger(key), value);  
180 - return {std::make_shared<NNTreeIterator>(i)};  
181 -}  
182 -  
183 -bool  
184 -QPDFNumberTreeObjectHelper::remove(numtree_number key, QPDFObjectHandle* value)  
185 -{  
186 - return m->impl->remove(QPDFObjectHandle::newInteger(key), value);  
187 -}  
188 -  
189 -QPDFNumberTreeObjectHelper::numtree_number  
190 -QPDFNumberTreeObjectHelper::getMin()  
191 -{  
192 - auto i = begin();  
193 - if (i == end()) {  
194 - return 0;  
195 - }  
196 - return i->first;  
197 -}  
198 -  
199 -QPDFNumberTreeObjectHelper::numtree_number  
200 -QPDFNumberTreeObjectHelper::getMax()  
201 -{  
202 - auto i = last();  
203 - if (i == end()) {  
204 - return 0;  
205 - }  
206 - return i->first;  
207 -}  
208 -  
209 -bool  
210 -QPDFNumberTreeObjectHelper::hasIndex(numtree_number idx)  
211 -{  
212 - auto i = find(idx);  
213 - return (i != this->end());  
214 -}  
215 -  
216 -bool  
217 -QPDFNumberTreeObjectHelper::findObject(numtree_number idx, QPDFObjectHandle& oh)  
218 -{  
219 - auto i = find(idx);  
220 - if (i == end()) {  
221 - return false;  
222 - }  
223 - oh = i->second;  
224 - return true;  
225 -}  
226 -  
227 -bool  
228 -QPDFNumberTreeObjectHelper::findObjectAtOrBelow(  
229 - numtree_number idx, QPDFObjectHandle& oh, numtree_number& offset)  
230 -{  
231 - auto i = find(idx, true);  
232 - if (i == end()) {  
233 - return false;  
234 - }  
235 - oh = i->second;  
236 - QIntC::range_check_subtract(idx, i->first);  
237 - offset = idx - i->first;  
238 - return true;  
239 -}  
240 -  
241 -void  
242 -QPDFNumberTreeObjectHelper::setSplitThreshold(int t)  
243 -{  
244 - m->impl->setSplitThreshold(t);  
245 -}  
246 -  
247 -std::map<QPDFNumberTreeObjectHelper::numtree_number, QPDFObjectHandle>  
248 -QPDFNumberTreeObjectHelper::getAsMap() const  
249 -{  
250 - std::map<numtree_number, QPDFObjectHandle> result;  
251 - result.insert(begin(), end());  
252 - return result;  
253 -}  
254 -  
255 -bool  
256 -QPDFNumberTreeObjectHelper::validate(bool repair)  
257 -{  
258 - return m->impl->validate(repair);  
259 -}  
libqpdf/qpdf/NNTree.hh
@@ -2,22 +2,14 @@ @@ -2,22 +2,14 @@
2 #define NNTREE_HH 2 #define NNTREE_HH
3 3
4 #include <qpdf/QPDF.hh> 4 #include <qpdf/QPDF.hh>
5 -#include <qpdf/QPDFObjectHandle.hh> 5 +#include <qpdf/QPDFObjectHandle_private.hh>
6 6
7 #include <iterator> 7 #include <iterator>
8 #include <list> 8 #include <list>
9 #include <memory> 9 #include <memory>
10 10
11 -class NNTreeDetails  
12 -{  
13 - public:  
14 - virtual std::string const& itemsKey() const = 0;  
15 - virtual bool keyValid(QPDFObjectHandle) const = 0;  
16 - virtual int compareKeys(QPDFObjectHandle, QPDFObjectHandle) const = 0;  
17 -};  
18 -  
19 class NNTreeImpl; 11 class NNTreeImpl;
20 -class NNTreeIterator 12 +class NNTreeIterator final
21 { 13 {
22 friend class NNTreeImpl; 14 friend class NNTreeImpl;
23 15
@@ -29,9 +21,20 @@ class NNTreeIterator @@ -29,9 +21,20 @@ class NNTreeIterator
29 using pointer = T*; 21 using pointer = T*;
30 using reference = T&; 22 using reference = T&;
31 23
32 - virtual ~NNTreeIterator() = default;  
33 - bool valid() const;  
34 - NNTreeIterator& operator++(); 24 + ~NNTreeIterator() = default;
  25 + // iterator can be incremented or decremented, or dereferenced. This does not imply that it
  26 + // points to a valid item.
  27 + bool
  28 + valid() const
  29 + {
  30 + return item_number >= 0;
  31 + }
  32 + NNTreeIterator&
  33 + operator++()
  34 + {
  35 + increment(false);
  36 + return *this;
  37 + }
35 NNTreeIterator 38 NNTreeIterator
36 operator++(int) 39 operator++(int)
37 { 40 {
@@ -39,7 +42,12 @@ class NNTreeIterator @@ -39,7 +42,12 @@ class NNTreeIterator
39 ++(*this); 42 ++(*this);
40 return t; 43 return t;
41 } 44 }
42 - NNTreeIterator& operator--(); 45 + NNTreeIterator&
  46 + operator--()
  47 + {
  48 + increment(true);
  49 + return *this;
  50 + }
43 NNTreeIterator 51 NNTreeIterator
44 operator--(int) 52 operator--(int)
45 { 53 {
@@ -47,8 +55,18 @@ class NNTreeIterator @@ -47,8 +55,18 @@ class NNTreeIterator
47 --(*this); 55 --(*this);
48 return t; 56 return t;
49 } 57 }
50 - reference operator*();  
51 - pointer operator->(); 58 + reference
  59 + operator*()
  60 + {
  61 + updateIValue(false);
  62 + return ivalue;
  63 + }
  64 + pointer
  65 + operator->()
  66 + {
  67 + updateIValue(false);
  68 + return &ivalue;
  69 + }
52 bool operator==(NNTreeIterator const& other) const; 70 bool operator==(NNTreeIterator const& other) const;
53 bool 71 bool
54 operator!=(NNTreeIterator const& other) const 72 operator!=(NNTreeIterator const& other) const
@@ -69,11 +87,24 @@ class NNTreeIterator @@ -69,11 +87,24 @@ class NNTreeIterator
69 int kid_number; 87 int kid_number;
70 }; 88 };
71 89
72 - NNTreeIterator(NNTreeImpl& impl); 90 + NNTreeIterator(NNTreeImpl& impl) :
  91 + impl(impl)
  92 + {
  93 + }
73 void updateIValue(bool allow_invalid = true); 94 void updateIValue(bool allow_invalid = true);
74 bool deepen(QPDFObjectHandle node, bool first, bool allow_empty); 95 bool deepen(QPDFObjectHandle node, bool first, bool allow_empty);
75 - void setItemNumber(QPDFObjectHandle const& node, int);  
76 - void addPathElement(QPDFObjectHandle const& node, int kid_number); 96 + void
  97 + setItemNumber(QPDFObjectHandle const& a_node, int n)
  98 + {
  99 + node = a_node;
  100 + item_number = n;
  101 + updateIValue();
  102 + }
  103 + void
  104 + addPathElement(QPDFObjectHandle const& a_node, int kid_number)
  105 + {
  106 + path.emplace_back(a_node, kid_number);
  107 + }
77 QPDFObjectHandle getNextKid(PathElement& element, bool backward); 108 QPDFObjectHandle getNextKid(PathElement& element, bool backward);
78 void increment(bool backward); 109 void increment(bool backward);
79 void resetLimits(QPDFObjectHandle node, std::list<PathElement>::iterator parent); 110 void resetLimits(QPDFObjectHandle node, std::list<PathElement>::iterator parent);
@@ -88,7 +119,7 @@ class NNTreeIterator @@ -88,7 +119,7 @@ class NNTreeIterator
88 value_type ivalue; 119 value_type ivalue;
89 }; 120 };
90 121
91 -class NNTreeImpl 122 +class NNTreeImpl final
92 { 123 {
93 friend class NNTreeIterator; 124 friend class NNTreeIterator;
94 125
@@ -96,13 +127,25 @@ class NNTreeImpl @@ -96,13 +127,25 @@ class NNTreeImpl
96 typedef NNTreeIterator iterator; 127 typedef NNTreeIterator iterator;
97 128
98 NNTreeImpl( 129 NNTreeImpl(
99 - NNTreeDetails const&,  
100 - QPDF&,  
101 - QPDFObjectHandle&, 130 + QPDF& qpdf,
  131 + QPDFObjectHandle& oh,
  132 + qpdf_object_type_e key_type,
102 std::function<bool(QPDFObjectHandle const&)> value_validator, 133 std::function<bool(QPDFObjectHandle const&)> value_validator,
103 - bool auto_repair = true); 134 + bool auto_repair) :
  135 + qpdf(qpdf),
  136 + oh(oh),
  137 + key_type(key_type),
  138 + items_key(key_type == ::ot_string ? "/Names" : "/Nums"),
  139 + value_valid(value_validator),
  140 + auto_repair(auto_repair)
  141 + {
  142 + }
104 iterator begin(); 143 iterator begin();
105 - iterator end(); 144 + iterator
  145 + end()
  146 + {
  147 + return {*this};
  148 + }
106 iterator last(); 149 iterator last();
107 iterator find(QPDFObjectHandle key, bool return_prev_if_not_found = false); 150 iterator find(QPDFObjectHandle key, bool return_prev_if_not_found = false);
108 iterator insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value); 151 iterator insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value);
@@ -113,27 +156,44 @@ class NNTreeImpl @@ -113,27 +156,44 @@ class NNTreeImpl
113 156
114 // Change the split threshold for easier testing. There's no real reason to expose this to 157 // Change the split threshold for easier testing. There's no real reason to expose this to
115 // downstream tree helpers, but it has to be public so we can call it from the test suite. 158 // downstream tree helpers, but it has to be public so we can call it from the test suite.
116 - void setSplitThreshold(int split_threshold); 159 + void
  160 + setSplitThreshold(int threshold)
  161 + {
  162 + split_threshold = threshold;
  163 + }
117 164
118 private: 165 private:
119 void repair(); 166 void repair();
120 iterator findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_found = false); 167 iterator findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_found = false);
121 - int withinLimits(QPDFObjectHandle const& key, QPDFObjectHandle const& node);  
122 int binarySearch( 168 int binarySearch(
123 - QPDFObjectHandle key,  
124 - QPDFObjectHandle items, 169 + QPDFObjectHandle const& key,
  170 + qpdf::Array const& items,
125 size_t num_items, 171 size_t num_items,
126 bool return_prev_if_not_found, 172 bool return_prev_if_not_found,
127 - int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item));  
128 - int compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx);  
129 - int compareKeyKid(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx); 173 + int (NNTreeImpl::*compare)(QPDFObjectHandle const& key, qpdf::Array const& arr, int item)
  174 + const) const;
  175 + int compareKeyItem(QPDFObjectHandle const& key, qpdf::Array const& items, int idx) const;
  176 + int compareKeyKid(QPDFObjectHandle const& key, qpdf::Array const& items, int idx) const;
130 void warn(QPDFObjectHandle const& node, std::string const& msg); 177 void warn(QPDFObjectHandle const& node, std::string const& msg);
131 - void error(QPDFObjectHandle const& node, std::string const& msg); 178 + void error(QPDFObjectHandle const& node, std::string const& msg) const;
  179 +
  180 + std::string const&
  181 + itemsKey() const
  182 + {
  183 + return items_key;
  184 + }
  185 + bool
  186 + keyValid(QPDFObjectHandle o) const
  187 + {
  188 + return o.resolved_type_code() == key_type;
  189 + }
  190 + int compareKeys(QPDFObjectHandle a, QPDFObjectHandle b) const;
132 191
133 - NNTreeDetails const& details;  
134 QPDF& qpdf; 192 QPDF& qpdf;
135 int split_threshold{32}; 193 int split_threshold{32};
136 QPDFObjectHandle oh; 194 QPDFObjectHandle oh;
  195 + const qpdf_object_type_e key_type;
  196 + const std::string items_key;
137 const std::function<bool(QPDFObjectHandle const&)> value_valid; 197 const std::function<bool(QPDFObjectHandle const&)> value_valid;
138 bool auto_repair{true}; 198 bool auto_repair{true};
139 size_t error_count{0}; 199 size_t error_count{0};