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 176 void setSplitThreshold(int);
177 177  
178 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 181 std::shared_ptr<Members> m;
198 182 };
... ...
include/qpdf/QPDFNumberTreeObjectHelper.hh
... ... @@ -192,24 +192,7 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper
192 192 void setSplitThreshold(int);
193 193  
194 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 197 std::shared_ptr<Members> m;
215 198 };
... ...
libqpdf/CMakeLists.txt
... ... @@ -71,8 +71,6 @@ set(libqpdf_SOURCES
71 71 QPDFJob_json.cc
72 72 QPDFLogger.cc
73 73 QPDFMatrix.cc
74   - QPDFNameTreeObjectHelper.cc
75   - QPDFNumberTreeObjectHelper.cc
76 74 QPDFObject.cc
77 75 QPDFObjectHandle.cc
78 76 QPDFObjectHelper.cc
... ...
libqpdf/NNTree.cc
... ... @@ -2,6 +2,9 @@
2 2  
3 3 #include <qpdf/NNTree.hh>
4 4  
  5 +#include <qpdf/QPDFNameTreeObjectHelper.hh>
  6 +#include <qpdf/QPDFNumberTreeObjectHelper.hh>
  7 +
5 8 #include <qpdf/QPDFObjectHandle_private.hh>
6 9 #include <qpdf/QPDF_private.hh>
7 10 #include <qpdf/QTC.hh>
... ... @@ -33,16 +36,11 @@ NNTreeImpl::warn(QPDFObjectHandle const&amp; node, std::string const&amp; msg)
33 36 }
34 37  
35 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 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 44 void
47 45 NNTreeIterator::updateIValue(bool allow_invalid)
48 46 {
... ... @@ -68,7 +66,7 @@ NNTreeIterator::updateIValue(bool allow_invalid)
68 66 ivalue.second = QPDFObjectHandle();
69 67 return;
70 68 }
71   - Array items = node.getKey(impl.details.itemsKey());
  69 + Array items = node.getKey(impl.itemsKey());
72 70 if (!std::cmp_less(item_number + 1, items.size())) {
73 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 89 if (pe.kid_number >= 0 && std::cmp_less(pe.kid_number, kids.size())) {
92 90 auto result = kids[pe.kid_number];
93 91 if (result.isDictionary() &&
94   - (result.hasKey("/Kids") || result.hasKey(impl.details.itemsKey()))) {
  92 + (result.hasKey("/Kids") || result.hasKey(impl.itemsKey()))) {
95 93 return result;
96 94 } else {
97 95 impl.warn(
... ... @@ -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 103 void
115 104 NNTreeIterator::increment(bool backward)
116 105 {
... ... @@ -121,7 +110,7 @@ NNTreeIterator::increment(bool backward)
121 110  
122 111 while (valid()) {
123 112 item_number += backward ? -2 : 2;
124   - Array items = node.getKey(impl.details.itemsKey());
  113 + Array items = node.getKey(impl.itemsKey());
125 114 if (item_number < 0 || std::cmp_greater_equal(item_number, items.size())) {
126 115 bool found = false;
127 116 setItemNumber(QPDFObjectHandle(), -1);
... ... @@ -136,10 +125,10 @@ NNTreeIterator::increment(bool backward)
136 125 }
137 126 }
138 127 if (item_number >= 0) {
139   - items = node.getKey(impl.details.itemsKey());
  128 + items = node.getKey(impl.itemsKey());
140 129 if (std::cmp_greater_equal(item_number + 1, items.size())) {
141 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 132 impl.warn(node, ("item " + std::to_string(item_number) + " has the wrong type"));
144 133 } else if (!impl.value_valid(items[item_number + 1])) {
145 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 149 }
161 150 Array kids = a_node.getKey("/Kids");
162 151 size_t nkids = kids.size();
163   - Array items = a_node.getKey(impl.details.itemsKey());
  152 + Array items = a_node.getKey(impl.itemsKey());
164 153 size_t nitems = items.size();
165 154  
166 155 bool changed = true;
... ... @@ -187,9 +176,8 @@ NNTreeIterator::resetLimits(QPDFObjectHandle a_node, std::list&lt;PathElement&gt;::ite
187 176 if (olimits.size() == 2) {
188 177 auto ofirst = olimits[0];
189 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 181 changed = false;
194 182 }
195 183 }
... ... @@ -246,7 +234,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
246 234 // Find the array we actually need to split, which is either this node's kids or items.
247 235 Array kids = to_split.getKey("/Kids");
248 236 size_t nkids = kids.size();
249   - Array items = to_split.getKey(impl.details.itemsKey());
  237 + Array items = to_split.getKey(impl.itemsKey());
250 238 size_t nitems = items.size();
251 239  
252 240 Array first_half;
... ... @@ -261,7 +249,7 @@ NNTreeIterator::split(QPDFObjectHandle to_split, std::list&lt;PathElement&gt;::iterato
261 249 first_half = items;
262 250 n = nitems;
263 251 threshold *= 2;
264   - key = impl.details.itemsKey();
  252 + key = impl.itemsKey();
265 253 } else {
266 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 283 Array new_kids;
296 284 new_kids.push_back(first_node);
297 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 287 to_split.replaceKey("/Kids", new_kids);
300 288 if (is_leaf) {
301 289 node = first_node;
... ... @@ -375,7 +363,7 @@ NNTreeIterator::insertAfter(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp;
375 363 return;
376 364 }
377 365  
378   - Array items = node.getKey(impl.details.itemsKey());
  366 + Array items = node.getKey(impl.itemsKey());
379 367 if (!items) {
380 368 impl.error(node, "node contains no items array");
381 369 }
... ... @@ -404,7 +392,7 @@ NNTreeIterator::remove()
404 392 if (!valid()) {
405 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 396 int nitems = static_cast<int>(items.size());
409 397 if (std::cmp_greater(item_number + 2, nitems)) {
410 398 impl.error(node, "found short items array while removing an item");
... ... @@ -478,7 +466,7 @@ NNTreeIterator::remove()
478 466 if (parent == path.end()) {
479 467 // We erased the very last item. Convert the root to an empty items array.
480 468 element->node.removeKey("/Kids");
481   - element->node.replaceKey(impl.details.itemsKey(), Array());
  469 + element->node.replaceKey(impl.itemsKey(), Array());
482 470 path.clear();
483 471 setItemNumber(impl.oh, -1);
484 472 return;
... ... @@ -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 480 bool
521 481 NNTreeIterator::operator==(NNTreeIterator const& other) const
522 482 {
... ... @@ -538,20 +498,6 @@ NNTreeIterator::operator==(NNTreeIterator const&amp; other) const
538 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 501 bool
556 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 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 529 int nitems = static_cast<int>(items.size());
584 530 if (nitems > 1) {
585 531 setItemNumber(a_node, first ? 0 : nitems - 2);
... ... @@ -616,33 +562,12 @@ NNTreeIterator::deepen(QPDFObjectHandle a_node, bool first, bool allow_empty)
616 562 } else {
617 563 return fail(
618 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 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 571 NNTreeImpl::iterator
647 572 NNTreeImpl::begin()
648 573 {
... ... @@ -652,12 +577,6 @@ NNTreeImpl::begin()
652 577 }
653 578  
654 579 NNTreeImpl::iterator
655   -NNTreeImpl::end()
656   -{
657   - return {*this};
658   -}
659   -
660   -NNTreeImpl::iterator
661 580 NNTreeImpl::last()
662 581 {
663 582 iterator result(*this);
... ... @@ -666,28 +585,28 @@ NNTreeImpl::last()
666 585 }
667 586  
668 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 603 int
685 604 NNTreeImpl::binarySearch(
686   - QPDFObjectHandle key,
687   - QPDFObjectHandle items,
  605 + QPDFObjectHandle const& key,
  606 + Array const& items,
688 607 size_t num_items,
689 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 611 size_t max_idx = std::bit_ceil(num_items);
693 612  
... ... @@ -726,36 +645,46 @@ NNTreeImpl::binarySearch(
726 645 }
727 646  
728 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 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 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 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 675 void
747 676 NNTreeImpl::repair()
748 677 {
749 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 681 for (auto const& [key, value]: *this) {
753 682 if (key && value) {
754 683 repl.insert(key, value);
755 684 }
756 685 }
757 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 690 NNTreeImpl::iterator
... ... @@ -783,13 +712,13 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
783 712 return end();
784 713 }
785 714 if (first_item.valid()) {
786   - if (!details.keyValid(first_item->first)) {
  715 + if (!keyValid(first_item->first)) {
787 716 error(oh, "encountered invalid key in find");
788 717 }
789 718 if (!value_valid(first_item->second)) {
790 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 722 // Before the first key
794 723 return end();
795 724 }
... ... @@ -805,14 +734,14 @@ NNTreeImpl::findInternal(QPDFObjectHandle const&amp; key, bool return_prev_if_not_fo
805 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 738 size_t nitems = items.size();
810 739 if (nitems > 1) {
811 740 int idx = binarySearch(
812 741 key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem);
813 742 if (idx >= 0) {
814 743 result.setItemNumber(node, 2 * idx);
815   - if (!result.impl.details.keyValid(result.ivalue.first)) {
  744 + if (!result.impl.keyValid(result.ivalue.first)) {
816 745 error(node, "encountered invalid key in find");
817 746 }
818 747 if (!result.impl.value_valid(result.ivalue.second)) {
... ... @@ -843,7 +772,7 @@ NNTreeImpl::insertFirst(QPDFObjectHandle const&amp; key, QPDFObjectHandle const&amp; val
843 772 auto iter = begin();
844 773 Array items(nullptr);
845 774 if (iter.node.isDictionary()) {
846   - items = iter.node.getKey(details.itemsKey());
  775 + items = iter.node.getKey(itemsKey());
847 776 }
848 777 if (!items) {
849 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 797 auto iter = find(key, true);
869 798 if (!iter.valid()) {
870 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 802 items.set(iter.item_number + 1, value);
874 803 iter.updateIValue();
875 804 } else {
... ... @@ -899,7 +828,7 @@ NNTreeImpl::validate(bool a_repair)
899 828 QPDFObjectHandle last_key;
900 829 try {
901 830 for (auto const& [key, value]: *this) {
902   - if (!details.keyValid(key)) {
  831 + if (!keyValid(key)) {
903 832 error(oh, "invalid key in validate");
904 833 }
905 834 if (!value_valid(value)) {
... ... @@ -907,7 +836,7 @@ NNTreeImpl::validate(bool a_repair)
907 836 }
908 837 if (first) {
909 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 840 error(oh, "keys are not sorted in validate");
912 841 }
913 842 last_key = key;
... ... @@ -921,3 +850,426 @@ NNTreeImpl::validate(bool a_repair)
921 850 }
922 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 2 #define NNTREE_HH
3 3  
4 4 #include <qpdf/QPDF.hh>
5   -#include <qpdf/QPDFObjectHandle.hh>
  5 +#include <qpdf/QPDFObjectHandle_private.hh>
6 6  
7 7 #include <iterator>
8 8 #include <list>
9 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 11 class NNTreeImpl;
20   -class NNTreeIterator
  12 +class NNTreeIterator final
21 13 {
22 14 friend class NNTreeImpl;
23 15  
... ... @@ -29,9 +21,20 @@ class NNTreeIterator
29 21 using pointer = T*;
30 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 38 NNTreeIterator
36 39 operator++(int)
37 40 {
... ... @@ -39,7 +42,12 @@ class NNTreeIterator
39 42 ++(*this);
40 43 return t;
41 44 }
42   - NNTreeIterator& operator--();
  45 + NNTreeIterator&
  46 + operator--()
  47 + {
  48 + increment(true);
  49 + return *this;
  50 + }
43 51 NNTreeIterator
44 52 operator--(int)
45 53 {
... ... @@ -47,8 +55,18 @@ class NNTreeIterator
47 55 --(*this);
48 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 70 bool operator==(NNTreeIterator const& other) const;
53 71 bool
54 72 operator!=(NNTreeIterator const& other) const
... ... @@ -69,11 +87,24 @@ class NNTreeIterator
69 87 int kid_number;
70 88 };
71 89  
72   - NNTreeIterator(NNTreeImpl& impl);
  90 + NNTreeIterator(NNTreeImpl& impl) :
  91 + impl(impl)
  92 + {
  93 + }
73 94 void updateIValue(bool allow_invalid = true);
74 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 108 QPDFObjectHandle getNextKid(PathElement& element, bool backward);
78 109 void increment(bool backward);
79 110 void resetLimits(QPDFObjectHandle node, std::list<PathElement>::iterator parent);
... ... @@ -88,7 +119,7 @@ class NNTreeIterator
88 119 value_type ivalue;
89 120 };
90 121  
91   -class NNTreeImpl
  122 +class NNTreeImpl final
92 123 {
93 124 friend class NNTreeIterator;
94 125  
... ... @@ -96,13 +127,25 @@ class NNTreeImpl
96 127 typedef NNTreeIterator iterator;
97 128  
98 129 NNTreeImpl(
99   - NNTreeDetails const&,
100   - QPDF&,
101   - QPDFObjectHandle&,
  130 + QPDF& qpdf,
  131 + QPDFObjectHandle& oh,
  132 + qpdf_object_type_e key_type,
102 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 143 iterator begin();
105   - iterator end();
  144 + iterator
  145 + end()
  146 + {
  147 + return {*this};
  148 + }
106 149 iterator last();
107 150 iterator find(QPDFObjectHandle key, bool return_prev_if_not_found = false);
108 151 iterator insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value);
... ... @@ -113,27 +156,44 @@ class NNTreeImpl
113 156  
114 157 // Change the split threshold for easier testing. There's no real reason to expose this to
115 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 165 private:
119 166 void repair();
120 167 iterator findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_found = false);
121   - int withinLimits(QPDFObjectHandle const& key, QPDFObjectHandle const& node);
122 168 int binarySearch(
123   - QPDFObjectHandle key,
124   - QPDFObjectHandle items,
  169 + QPDFObjectHandle const& key,
  170 + qpdf::Array const& items,
125 171 size_t num_items,
126 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 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 192 QPDF& qpdf;
135 193 int split_threshold{32};
136 194 QPDFObjectHandle oh;
  195 + const qpdf_object_type_e key_type;
  196 + const std::string items_key;
137 197 const std::function<bool(QPDFObjectHandle const&)> value_valid;
138 198 bool auto_repair{true};
139 199 size_t error_count{0};
... ...